当前位置:   article > 正文

FFmpeg源码分析:avcodec_send_frame()和avcodec_receive_packet()音视频编码

FFmpeg源码分析:avcodec_send_frame()和avcodec_receive_packet()音视频编码

FFmpeg在libavcodec模块,旧版本提供avcodec_encode_video2()作为视频编码函数,avcodec_encode_audio2()作为音频编码函数。在FFmpeg 3.1版本新增avcodec_send_frame()与avcodec_receive_packet()作为音视频编码函数。后来,在3.4版本把avcodec_encode_video2()和avcodec_encode_audio2()标记为过时API。

在上一篇文章介绍到音视频解码的分析:avcodec_send_packet和avcodec_receive_frame

目录

一、avcodec_send_frame发送AVFrame

1、avcodec_send_frame

二、avcodec_receive_packet接收AVPacket

1、avcodec_receive_packet

2、encode_receive_packet_internal

3、encode_simple_receive_packet

4、encode_simple_internal

5、X264_frame

三、avcodec_encode_video2视频编码

四、avcodec_encode_audio2音频编码


avcodec_send_frame和avcodec_receive_packet函数定义位于libavcodec/avcodec.h:

  1. /**
  2. * Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet()
  3. * to retrieve buffered output packets.
  4. *
  5. * @param avctx codec context
  6. * @param[in] frame AVFrame containing the raw audio or video frame to be encoded.
  7. *
  8. * For audio:
  9. * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
  10. * can have any number of samples.
  11. * @return 0 on success, otherwise negative error code:
  12. * AVERROR(EAGAIN): input is not accepted in the current state, try again.
  13. * AVERROR_EOF: the encoder has been flushed, and no new frames.
  14. * AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a
  15. * decoder, or requires flush
  16. * AVERROR(ENOMEM): failed to add packet to internal queue, or similar
  17. * other errors: legitimate encoding errors
  18. */
  19. int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
  20. /**
  21. * Read encoded data from the encoder.
  22. *
  23. * @param avctx codec context
  24. * @param avpkt This will be set to a reference-counted packet
  25. *
  26. * @return 0 on success, otherwise negative error code:
  27. * AVERROR(EAGAIN): output is not available in the current state, try again.
  28. * AVERROR_EOF: the encoder has been fully flushed, and there will be
  29. * no more output packets
  30. * AVERROR(EINVAL): codec not opened, or it is a decoder
  31. * other errors: legitimate encoding errors
  32. */
  33. int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

由描述可知, avcodec_send_frame()负责给编码器提供未压缩的原始数据,avcodec_receive_packet()则从编码器取出编码后的数据。如果返回0,代表成功;返回AGAIN,代表当前状态没有可输出数据;返回EOF,代表已经到达输入流结尾;返回INVAL,代表编码器没有打开或者打开的是解码器。

音视频编码的函数执行流程图如下:

一、avcodec_send_frame发送AVFrame

1、avcodec_send_frame

avcodec_send_frame()函数位于libavcodec/encode.c,首先判断编码器有没打开、是否为编码器,然后调用internal函数执行具体操作:

  1. int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. int ret;
  5. // 判断编码器有没打开,是否为编码器
  6. if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
  7. return AVERROR(EINVAL);
  8. if (avci->draining)
  9. return AVERROR_EOF;
  10. if (avci->buffer_frame->data[0])
  11. return AVERROR(EAGAIN);
  12. if (!frame) {
  13. avci->draining = 1;
  14. } else {
  15. ret = encode_send_frame_internal(avctx, frame);
  16. if (ret < 0)
  17. return ret;
  18. }
  19. if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data) {
  20. ret = encode_receive_packet_internal(avctx, avci->buffer_pkt);
  21. if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
  22. return ret;
  23. }
  24. return 0;
  25. }

 encode_send_frame_internal()函数没做多少事情,只是解析音频metadata、检查frame是否有效,真正的赋值操作不在这里。代码如下:

  1. static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. AVFrame *dst = avci->buffer_frame;
  5. int ret;
  6. if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
  7. // 解析音频metadata
  8. AVFrameSideData *sd = av_frame_get_side_data(src, AV_FRAME_DATA_AUDIO_SERVICE_TYPE);
  9. if (sd && sd->size >= sizeof(enum AVAudioServiceType))
  10. avctx->audio_service_type = *(enum AVAudioServiceType*)sd->data;
  11. // 检查frame大小是否有效
  12. if (avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME) {
  13. if (src->nb_samples > avctx->frame_size) {
  14. return AVERROR(EINVAL);
  15. }
  16. } else if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) {
  17. if (avctx->internal->last_audio_frame) {
  18. return AVERROR(EINVAL);
  19. }
  20. if (src->nb_samples < avctx->frame_size) {
  21. ret = pad_last_frame(avctx, dst, src);
  22. if (ret < 0)
  23. return ret;
  24. avctx->internal->last_audio_frame = 1;
  25. } else if (src->nb_samples > avctx->frame_size) {
  26. return AVERROR(EINVAL);
  27. }
  28. }
  29. }
  30. if (!dst->data[0]) {
  31. ret = av_frame_ref(dst, src);
  32. if (ret < 0)
  33. return ret;
  34. }
  35. return 0;
  36. }

