找回密码
 立即注册
首页 业界区 安全 FFmpeg 内存输入&输出

FFmpeg 内存输入&输出

劳欣笑 2 小时前
[[N_FFmpeg]]
实例 内存输入/输出

ffmpeg内存编解码

内存输入

关键点就两个:

  • 初始化AVIOContext时, 指定自定义的回调函数指针
  1. //AVIOContext中的缓存
  2. unsigned char* _iobuffer = (unsigned char*)av_malloc(40690);// 注意缓存容器要足够大
  3. AVIOContext* avio = avio_alloc_context(_iobuffer , 40690 , 0 , this, fill_buffer,nullptr,nullptr);
  4. assert( avio != NULL);
  5. pFormatCtx->pb = avio;//给AVFormatContext
  6. if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){
  7.     printf("Couldn't open inputstream.(无法打开输入流)\n");
  8.     return -1;
  9. }
复制代码

  • 回调函数
  1. ///注意缓存 容器 要足够大
  2. int fill_buffer(void * opaque,uint8_t *buf, int bufsize){
  3.     SDKSource2Player *instance = static_cast<SDKSource2Player*>( opaque);
  4.     callback_data * data =  instance->source2->takeDataPacketSync();
  5.     memcpy( buf, data->data, data->size);//复制到容器 给 ffmpeg
  6.     int size =  data->size;
  7.     SDKSource2::destoryDataPacket(data);//销毁
  8.     return size;
  9.   }
复制代码
内存输出


  • 回调函数
  1. //内存输出数据
  2. int fill_buffer_out(void * opaque, uint8_t *buf, int bufsize){
  3.     WebSocketPlayer *instance = static_cast<WebSocketPlayer*>( opaque);
  4.     instance->onAVPacketOutData(buf,bufsize );
  5.    return bufsize;
  6. }
复制代码

  • 分配
  1. // 内存输出
  2. unsigned char* out_buffer = (unsigned char*)av_malloc(40690);//~40k
  3. AVIOContext* out_avio = avio_alloc_context(out_buffer , 40690 , 1 , this, nullptr,nullptr,nullptr);
  4. assert( out_avio != NULL);
  5. AVOutputFormat *oformat = av_guess_format("mp4", nullptr, nullptr);
  6. ret = avformat_alloc_output_context2(&outAvFormatCtx,oformat,nullptr,nullptr);
  7. outAvFormatCtx->pb = out_avio;
  8. outAvFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
复制代码

  • 踩坑指南


  • 内存输出的时候需要注意 avio_alloc_context 的第三个参数 write_flag=1 表示 可写
  • 输出 AVFormatContext 的flag 需要标记为 AVFMT_FLAG_CUSTOM_IO
  1. ...
  2. outAvFormatCtx->pb = out_avio;
  3. outAvFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
  4. ...
复制代码

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

