劳欣笑 发表于 2025-12-21 11:45:00

FFmpeg 内存输入&输出

[]
实例 内存输入/输出

ffmpeg内存编解码

内存输入

关键点就两个:

[*]初始化AVIOContext时, 指定自定义的回调函数指针
//AVIOContext中的缓存
unsigned char* _iobuffer = (unsigned char*)av_malloc(40690);// 注意缓存容器要足够大
AVIOContext* avio = avio_alloc_context(_iobuffer , 40690 , 0 , this, fill_buffer,nullptr,nullptr);
assert( avio != NULL);
pFormatCtx->pb = avio;//给AVFormatContext
if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){
    printf("Couldn't open inputstream.(无法打开输入流)\n");
    return -1;
}
[*]回调函数
///注意缓存 容器 要足够大
int fill_buffer(void * opaque,uint8_t *buf, int bufsize){
    SDKSource2Player *instance = static_cast<SDKSource2Player*>( opaque);
    callback_data * data =instance->source2->takeDataPacketSync();
    memcpy( buf, data->data, data->size);//复制到容器 给 ffmpeg
    int size =data->size;
    SDKSource2::destoryDataPacket(data);//销毁
    return size;
}内存输出


[*]回调函数
//内存输出数据
int fill_buffer_out(void * opaque, uint8_t *buf, int bufsize){
    WebSocketPlayer *instance = static_cast<WebSocketPlayer*>( opaque);
    instance->onAVPacketOutData(buf,bufsize );
   return bufsize;
}
[*]分配
// 内存输出
unsigned char* out_buffer = (unsigned char*)av_malloc(40690);//~40k
AVIOContext* out_avio = avio_alloc_context(out_buffer , 40690 , 1 , this, nullptr,nullptr,nullptr);
assert( out_avio != NULL);
AVOutputFormat *oformat = av_guess_format("mp4", nullptr, nullptr);
ret = avformat_alloc_output_context2(&outAvFormatCtx,oformat,nullptr,nullptr);
outAvFormatCtx->pb = out_avio;
outAvFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
[*]踩坑指南


[*]内存输出的时候需要注意 avio_alloc_context 的第三个参数 write_flag=1 表示 可写
[*]输出 AVFormatContext 的flag 需要标记为 AVFMT_FLAG_CUSTOM_IO
...
outAvFormatCtx->pb = out_avio;
outAvFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
...

[*]打开输出avio_open的时候, 第二参数不能是 NULL, 即使没有什么意义; ~~貌似自定义输出,不调用也可以 (实测) ~~
avio_open(&outAvFormatCtx->pb, "nothing",AVIO_FLAG_WRITE) ;
if (!outAvFormatCtx->pb ) {
    qDebug()<<"错误: format->pd is NULL";
    throw ("Error open ouput fail ");
}AVFormatContext 的pd 属性

AVFormatContext 有个 pd 属性指针, 读写数据都是通过它
// ============================
//      自定义 AVIO 输出
// ============================

struct AVIOCtxWrapper {
    JNIEnv *env;
    jobject wsServer;
    jmethodID broadcastFrame;
};
// 将 FFmpeg 封装输出的数据通过 WebSocket 发出
static int write_packet(void *opaque, uint8_t *buf, int buf_size) {
    auto *wrapper = (AVIOCtxWrapper *)opaque;
    if (!wrapper || !wrapper->env || !wrapper->wsServer) return 0;

    jbyteArray arr = wrapper->env->NewByteArray(buf_size);
    if (!arr) return 0;
    wrapper->env->SetByteArrayRegion(arr, 0, buf_size, reinterpret_cast<const jbyte*>(buf));
    wrapper->env->CallVoidMethod(wrapper->wsServer, wrapper->broadcastFrame, arr);
    wrapper->env->DeleteLocalRef(arr);
    return buf_size;
}

................

       // --- 创建输出 fMP4 ---
        if (avformat_alloc_output_context2(&out_fmt, nullptr, "mp4", nullptr) < 0) {
                LOGE("Failed to alloc output context");
                cleanup();
                return;
        }
        out_fmt->flags = AVFMT_FLAG_CUSTOM_IO;
       
