使用ffprobe处理内存中的mp4文件
Dealing with mp4 files in memory with ffprobe
在上传文件的过程中,我想对内存中的内容进行操作,而不是将文件保存到磁盘中。为此,我使用 FFmpeg
将 IFormFile
转换为 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
在上传文件的过程中,我想对内存中的内容进行操作,而不是将文件保存到磁盘中。为此,我使用 FFmpeg
将 IFormFile
转换为 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