二、avcodec_receive_packet接收AVPacket

1、avcodec_receive_packet

avcodec_receive_packet()函数也是首先编码器是否打开、是否为编码器,然后调用encode_receive_packet_internal()函数去执行具体操作:

  1. int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. int ret;
  5. av_packet_unref(avpkt);
  6. // 判断编码器是否打开,是否为编码器
  7. if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
  8. return AVERROR(EINVAL);
  9. if (avci->buffer_pkt->data || avci->buffer_pkt->side_data) {
  10. av_packet_move_ref(avpkt, avci->buffer_pkt);
  11. } else {
  12. ret = encode_receive_packet_internal(avctx, avpkt);
  13. if (ret < 0)
  14. return ret;
  15. }
  16. return 0;
  17. }

2、encode_receive_packet_internal

encode_receive_packet_internal()函数首先检测视频宽高、像素格式,然后判断使用receive_packet还是encode_simple_receive_packet执行编码操作:

  1. static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. int ret;
  5. if (avci->draining_done)
  6. return AVERROR_EOF;
  7. // 检查视频宽、高、像素格式
  8. if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
  9. if (av_image_check_size2(avctx->width, avctx->height,
  10. avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx))
  11. return AVERROR(EINVAL);
  12. }
  13. // 判断使用receive_packet还是encode_simple_receive_packet编码
  14. if (avctx->codec->receive_packet) {
  15. ret = avctx->codec->receive_packet(avctx, avpkt);
  16. if (ret < 0)
  17. av_packet_unref(avpkt);
  18. else
  19. av_assert0(!avpkt->data || avpkt->buf);
  20. } else
  21. ret = encode_simple_receive_packet(avctx, avpkt);
  22. if (ret == AVERROR_EOF)
  23. avci->draining_done = 1;
  24. return ret;
  25. }

3、encode_simple_receive_packet

encode_simple_receive_packet()函数比较简单,主要是调用encode_simple_internal()函数:

  1. static int encode_simple_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
  2. {
  3. int ret;
  4. while (!avpkt->data && !avpkt->side_data) {
  5. ret = encode_simple_internal(avctx, avpkt);
  6. if (ret < 0)
  7. return ret;
  8. }
  9. return 0;
  10. }

4、encode_simple_internal

