本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10584925.html

FFmpeg编解码处理系列笔记:

[0]. FFmpeg时间戳详解

[1]. FFmpeg编解码处理1-转码全流程简介

[2]. FFmpeg编解码处理2-编解码API详解

[3]. FFmpeg编解码处理3-视频编码

[4]. FFmpeg编解码处理4-音频编码

基于FFmpeg 4.1版本。

4. 编解码API详解

解码使用 avcodec_send_packet() 和 avcodec_receive_frame() 两个函数。

编码使用 avcodec_send_frame() 和 avcodec_receive_packet() 两个函数。

4.1 API定义

4.1.1 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 in the current state - user
  41. * must read output with avcodec_receive_frame() (once
  42. * all output is read, the packet should be resent, and
  43. * the call will not fail with EAGAIN).
  44. * AVERROR_EOF: the decoder has been flushed, and no new packets can
  45. * be sent to it (also returned if more than 1 flush
  46. * packet is sent)
  47. * AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush
  48. * AVERROR(ENOMEM): failed to add packet to internal queue, or similar
  49. * other errors: legitimate decoding errors
  50. */
  51. int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

4.1.2 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 in this state - 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);

4.1.3 avcodec_send_frame()

  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. * Ownership of the frame remains with the caller, and the
  8. * encoder will not write to the frame. The encoder may create
  9. * a reference to the frame data (or copy it if the frame is
  10. * not reference-counted).
  11. * It can be NULL, in which case it is considered a flush
  12. * packet. This signals the end of the stream. If the encoder
  13. * still has packets buffered, it will return them after this
  14. * call. Once flushing mode has been entered, additional flush
  15. * packets are ignored, and sending frames will return
  16. * AVERROR_EOF.
  17. *
  18. * For audio:
  19. * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame
  20. * can have any number of samples.
  21. * If it is not set, frame->nb_samples must be equal to
  22. * avctx->frame_size for all frames except the last.
  23. * The final frame may be smaller than avctx->frame_size.
  24. * @return 0 on success, otherwise negative error code:
  25. * AVERROR(EAGAIN): input is not accepted in the current state - user
  26. * must read output with avcodec_receive_packet() (once
  27. * all output is read, the packet should be resent, and
  28. * the call will not fail with EAGAIN).
  29. * AVERROR_EOF: the encoder has been flushed, and no new frames can
  30. * be sent to it
  31. * AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a
  32. * decoder, or requires flush
  33. * AVERROR(ENOMEM): failed to add packet to internal queue, or similar
  34. * other errors: legitimate decoding errors
  35. */
  36. int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

4.1.4 avcodec_receive_packet()

  1. /**
  2. * Read encoded data from the encoder.
  3. *
  4. * @param avctx codec context
  5. * @param avpkt This will be set to a reference-counted packet allocated by the
  6. * encoder. Note that the function will always call
  7. * av_frame_unref(frame) before doing anything else.
  8. * @return 0 on success, otherwise negative error code:
  9. * AVERROR(EAGAIN): output is not available in the current state - user
  10. * must try to send input
  11. * AVERROR_EOF: the encoder has been fully flushed, and there will be
  12. * no more output packets
  13. * AVERROR(EINVAL): codec not opened, or it is an encoder
  14. * other errors: legitimate decoding errors
  15. */
  16. int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

4.2 API使用说明

4.2.1 解码API使用详解

关于 avcodec_send_packet() 与 avcodec_receive_frame() 的使用说明:

[1] 按 dts 递增的顺序向解码器送入编码帧 packet,解码器按 pts 递增的顺序输出原始帧 frame,实际上解码器不关注输入 packe t的 dts(错值都没关系),它只管依次处理收到的 packet,按需缓冲和解码

[2] avcodec_receive_frame() 输出 frame 时,会根据各种因素设置好 frame->best_effort_timestamp(文档明确说明),实测 frame->pts 也会被设置(通常直接拷贝自对应的 packet.pts,文档未明确说明)用户应确保 avcodec_send_packet() 发送的 packet 具有正确的 pts,编码帧 packet 与原始帧 frame 间的对应关系通过 pts 确定

