FFMPEG I/O 输出缓冲区

7

我目前在尝试将原始H264 NAL数据包封装到mp4容器中,但遇到了问题。不过,我希望将结果存储在内存中,而不是写入磁盘。我按照这个方法Raw H264 frames in mpegts container using libavcodec去做,但迄今为止还没有成功。

首先,这是写入内存的正确方法吗?我在头文件中有一个小结构体。

struct IOOutput {
    uint8_t* outBuffer;
    int bytesSet;
};

我在这里初始化缓冲区和字节设置。然后我初始化我的AVIOContext变量。

AVIOContext* pIOCtx = avio_alloc_context(pBuffer, iBufSize, 1, outptr, NULL, write_packet, NULL);

其中outptr是指向IOOutput输出的void指针,write_packet如下所示:
int write_packet (void *opaque, uint8_t *buf, int buf_size) {
    IOOutput* out = reinterpret_cast<IOOutput*>(opaque);
    memcpy(out->outBuffer+out->bytesSet, buf, buf_size);
    out->bytesSet+=buf_size;
    return buf_size;
}

我随后设置。
fc->pb = pIOCtx;
fc->flags = AVFMT_FLAG_CUSTOM_IO;

在我的AVFormatContext *fc变量上。

然后,每当我编码帧中的nal包时,我通过av_interleaved_write_frame将它们写入AVFormatContext,然后通过以下方法获取mp4内容:

void getBufferContent(char* buffer) {
    memcpy(buffer, output.outBuffer, output.bytesSet);
    output.bytesSet=0;
}

因此重置变量bytesSet,所以下一次写操作时将插入字节到缓冲区的开始。有更好的方法吗?这样做是否有效?如果我仅调用av_interleaved_write_frame和avformat_write_header以添加数据包,FFMPEG会执行任何读取操作吗?
非常感谢您的预先帮助!
编辑:
这是有关复用过程的代码 - 在我的编码函数中,我有类似以下的内容:
int frame_size = x264_encoder_encode(obj->mEncoder, &obj->nals, &obj->i_nals, obj->pic_in, obj->pic_out);
int total_size=0;

for(int i=0; i<obj->i_nals;i++)
    {
        if ( !obj->fc ) {
            obj->create( obj->nals[i].p_payload, obj->nals[i].i_payload );
        } 

        if ( obj->fc ) {
            obj->write_frame( obj->nals[i].p_payload, obj->nals[i].i_payload);
        }
    }

// Here I get the output values
int currentBufferSize = obj->output.bytesSet;
char* mem = new char[currentBufferSize];
obj->getBufferContent(mem);

而创建和写入功能看起来像这样:
int create(void *p, int len) {

   AVOutputFormat *of = av_guess_format( "mp4", 0, 0 );

   fc = avformat_alloc_context();

   // Add video stream
   AVStream *pst = av_new_stream( fc, 0 );
   vi = pst->index;

   void* outptr = (void*) &output;

// Create Buffer
   pIOCtx = avio_alloc_context(pBuffer, iBufSize, 1, outptr, NULL, write_packet, NULL);

   fc->oformat = of;
   fc->pb = pIOCtx;
   fc->flags = AVFMT_FLAG_CUSTOM_IO;

   pcc = pst->codec;

   AVCodec c= {0};
   c.type= AVMEDIA_TYPE_VIDEO;

   avcodec_get_context_defaults3( pcc, &c );
   pcc->codec_type = AVMEDIA_TYPE_VIDEO;

   pcc->codec_id = codec_id;
   pcc->bit_rate = br;
   pcc->width = w;
   pcc->height = h;
   pcc->time_base.num = 1;
   pcc->time_base.den = fps;
}

void write_frame( const void* p, int len ) {

   AVStream *pst = fc->streams[ vi ];

   // Init packet
   AVPacket pkt;
   av_init_packet( &pkt );
   pkt.flags |= ( 0 >= getVopType( p, len ) ) ? AV_PKT_FLAG_KEY : 0;   
   pkt.stream_index = pst->index;
   pkt.data = (uint8_t*)p;
   pkt.size = len;

   pkt.dts = AV_NOPTS_VALUE;
   pkt.pts = AV_NOPTS_VALUE;

   av_interleaved_write_frame( fc, &pkt );

}

你可能会对这个FFmpeg补丁感兴趣: https://ffmpeg.org/pipermail/ffmpeg-devel/2014-November/164996.html 虽然我在FFmpeg的git中找不到实际的“doc/examples/avio_writing.c”文件... - Gediminas
1个回答

5
请查看AVFormatContext.pb文档。您已正确设置它,但不应触摸AVFormatContext.flags。另外,请确保在调用avformat_write_header()之前设置它。
当您说“它不起作用”时,究竟哪里出了问题?回调函数是否未被调用?其数据格式与预期类型/格式是否不符?还是其他原因?如果您只想写入原始nal包,则可以直接从编码器(在AVPacket中)中获取编码数据,那是原始的nal数据。如果您直接使用libx264的API,则它甚至会单独给出每个nal,因此您无需解析它。

非常感谢您的回复!所以我不需要手动设置AVFMT_FLAG_CUSTOM_IO,是吗?到目前为止,我在将nal数据包编码到mp4容器中方面没有成功,我想也许数据没有正确地写入缓冲区 - 我不知道这是否是正确的方法,或者我是否可以使用一些FFMPEG类,比如AVStream来完成任务。现在,我创建一个AVPacket,初始化它,将所有nals复制到内存中,将其分配给数据包,然后使用av_interleaved_write_frame。然而,这似乎并不起作用。 - peacer212
应该可以工作,所以我想了解一下你的其他代码。你能展示一下将原始NAL封装成AVPackets并写出的代码吗?此外,你能展示一些诊断信息,比如write_packet()回调函数被调用的频率有多高,对于给定的NAL长度,这些回调中buf_size的典型值是什么? - Ronald S. Bultje
你好,我编辑了原帖并添加了复用部分 - 非常感谢您的帮助!经过一些测试,我发现我遇到了以下错误:[mp4 @ 0x101076c00] 复用器不支持非可寻址输出!这意味着我需要在avio_alloc_context中实现寻址功能,对吗?如果我不断地覆盖我的缓冲区,我该怎么做呢? - peacer212
我刚刚发现我的回调函数(write_packet)在av_interleaved_write_frame调用时没有被调用。 - peacer212
如何实现寻址是由您决定的,但确实,没有寻址就无法完成.mp4混合。为此,您可能希望使用mpeg ts或类似工具。如果您不关心,可以只实现一个什么也不做的寻址回调函数,但显然输出的.mp4文件将无效。 - Ronald S. Bultje

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接