encode_simple_internal()函数首先判断frame,如果frame为空则调用ff_encode_get_frame()取出一帧未压缩的数据,然后判断使用ff_thread_video_encode_frame还是avctx->codec->encode2执行真正的编码操作:

  1. static int encode_simple_internal(AVCodecContext *avctx, AVPacket *avpkt)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. EncodeSimpleContext *es = &avci->es;
  5. AVFrame *frame = es->in_frame;
  6. int got_packet;
  7. int ret;
  8. if (avci->draining_done)
  9. return AVERROR_EOF;
  10. // 如果frame为空,调用ff_encode_get_frame取一帧数据
  11. if (!frame->buf[0] && !avci->draining) {
  12. av_frame_unref(frame);
  13. ret = ff_encode_get_frame(avctx, frame);
  14. if (ret < 0 && ret != AVERROR_EOF)
  15. return ret;
  16. }
  17. if (!frame->buf[0]) {
  18. if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
  19. (avci->frame_thread_encoder && avctx->active_thread_type
  20. & FF_THREAD_FRAME)))
  21. return AVERROR_EOF;
  22. frame = NULL;
  23. }
  24. got_packet = 0;
  25. // 判断使用ff_thread_video_encode_frame还是avctx->codec->encode2进行编码
  26. if (CONFIG_FRAME_THREAD_ENCODER &&
  27. avci->frame_thread_encoder && (avctx->active_thread_type & FF_THREAD_FRAME))
  28. ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet);
  29. else {
  30. ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
  31. if (avctx->codec->type == AVMEDIA_TYPE_VIDEO && !ret && got_packet &&
  32. !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
  33. avpkt->pts = avpkt->dts = frame->pts;
  34. }
  35. if (!ret && got_packet) {
  36. if (avpkt->data) {
  37. ret = av_packet_make_refcounted(avpkt);
  38. if (ret < 0)
  39. goto end;
  40. }
  41. if (frame && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
  42. if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
  43. if (avpkt->pts == AV_NOPTS_VALUE)
  44. avpkt->pts = frame->pts;
  45. if (!avpkt->duration)
  46. avpkt->duration = ff_samples_to_time_base(
  47. avctx, frame->nb_samples);
  48. }
  49. }
  50. // 如果是音频,flags都设为关键帧
  51. if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
  52. avpkt->flags |= AV_PKT_FLAG_KEY;
  53. avpkt->dts = avpkt->pts;
  54. }
  55. }
  56. if (avci->draining && !got_packet)
  57. avci->draining_done = 1;
  58. end:
  59. if (ret < 0 || !got_packet)
  60. av_packet_unref(avpkt);
  61. if (frame) {
  62. if (!ret)
  63. avctx->frame_number++;
  64. av_frame_unref(frame);
  65. }
  66. return ret;
  67. }

 在上面有提到,avcodec_send_frame()函数没有进行frame的赋值操作,真正执行赋值操作的是ff_encode_get_frame()函数,具体如下:

  1. int ff_encode_get_frame(AVCodecContext *avctx, AVFrame *frame)
  2. {
  3. AVCodecInternal *avci = avctx->internal;
  4. if (avci->draining)
  5. return AVERROR_EOF;
  6. if (!avci->buffer_frame->buf[0])
  7. return AVERROR(EAGAIN);
  8. // avci->buffer_frame赋值给frame
  9. av_frame_move_ref(frame, avci->buffer_frame);
  10. return 0;
  11. }

5、X264_frame

以libx264编码器为例,位于libavcodec/libx264.c,对应的AVCodec如下:

  1. AVCodec ff_libx264_encoder = {
  2. .name = "libx264",
  3. .long_name = NULL_IF_CONFIG_SMALL("libx264 H.264/AVC /MPEG-4 part10"),
  4. .type = AVMEDIA_TYPE_VIDEO,
  5. .id = AV_CODEC_ID_H264,
  6. .priv_data_size = sizeof(X264Context),
  7. .init = X264_init,
  8. .encode2 = X264_frame,
  9. .close = X264_close,
  10. .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS |
  11. AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
  12. .caps_internal = FF_CODEC_CAP_AUTO_THREADS,
  13. .priv_class = &x264_class,
  14. .defaults = x264_defaults,
  15. .pix_fmts = pix_fmts_all,
  16. .wrapper_name = "libx264",
  17. };

