在分段实时MP4流中发送定期元数据?

时间:2019-01-14 17:45:00

标签: ffmpeg mp4 live-streaming

如该主题所建议,我想知道是否有可能在分段的MP4实时流中定期发送有关流内容的元数据。

我正在使用以下命令(1)来获取碎片化的MP4:

ffmpeg -i rtsp://admin:12345@192.168.0.157 -c:v copy -an -movflags empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof -f mp4 ...

我的主程序从stdout或(unix域)套接字读取此命令的片段,并获得:

ftyp
moov
moof
mdat
moof
mdat
moof
mdat 
...

因此,我得到的第一个片段是 ftyp moov ,它们是元数据并描述了流的内容。

现在,客户端程序稍后会连接到主程序。问题在于,那时 ftype moov 片段早已消失。

是否有一种方法(= ffmpeg命令选项)使其类似于MPEGTS(又名mpeg传输流),并与该流定期重新发送元数据?像这样:

ftyp
moov
moof
mdat
moof
mdat
moof
mdat 
ftyp
moov
moof
mdat
moof
mdat
moof
mdat 
...

..或我的唯一选择是将 ftyp moov 数据包缓存在我的主程序中,然后将它们重新发送到<当请求流时,strong> client程序?

相关链接:What exactly is Fragmented mp4(fMP4)? How is it different from normal mp4?

每次连接新客户端时,

缓存并重新发送 ftyp moov 也不那么简单..因为它以某种方式中断了流(至少浏览器MSE扩展没有不喜欢这样的流)。 moof 数据包中似乎有很多序列号和内容,需要修改。 (+)

另一种选择是将流通过另一个进行重混合的FFmpeg进程(并纠正 moof 数据包)。由于命令(1)不会提供干净分隔的 ftyp moov moof 等数据包,因此事情变得更加复杂。 / p>

感谢任何想法/解决方案。

编辑:关于(+),MSE似乎在播放带有缺口的碎片MP4时遇到问题:https://bugs.chromium.org/p/chromium/issues/detail?id=516114

2 个答案:

答案 0 :(得分:3)

ftyp / moov形成所谓的“初始化片段”,并且仅应在更改流时写入MSE。通常,通过在清单中包含init的URL来解决此问题,并且加入流时,玩家的工作就是请求它。

答案 1 :(得分:0)

我终于能够将片段的MP4毫无问题地提供给浏览器MSE扩展。

如果有人开始向MSE扩展程序添加 moof mdat 数据包,而这些数据包不是在原始 ftyp moov之后立即出现的,然后是..

..进入MSE扩展的第一个 moof 数据包必须是具有设置为 first_sample_flags_preset 的特殊标志的 moof 数据包。 (有关更多信息,请参见ISO / IEC 14496-12:2012(E)规范)

..否则,所有流行的浏览器中的MSE都将冻结,并且不会播放视频(顺便说一句, moof 从> 1开始的序列号完全没有问题)。

此python软件包对于分析非常有用:https://github.com/beardypig/pymp4

要获取该标志,此答案中提供了客户端javascript功能。

使用功能 getBox 找出盒子的类型( ftyp moov moof ,等)。

对于 moof 框,应用函数 findFirstSampleFlag 来查看 moof 框是否启用了 first_sample_flags_preset

function toInt(arr, index) { // From bytes to big-endian 32-bit integer.  Input: Uint8Array, index
    var dv = new DataView(arr.buffer, 0);
    return dv.getInt32(index, false); // big endian
}

function toString(arr, fr, to) { // From bytes to string.  Input: Uint8Array, start index, stop index.
    return String.fromCharCode.apply(null, arr.slice(fr,to));
}

function getBox(arr, i) { // input Uint8Array, start index
    return [toInt(arr, i), toString(arr, i+4, i+8)]
}

function getSubBox(arr, box_name) { // input Uint8Array, box name
    var i = 0;
    res = getBox(arr, i);
    main_length = res[0]; name = res[1]; // this boxes length and name
    i = i + 8;

    var sub_box = null;

    while (i < main_length) {
        res = getBox(arr, i);
        l = res[0]; name = res[1];

        if (box_name == name) {
            sub_box = arr.slice(i, i+l)
        }
        i = i + l;
    }
    return sub_box;
}

function findFirstSampleFlag(arr) { // input Uint8Array
    // [moof [mfhd] [traf [tfhd] [tfdt] [trun]]]

    var traf = getSubBox(arr, "traf");
    if (traf==null) { return false; }

    var trun = getSubBox(traf, "trun");
    if (trun==null) { return false; }

    // ISO/IEC 14496-12:2012(E) .. pages 5 and 57
    // bytes: (size 4), (name 4), (version 1 + tr_flags 3)
    var flags = trun.slice(10,13); // console.log(flags);
    f = flags[1] & 4; // console.log(f);
    return f == 4;
}