import os import subprocess import ffmpeg def transcode_video(input_path: str, output_dir: str, orig_width: int, orig_height: int): os.makedirs(output_dir, exist_ok=True) def get_video_bitrate(): try: probe = ffmpeg.probe(input_path) for stream in probe["streams"]: if stream["codec_type"] == "video": return int(stream.get("bit_rate", "800000")) except: pass return 800000 def get_audio_bitrate(): try: probe = ffmpeg.probe(input_path) for stream in probe["streams"]: if stream["codec_type"] == "audio": return int(stream.get("bit_rate", "64000")) except: pass return 64000 def has_audio(): try: probe = ffmpeg.probe(input_path) return any(s["codec_type"] == "audio" for s in probe.get("streams", [])) except: return False def get_fps(): try: probe = ffmpeg.probe(input_path) v_stream = next(s for s in probe["streams"] if s["codec_type"] == "video") return eval(v_stream["r_frame_rate"]) except: return 30 renditions = [ {"name": "1080p", "width": 1920, "height": 1080, "crf": "22", "audio_bitrate": 192000}, {"name": "720p", "width": 1280, "height": 720, "crf": "24", "audio_bitrate": 128000}, {"name": "480p", "width": 854, "height": 480, "crf": "26", "audio_bitrate": 96000}, {"name": "360p", "width": 640, "height": 360, "crf": "28", "audio_bitrate": 64000}, ] renditions = [r for r in renditions if r["width"] <= orig_width and r["height"] <= orig_height] if not renditions: renditions = [{ "name": "source", "width": orig_width, "height": orig_height, "crf": "28", "audio_bitrate": 64000 }] video_bitrate = get_video_bitrate() audio_bitrate = get_audio_bitrate() audio_exists = has_audio() fps = get_fps() gop_size = int(fps * 4) video_split = f"[0:v]split={len(renditions)}" + "".join([f"[v{i}]" for i in range(len(renditions))]) scale_filters = [f"[v{i}]scale=w={r['width']}:h={r['height']}[v{i}out]" for i, r in enumerate(renditions)] filter_parts = [video_split] + scale_filters map_cmds = [] var_stream_map = [] for idx, r in enumerate(renditions): map_cmds.extend([ "-map", f"[v{idx}out]", f"-c:v:{idx}", "libx264", "-crf", str(r["crf"]), "-preset", "medium", "-g", str(gop_size), "-keyint_min", str(gop_size), "-sc_threshold", "0" ]) if audio_exists: safe_ab = min(audio_bitrate, r["audio_bitrate"]) map_cmds.extend([ "-map", "0:a:0", f"-c:a:{idx}", "aac", f"-b:a:{idx}", str(safe_ab), "-ac", "2", "-ar", "44100" ]) var_stream_map.append(f"v:{idx},a:{idx}") else: var_stream_map.append(f"v:{idx}") ffmpeg_cmd = [ "ffmpeg", "-y", "-i", input_path, "-filter_complex", ";".join(filter_parts), *map_cmds, "-f", "hls", "-hls_time", "4", "-hls_playlist_type", "vod", "-hls_flags", "independent_segments+single_file", "-hls_segment_type", "mpegts", "-hls_segment_filename", os.path.join(output_dir, "stream_%v.ts"), "-master_pl_name", "master.m3u8", "-var_stream_map", " ".join(var_stream_map), "-avoid_negative_ts", "make_zero", "-movflags", "+faststart", os.path.join(output_dir, "stream_%v.m3u8") ] print("[FFMPEG FULL CMD]", " ".join(ffmpeg_cmd)) subprocess.run(ffmpeg_cmd, check=True)