当前位置:   article > 正文

FFmpeg3最新的解码接口avcodec_send_packet和avcodec_receive_frame

avcodec_send_packet

ffmpeg3版本的解码接口做了不少调整,之前的视频解码接口avcodec_decode_video2和avcodec_decode_audio4音频解码被设置为deprecated(极不赞成、强烈反对、否决)对这两个接口做了合并,使用统一的接口。并且将音视频解码步骤分为了两步,第一步avcodec_send_packet,第二步avcodec_receive_frame,通过接口名字我们就可以知道第一步是发送编码数据包,第二步是接收解码后数据。新版本是否只是做了接口的变化,还有有哪些我们需要注意的事项,我们来分析一下。

      首先我们先看一下这两个接口。

avcodec_send_packet

  • 接口源码

  1. /**
  2.  * Supply raw packet data as input to a decoder.
  3.  *
  4.  * Internally, this call will copy relevant AVCodecContext fields, which can
  5.  * influence decoding per-packet, and apply them when the packet is actually
  6.  * decoded. (For example AVCodecContext.skip_frame, which might direct the
  7.  * decoder to drop the frame contained by the packet sent with this function.)
  8.  *
  9.  * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE
  10.  *          larger than the actual read bytes because some optimized bitstream
  11.  *          readers read 32 or 64 bits at once and could read over the end.
  12.  *
  13.  * @warning Do not mix this API with the legacy API (like avcodec_decode_video2())
  14.  *          on the same AVCodecContext. It will return unexpected results now
  15.  *          or in future libavcodec versions.
  16.  *
  17.  * @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
  18.  *       before packets may be fed to the decoder.
  19.  *
  20.  * @param avctx codec context
  21.  * @param[in] avpkt The input AVPacket. Usually, this will be a single video
  22.  *                  frame, or several complete audio frames.
  23.  *                  Ownership of the packet remains with the caller, and the
  24.  *                  decoder will not write to the packet. The decoder may create
  25.  *                  a reference to the packet data (or copy it if the packet is
  26.  *                  not reference-counted).
  27.  *                  Unlike with older APIs, the packet is always fully consumed,
  28.  *                  and if it contains multiple frames (e.g. some audio codecs),
  29.  *                  will require you to call avcodec_receive_frame() multiple
  30.  *                  times afterwards before you can send a new packet.
  31.  *                  It can be NULL (or an AVPacket with data set to NULL and
  32.  *                  size set to 0); in this case, it is considered a flush
  33.  *                  packet, which signals the end of the stream. Sending the
  34.  *                  first flush packet will return success. Subsequent ones are
  35.  *                  unnecessary and will return AVERROR_EOF. If the decoder
  36.  *                  still has frames buffered, it will return them after sending
  37.  *                  a flush packet.
  38.  *
  39.  * @return 0 on success, otherwise negative error code:
  40.  *      AVERROR(EAGAIN):   input is not accepted right now - the packet must be
  41.  *                         resent after trying to read output
  42.  *      AVERROR_EOF:       the decoder has been flushed, and no new packets can
  43.  *                         be sent to it (also returned if more than 1 flush
  44.  *                         packet is sent)
  45.  *      AVERROR(EINVAL):   codec not opened, it is an encoder, or requires flush
  46.  *      AVERROR(ENOMEM):   failed to add packet to internal queue, or similar
  47.  *      other errors: legitimate decoding errors
  48.  */
  49. int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
  • 参数分析

AVCodecContext *avctx:第一个参数与旧的接口一致,是视频解码的上下文,包含解码器

const AVPacket *avpkt: 编码的音视频帧数据

  • 为什么要传递空的avpkt

这里有一个说明是可以传递NULL,什么情况下需要传递NULL,你平时看一些视频播放器,播放经常会少最后几帧,很多情况就是因为没有处理好缓冲帧的问题,ffmpeg内部会缓冲几帧,要想取出来就需要传递空的AVPacket进去。

avcodec_receive_frame

  • 接口源码

  1. /**
  2.  * Return decoded output data from a decoder.
  3.  *
  4.  * @param avctx codec context
  5.  * @param frame This will be set to a reference-counted video or audio
  6.  *              frame (depending on the decoder type) allocated by the
  7.  *              decoder. Note that the function will always call
  8.  *              av_frame_unref(frame) before doing anything else.
  9.  *
  10.  * @return
  11.  *      0:                 success, a frame was returned
  12.  *      AVERROR(EAGAIN):   output is not available right now - user must try
  13.  *                         to send new input
  14.  *      AVERROR_EOF:       the decoder has been fully flushed, and there will be
  15.  *                         no more output frames
  16.  *      AVERROR(EINVAL):   codec not opened, or it is an encoder
  17.  *      other negative values: legitimate decoding errors
  18.  */
  19. int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
  • 参数分析

AVCodecContext *avctx:第一个参数视频解码的上下文,与上面接口一致。

AVFrame *frame:解码后的视频帧数据

  • 空间申请和释放问题

     解码后图像空间由函数内部申请,你所做的只需要分配 AVFrame 对象空间,如果你每次调用avcodec_receive_frame传递同一个对象,接口内部会判断空间是否已经分配,如果没有分配会在函数内部分配。

avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一对一的,比如一些音频数据一个AVPacket中包含了1秒钟的音频,调用一次avcodec_send_packet之后,可能需要调用25次 avcodec_receive_frame才能获取全部的解码音频数据,所以要做如下处理:

  1. int re = avcodec_send_packet(codec, pkt);
  2. if (re != 0)
  3. {
  4.     return;
  5. }
  6. while( avcodec_receive_frame(codec, frame) == 0)
  7. {
  8.     //读取到一帧音频或者视频
  9.     //处理解码后音视频 frame
  10. }

1.对于解码:

发送一个packet

函数为avcodec_send_packet(AVCodecContext *avctx,AVPacket *avpkt),该函数只是将一个packet放入到队列中等待解码并不是一个packet,就代表一个frame,解码操作是在该函数中进行的完成此操作后,解码后的数据放在avctx->internal->buff_frame中。

收到一个解码后的frame

函数为av_receive_frame(AVCodecContext *avctx,AVFrame *avframe);此函数只是进行一个拷贝的操作,将avctx中解码后的数据拷贝给avframe。

关于函数的具体解析可以看:https://blog.csdn.net/weixin_34060299/article/details/86913439

2.对于编码

与上面基本类似,只不过过程是相反的
发送一个frame:函数为avcodec_send_frame(AVCodecContext *avctx,AVFrame *avframe)
收到一个packet:函数为avcodec_receive_packet(AVCodecContext *avctx,AVPacket *avpkt)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/446418
推荐阅读
  

闽ICP备14008679号