【发现一个问题】ffmpeg c api:从内存打开一个mp4文件的时候,没法不拷贝

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


我使用如下的方法,从内存中打开一个已经加载好的 mp4 文件:

  avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
  avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0,
                                      inst, read_packet, NULL, seek);
  fmt_ctx = avformat_alloc_context();
  fmt_ctx->pb = avio_ctx;

其中,read_packet 实现了一个回调函数,函数中使用 memcpy() 把 mp4 的数据块,拷贝到 avio_ctx 内部的缓冲区。

明显的问题是:既然 mp4 文件已经在内存中,为什么不能直接用指针指向这片数据,从而避免拷贝。

于是我把上述的一段代码修改为:

avio_ctx =  avio_alloc_context(data, data_size, 0, NULL, NULL, NULL, NULL);

运行时出现错误:

test_each_frame_to_jpg_bin(32498,0x207e03ac0) malloc: *** error for object 0x108e40000: pointer being freed was not allocated
test_each_frame_to_jpg_bin(32498,0x207e03ac0) malloc: *** set a breakpoint in malloc_error_break to debug

通过调试器跟踪,发现了问题所在:

//libavformat/aviobuf.c
int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **bufp, int buf_size)
{
    // .... 
    av_free(s->buffer);  // 第一次解析 mp4 的格式结束后,这里会释放缓冲区。而 mp4 文件的内存数据使用了 mmap,导致无法释放。
    s->buf_ptr = s->buffer = buf;
    s->buffer_size = alloc_size;
    s->pos = buf_size;
    s->buf_end = s->buf_ptr + buf_size;
    s->eof_reached = 0;

    return 0;    
}

由此可见,ffmpeg c api 的流程设计上,是不允许不拷贝的。
后面我会考虑直接解析mp4,绕过 ffmpeg 的 api.

posted on 2025-03-06 16:11  ahfuzhang  阅读(30)  评论(0)    收藏  举报