使用ffprobe处理内存中的mp4文件

Dealing with mp4 files in memory with ffprobe

在上传文件的过程中,我想对内存中的内容进行操作,而不是将文件保存到磁盘中。为此,我使用 FFmpegIFormFile 转换为 MemoryStream 并且进一步使用 FFmpeg 更改内容并将结果再次输出到管道以进行进一步处理。除了无法处理某些文件外,整个工作正常。

同样,我在此过程中处理的不是直接文件,而是它们的内存流表示。

当我使用 ffprobe 检查第一个文件的详细信息时,我得到了

format": {
        "filename": "file_example_MP4_480_1_5MG.mp4",
        "nb_streams": 2,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "30.526667",
        "size": "1570024",
        "bit_rate": "411449",
        "probe_score": 100,
        "tags": {
            "major_brand": "mp42",
            "minor_version": "0",
            "compatible_brands": "mp42mp41isomavc1",
            "creation_time": "2015-08-07T09:13:02.000000Z"
        }
    }

此文件的内存流表示作为命令中的输入管道可以按预期处理和输出。

ffmpeg -f mp4 -i pipe:0 -c:v libx264 -movflags frag_keyframe+empty_moov -s 800x600 -f mpeg pipe:1

错误:用完整的错误消息更新

Consider increasing the value for the 'analyzeduration' and 'probesize' options Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:0': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.45.100 Duration: 00:00:46.64, start: 0.000000, bitrate: N/A Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 960x400, 2882 kb/s, SAR 1:1 DAR 12:5, 23.98 fps, 23.98 tbr, 24k tbn, 48k tbc (default) Metadata: handler_name : GPAC ISO Video Handler Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 123 kb/s (default) Metadata: handler_name : GPAC ISO Audio Handler Stream mapping: Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264)) Stream #0:1 -> #0:1 (aac (native) -> mp2 (native)) [mov,mp4,m4a,3gp,3g2,mj2 @ 0x55dfc34c2100] stream 0, offset 0x30: partial file pipe:0: Invalid data found when processing input Cannot determine format of input stream 0:0 after EOF Error marking filters as finished Conversion failed!

我的问题是:我需要在命令中更改什么才能成功完成第二个文件的处理?

更新:

我尝试使用 -analyzeduration 2147483647 -probesize 2147483647-probesize 4G -analyzeduration 10G,但它仍然产生相同的错误。

ffmpeg -analyzeduration 2147483647 -probesize 2147483647 -f mp4 -i pipe:0 -c:v libx264 -movflags frag_keyframe+empty_moov -s 800x600 -f mpeg pipe:1

第二次更新: 当我在输入前使用 -pix_fmt yuv420p 时,我得到 Option pixel_format not found。在 -i 之后使用作为输出参数不会改变任何事情。

这是关于我正在处理的 mp4 的完整 ffprobe 信息

{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "High",
            "codec_type": "video",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1280,
            "height": 720,
            "coded_width": 1280,
            "coded_height": 720,
            "closed_captions": 0,
            "film_grain": 0,
            "has_b_frames": 2,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "16:9",
            "pix_fmt": "yuv420p",
            "level": 31,
            "chroma_location": "left",
            "field_order": "progressive",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "id": "0x1",
            "r_frame_rate": "24000/1001",
            "avg_frame_rate": "24000/1001",
            "time_base": "1/24000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 4393389,
            "duration": "183.057875",
            "bit_rate": "3001682",
            "bits_per_raw_sample": "8",
            "nb_frames": "4389",
            "extradata_size": 45,
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0
            },
            "tags": {
                "language": "und",
                "handler_name": "VideoHandler",
                "vendor_id": "[0][0][0][0]"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "id": "0x2",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/48000",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 8790000,
            "duration": "183.125000",
            "bit_rate": "128606",
            "nb_frames": "8585",
            "extradata_size": 5,
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0
            },
            "tags": {
                "language": "eng",
                "handler_name": "Stereo",
                "vendor_id": "[0][0][0][0]"
            }
        }
    ],
    "format": {
        "filename": "sample_1280x720_surfing_with_audio.mp4",
        "nb_streams": 2,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "183.147000",
        "size": "71753110",
        "bit_rate": "3134230",
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf58.45.100"
        }
    }
}

此行为是由 MP4 的 MOOV 原子的位置引起的,必须先读取它才能对容器进行多路分解。当 MOOV atom 位于文件末尾时,FFmpeg 需要提前读取到文件末尾以检索该 atom 然后 seek 回到开头处理文件。问题在于:管道不允许查找。因此,OP 的错误。

我已经在 Python/Windows 中验证了 FFmpeg 5.0 的行为如下。

首先,读入一个non-working MP4:

mp4file = 'test.mp4'
with open(mp4file,'rb') as f:
    b = f.read()

print(len(b),b.find(b'moov'))

此打印

423691, 421513

表示moov原子放在文件末尾。

然后,我将 moov 原子移动到开头

ffmpeg -i test.mp4 -codec copy -movflags faststart fasttest.mp4

当你运行这个时,FFmpeg 日志将包含:

[mp4 @ 00000148fe2eba40] Starting second pass: moving the moov atom to the beginning of the file

现在,我重复搜索 moov 原子:

mp4file = 'fasttest.mp4'
with open(mp4file,'rb') as f:
    b1 = f.read()

print(len(b1),b1.find(b'moov'))

此打印

423711, 36

并将 b1 字节输入 FFmpeg 运行ning 作为子进程与管道标准输入工作:

sp.run(['ffmpeg','-i','-','-f','mp4','-codec','copy','NUL','-y'], input=b1)

因此,不幸的是,似乎唯一的方法是在输入内存中筛选 moov 原子的位置,并引用不兼容的 mp4 文件。如果 .net 提供 memory-mapped 文件支持,您可以将数据作为这样的文件放置,FFmpeg 可以使用它。否则,您可能需要求助于将数据转储到临时文件。

P.S., 如果您想确切了解这种行为是如何发生的,您可以查看 the mov.c source code