[3] avcodec_receive_frame() 输出 frame 时,frame->pkt_dts 拷贝自当前avcodec_send_packet() 发送的 packet 中的 dts,如果当前 packet 为 NULL(flush packet),解码器进入 flush 模式,当前及剩余的 frame->pkt_dts 值总为 AV_NOPTS_VALUE。因为解码器中有缓存帧,当前输出的 frame 并不是由当前输入的 packet 解码得到的,所以这个 frame->pkt_dts 没什么实际意义,可以不必关注

[4] avcodec_send_packet() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF

[5] avcodec_send_packet() 多次发送 NULL 并不会导致解码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉解码器中缓存帧。因此播放完毕时应 avcodec_send_packet(NULL) 来取完缓存的帧,而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧

[6] 解码器通常的冲洗方法:调用一次 avcodec_send_packet(NULL)(返回成功),然后不停调用 avcodec_receive_frame() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_frame() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志

4.2.2 编码API使用详解

关于 avcodec_send_frame() 与 avcodec_receive_packet() 的使用说明:

[1] 按 pts 递增的顺序向编码器送入原始帧 frame,编码器按 dts 递增的顺序输出编码帧 packet,实际上编码器关注输入 frame 的 pts 不关注其 dts,它只管依次处理收到的 frame,按需缓冲和编码

[2] avcodec_receive_packet() 输出 packet 时,会设置 packet.dts,从 0 开始,每次输出的 packet 的 dts 加 1,这是视频层的 dts,用户写输出前应将其转换为容器层的 dts

[3] avcodec_receive_packet() 输出 packet 时,packet.pts 拷贝自对应的 frame.pts,这是视频层的 pts,用户写输出前应将其转换为容器层的 pts

[4] avcodec_send_frame() 发送 NULL frame 时,编码器进入 flush 模式

[5] avcodec_send_frame() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF

[6] avcodec_send_frame() 多次发送 NULL 并不会导致编码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉编码器中缓存帧。因此编码完毕时应使用 avcodec_send_frame(NULL) 来取完缓存的帧,而SEEK操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧

[7] 编码器通常的冲洗方法:调用一次 avcodec_send_frame(NULL)(返回成功),然后不停调用 avcodec_receive_packet() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_packet() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志

[8] 对音频来说,如果 AV_CODEC_CAP_VARIABLE_FRAME_SIZE(在 AVCodecContext.codec.capabilities 变量中,只读)标志有效,表示编码器支持可变尺寸音频帧,送入编码器的音频帧可以包含任意数量的采样点。如果此标志无效,则每一个音频帧的采样点数目(frame->nb_samples)必须等于编码器设定的音频帧尺寸(avctx->frame_size),最后一帧除外,最后一帧音频帧采样点数可以小于 avctx->frame_size

4.3 API使用例程

