You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FFmpeg在AWS Lambda压缩大视频时输出截断、0字节问题求助

问题根因
  • 大文件截断核心原因:第一版代码通过subprocess.PIPE把FFmpeg的输出全存在内存里,3008MB的Lambda内存扣除运行时、依赖、FFmpeg进程本身占用后,留给管道缓存的空间不足,输出视频流超过剩余内存阈值就会被强制截断,加-nostdin参数解决不了内存上限问题。
  • 移除-f mpegts就出0字节文件的原因:MP4容器编码完成后需要回写moov元数据头到文件开头,流式管道不支持随机写入,FFmpeg没法完成回写操作,最终输出的就是无有效数据的损坏文件;mpegts是面向流式传输设计的容器,不需要回写文件头,所以管道输出能拿到可读内容,但依然受内存缓存限制。
  • 第二版代码提示output.mp4不存在的原因:直接把S3对象key拼接到shell命令字符串里用os.system调用,key里带的空格、特殊符号会导致shell解析命令失败,FFmpeg根本没有正常启动;同时你没有打印FFmpeg的运行日志,看不到命令执行失败的报错。
  • 日志无报错的原因:代码没有捕获、打印FFmpeg执行时的stderr输出,所有转码过程的错误信息都存在进程内存里没输出到CloudWatch,自然只能看到函数启动、结束的基础日志。
  • 第一版代码的FFmpeg命令还存在参数错误:漏写了视频编码器指定参数-c:v,直接写libx264会被FFmpeg识别为输入路径,导致编码逻辑异常。
修复步骤
  • 放弃用内存管道接FFmpeg输出的方案,直接读写Lambda的/tmp临时目录存储输入、输出文件;当前2048MB临时存储可支持处理单文件总大小(输入+输出)不超过2GB的场景,处理更大文件时直接在Lambda配置中将临时存储上限调整到10240MB即可。
  • 所有外部命令调用统一用subprocess.run传参数数组,不要用字符串拼接+os.system的方式,避免特殊字符转义失败。
  • 必须在代码中打印FFmpeg的stderr输出到CloudWatch,转码后检查进程返回码,非0返回码直接抛错,避免静默失败。
  • 将Lambda函数超时时间调整到15分钟(Lambda单实例最长执行时长),内存配置拉到10240MB时会分配满额vCPU,转码速度比3008MB快2-3倍,可大幅降低超时概率。
  • 输出文件上传S3成功后再删除源文件,避免转码失败导致源文件丢失;每次执行结束清理/tmp下的临时文件,避免残留文件占满磁盘空间。
修正后可直接运行的代码
import json
import os
import subprocess
import boto3

S3_DESTINATION_BUCKET = "rw-video-out"
os.chdir('/tmp')

def lambda_handler(event, context):
    s3_source_bucket = event['Records'][0]['s3']['bucket']['name']
    s3_source_key = event['Records'][0]['s3']['object']['key']

    s3_source_basename = os.path.splitext(os.path.basename(s3_source_key))[0]
    local_input_path = f"/tmp/input_{context.aws_request_id}"
    local_output_path = f"/tmp/output_{context.aws_request_id}.mp4"
    s3_destination_key = f"{s3_source_basename}-comp.mp4"

    s3_client = boto3.client('s3')
    # 下载源文件到本地临时存储
    s3_client.download_file(s3_source_bucket, s3_source_key, local_input_path)

    # 构造FFmpeg参数数组,避免shell转义问题
    ffmpeg_cmd = [
        "/opt/bin/ffmpeg",
        "-y",
        "-i", local_input_path,
        "-c:v", "libx264",
        "-preset", "fast",
        "-crf", "28",
        "-c:a", "copy",
        "-movflags", "+faststart",
        local_output_path
    ]

    # 执行转码,捕获输出
    proc = subprocess.run(
        ffmpeg_cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        check=False
    )
    # 打印FFmpeg日志到CloudWatch
    print("FFmpeg run log:", proc.stderr.decode('utf-8', errors='ignore'))

    # 校验转码结果
    if proc.returncode != 0:
        raise RuntimeError(f"FFmpeg transcode failed, exit code: {proc.returncode}")
    if not os.path.exists(local_output_path) or os.path.getsize(local_output_path) == 0:
        raise RuntimeError("Transcode output file is invalid")

    # 上传结果到目标桶
    s3_client.upload_file(local_output_path, S3_DESTINATION_BUCKET, s3_destination_key)

    # 清理临时文件
    for f in [local_input_path, local_output_path]:
        if os.path.exists(f):
            os.remove(f)

    # 确认上传成功后删除源文件
    s3_client.delete_object(Bucket=s3_source_bucket, Key=s3_source_key)

    return {
        'statusCode': 200,
        'body': json.dumps('Processing complete successfully')
    }
额外优化建议
  • 如果需要处理单文件超过10GB的场景,给Lambda挂载EFS文件系统作为存储目录,不受临时存储10GB上限限制。
  • 可以给FFmpeg命令添加-progress pipe:1参数输出转码进度,避免长时间无日志输出被Lambda判定为僵死进程终止。
  • 配置S3事件触发时增加后缀过滤,避免处理已经压缩过的输出文件导致循环触发。

内容的提问来源于stack exchange,提问作者Joesph Stah Lynn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 14:36:16