AVFormatContext 有个 pd 属性指针, 读写数据都是通过它
  1. // ============================
  2. //      自定义 AVIO 输出
  3. // ============================
  4. struct AVIOCtxWrapper {
  5.     JNIEnv *env;
  6.     jobject wsServer;
  7.     jmethodID broadcastFrame;
  8. };
  9. // 将 FFmpeg 封装输出的数据通过 WebSocket 发出
  10. static int write_packet(void *opaque, uint8_t *buf, int buf_size) {
  11.     auto *wrapper = (AVIOCtxWrapper *)opaque;
  12.     if (!wrapper || !wrapper->env || !wrapper->wsServer) return 0;
  13.     jbyteArray arr = wrapper->env->NewByteArray(buf_size);
  14.     if (!arr) return 0;
  15.     wrapper->env->SetByteArrayRegion(arr, 0, buf_size, reinterpret_cast<const jbyte*>(buf));
  16.     wrapper->env->CallVoidMethod(wrapper->wsServer, wrapper->broadcastFrame, arr);
  17.     wrapper->env->DeleteLocalRef(arr);
  18.     return buf_size;
  19. }
  20. ................
  21.          // --- 创建输出 fMP4 ---
  22.         if (avformat_alloc_output_context2(&out_fmt, nullptr, "mp4", nullptr) < 0) {
  23.                 LOGE("Failed to alloc output context");
  24.                 cleanup();
  25.                 return;
  26.         }
  27.         out_fmt->flags = AVFMT_FLAG_CUSTOM_IO;
  28.        
  29.   // 注意:write_packet 期望 wrapper 中持有一个有效的 JNIEnv 与 wsServer/global ref
  30.     AVIOCtxWrapper wrapper;
  31.     wrapper.env = env;
  32.     wrapper.wsServer = ctx->wsServer;
  33.     wrapper.broadcastFrame = broadcastFrame;
  34.     avio = avio_alloc_context(
  35.             avioBuffer, 8192, 1, &wrapper,
  36.             nullptr,
  37.             reinterpret_cast<int (*)(void *, const uint8_t *, int)>(write_packet),
  38.             nullptr
  39.     );
  40.     if (!avio) {
  41.         LOGE("avio_alloc_context failed");
  42.         cleanup();
  43.         return;
  44.     }
  45.     out_fmt->pb = avio;
复制代码
avio_alloc_context
  1. /**
  2.     * I/O context.
  3.     *
  4.     * - demuxing: either set by the user before avformat_open_input() (then
  5.     *             the user must close it manually) or set by avformat_open_input().
  6.     * - muxing: set by the user before avformat_write_header(). The caller must
  7.     *           take care of closing / freeing the IO context.
  8.     *
  9.     * Do NOT set this field if AVFMT_NOFILE flag is set in
  10.     * iformat/oformat.flags. In such a case, the (de)muxer will handle
  11.     * I/O in some other way and this field will be NULL.
  12.     */
  13. AVIOContext *pb;
复制代码
可参考官方API 关于 avformat_open_input的文档:
If you want to use custom IO, preallocate the format context and set its pb field.
  1. /**
  2. * Allocate and initialize an AVIOContext for buffered I/O. It must be later
  3. * freed with avio_context_free().
  4. *
  5. * @param buffer Memory block for input/output operations via AVIOContext.
  6. *        The buffer must be allocated with av_malloc() and friends.
  7. *        It may be freed and replaced with a new buffer by libavformat.
  8. *        AVIOContext.buffer holds the buffer currently in use,
  9. *        which must be later freed with av_free(). (输入输出的'操作容器',必须使用av_malloc()和friends来分配,av_free()释放, AVIOContext.buffer属性保存当前正在使用的)
  10. * @param buffer_size The buffer size is very important for performance.
  11. *        For protocols with fixed blocksize it should be set to this blocksize.
  12. *        For others a typical size is a cache page, e.g. 4kb.['操作容器' 的大小]
  13. * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.(如果 '操作容器' 可以写设置为1, 否则设置为0] 简而言之是否可写 /注意是个小坑)
  14. * @param opaque An opaque pointer to user-specific data.[用户数据指针, 回调用]
  15. * @param read_packet  A function for refilling the buffer, may be NULL.
  16. *                     For stream protocols, must never return 0 but rather
  17. *                     a proper AVERROR code. [读的 回调函数指针]
  18. * @param write_packet A function for writing the buffer contents, may be NULL.
  19. *        The function may not change the input buffers content.[写的 回调函数指针]
  20. * @param seek A function for seeking to specified byte position, may be NULL.
  21. *
  22. * @return Allocated AVIOContext or NULL on failure.
  23. */
  24. AVIOContext *avio_alloc_context(
  25.                   unsigned char *buffer,
  26.                   int buffer_size,
  27.                   int write_flag,
  28.                   void *opaque,
  29.                   int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
  30.                   int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
  31.                   int64_t (*seek)(void *opaque, int64_t offset, int whence));
复制代码
雷神的博客 - https://blog.csdn.net/leixiaohua1020/article/details/39759163

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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册