4.3.1 解码API例程

  1. // retrun 0: got a frame success
  2. // AVERROR(EAGAIN): need more packet
  3. // AVERROR_EOF: end of file, decoder has been flushed
  4. // <0: error
  5. int av_decode_frame(AVCodecContext *dec_ctx, AVPacket *packet, bool *new_packet, AVFrame *frame)
  6. {
  7. int ret = AVERROR(EAGAIN);
  8. while (1)
  9. {
  10. // 2. 从解码器接收frame
  11. if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
  12. {
  13. // 2.1 一个视频packet含一个视频frame
  14. // 解码器缓存一定数量的packet后,才有解码后的frame输出
  15. // frame输出顺序是按pts的顺序,如IBBPBBP
  16. // frame->pkt_pos变量是此frame对应的packet在视频文件中的偏移地址,值同pkt.pos
  17. ret = avcodec_receive_frame(dec_ctx, frame);
  18. if (ret >= 0)
  19. {
  20. if (frame->pts == AV_NOPTS_VALUE)
  21. {
  22. frame->pts = frame->best_effort_timestamp;
  23. printf("set video pts %d\n", frame->pts);
  24. }
  25. }
  26. }
  27. else if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
  28. {
  29. // 2.2 一个音频packet含一至多个音频frame,每次avcodec_receive_frame()返回一个frame,此函数返回。
  30. // 下次进来此函数,继续获取一个frame,直到avcodec_receive_frame()返回AVERROR(EAGAIN),
  31. // 表示解码器需要填入新的音频packet
  32. ret = avcodec_receive_frame(dec_ctx, frame);
  33. if (ret >= 0)
  34. {
  35. if (frame->pts == AV_NOPTS_VALUE)
  36. {
  37. frame->pts = frame->best_effort_timestamp;
  38. printf("set audio pts %d\n", frame->pts);
  39. }
  40. }
  41. }
  42. if (ret >= 0) // 成功解码得到一个视频帧或一个音频帧,则返回
  43. {
  44. return ret;
  45. }
  46. else if (ret == AVERROR_EOF) // 解码器已冲洗,解码中所有帧已取出
  47. {
  48. avcodec_flush_buffers(dec_ctx);
  49. return ret;
  50. }
  51. else if (ret == AVERROR(EAGAIN))// 解码器需要喂数据
  52. {
  53. if (!(*new_packet)) // 本函数中已向解码器喂过数据,因此需要从文件读取新数据
  54. {
  55. //av_log(NULL, AV_LOG_INFO, "decoder need more packet\n");
  56. return ret;
  57. }
  58. }
  59. else // 错误
  60. {
  61. av_log(NULL, AV_LOG_ERROR, "decoder error %d\n", ret);
  62. return ret;
  63. }
  64. /*
  65. if (packet == NULL || (packet->data == NULL && packet->size == 0))
  66. {
  67. // 复位解码器内部状态/刷新内部缓冲区。当seek操作或切换流时应调用此函数。
  68. avcodec_flush_buffers(dec_ctx);
  69. }
  70. */
  71. // 1. 将packet发送给解码器
  72. // 发送packet的顺序是按dts递增的顺序,如IPBBPBB
  73. // pkt.pos变量可以标识当前packet在视频文件中的地址偏移
  74. // 发送第一个 flush packet 会返回成功,后续的 flush packet 会返回AVERROR_EOF
  75. ret = avcodec_send_packet(dec_ctx, packet);
  76. *new_packet = false;
  77. if (ret != 0)
  78. {
  79. av_log(NULL, AV_LOG_ERROR, "avcodec_send_packet() error, return %d\n", ret);
  80. return ret;
  81. }
  82. }
  83. return -1;
  84. }

4.3.2 编码API例程

  1. int av_encode_frame(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *packet)
  2. {
  3. int ret = -1;
  4. // 第一次发送flush packet会返回成功,进入冲洗模式,可调用avcodec_receive_packet()
  5. // 将编码器中缓存的帧(可能不止一个)取出来
  6. // 后续再发送flush packet将返回AVERROR_EOF
  7. ret = avcodec_send_frame(enc_ctx, frame);
  8. if (ret == AVERROR_EOF)
  9. {
  10. //av_log(NULL, AV_LOG_INFO, "avcodec_send_frame() encoder flushed\n");
  11. }
  12. else if (ret == AVERROR(EAGAIN))
  13. {
  14. //av_log(NULL, AV_LOG_INFO, "avcodec_send_frame() need output read out\n");
  15. }
  16. else if (ret < 0)
  17. {
  18. //av_log(NULL, AV_LOG_INFO, "avcodec_send_frame() error %d\n", ret);
  19. return ret;
  20. }
  21. ret = avcodec_receive_packet(enc_ctx, packet);
  22. if (ret == AVERROR_EOF)
  23. {
  24. av_log(NULL, AV_LOG_INFO, "avcodec_recieve_packet() encoder flushed\n");
  25. }
  26. else if (ret == AVERROR(EAGAIN))
  27. {
  28. //av_log(NULL, AV_LOG_INFO, "avcodec_recieve_packet() need more input\n");
  29. }
  30. return ret;
  31. }

