File size: 2,178 Bytes
c12eb19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""科普视频成片完整技术审查(比 ffprobe 文件头检查更严格)。

完整解码所有帧并检查音频存在。该脚本不判断科学事实是否正确。
"""
from __future__ import annotations
import argparse,json,subprocess,sys
from pathlib import Path

def run(path:Path,expected_width=1080,expected_height=1920,expected_fps=30):
    if not path.is_file():raise FileNotFoundError(path)
    obj=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_format','-show_streams','-of','json',str(path)],text=True))
    video=next((x for x in obj['streams'] if x.get('codec_type')=='video'),None)
    audio=next((x for x in obj['streams'] if x.get('codec_type')=='audio'),None)
    assert video and audio,'Missing audio or video'
    actual_rate=video.get('avg_frame_rate',video.get('r_frame_rate','0/1'))
    numerator,denominator=map(float,actual_rate.split('/'))
    assert denominator and abs(numerator/denominator-expected_fps)<.12
    assert video.get('width')==expected_width and video.get('height')==expected_height
    assert video.get('codec_name')=='h264' and audio.get('codec_name')=='aac'
    # Prevent writing garbage to stdout or disk: decode both streams into null sink.
    proc=subprocess.run(['ffmpeg','-hide_banner','-nostdin','-v','error','-xerror','-i',str(path),'-f','null','-'],capture_output=True,text=True)
    if proc.returncode:raise RuntimeError('Full decode failed: '+proc.stderr[-1500:])
    return {'file':str(path),'duration':round(float(obj['format']['duration']),3),'fps':actual_rate,'video_codec':video['codec_name'],'audio_codec':audio['codec_name'],'whole_movie_decode_ok':True}
if __name__=='__main__':
    p=argparse.ArgumentParser();p.add_argument('video');p.add_argument('--width',type=int,default=1080);p.add_argument('--height',type=int,default=1920);p.add_argument('--fps',type=int,default=30);a=p.parse_args()
    try:
        report=run(Path(a.video),a.width,a.height,a.fps)
        print(json.dumps(report,ensure_ascii=False,indent=2))
    except Exception as e:
        print(json.dumps({'qa_pass':False,'error':str(e)},ensure_ascii=False));sys.exit(2)