FFmpeg 媒体处理 · FFmpeg
成片深度质检NEW
📌 这是什么:只读质检:把成片完整解码两遍(视频一遍、音频一遍)跑检测滤镜,产出一份小体积 JSON 报告——黑帧、定格(画面冻结)、静音三类问题区间 + EBU R128 响度指标。不生成任何媒体文件,原片不变。
💡 什么时候用:成片交付前做自动化验收时用:拿报告里的区间和响度值判定 pass / warning / block,替代人工全片过一遍。检测阈值由服务端固定(黑帧 d=0.5、定格 d=2、静音 -45dB/1.5s),不开放调整——阈值可调会让报告「完全合法但结论错误」。除 inputUrl 外不接受其它参数,多传的键被忽略。
接口地址
| 用途 | 方法 | 路径 |
|---|---|---|
| 提交任务 | POST | /v1/ffmpeg/qc |
| 查询结果 | GET | /v1/ffmpeg/info?id={taskId} |
⏱ 建议轮询时间:建议每 3~5 秒轮询一次(响应含 progress 0→100),直到 status = succeeded / failed。status = queued 表示已入队、排队等待执行(尚未扣费),继续轮询即可。单任务超时 300 秒、超时退款。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| inputUrl | string | 必填 | 输入媒体 URL,须为受信白名单域名(平台 CDN 或受信来源 CDN);可先用 upload 上传获取,或直接用已在受信 CDN 上的素材 URL |
| webhookUrl | string | — | 完成回调地址(可选) |
请求示例
curl -X POST https://api.cqtai.com/v1/ffmpeg/qc \
-H 'Authorization: Bearer <API_KEY>' \
-H 'Content-Type: application/json' \
-d '{ "inputUrl": "https://cdn.novapi.ai/veo31/1788164205419_de868f9d69d445baaf577d42e87a236f.mp4" }'
# -> { "code":200, "data":"<taskId>" }响应示例
{
"code": 200,
"msg": "success",
"data": {
"taskId": "<taskId>",
"operation": "qc",
"status": "succeeded",
"progress": 100,
"resultUrl": "https://cdn.novapi.ai/ffmpeg/qc/1756800000000_0f1e2d3c4b5a.json",
"costPoints": 10,
"errorMsg": ""
}
}质检报告 JSON
resultUrl 指向报告 JSON(Content-Type application/json,体积 ≤ 1 MiB,公网直链无跳转,长期保留)。顶层 10 个字段一个都不会缺——序列化不省略空值:段落为空是 [](不是 null),响度取不到才是 null。progress 在视频分析结束时到 45、音频结束时到 90、上传落库后 100。
| 字段 | 类型 | 说明 |
|---|---|---|
| schemaVersion | int | 恒为 1;阈值 / 字段语义变更时才会升版 |
| analyzedVideo | bool | 视频那一路是否跑完(跑完 ≠ 没查出问题) |
| analyzedAudio | bool | 音频那一路是否跑完;无音轨 / 该路失败为 false |
| blackSegments | object[] | 黑帧区间(blackdetect d=0.5 pix_th=0.10),空则 [] |
| freezeSegments | object[] | 定格 / 画面冻结区间(freezedetect n=-50dB d=2),空则 [] |
| silenceSegments | object[] | 静音区间(silencedetect n=-45dB d=1.5),空则 [] |
| integratedLufs | number | null | 整体响度(LUFS,带符号,通常为负);取不到为 null |
| loudnessRangeLu | number | null | 响度范围(LU);取不到为 null |
| truePeakDbfs | number | null | 真峰值(dBFS,带符号,削波时可能 ≥ 0);取不到为 null |
| error | string | null | 降级说明(英文,多条以 "; " 拼接)。⚠ error 非 null 不代表任务失败 |
| 段落形状 | { startMs, endMs, durationMs } | 三类段落统一形状,全部为整数毫秒、以成片自身时间轴从 0 起算。⚠ 跨到片尾未闭合的段:endMs 与 durationMs 都是 null(不是 0、不会丢段)——结尾长静音靠这条才不会被当噪声忽略。段落可能相邻或重叠,服务端不做区间归并,是否合并由调用方决定 |
{
"schemaVersion": 1,
"analyzedVideo": true,
"analyzedAudio": true,
"blackSegments": [
{ "startMs": 121500, "endMs": 122510, "durationMs": 1010 }
],
"freezeSegments": [],
"silenceSegments": [
{ "startMs": 121480, "endMs": 124500, "durationMs": 3020 },
{ "startMs": 331000, "endMs": null, "durationMs": null }
],
"integratedLufs": -18.4,
"loudnessRangeLu": 7.2,
"truePeakDbfs": -1.1,
"error": null
}部分失败仍算成功:视频、音频两路独立跑、独立成败。只挂一路时任务仍是 succeeded,失败那一路的段落为 []、对应 analyzed* 为 false,error 点名原因(如 input has no audio track / video analysis failed: ...)——半份真报告好过一份编出来的完整报告。两路都失败才是 failed 并退款。
上限与截断:每类段落最多 500 条,超出截断并在 error 注明 <类>Segments truncated to 500;报告超 1 MiB 则丢掉全部段落、保留 analyzed* 与响度值,error 追加 report exceeded 1048576 bytes; all segments dropped。
超时:视频、音频两路各自享有完整超时预算(默认 300 秒,不共享)。单路超时按部分失败处理,两路都超时才失败退款。