跨设备编码静态文件以使用 FFMPEG(分段 h264?)在浏览器中流式传输

Posted

技术标签:

【中文标题】跨设备编码静态文件以使用 FFMPEG(分段 h264?)在浏览器中流式传输【英文标题】:Crossdevice encoding static file to stream in browser using FFMPEG (segmented h264 ?) 【发布时间】:2013-09-16 11:28:06 【问题描述】:

我正在 NodeJS 中构建一个媒体中心应用程序,它运行良好。 (你可以在 Github 上查看:https://github.com/jansmolders86/mediacenterjs)

我正在使用 FFMPEG 将本地(静态)电影转码为流,然后发送到浏览器。

一开始我使用 h264 和 Flash 并在浏览器中工作,但我真的需要它在 androidios 上工作(所以没有 Flash),最好是在 Raspberry Pi 上工作。

但是让它在所有设备上播放简直让我发疯!

我拥有从无数小时阅读文章、教程和堆栈溢出帖子中收集到的所有这些难题,这使我得出结论,我需要制作以下内容:

使用视频编解码器 H264 转码为 MP4 移动 moovatom '-movflags' 以使 MP4 可流式传输 对流进行分段,以便 Apple 也可以播放流。

但是这无济于事。每次我生成一系列 FFMPEG 设置,这些设置要么不起作用,要么适用于某些设备而不是所有设备。

我的一些失败尝试:

我的 flash 尝试 -> 主要问题(不在 IOS 中运行):

    '-y','-ss 0','-b 800k','-vcodec libx264','-acodec mp3'\ 
    '-ab 128','-ar 44100','-bufsize 62000', '-maxrate 620k'\
    metaDuration,tDuration,'-f flv

我的 HLS 尝试 -> 主要问题(不在浏览器中运行):

        '-r 15','-b:v 128k','-c:v libx264','-x264opts level=41'\
        '-threads 4','-s 640x480','-map 0:v','-map 0:a:0','-c:a mp3'\
        '-b:a 160000','-ac 2','-f hls','-hls_time 10','-hls_list_size 6'\
        '-hls_wrap 18','-start_number 1'

我的 MP4 尝试 -> 主要问题(持续时间缩短,视频的后半部分正在加速)

       '-y','-ss 0','-b 800k','-vcodec libx264','-acodec mp3'\
       '-ab 128','-ar 44100','-bufsize 62000', '-maxrate 620k'\
       metaDuration,tDuration,'-f mp4','-movflags','frag_keyframe+empty_moov'

第二次 MP4 尝试:-> 主要问题(持续时间缩短,视频的后半部分正在加速)

    '-y','-vcodec libx264','-pix_fmt yuv420p','-b 1200k','-flags +loop+mv4'\
    '-cmp 256','-partitions +parti4x4+parti8x8+partp4x4+partp8x8+partb8x8'\
    '-me_method hex','-subq 7','-trellis 1','-refs 5','-bf 3','-coder 1'\
    '-me_range 16','-g 150','-keyint_min 25','-sc_threshold 40'\
    '-i_qfactor 0.71','-acodec mp3','-qmin 10','-qdiff 4','-qmax 51'\
    '-ab 128k','-ar 44100','-threads 2','-f mp4','-movflags','frag_keyframe+empty_moov'])

以下是使用这些设置运行的 FFMPEG 日志示例:

        file conversion error ffmpeg version N-52458-gaa96439 Copyright (c) 2000-2013 the FFmpeg developers
          built on Apr 24 2013 22:19:32 with gcc 4.8.0 (GCC)
          configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --e
        nable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libcaca --enable-libfreetype --enable
        -libgsm --enable-libilbc --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --ena
        ble-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwola
        me --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libxavs --enabl
        e-libxvid --enable-zlib
          libavutil      52. 27.101 / 52. 27.101
          libavcodec     55.  6.100 / 55.  6.100
          libavformat    55.  3.100 / 55.  3.100
          libavdevice    55.  0.100 / 55.  0.100
          libavfilter     3. 60.101 /  3. 60.101
          libswscale      2.  2.100 /  2.  2.100
          libswresample   0. 17.102 /  0. 17.102
          libpostproc    52.  3.100 / 52.  3.100
        [avi @ 02427900] non-interleaved AVI
        Guessed Channel Layout for  Input Stream #0.1 : mono
        Input #0, avi, from 'C:/temp/the avengers.avi':
          Duration: 00:00:34.00, start: 0.000000, bitrate: 1433 kb/s
            Stream #0:0: Video: cinepak (cvid / 0x64697663), rgb24, 320x240, 15 tbr, 15 tbn, 15 tbc
            Stream #0:1: Audio: pcm_u8 ([1][0][0][0] / 0x0001), 22050 Hz, mono, u8, 176 kb/s
        Please use -b:a or -b:v, -b is ambiguous
        [libx264 @ 02527c60] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
        [libx264 @ 02527c60] profile High, level 2.0
        [libx264 @ 02527c60] 264 - core 130 r2274 c832fe9 - H.264/MPEG-4 AVC codec - Copyleft 2003-2013 - http://www.videolan.org/x26
        4.html - options: cabac=1 ref=5 deblock=1:0:0 analyse=0x3:0x133 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16
         chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=2 lookahead_threads=1 sliced_th
        reads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 we
        ightb=1 open_gop=0 weightp=2 keyint=150 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=abr mbtree=1 bitrate=120
        0 ratetol=1.0 qcomp=0.60 qpmin=10 qpmax=51 qpstep=4 ip_ratio=1.41 aq=1:1.00
        Output #0, mp4, to 'pipe:1':
          Metadata:
            encoder         : Lavf55.3.100
            Stream #0:0: Video: h264 ([33][0][0][0] / 0x0021), yuv420p, 320x240, q=10-51, 1200 kb/s, 15360 tbn, 15 tbc
            Stream #0:1: Audio: mp3 (i[0][0][0] / 0x0069), 44100 Hz, mono, s16p, 128 kb/s
        Stream mapping:
          Stream #0:0 -> #0:0 (cinepak -> libx264)
          Stream #0:1 -> #0:1 (pcm_u8 -> libmp3lame)
        Press [q] to stop, [?] for help
        frame=  106 fps=0.0 q=10.0 size=       1kB time=00:00:06.94 bitrate=   1.4kbits/s
        frame=  150 fps=149 q=14.0 size=       1kB time=00:00:09.87 bitrate=   1.0kbits/s
        frame=  191 fps=126 q=16.0 size=       1kB time=00:00:12.61 bitrate=   0.8kbits/s
        frame=  244 fps=121 q=16.0 size=    2262kB time=00:00:16.14 bitrate=1147.6kbits/s
        frame=  303 fps=120 q=14.0 size=    2262kB time=00:00:20.08 bitrate= 922.2kbits/s
        frame=  354 fps=117 q=15.0 size=    3035kB time=00:00:23.48 bitrate=1058.6kbits/s
        frame=  402 fps=113 q=15.0 size=    3035kB time=00:00:26.67 bitrate= 932.1kbits/s
        frame=  459 fps=113 q=16.0 size=    4041kB time=00:00:30.43 bitrate=1087.7kbits/s
        frame=  510 fps=103 q=2686559.0 Lsize=    5755kB time=00:00:33.93 bitrate=1389.3kbits/s

        video:5211kB audio:531kB subtitle:0 global headers:0kB muxing overhead 0.235111%
        [libx264 @ 02527c60] frame I:6     Avg QP:10.55  size: 25921
        [libx264 @ 02527c60] frame P:245   Avg QP:12.15  size: 14543
        [libx264 @ 02527c60] frame B:259   Avg QP:15.55  size:  6242
        [libx264 @ 02527c60] consecutive B-frames:  6.1% 73.7% 14.7%  5.5%
        [libx264 @ 02527c60] mb I  I16..4: 19.9%  6.2% 73.9%
        [libx264 @ 02527c60] mb P  I16..4:  6.0%  0.2% 12.0%  P16..4: 35.4%  9.6% 16.3%  7.0%  5.6%    skip: 7.8%
        [libx264 @ 02527c60] mb B  I16..4:  0.7%  0.0%  4.3%  B16..8: 27.6% 17.2% 17.0%  direct:17.3%  skip:15.9%  L0:39.4% L1:43.2%
        BI:17.4%
        [libx264 @ 02527c60] final ratefactor: 11.41
        [libx264 @ 02527c60] 8x8 transform intra:1.6% inter:4.0%
        [libx264 @ 02527c60] coded y,uvDC,uvAC intra: 93.0% 97.0% 94.9% inter: 58.4% 58.7% 50.6%
        [libx264 @ 02527c60] i16 v,h,dc,p: 15% 26% 54%  5%
        [libx264 @ 02527c60] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 16% 17% 39%  4%  4%  3%  1%  6%  9%
        [libx264 @ 02527c60] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 28% 34% 21%  4%  2%  2%  2%  2%  5%
        [libx264 @ 02527c60] i8c dc,h,v,p: 51% 24% 19%  6%
        [libx264 @ 02527c60] Weighted P-Frames: Y:4.1% UV:1.2%
        [libx264 @ 02527c60] ref P L0: 68.2%  9.8% 11.0%  5.6%  4.6%  0.8%  0.0%
        [libx264 @ 02527c60] ref B L0: 87.7%  8.0%  3.9%  0.4%
        [libx264 @ 02527c60] ref B L1: 97.8%  2.2%
        [libx264 @ 02527c60] kb/s:1255.36

最后,这是我启动 FFMPEG 的节点代码。 (我使用Fluent-ffmpeg模块:https://github.com/schaermu/node-fluent-ffmpeg)

    var proc = new ffmpeg( source: movie, nolog: true, timeout:15000)                         
        .addOptions(['-r 15','-b:v 128k','-c:v libx264','-x264opts level=41','-threads 4','-s 640x480','-map 0:v','-map 0:a:0','-c:a mp3','-b:a 160000','-ac 2','-f hls','-hls_time 10','-hls_list_size 6','-hls_wrap 18','-start_number 1 stream.m3u8'])
        .writeToStream(res, function(retcode, error)
            if (!error)
                console.log('file has been converted succesfully',retcode .green);
            else
                console.log('file conversion error',error .red);
            
        );

总结一下这个冗长且代码繁重的问题:

我希望这不是一个懒惰的请求,但有人可以向我展示/解释哪些 FFMPEG 设置可以/应该在所有平台(现代浏览器、Android 和 iOS)上工作,生成静态文件流,我可以发送到 HTML5 播放器。

[编辑]如果通用选项不可用,我需要什么

如果这不可能,正如某些帖子所暗示的那样,我希望看到一组 FFMPEG 设置能够正确完成 mp4 流式传输方面的工作。 (例如,对可流式传输的 mp4 进行编码)。

流媒体 mp4 需要以下内容

移动的 moovAtom 必须是h264

非常感谢您的帮助!

【问题讨论】:

【参考方案1】:

没有一种格式可以在所有设备和浏览器上播放。 HTML5 让我们更加接近,但仍然存在关于格式和编解码器的争论。我在 Zencoder 的朋友有一篇新的博文博文 (HERE) 解决了这个确切的问题。

编辑:您要求提供更多细节。同样,这取决于您希望定位的平台。我将在这里介绍几个。

这应该可以在所有支持 h.264 编解码器的现代浏览器上播放。它也应该在 iPhone4 及更高版本上播放:

ffmpeg -i ~/Dropbox/Test\ Content/bigbuckbunny/bigbuckbunny_1500.mp4 -vcodec libx264 -profile:v main -b:v 512k -s 1280x720 -r:v 30 -acodec libfdk_aac -b:a 128k -movflags faststart -y movie1.mp4

iPhone 3gs 不支持主配置文件,其支持的最大分辨率为 640x480。此命令将为这个旧设备编码。

ffmpeg -i ~/Dropbox/Test\ Content/bigbuckbunny/bigbuckbunny_1500.mp4 -vcodec libx264 -profile:v baseline -b:v 512k -s 640x432 -r:v 30 -acodec libfdk_aac -b:a 128k -movflags faststart -y movie2.mp4

我编码了一些示例文件并在这里​​创建了一个网页: http://szatmary.org/***/18758133/

HTML 如下所示:

<!DOCTYPE html>
<html>
<body>
<br>ffmpeg -i ~/Dropbox/Test\ Content/bigbuckbunny/bigbuckbunny_1500.mp4 -vcodec libx264 -profile:v main -b:v 512k -s 1280x720 -r:v 30 -acodec libfdk_aac -b:a 128k -movflags faststart -y movie1.mp4<br>
<video controls>
  <source src="movie1.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video>
<br>ffmpeg -i ~/Dropbox/Test\ Content/bigbuckbunny/bigbuckbunny_1500.mp4 -vcodec libx264 -profile:v baseline -b:v 512k -s 640x432 -r:v 30 -acodec libfdk_aac -b:a 128k -movflags faststart -y movie2.mp4<br>
<video controls>
  <source src="movie2.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video>
</body>
</html>

这里将命令分解为每个元素的含义:

命令+输入文件(应该很明显):

ffmpeg -i ~/Dropbox/Test\ Content/bigbuckbunny/bigbuckbunny_1500.mp4

使用 libx264 对视频进行编码:

-vcodec libx264

将 h.264 配置文件设置为 main。基线将允许在旧设备上播放,但您会牺牲一点质量:

-profile:v main 

将比特率设置为每秒 512 千比特。根据可用带宽选择一个值。 LAN/WiFi 更高,3G/LTE 更低

-b:v 512k 

将视频缩放到 720p 分辨率(同样取决于目标平台)

-s 1280x720 

以每秒 30 帧的速度编码:

-r:v 30 

使用 libfdk_aac 对音频进行编码。如果你想要 mp3,或者使用 libmp3lame。强烈推荐 AAC。它对 ios 有更好的支持并产生更高质量的音频:

-acodec libfdk_aac

将音频比特率设置为每秒 128 千比特。您也可以针对带宽进行调整。使用 AAC,您可能可以低至 32k

-b:a 128k 

将音频采样率设置为每秒 48000 个样本。如果使用 mp3,请为 ios 执行 44100

-r:a 48000

这告诉 ffmpeg 将 moov atom 放在 mp4 文件的开头。

-movflags faststart 

输出文件(-y 告诉 ffmpeg 它可以在不询问的情况下覆盖文件)

-y movie1.mp4

【讨论】:

哇萨特玛丽!这正是我想要的!我会在今天晚些时候尝试一下,如果它对我有用。到目前为止谢谢! 再次感谢您的回答 Szatmary!我试过你的设置,仍然有两个问题。第一个是-movflags faststart 给了我error muxer does not support non seekable putput。我没有收到此错误:frag_keyframe+empty_moov。个人资料主要给了我error: 'main profile doesn't support 4:4:4。如果我使用high444,它确实可以工作。所以视频播放但仍然播放速度很快。我做错了什么大错特错了吗? 好的,所以你是按需转码? faststart 所做的是,一旦编码完成,它会将 moov atom 写入文件的前面(而不是末尾)。这样做是因为 moov 包含有关每一帧的信息。在编码完成之前它没有的信息。如果不先阅读 moov,玩家将无法玩游戏。 empty_moov 用于编码为 DASH 或平滑流。 ios 中不支持的格式。大多数浏览器不支持 HLS。所以,你要找的东西不存在。 您有两个选择。首先使用JW播放器的商业版在不支持HLS的浏览器中播放HLS。第二。查看 HTTP 请求中的 User-Agent 字符串,并将 FLV 提供给 flash,将 HLS 提供给 IOS。 (或者让客户端将所需的编码格式放在 URL 中,例如 mediacenter.js/file3?format=hls ) 哦,在 -vcodec libx264 之后添加 -pix_fmt yuv420p。这是您的原始视频颜色格式。我看 444 出现的不多,所以我通常不需要使用它。【参考方案2】:

据我所知,您不会找到适用于所有设备的设置。我建议您检查用户代理,然后为不同的设备使用不同的设置。这样您还可以使用设备优化设置。

【讨论】:

我很害怕,但我认为这是可能的。分段的 mp4 流可以解决问题。但如果我知道如何让常规 mp4 流在浏览器中工作,我会是一个快乐的露营者。你能指出我正确的方向吗? 你应该另外做两件事。首先对视频进行去隔行扫描:sonnati.wordpress.com/2012/10/19/…,然后使用基线配置文件...参见此处:trac.ffmpeg.org/wiki/x264EncodingGuide#Compatibility 谢谢,这是非常有用的信息!我可以同时编码和去隔行吗?该项目的整个想法是可以在几秒钟内将本地文件流式传输到设备。因此我使用模块“Fluent-FFMPEG”。如果我需要在能够流式传输之前先对电影进行去隔行扫描,那将违背它的目的:) 忘掉 node.js、flash 等,只使用第三方播放器播放 rtsp 流怎么样,你已经在使用 ffmpeg,这是该路线的唯一缺点。 谢谢!这也可能是一种可能性,尽管我必须弄清楚如何使用 node 控制这个 3rd 方服务器,因为 GUI 是建立在 Node 之上的。我怀疑这将是一件容易的事情。【参考方案3】:

我正在考虑 2 种解决方法来启用搜索。我假设您和我一样不想存储提前转码的文件。

第一个只是从浏览器伪造搜索。使用自定义时间线控件,并在搜索时将视频 src 更改为包含所需开始时间的 URL,并将其传递给 ffmpeg。当然,这完全破坏了浏览器预取。我在我的项目中实现了这个,它工作正常。

第二个选项技术性更强,我不知道该怎么做。想法是提前对所有可用视频进行转码,提取 moov atom,将其保存到磁盘并在流式传输时手动写入。这似乎很难做到,但并非不可能。

【讨论】:

以上是关于跨设备编码静态文件以使用 FFMPEG(分段 h264?)在浏览器中流式传输的主要内容,如果未能解决你的问题,请参考以下文章

第三章 FFmpeg的介绍与使用

使用 ffmpeg 编码声音以在 Google Hangouts 应用程序中播放

使用 ffmpeg 转码和分段

美国法院系统存在跨站请求伪造漏洞近30年 .NET编码库存在严重的反序列化漏洞

我可以将多个 ffmpeg 输出通过管道传输到不同的管道吗?

使用 ffmpeg 进行最快解码的编码