// 注意:write_packet 期望 wrapper 中持有一个有效的 JNIEnv 与 wsServer/global ref
    AVIOCtxWrapper wrapper;
    wrapper.env = env;
    wrapper.wsServer = ctx->wsServer;
    wrapper.broadcastFrame = broadcastFrame;

    avio = avio_alloc_context(
            avioBuffer, 8192, 1, &wrapper,
            nullptr,
            reinterpret_cast<int (*)(void *, const uint8_t *, int)>(write_packet),
            nullptr
    );
    if (!avio) {
      LOGE("avio_alloc_context failed");
      cleanup();
      return;
    }
    out_fmt->pb = avio;avio_alloc_context

/**
    * I/O context.
    *
    * - demuxing: either set by the user before avformat_open_input() (then
    *             the user must close it manually) or set by avformat_open_input().
    * - muxing: set by the user before avformat_write_header(). The caller must
    *         take care of closing / freeing the IO context.
    *
    * Do NOT set this field if AVFMT_NOFILE flag is set in
    * iformat/oformat.flags. In such a case, the (de)muxer will handle
    * I/O in some other way and this field will be NULL.
    */
AVIOContext *pb;可参考官方API 关于 avformat_open_input的文档:
If you want to use custom IO, preallocate the format context and set its pb field.
/**
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
* freed with avio_context_free().
*
* @param buffer Memory block for input/output operations via AVIOContext.
*      The buffer must be allocated with av_malloc() and friends.
*      It may be freed and replaced with a new buffer by libavformat.
*      AVIOContext.buffer holds the buffer currently in use,
*      which must be later freed with av_free(). (输入输出的'操作容器',必须使用av_malloc()和friends来分配,av_free()释放, AVIOContext.buffer属性保存当前正在使用的)
* @param buffer_size The buffer size is very important for performance.
*      For protocols with fixed blocksize it should be set to this blocksize.
*      For others a typical size is a cache page, e.g. 4kb.['操作容器' 的大小]
* @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.(如果 '操作容器' 可以写设置为1, 否则设置为0] 简而言之是否可写 /注意是个小坑)
* @param opaque An opaque pointer to user-specific data.[用户数据指针, 回调用]
* @param read_packetA function for refilling the buffer, may be NULL.
*                     For stream protocols, must never return 0 but rather
*                     a proper AVERROR code. [读的 回调函数指针]
* @param write_packet A function for writing the buffer contents, may be NULL.
*      The function may not change the input buffers content.[写的 回调函数指针]
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
*/
AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence));雷神的博客 - https://blog.csdn.net/leixiaohua1020/article/details/39759163

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

笃扇 发表于 2026-1-1 08:41:37

分享、互助 让互联网精神温暖你我

凉砧掌 发表于 2026-1-21 17:08:53

过来提前占个楼

跑两獗 发表于 2026-1-22 21:35:02

很好很强大我过来先占个楼 待编辑

戈森莉 发表于 2026-1-23 09:58:22

这个好,看起来很实用

喳谍 发表于 2026-1-24 02:43:43

感谢发布原创作品,程序园因你更精彩

悯拄等 发表于 2026-1-27 04:12:23

感谢,下载保存了

驼娑 发表于 2026-1-27 05:25:23

懂技术并乐意极积无私分享的人越来越少。珍惜

啪炽 发表于 2026-1-28 03:00:50

分享、互助 让互联网精神温暖你我

丘奕奕 发表于 2026-1-29 00:50:53

过来提前占个楼

魄柜 发表于 2026-1-29 05:00:34

感谢分享,下载保存了,貌似很强大

沃盼盼 发表于 2026-2-8 05:31:13

yyds。多谢分享

余思洁 发表于 2026-2-8 06:44:11

喜欢鼓捣这些软件,现在用得少,谢谢分享!

宗和玉 发表于 2026-2-8 23:46:41

喜欢鼓捣这些软件,现在用得少,谢谢分享!

兼罔 发表于 2026-2-11 12:18:12

yyds。多谢分享

鞭氅 发表于 2026-2-11 17:04:01

谢谢楼主提供!

聱嘹 发表于 2026-2-11 21:46:02

感谢,下载保存了

施婉秀 发表于 2026-2-27 22:13:48

分享、互助 让互联网精神温暖你我

喳谍 发表于 2026-3-3 01:59:10

谢谢楼主提供!

国语诗 发表于 2026-3-11 05:27:52

谢谢分享,辛苦了
页: [1] 2
查看完整版本: FFmpeg 内存输入&输出