feat: 上传时自动检测 HEVC 编码并转码为 1080p H.264
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 55s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 28s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m30s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m51s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 652h34m59s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 652h34m57s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 652h35m48s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 652h35m52s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 652h35m54s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 652h35m55s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 652h35m55s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 653h8m44s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 653h9m35s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 653h9m40s

解决 4K HEVC 素材在浏览器 WebCodecs 硬件解码时输出黑帧的问题。

改动范围:仅 ingest.py(+88 行)
- metadata 提取后检测 codec 是否为 hevc/h265/hvh1
- HEVC 素材自动转码:H.264 CRF18 + 1080p + yuv420p + faststart
- 转码产物上传 OSS,storage_key 指向 _h264.mp4
- 转码后重新提取 metadata(新 codec/size/分辨率)
- 非 HEVC 素材(H.264 等)不做任何处理
- 转码失败降级使用原始文件,不阻塞上传流程
- 超时 300s,临时文件 finally 清理
This commit is contained in:
CI Bot
2026-08-20 10:50:06 +08:00
parent 355431b867
commit 773dafe5a8
+88
View File
@@ -233,6 +233,94 @@ def ingest_asset(job_id: str) -> dict:
job_id,
thumb_err,
)
# ── HEVC 自动转码为 1080p H.264 ──────────────────────────────
# 浏览器 WebCodecs 硬件解码 HEVC 输出黑帧,上传时自动转码
# 失败时降级使用原始文件,不阻塞上传流程
if (
media_type == "video"
and extract_success
and local_file
and local_file.exists()
):
codec = (metadata.get("codec") or "").lower()
if codec in ("hevc", "h265", "hvh1"):
logger.info(
"检测到 HEVC 编码 (codec=%s),启动转码: job_id=%s",
codec, job_id,
)
_tc_tmp = None
try:
_tc_tmp = Path(tempfile.mktemp(suffix="_h264.mp4"))
_cmd = [
"ffmpeg", "-y",
"-i", str(local_file),
"-c:v", "libx264",
"-preset", "fast",
"-crf", "18",
"-vf",
"scale='if(gt(ih,1080),-2,iw)':'if(gt(ih,1080),1080,ih)'",
"-pix_fmt", "yuv420p",
"-c:a", "aac",
"-b:a", "128k",
"-movflags", "+faststart",
str(_tc_tmp),
]
_proc = subprocess.run(
_cmd, capture_output=True, text=True, timeout=300,
)
if (
_proc.returncode == 0
and _tc_tmp.exists()
and _tc_tmp.stat().st_size > 0
):
from video_processing.oss_helpers import upload_to_oss
_p = Path(job.storage_key)
_new_key = str(
_p.parent / (_p.stem + "_h264" + _p.suffix)
)
_url = upload_to_oss(_tc_tmp, _new_key)
if _url:
job.storage_key = _new_key
metadata, extract_success = (
extract_media_metadata(
str(_tc_tmp), media_type,
)
)
logger.info(
"HEVC→H.264 转码完成: job_id=%s key=%s",
job_id, _new_key[:80],
)
else:
logger.warning(
"转码文件上传 OSS 失败,使用原始文件: job_id=%s",
job_id,
)
else:
_tail = (
_proc.stderr[-300:] if _proc.stderr else ""
)
logger.warning(
"FFmpeg 转码失败 rc=%s,降级原始文件: job_id=%s",
_proc.returncode, job_id,
)
except subprocess.TimeoutExpired:
logger.warning(
"FFmpeg 转码超时 (300s),降级原始文件: job_id=%s",
job_id,
)
except Exception as _e:
logger.warning(
"HEVC 转码异常(降级原始文件): job_id=%s err=%s",
job_id, _e,
)
finally:
if _tc_tmp and _tc_tmp.exists():
try:
_tc_tmp.unlink()
except OSError:
pass
finally:
if local_file and local_file.exists():
try: