【问题标题】:Fragmented MP4 doesn't show up in Chrome碎片化的 MP4 不显示在 Chrome 中
【发布时间】:2017-11-13 15:36:22
【问题描述】:

但在 Firefox 中运行良好。

谷歌浏览器版本:61.0.3163.100(官方版本)(64位)

Mozilla Firefox 版本:56.0.2(64 位)

我的视频是分段的 MP4,通过 WebSocket 流式传输到客户端 html 页面,在那里它被提取到 MSE。视频编解码器是 H264 Main Profile。已在 FFPROBE 和其他检查员中检查视频信息,以确保数据完整性正常。多路复用 FMP4 时使用以下标志:

 "empty_moov+default_base_moof+frag_keyframe"

我还仔细检查了第一个碎片是所谓的“初始化段”,大小为 24 字节。 正如我所说,Firefox 播放很好。

这是客户端代码(大部分是从here借来的):

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>MSE Demo</title>
</head>

<body>
   <h1>MSE Demo</h1>
   <div>
      <video id="video1" controls width="80%"   autoplay="true"> </video>
   </div>
 <script type="text/javascript">
    (function () {

        var mime = 'video/mp4; codecs="avc1.4D401E"';

        if (!MediaSource.isTypeSupported(mime)) {
            document.querySelector('h1').append(' - Unsuported mime type :(');
            return;
        }

        var buffer;
        var websocket;
        var buffer_size = 4 * 1024 * 1024;
        var buffer_index = 0;
        var frag_mp4_buffer = new Uint8Array(buffer_size);
        var video = document.querySelector('video');
        var mediaSource = new MediaSource();

        mediaSource.addEventListener('sourceended', function (e) { console.log('sourceended: ' + mediaSource.readyState); });
        mediaSource.addEventListener('sourceclose', function (e) { console.log('sourceclose: ' + mediaSource.readyState); });
        mediaSource.addEventListener('error', function (e) { console.log('error: ' + mediaSource.readyState); });

        video.src = window.URL.createObjectURL(mediaSource);
        video.crossOrigin = 'anonymous';

        mediaSource.addEventListener('sourceopen', function (e) {
            console.log('sourceopen: ' + mediaSource.readyState);
             //doesn't help:
            //  var playPromise =  video.play();
            // In browsers that don’t yet support this functionality,
            // playPromise won’t be defined.
            /*
            if (playPromise !== undefined) {
                playPromise.then(function () {
                    // Automatic playback started!
                }).catch(function (error) {
                    // Automatic playback failed.
                    // Show a UI element to let the user manually start playback.
                });
            }
            */

            buffer = mediaSource.addSourceBuffer(mime);

            buffer.addEventListener('updateend', function (e) {
                if (video.duration && !video.currentTime) {
                    video.currentTime = video.duration;
                }
            });

            var websocket = new WebSocket('ws://' + document.location.hostname + ':8080');
            websocket.binaryType = 'arraybuffer';

            websocket.addEventListener('message', function (e) {
                var data = new Uint8Array(e.data);
                console.log("got packet! size:" + data.length);
                if (data.length) {
                    if ((buffer_index + data.length) <= buffer_size) {
                        frag_mp4_buffer.set(data, buffer_index);
                        buffer_index = buffer_index + data.length;

                        if (!buffer.updating && mediaSource.readyState == 'open')
                         {
                            var appended = frag_mp4_buffer.slice(0, buffer_index);
                            buffer.appendBuffer(appended);
                            frag_mp4_buffer.fill(0);
                            buffer_index = 0;

                        }
                    }
                }
            }, false);
        }, false);
    })();
    </script>
 </body>

另一个重要信息,你可以看到我注释掉了video.play() 电话。这实际上是应用启动时唯一引发错误的地方:

Uncaught (in promise) DOMException: Failed to load because no 找到支持的来源

我从here 尝试了以下解决方案:

      var playPromise =  video.play();

      if (playPromise !== undefined) {
                playPromise.then(function () {
                    // Automatic playback started!
                }).catch(function (error) {
                    // Automatic playback failed.
                    $(document).on('click', '#video1', function (e) {
                        var video = $(this).get(0);
                        if (video.paused === false) {
                            video.pause();
                        } else {
                            video.play();
                        }

                        return false;
                    });
                });
       }

但它什么也没改变。视频区域始终为白色。

【问题讨论】:

    标签: javascript html google-chrome video video-streaming


    【解决方案1】:

    我刚刚遇到了一个类似的问题,我只能使用 mse example 让碎片化的 mp4 在 Firefox 上播放,但不能在 chrome 上播放。我试图先提供一个完整的文件,而不是通过 websocket 发送它,因为我想确保我的碎片 mp4 格式正确。我发现的问题是,在我的 ffmpeg 命令中,如果我包含 -an 来删除音频,那么我的文件将无法在 chrome 中播放,但仍然可以在 Firefox 中运行。不使用音频标志或专门使用 -c:a libfdk_aac 允许我的 mp4 在 chrome 和 firefox 上播放。此外,您应该只需要 -movflags +dash 而不是所有其他 movflags。对于下面我的代码 sn-ps 的参考,我正在使用来自使用 h264 视频编码的 ip 摄像机的 rtsp 提要,因此-c:v copy

    在 mac 上测试,在 firefox 和 safari 上工作,但不是 chrome:

    ffmpeg -i input_source -an -c:v copy -f mp4 -movflags +dash dash.mp4

    在 mac 上测试,在 firefox 和 safari 和 chrome 上工作

    ffmpeg -i input_source -c:v copy -f mp4 -movflags +dash dash.mp4

    ffmpeg -i input_source -c:a libfdk_aac -c:v copy -f mp4 -movflags +dash dash.mp4

    *edit 我刚刚发现了更多关于为什么音频编码与在 chrome 上播放相关的信息。我的 mimetype/codec 错误,chrome 不像 firefox 和 safari 那样宽容。 var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; 我将其更改为var mimeCodec = 'video/mp4; codecs="avc1.42E01E"';,我的 mp4 编码 无音频 在 chrome 以及其他浏览器中播放。也许您有音频但不包括编解码器的音频部分?或者您的视频编解码器不适合您的视频?如果没有看到用于创建文件的完整 ffmpeg 命令,很难说。

    *第二次编辑。我创建了一个little project 来测试使用 ffmpeg、nodejs、express 和 socket.io 在媒体源扩展上的实时流媒体 mp4。 它的边缘有点粗糙,但它主要是有效的。

    【讨论】:

    • 这和删除-frag_size 似乎为我解决了这个问题。谢谢!
    • @MattiasWadman 自从我上一篇文章以来,我发现了另一个有助于跨浏览器兼容性的小设置。在媒体源扩展播放器中将sourceBuffer.mode 设置为sequence 时,Chrome 无法播放我的实时提要。对于 ffmpeg,我将我的 movflags 更改为 +dash+negative_cts_offsets 并且 chrome 不再给出有关时间戳的警告/错误。我已将这些设置包含在我的最新项目github.com/kevinGodell/ffmpeg-streamer
    • 好的,很高兴知道!您知道在流式传输这样的破折号片段时是否可以操纵音频/视频点?我尝试使用setptsasetpts ffmpeg 过滤器,但它们似乎被忽略或重置为零。如果我删除所有 movflags 并将 mp4 写入文件(不流式传输到标准输出),则可以抵消 pts。我正在做的项目是我想动态转码文件的一部分并在视频元素中播放它(以便能够寻找)。在 appendBuffer 之前还做了一些更改 SourceBuffer.timestampOffset 的实验,但这感觉很奇怪。
    【解决方案2】:

    这适用于 Firefox 和 Chrome:

    ffmpeg -i input.any \
      -f mp4 \
      -movflags faststart+separate_moof+empty_moov+default_base_moof \
      -acodec aac -b:a 256000 \
      -frag_duration 500K \
       output.mp4
    

    这也应该与来自媒体源扩展 API 的 SourceBuffer 一起使用。您在此处查找的 Mime-Type 是 audio/mp4; codecs="mp4a.40.2"

    然而,在这一点上,我不确定如何在这个文件中寻找(如果可能的话),例如如果您不想将整个文件发送给客户端,而是从特定时间戳开始并直接跳转到 [moof][mdata]-pair 以开始加载。

    【讨论】:

      【解决方案3】:

      当视频没有音频时,Chrome 不喜欢它。

      如果您使用 m4s 片段(本质上是分段 mp4 的块)生成 HLS 播放列表,那么您还需要确保有音频,否则它根本不会播放。

      这个应该适用于所有浏览器:

      ffmpeg -y -i SOURCE.mp4 -f lavfi -i aevalsrc=0 -af apad -shortest -c:v copy -movflags empty_moov+default_base_moof+frag_keyframe+omit_tfhd_offset -write_tmcd off FRAGMENTED_WITH_PADDED.mp4
      

      【讨论】:

        猜你喜欢
        • 2014-05-23
        • 1970-01-01
        • 2021-02-16
        • 2021-02-07
        • 1970-01-01
        • 1970-01-01
        • 2012-01-26
        • 1970-01-01
        • 2020-12-31
        相关资源
        最近更新 更多