在AWS Lambda上用FFmpeg处理Mobile Safari录制视频仅输出约0.5秒
Mobile Safari录制视频转码后仅约0.5秒的原因及解决方法
问题描述
在AWS Lambda上使用Node.js结合fluent-ffmpeg处理视频转码时,多数设备/浏览器录制的视频均可正常转码,但处理Mobile Safari录制的视频时,无论输入时长多少,输出视频长度始终约为0.5秒。
转码代码(TypeScript)
import * as ffmpeg from 'fluent-ffmpeg'; public static StandardizeVideoFormat( inputSignedUrl: string, filename: string, ): Promise<string> { const localFilePath = `/tmp/${Date.now()}_${filename}.mp4`; return new Promise((resolve, reject) => { ffmpeg(inputSignedUrl) .outputOptions([ '-crf', '18', '-vcodec', 'libx264', '-codec:a', 'aac', '-preset', 'medium', ]) .output(localFilePath) .on('start', function (commandLine) { console.log('Spawned Ffmpeg with command: ' + commandLine); }) .on('error', function (err) { reject(err); }) .on('progress', function (progress) { console.log('progress', progress); }) .on('end', function () { resolve(localFilePath); }) .run(); }); }
输入文件FFprobe元数据
{ streams: [ { index: 0, codec_name: 'h264', codec_long_name: 'H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10', profile: 'Baseline', codec_type: 'video', codec_tag_string: 'avc1', codec_tag: '0x31637661', width: 1920, height: 1080, coded_width: 1920, coded_height: 1080, closed_captions: 0, film_grain: 0, has_b_frames: 0, sample_aspect_ratio: 'N/A', display_aspect_ratio: 'N/A', pix_fmt: 'yuvj420p', level: 40, color_range: 'pc', color_space: 'unknown', color_transfer: 'unknown', color_primaries: 'unknown', chroma_location: 'left', field_order: 'progressive', refs: 1, is_avc: 'true', nal_length_size: 4, id: '0x1', r_frame_rate: '30000/1001', avg_frame_rate: '60000/1991', time_base: '1/600', start_pts: 26, start_time: 0.043333, duration_ts: 4008, duration: 6.68, bit_rate: 10175170, max_bit_rate: 'N/A', bits_per_raw_sample: 8, nb_frames: 'N/A', nb_read_frames: 'N/A', nb_read_packets: 'N/A', extradata_size: 31, side_data_type: 'Display Matrix', displaymatrix: '', rotation: '-90', tags: [Object], disposition: [Object] }, { 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: 1, channel_layout: 'mono', bits_per_sample: 0, initial_padding: 0, id: '0x2', r_frame_rate: '0/0', avg_frame_rate: '0/0', time_base: '1/48000', start_pts: 0, start_time: 0, duration_ts: 319488, duration: 6.656, bit_rate: 181302, max_bit_rate: 'N/A', bits_per_raw_sample: 'N/A', nb_frames: 'N/A', nb_read_frames: 'N/A', nb_read_packets: 'N/A', extradata_size: 2, tags: [Object], disposition: [Object] } ], format: { filename: 'input.mp4', nb_streams: 2, nb_programs: 0, format_name: 'mov,mp4,m4a,3gp,3g2,mj2', format_long_name: 'QuickTime / MOV', start_time: 0, duration: 6.723333, size: 8651792, bit_rate: 10294646, probe_score: 100, tags: { major_brand: 'iso5', minor_version: '1', compatible_brands: 'isomiso5hlsf', creation_time: '2023-08-05T19:03:15.000000Z' } }, chapters: [] }
原因分析
从FFprobe元数据的format.tags.compatible_brands字段可以看到hlsf标识,说明Mobile Safari录制的视频是分段式MP4(Fragmented MP4)——这类文件采用HLS分段封装,视频数据被拆分成多个小片段存储。
FFmpeg默认的probesize(探针大小)和analyzeduration(分析时长)参数较小,不足以扫描完整的分段文件结构,导致FFmpeg仅读取了第一个视频片段(约0.5秒)就停止解析,最终输出截断后的视频。
解决方法
1. 添加输入选项,增大探针大小和分析时长
给FFmpeg添加输入选项,让它能读取足够多的文件内容以识别完整的视频结构:
.inputOptions([ '-probesize 100M', '-analyzeduration 100M' ])
2. 添加输出选项优化文件结构
针对分段MP4的特性,添加-movflags +faststart输出选项,将输出文件的moov原子移到文件开头,既优化播放体验,也能辅助FFmpeg正确处理输入的分段结构。
修改后的完整代码
import * as ffmpeg from 'fluent-ffmpeg'; public static StandardizeVideoFormat( inputSignedUrl: string, filename: string, ): Promise<string> { const localFilePath = `/tmp/${Date.now()}_${filename}.mp4`; return new Promise((resolve, reject) => { ffmpeg(inputSignedUrl) // 添加输入选项,确保FFmpeg能识别完整的分段文件 .inputOptions([ '-probesize 100M', '-analyzeduration 100M' ]) .outputOptions([ '-crf', '18', '-vcodec', 'libx264', '-codec:a', 'aac', '-preset', 'medium', // 优化输出MP4的结构,同时辅助处理分段输入 '-movflags +faststart' ]) .output(localFilePath) .on('start', function (commandLine) { console.log('Spawned Ffmpeg with command: ' + commandLine); }) .on('error', function (err) { reject(err); }) .on('progress', function (progress) { console.log('progress', progress); }) .on('end', function () { resolve(localFilePath); }) .run(); }); }
额外尝试方案
如果上述方法无效,可以添加-fflags +genpts输入选项,强制FFmpeg生成连续的时间戳,解决分段视频的时间戳不连续问题:
.inputOptions([ '-probesize 100M', '-analyzeduration 100M', '-fflags +genpts' ])
内容的提问来源于stack exchange,提问作者user1825257
相关产品推荐
相关产品推荐

