You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
import ffmpeg
|
|
import subprocess
|
|
import json
|
|
import os
|
|
from collections import Counter
|
|
|
|
def is_av1(filepath):
|
|
"""Check if a video file is already AV1-encoded."""
|
|
try:
|
|
probe = ffmpeg.probe(filepath)
|
|
for stream in probe['streams']:
|
|
if stream['codec_type'] == 'video' and 'codec_name' in stream:
|
|
if stream['codec_name'] == 'av1':
|
|
return True
|
|
except ffmpeg.Error as e:
|
|
print(f"Error probing {filepath}: {e}")
|
|
return False
|
|
|
|
|
|
def get_fps(filepath):
|
|
"""Get the frames per second (FPS) of the input video using ffmpeg.probe."""
|
|
try:
|
|
probe = ffmpeg.probe(filepath)
|
|
video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None)
|
|
if video_stream and 'r_frame_rate' in video_stream:
|
|
fps_str = video_stream['r_frame_rate'] # e.g. "30/1", "25/1"
|
|
num, den = map(int, fps_str.split('/'))
|
|
return num / den
|
|
except ffmpeg.Error as e:
|
|
print(f"Error getting FPS for {filepath}: {e}")
|
|
return None
|
|
|
|
def get_video_info(filepath):
|
|
"""
|
|
Returns dict:
|
|
{ 'width': int, 'height': int, 'bitrate': int, 'fps': float }
|
|
- bitrate is Kbps (rounded down)
|
|
- uses stream bit_rate, else format bit_rate, else computed
|
|
"""
|
|
cmd = [
|
|
"ffprobe","-v","error",
|
|
"-select_streams","v:0",
|
|
"-show_entries","stream=width,height,bit_rate,r_frame_rate",
|
|
"-show_entries","format=bit_rate,duration",
|
|
"-of","json", filepath
|
|
]
|
|
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
if r.returncode:
|
|
return {"width": 0, "height": 0, "bitrate": 0, "fps": 0.0}
|
|
|
|
try:
|
|
d = json.loads(r.stdout or "{}")
|
|
s = (d.get("streams") or [{}])[0]
|
|
f = d.get("format") or {}
|
|
|
|
width = int(s.get("width") or 0)
|
|
height = int(s.get("height") or 0)
|
|
|
|
# fps (r_frame_rate like "30000/1001")
|
|
fps = 0.0
|
|
rfr = s.get("r_frame_rate")
|
|
if rfr and rfr != "0/0":
|
|
try:
|
|
num, den = rfr.split("/")
|
|
num = float(num); den = float(den)
|
|
fps = (num/den) if den else 0.0
|
|
except Exception:
|
|
pass
|
|
|
|
# bitrate in bps → prefer stream, fallback to format, else compute
|
|
br_bps = s.get("bit_rate") or f.get("bit_rate")
|
|
if not br_bps:
|
|
try:
|
|
dur = float(f.get("duration") or 0)
|
|
if dur > 0:
|
|
br_bps = int(os.path.getsize(filepath) * 8 / dur)
|
|
except Exception:
|
|
br_bps = 0
|
|
br_kbps = int(int(br_bps or 0) / 1000)
|
|
|
|
return {"width": width, "height": height, "bitrate": br_kbps, "fps": fps}
|
|
except Exception:
|
|
return {"width": 0, "height": 0, "bitrate": 0, "fps": 0.0}
|
|
|
|
def get_target_resolution(group):
|
|
"""Collect the most common resolution from the group's videos."""
|
|
resolutions = []
|
|
for v in group:
|
|
info = get_video_info(v["filepath"])
|
|
w, h = info["width"], info["height"]
|
|
if w and h:
|
|
resolutions.append((w, h))
|
|
if not resolutions:
|
|
return (1280, 720)
|
|
return Counter(resolutions).most_common(1)[0][0]
|
|
|
|
def get_target_bitrate(width, height):
|
|
"""Your existing function to choose a bitrate based on resolution."""
|
|
resolutions = {(854, 480): 1000,(1280, 720): 1500,(1920, 1080): 3000,(2560, 1440): 5000,(3840, 2160): 12000}
|
|
for res, bitrate in resolutions.items():
|
|
if width <= res[0] and height <= res[1]:
|
|
return bitrate
|
|
return 2500 |