我的目标是流出 mpegts。作为输入,我采用 mp4 文件流。也就是说,视频制作者将 mp4 文件写入我尝试使用的流中。视频可能从一分钟到十分钟不等。因为生产者将字节写入流中,所以最初写入的 mp4 标头不完整(ftyp 之前的前 32 个字节是 0x00,因为它还不知道各种偏移量......我认为这些偏移量是在录制后写入的):
这是典型 mp4 的标头的样子:
00 00 00 18 66 74 79 70 69 73 6f 6d 00 00 00 00 ....ftypisom....
69 73 6f 6d 33 67 70 34 00 01 bb 8c 6d 64 61 74 isom3gp4..»Œmdat
这就是“进行中”mp4 的标题的样子:
00 00 00 00 66 74 79 70 69 73 6f 6d 00 00 00 00 ....ftypisom....
69 73 6f 6d 33 67 70 34 00 00 00 18 3f 3f 3f 3f isom3gp4....????
6d 64 61 74 mdat
这是我的猜测,但我假设一旦制作者完成录制,它会通过写入所有必要的偏移量来更新标题。
在尝试完成这项工作时,我遇到了两个问题:
- 我创建了一个不支持查找的具有读取功能的自定义 AVIO。在我的驱动程序中,我决定以正确格式的 mp4 文件进行流式传输。我能够检测到它的输入格式。当我尝试打开它时,我看到我的自定义读取函数在 avformat_open_input 中执行,直到读入整个文件。
我的代码示例:
av_register_all();
AVFormatContext* pCtx = avformat_alloc_context();
pCtx->pb = avio_alloc_context(
pBuffer, // internal buffer
iBufSize, // internal buffer size
0, // bWriteable (1=true,0=false)
stream, // user data ; will be passed to our callback functions
read_stream, // read callback function
NULL, // write callback function (not used in this example)
NULL // seek callback function
);
pCtx->pb->seekable = 0;
pCtx->pb->write_flag = 0;
pCtx->iformat = av_find_input_format( "mp4" );
pCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
avformat_open_input( &pCtx, "", pCtx->iformat, NULL );
显然,这不能按我的需要工作(我的期望是错误的)。一旦我用不同长度的流替换了有限大小的文件,我就不能让 avformat_open_input 在尝试进行进一步处理之前等待流完成。
因此,我需要找到一种方法来打开输入而不尝试读取它,并且只在我执行 av_read_frame 时读取。这是否可以通过使用自定义 AVIO 来实现。也就是说,准备/打开输入 -> 将初始输入数据读取到输入缓冲区 -> 从输入缓冲区读取帧/数据包 -> 将数据包写入输出 -> 重复读取输入数据直到流结束。
我确实清除了谷歌,只看到了两种选择:提供自定义 URLProtocol 和使用 AVFMT_NOFILE。
自定义 URLProtocol
对于我想要完成的工作,这听起来有点倒退。我知道最好在有可用文件源时使用它。而我正在尝试从字节流中读取。另外,我认为它不符合我需要的另一个原因是需要将自定义 URLProtocol 编译到 ffmpeg lib 中,对吗?或者有没有办法在运行时手动注册它?
AVFMT NOFILE
这似乎对我来说最有效。该标志本身表示没有底层源文件,并假设我将处理输入数据的所有读取和配置。问题是到目前为止我还没有看到任何在线代码片段,但我的假设如下:
我真的希望从任何人那里得到一些关于大脑食物的建议,因为我是 ffmpeg 和数字媒体的新手,我的第二期希望我可以在摄取输入的同时流式输出。
正如我上面提到的,我有一个 mp4 文件字节流的句柄,因为它将被写入硬盘。格式为 mp4(h.264 和 aac)。在将其流式传输之前,我需要将其重新混合为 mpegts。这应该不难,因为 mp4 和 mpegts 只是容器。根据我目前所学到的,mp4 文件如下所示:
[包含格式版本的标题信息]
mdat
[流数据,在我的情况下是 h.264 和 aac 流]
[一些预告片分隔符]
[预告片数据]
如果这是正确的,我应该能够通过简单地在“mdat”标识符之后开始读取流来获取 h.264 和 aac 交错数据的句柄,对吗?
如果这是真的并且我决定使用 AVFMT_NOFILE 方法来管理输入数据,我可以只摄取流数据(进入 AVFormatContext 缓冲区)-> av_read_frame -> 处理它 -> 用更多数据填充 AVFormatContext -> av_read_frame -> 等等直到流结束。
我知道,这是我的想法和想法,但我将不胜感激任何讨论、指点和想法!