"""Bounded, tiled Real-ESRGAN video inference. No CPU fallback or frame interpolation.""" import argparse, json, subprocess, sys, os, hashlib from fractions import Fraction from pathlib import Path import numpy as np import torch from PIL import Image sys.path.insert(0, str(Path(__file__).parent / 'vendor')) from srvgg_arch import SRVGGNetCompact def probe(file): return json.loads(subprocess.check_output(['ffprobe','-v','error','-protocol_whitelist','file,pipe','-show_streams','-show_format','-of','json',file], timeout=30)) def main(): p=argparse.ArgumentParser() for name in ['input','output','model','resolution']: p.add_argument('--'+name, required=True) p.add_argument('--tile', type=int, default=192) a=p.parse_args() if not torch.cuda.is_available(): raise RuntimeError('CUDA unavailable; refusing CPU fallback') torch.set_num_threads(4) info=probe(a.input); v=next(s for s in info['streams'] if s['codec_type']=='video') if not any(name in info['format']['format_name'].split(',') for name in ['mov','mp4','matroska','webm','avi','flv','mpegts']): raise ValueError('Unsupported video container') rotation=next((int(side['rotation'])%360 for side in v.get('side_data_list',[]) if 'rotation' in side),0) if rotation%90: raise ValueError('Unsupported rotation angle') w,h=int(v['width']),int(v['height']) if rotation in [90,270]: w,h=h,w fps=Fraction(v['avg_frame_rate']) duration=float(info['format']['duration']) if not 03840*2160: raise ValueError('Input exceeds 120 s / 60 fps / 4K limits') # Reject variable frame rate rather than silently altering timing/audio sync. frame_times=subprocess.check_output(['ffprobe','-v','error','-protocol_whitelist','file,pipe','-select_streams','v:0','-show_entries','frame=best_effort_timestamp_time','-of','csv=p=0',a.input],timeout=60).decode().splitlines() timestamps=[float(s.split(',')[0]) for s in frame_times if s.split(',')[0].strip()] if any(abs((b-c)-float(1/fps))>max(.002,float(1/fps)*.05) for c,b in zip(timestamps,timestamps[1:])): raise ValueError('Variable frame rate video must be converted to constant frame rate first') long,short={'1080P':(1920,1080),'2K':(2560,1440),'4K':(3840,2160)}[a.resolution] scale=min((long if w>=h else short)/w,(short if w>=h else long)/h) if not 1.2: raise RuntimeError('Output timing mismatch') if any(s['codec_type']=='audio' for s in actual['streams']) != any(s['codec_type']=='audio' for s in info['streams']): raise RuntimeError('Output audio stream missing') os.unlink(temp) print(json.dumps({'result':{'width':ow,'height':oh,'durationSeconds':float(actual['format']['duration']),'fps':float(fps),'frames':count,'audio':any(s['codec_type']=='audio' for s in actual['streams']),'engine':'Real-ESRGAN','model':'realesr-general-x4v3'}}),flush=True) if __name__=='__main__': main()