FFmpeg 提供了多种输入方式,其中之一是通过内存输入。内存输入允许你直接从内存中读取数据,而不是从文件或网络流中读取。以下是如何使用 FFmpeg 的内存输入的步骤:1. **准备输入数据**:首先,你需要准备要输入的数据,这可以是任何格式的数据,比如

摘要:[[N_FFmpeg]] 实例 内存输入输出 ffmpeg内存编解码 内存输入 关键点就两个: 初始化AVIOContext时, 指定自定义的回调函数指针 AVIOContext中的缓存 unsigned char* _iobuffe
[[N_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 "); } Android NDK的一个例子 // ============================ // 自定义 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; AVFormatContext 的pd 属性 AVFormatContext 有个 pd 属性指针, 读写数据都是通过它 /** * 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; avio_alloc_context /** * 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_packet A 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)); 可参考官方API 关于 avformat_open_input的文档: If you want to use custom IO, preallocate the format context and set its pb field. int avformat_open_input ( AVFormatContext** ps, const char * url, AVInputFormat * fmt, AVDictionary** options ) 雷神的博客 - https://blog.csdn.net/leixiaohua1020/article/details/39759163