FFmpeg编解码处理2-编解码API详解的更多相关文章

  1. 大数据学习笔记——Spark工作机制以及API详解

    Spark工作机制以及API详解 本篇文章将会承接上篇关于如何部署Spark分布式集群的博客,会先对RDD编程中常见的API进行一个整理,接着再结合源代码以及注释详细地解读spark的作业提交流程,调 ...

  2. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

  3. jqGrid APi 详解

    jqGrid APi 详解 jqGrid皮肤 从3.5版本开始,jqGrid完全支持jquery UI的theme.我们可以从http://jqueryui.com/themeroller/下载我们所 ...

  4. hibernate学习(2)——api详解对象

    1   Configuration 配置对象 /详解Configuration对象 public class Configuration_test { @Test //Configuration 用户 ...

  5. 网络编程socket基本API详解(转)

    网络编程socket基本API详解   socket socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket ...

  6. 转】Mahout推荐算法API详解

    原博文出自于: http://blog.fens.me/mahout-recommendation-api/ 感谢! Posted: Oct 21, 2013 Tags: itemCFknnMahou ...

  7. dom4j api 详解--XPath 节点详解

    dom4j api 详解 http://871421448.iteye.com/blog/1546955 XPath 节点 http://www.w3school.com.cn/xpath/xpath ...

  8. 百度地图API详解之事件机制,function“闭包”解决for循环和监听器冲突的问题:

    原文:百度地图API详解之事件机制,function"闭包"解决for循环和监听器冲突的问题: 百度地图API详解之事件机制 2011年07月26日 星期二 下午 04:06 和D ...

  9. 【Unity编程】Unity中关于四元数的API详解

    本文为博主原创文章,欢迎转载,请保留出处:http://blog.csdn.net/andrewfan Unity中关于四元数的API详解 Quaternion类 Quaternion(四元数)用于计 ...

随机推荐

  1. IText实现对PDF文档属性的基本设置

    一.Itext简介 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML.Html文 ...

  2. wind量化交易

    https://www.joinquant.com/study?f=home&m=memu https://www.v2ex.com/member/mushroomqiu https://sa ...

  3. 用Rider写一个有IOC容器Autofac的.net core的程序

    一:Autofac是一个和Java里的Spring IOC容器一样的东西,不过它确实没有Spring里的那么方便,主要是在于它没有提供足够的Api和扫描方式等等,不过优点是它比Spring要快很多,而 ...

  4. 27、通过visual s'tudio 验证 SOCKET编程:搭建一个TCP服务器

    本文就是在windows下进行socket编程,搭建一个TCP客户端. 在visual studio下编程,首先在windows下进行初始化(这点在linux下是不需要的): /* 初始化 Winso ...

  5. Visual C++.NET设计

    首先,创建对应的工程,VS2010以及以前的版本,创建项目时都可以在CLR下找到“Windows窗体应用程序”的项目模板,但是VS2012以后的版本就没这么方便了.可以通过打开旧版本的项目来修改,也可 ...

  6. 冲刺博客NO.8

    今天做了什么: 多天学习后,实现了短信验证的功能,可以选择国家,可以在Mob的后台管理短信验证 遇到的困难: 注册回调事件,afterEvent的判定(事件完成后调用)

  7. Jquery 图片走马灯效果原理

    本篇只讲解水平走马灯效果,垂直向上走马灯效果不讲解,原理一样,但是水平走马灯效果有一个小坑.待会讲解 照例先上代码: HTML: <div class="box"> & ...

  8. C# Thread、ThreadPool、Task、Invoke、BeginInvoke、async、await 汇总

    本文将主要通过"同步调用"."异步调用"."异步回调"三个示例来讲解在用委托执行同一个"加法类"的时候的的区别和利弊. ...

  9. Java字符串池(String Pool)深度解析

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 在工作中,String类是我们使用频率非常高的一种对象类型.JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存 ...

  10. JavaScript中子类调用父类方法的实现

    一.前言 最近在项目中,前端框架使用JavaScript面向对象编程,遇到了诸多问题,其中最典型的问题就是子类调用父类(super class)同名方法,也就是如C#中子类中调用父类函数base.** ...