此时,encode2函数指针指向X264_frame,代码如下:

  1. static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame,
  2. int *got_packet)
  3. {
  4. X264Context *x4 = ctx->priv_data;
  5. x264_nal_t *nal;
  6. int nnal, i, ret;
  7. x264_picture_t pic_out = {0};
  8. int pict_type;
  9. int bit_depth;
  10. int64_t wallclock = 0;
  11. X264Opaque *out_opaque;
  12. AVFrameSideData *sd;
  13. // 初始化x264编码器相关参数
  14. x264_picture_init( &x4->pic );
  15. x4->pic.img.i_csp = x4->params.i_csp;
  16. #if X264_BUILD >= 153
  17. bit_depth = x4->params.i_bitdepth;
  18. #else
  19. bit_depth = x264_bit_depth;
  20. #endif
  21. if (bit_depth > 8)
  22. x4->pic.img.i_csp |= X264_CSP_HIGH_DEPTH;
  23. x4->pic.img.i_plane = avfmt2_num_planes(ctx->pix_fmt);
  24. if (frame) {
  25. for (i = 0; i < x4->pic.img.i_plane; i++) {
  26. x4->pic.img.plane[i] = frame->data[i];
  27. x4->pic.img.i_stride[i] = frame->linesize[i];
  28. }
  29. x4->pic.i_pts = frame->pts;
  30. x4->reordered_opaque[x4->next_reordered_opaque].reordered_opaque = frame->reordered_opaque;
  31. x4->reordered_opaque[x4->next_reordered_opaque].wallclock = wallclock;
  32. if (ctx->export_side_data & AV_CODEC_EXPORT_DATA_PRFT)
  33. x4->reordered_opaque[x4->next_reordered_opaque].wallclock = av_gettime();
  34. x4->pic.opaque = &x4->reordered_opaque[x4->next_reordered_opaque];
  35. x4->next_reordered_opaque++;
  36. x4->next_reordered_opaque %= x4->nb_reordered_opaque;
  37. // 给x264的pic.i_type赋值,包括IDR、I、P、B帧类型
  38. switch (frame->pict_type) {
  39. case AV_PICTURE_TYPE_I:
  40. x4->pic.i_type = x4->forced_idr > 0 ? X264_TYPE_IDR
  41. : X264_TYPE_KEYFRAME;
  42. break;
  43. case AV_PICTURE_TYPE_P:
  44. x4->pic.i_type = X264_TYPE_P;
  45. break;
  46. case AV_PICTURE_TYPE_B:
  47. x4->pic.i_type = X264_TYPE_B;
  48. break;
  49. default:
  50. x4->pic.i_type = X264_TYPE_AUTO;
  51. break;
  52. }
  53. // 重新配置编码器
  54. reconfig_encoder(ctx, frame);
  55. ......
  56. }
  57. do {
  58. // 调用x264_encoder_encode()执行编码
  59. if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0)
  60. return AVERROR_EXTERNAL;
  61. // 对nal单元进行编码
  62. ret = encode_nals(ctx, pkt, nal, nnal);
  63. if (ret < 0)
  64. return ret;
  65. } while (!ret && !frame && x264_encoder_delayed_frames(x4->enc));
  66. if (!ret)
  67. return 0;
  68. pkt->pts = pic_out.i_pts;
  69. pkt->dts = pic_out.i_dts;
  70. out_opaque = pic_out.opaque;
  71. if (out_opaque >= x4->reordered_opaque &&
  72. out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) {
  73. ctx->reordered_opaque = out_opaque->reordered_opaque;
  74. wallclock = out_opaque->wallclock;
  75. } else {
  76. ctx->reordered_opaque = 0;
  77. }
  78. // 给pict_type赋值,包括I、P、B帧类型
  79. switch (pic_out.i_type) {
  80. case X264_TYPE_IDR:
  81. case X264_TYPE_I:
  82. pict_type = AV_PICTURE_TYPE_I;
  83. break;
  84. case X264_TYPE_P:
  85. pict_type = AV_PICTURE_TYPE_P;
  86. break;
  87. case X264_TYPE_B:
  88. case X264_TYPE_BREF:
  89. pict_type = AV_PICTURE_TYPE_B;
  90. break;
  91. default:
  92. return AVERROR_EXTERNAL;
  93. }
  94. pkt->flags |= AV_PKT_FLAG_KEY*pic_out.b_keyframe;
  95. if (ret) {
  96. ff_side_data_set_encoder_stats(pkt,
  97. (pic_out.i_qpplus1 - 1) * FF_QP2LAMBDA, NULL, 0, pict_type);
  98. if (wallclock)
  99. ff_side_data_set_prft(pkt, wallclock);
  100. }
  101. *got_packet = ret;
  102. return 0;
  103. }

由此可见,X264_frame()函数主要有6个步骤:

  • 初始化x264编码器相关参数;
  • 给x264的pic.i_type赋值,包括IDR、I、P、B帧类型;
  • 调用reconfig_encoder()重新配置编码器;
  • 调用x264_encoder_encode()执行编码;
  • 对nal单元进行编码;
  • 给输出的pict_type赋值,包括I、P、B帧类型;

三、avcodec_encode_video2视频编码

由于avcodec_encode_video2()函数已经过时,所以内部提供compat_encode()来兼容旧版本,具体代码如下:

  1. int avcodec_encode_video2(AVCodecContext *avctx,
  2. AVPacket *avpkt,
  3. const AVFrame *frame,
  4. int *got_packet_ptr)
  5. {
  6. int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);
  7. if (ret < 0)
  8. av_packet_unref(avpkt);
  9. return ret;
  10. }

我们来看看compat_encode()函数源码,其实内部也是调用avcodec_send_frame()和avcodec_receive_packet()进行编码,具体如下: 

  1. static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
  2. int *got_packet, const AVFrame *frame)
  3. {
  4. AVCodecInternal *avci = avctx->internal;
  5. AVPacket user_pkt;
  6. int ret;
  7. *got_packet = 0;
  8. // 检测视频的pixel_format、width、height
  9. if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
  10. if (frame->format == AV_PIX_FMT_NONE)
  11. av_log(avctx, AV_LOG_WARNING, "format is not set\n");
  12. if (frame->width == 0 || frame->height == 0)
  13. av_log(avctx, AV_LOG_WARNING, "width or height is not set\n");
  14. }
  15. // 调用avcodec_send_frame()发送frame
  16. ret = avcodec_send_frame(avctx, frame);
  17. if (ret == AVERROR_EOF)
  18. ret = 0;
  19. else if (ret == AVERROR(EAGAIN)) {
  20. return AVERROR_BUG;
  21. } else if (ret < 0)
  22. return ret;
  23. av_packet_move_ref(&user_pkt, avpkt);
  24. while (ret >= 0) {
  25. // 调用avcodec_receive_packet()接收packet
  26. ret = avcodec_receive_packet(avctx, avpkt);
  27. if (ret < 0) {
  28. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  29. ret = 0;
  30. goto finish;
  31. }
  32. if (avpkt != avci->compat_encode_packet) {
  33. if (avpkt->data && user_pkt.data) {
  34. if (user_pkt.size >= avpkt->size) {
  35. memcpy(user_pkt.data, avpkt->data, avpkt->size);
  36. av_buffer_unref(&avpkt->buf);
  37. avpkt->buf = user_pkt.buf;
  38. avpkt->data = user_pkt.data;
  39. } else {
  40. av_packet_unref(avpkt);
  41. ret = AVERROR(EINVAL);
  42. goto finish;
  43. }
  44. }
  45. *got_packet = 1;
  46. avpkt = avci->compat_encode_packet;
  47. } else {
  48. if (!avci->compat_decode_warned) {
  49. avci->compat_decode_warned = 1;
  50. av_packet_unref(avpkt);
  51. }
  52. }
  53. if (avci->draining)
  54. break;
  55. }
  56. finish:
  57. if (ret < 0)
  58. av_packet_unref(&user_pkt);
  59. return ret;
  60. }

四、avcodec_encode_audio2音频编码

和avcodec_encode_video2()函数一样,avcodec_encode_audio2()函数已经过时,内部也是提供compat_encode()来兼容旧版本,具体代码如下:

  1. int avcodec_encode_audio2(AVCodecContext *avctx,
  2. AVPacket *avpkt,
  3. const AVFrame *frame,
  4. int *got_packet_ptr)
  5. {
  6. int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);
  7. if (ret < 0)
  8. av_packet_unref(avpkt);
  9. return ret;
  10. }

至此,avcodec_send_frame()和avcodec_receive_packet()组成的编码函数已经分析完毕。

学习FFmpeg与代码实践,可参考:FFmpegAndroid

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

闽ICP备14008679号