FFmpeg5.0源码阅读—— avcodec_send_frame && avcodec_receive_packet

这篇具有很好参考价值的文章主要介绍了FFmpeg5.0源码阅读—— avcodec_send_frame && avcodec_receive_packet。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  摘要:本文主要描述了FFmpeg中用于编码的接口的具体调用流程,详细描述了该接口被调用时所作的具体工作。
  关键字ffmpegavcodec_send_frameavcodec_receive_packet
  读者须知:读者需要了解FFmpeg的基本使用流程,以及一些FFmpeg的基本常识,了解FFmpegIO相关的内容,以及大致的解码流程。

1 avcodec_send_frame

  avcodec_send_frame用于在编码时将一帧raw数据发送给编码器,其基本的调用流程比较简单,主要工作就是将输入的数据ref到Internal Frame上。
FFmpeg5.0源码阅读—— avcodec_send_frame && avcodec_receive_packet,ffmpeg,音视频,ffmpeg

  avcodec_send_frame首先检查当前的codec是不是编码器且是否打开,并且检查codec中的buffer是否有数据没有,有的话就意味着上一帧的数据还没处理完需要等待这一帧处理完才能继续发送。

    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
        return AVERROR(EINVAL);

    if (avci->draining)
        return AVERROR_EOF;

    if (avci->buffer_frame->data[0])
        return AVERROR(EAGAIN);

  然后是根据输入的frame是否为空来设置标志位,如果为空就表示是最后一帧数据后续的数据就无效了。能够看到在最后如果codec中的packet buffer是空的就会尝试获取一帧packet。

    if (!frame) {
        avci->draining = 1;
    } else {
        ret = encode_send_frame_internal(avctx, frame);
        if (ret < 0)
            return ret;
    }

    if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data) {
        ret = encode_receive_packet_internal(avctx, avci->buffer_pkt);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
            return ret;
    }

  encode_send_frame_internal比较简单,主要就是针对音频数据进行参数检查并对数据进行填充,最后调用av_frame_ref将输入的数据的引用计数+1、

2 avcodec_receive_packet

2.1 基本流程

  avcodec_receive_packet相对复杂一点点儿,下面是其调用流程图。
FFmpeg5.0源码阅读—— avcodec_send_frame && avcodec_receive_packet,ffmpeg,音视频,ffmpeg

  首先是检查当前codec是否为编码器并且是否打开,如果是就继续。然后检查codec中的packet buffer是否有数据有的话就直接返回了,不然就会调用encode_receive_packet_internal

int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt){
    AVCodecInternal *avci = avctx->internal;
    int ret;

    av_packet_unref(avpkt);

    if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec))
        return AVERROR(EINVAL);

    if (avci->buffer_pkt->data || avci->buffer_pkt->side_data) {
        av_packet_move_ref(avpkt, avci->buffer_pkt);
    } else {
        ret = encode_receive_packet_internal(avctx, avpkt);
        if (ret < 0)
            return ret;
    }

    return 0;
}

  encode_receive_packet_internal首先就是参数检查,然后根据codec的函数指针设置看调用哪个流程获取编码流。encode_simple_receive_packet就是个while循环调用encode_simple_internal直到获取编码数据或者出错为止。

    if (avctx->codec->receive_packet) {
        ret = avctx->codec->receive_packet(avctx, avpkt);
        if (ret < 0)
            av_packet_unref(avpkt);
        else
            // Encoders must always return ref-counted buffers.
            // Side-data only packets have no data and can be not ref-counted.
            av_assert0(!avpkt->data || avpkt->buf);
    } else
        ret = encode_simple_receive_packet(avctx, avpkt);

  encode_simple_internal除了前面一大坨参数检查,主要救赎下面这块儿,看是利用多线程编码还是利用codec的encode接口编码。

    if (CONFIG_FRAME_THREAD_ENCODER &&
        avci->frame_thread_encoder && (avctx->active_thread_type & FF_THREAD_FRAME))
        /* This might modify frame, but it doesn't matter, because
         * the frame properties used below are not used for video
         * (due to the delay inherent in frame threaded encoding, it makes
         *  no sense to use the properties of the current frame anyway). */
        ret = ff_thread_video_encode_frame(avctx, avpkt, frame, &got_packet);
    else {
        ret = avctx->codec->encode2(avctx, avpkt, frame, &got_packet);
        if (avctx->codec->type == AVMEDIA_TYPE_VIDEO && !ret && got_packet &&
            !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
            avpkt->pts = avpkt->dts = frame->pts;
    }

2.2 多线程

  编码的线程和解码的线程一样都是在avcodec_open2时创建的,编码是调用ff_frame_thread_encoder_init创建的,其中主要就是调用pthread的接口创建线程和相关的参数,可以看到其工作的函数为static void * attribute_align_arg worker(void *v),编码过程中有多个线程每个线程都运行一个worker任务,通过信号量来进行消息的同步。该任务中最终会调用avctx->codec->encode2对数据进行编码。而所有的数据交互都是通过ThreadContext进行的,无论是输入数还是输出的数据还是消息同步都是通过该Context进行的。

typedef struct{
    AVCodecContext *parent_avctx;
    pthread_mutex_t buffer_mutex;

    pthread_mutex_t task_fifo_mutex; /* Used to guard (next_)task_index */
    pthread_cond_t task_fifo_cond;

    unsigned max_tasks;
    Task tasks[BUFFER_SIZE];
    pthread_mutex_t finished_task_mutex; /* Guards tasks[i].finished */
    pthread_cond_t finished_task_cond;

    unsigned next_task_index;
    unsigned task_index;
    unsigned finished_task_index;

    pthread_t worker[MAX_THREADS];
    atomic_int exit;
} ThreadContext;

  当数据到达时主线程会先拷贝数据然后发送信号量signal给任务线程,任务线程拿到消息后编码完成后给主线程发信号finish,主线程取走数据。文章来源地址https://www.toymoban.com/news/detail-604015.html

到了这里,关于FFmpeg5.0源码阅读—— avcodec_send_frame && avcodec_receive_packet的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FFmpeg5.0源码阅读——av_interleaved_write_frame

       摘要 :本文主要详细描述FFmpeg中封装时写packet到媒体文件的函数 av_interleaved_write_frame 的实现。    : av_interleaved_write_frame    读者须知 :读者需要熟悉ffmpeg的基本使用。    av_interleaved_write_frame 的基本调用流程图如下。   首先就是根据输入数据是否为空

    2024年02月14日
    浏览(47)
  • FFmpeg5.0源码阅读——FFmpeg大体框架

       摘要 :前一段时间熟悉了下FFmpeg主流程源码实现,对FFmpeg的整体框架有了个大概的认识,因此在此做一个笔记,希望以比较容易理解的文字描述FFmpeg本身的结构,加深对FFmpeg的框架进行梳理加深理解,如果文章中有纰漏或者错误欢迎指出。本文描述了FFmpeg编解码框架的

    2024年02月11日
    浏览(41)
  • FFmpeg5.0源码阅读——URLContext和URLProtocol

       摘要 :本文描述FFmpeg中URLContext和URLProtocal的实现。    :URLContext、URLProtocal   FFmpeg中 URLProtocol 是具体的协议的抽象,其中定义了对应协议的抽象,其中包含了具体协议的操作函数指针。而 URLContext 是对协议操作的抽象,描述了当前协议的读写状态。和其他结

    2024年02月10日
    浏览(32)
  • FFmpeg5.0源码阅读之AVClass和AVOption

       摘要 :本文通过阅读FFmpeg源码来理解FFmpeg中AVOption的实现原理和具体的使用方式。    :AVClss,AVOption,AVOptionRange    版本 :FFmpeg5.0    AVOption 是FFmpeg中设置参数的一个基本抽象结构。因为FFmpeg是一个支持多种封装解封装器,编解码器的框架,而不同的外部库

    2023年04月08日
    浏览(46)
  • FFmpeg5.0源码阅读——FFmpeg大体框架(以GIF转码为示例)

       摘要 :前一段时间熟悉了下FFmpeg主流程源码实现,对FFmpeg的整体框架有了个大概的认识,因此在此做一个笔记,希望以比较容易理解的文字描述FFmpeg本身的结构,加深对FFmpeg的框架进行梳理加深理解,如果文章中有纰漏或者错误欢迎指出。本文描述了FFmpeg编解码框架的

    2024年02月10日
    浏览(38)
  • FFmpeg5.0源码阅读——avformat_open_input

       摘要 :本文主要描述了FFmpeg中用于打开文件接口 avformat_open_input 的具体调用流程,详细描述了该接口被调用时所作的具体工作。    : ffmpeg 、 avformat_open_input    注意 :读者需要了解FFmpeg的基本使用流程,以及一些FFmpeg的基本常识,了解FFmpegIO相关的内容,

    2024年02月11日
    浏览(34)
  • 【Android音视频】MacOS上FFmpeg5.0.1编译

    1. FFmpeg官网下载链接(推荐下载release的版本): Download FFmpeg http://ffmpeg.org/download.html#releases  尽情去下载并开始编译吧 2. 下载压缩包,解压至自己想要的文件路径下即可。个人习惯用全英文路径,避免出现奇怪的问题。 3. Android Studio请预先下载好。点击AS右上角“SDK Manager”

    2024年02月02日
    浏览(40)
  • FFmpeg5.1.3编译动态库踩坑之旅(基于Linux虚拟机)

    环境准备 1.Windows安装Oracle VM VirtualBox 7.0.10,安装ubuntu-22.04.3。 坑一 :无法往虚拟机里拖放复制文件,解决办法:登录Ubuntu虚拟机时切换到xorg方式登录,参考地址:Ubuntu Desktop 22.04 无法实现拖放复制操作解决办法-CSDN博客 下载文件 下载ndk25 官网下载:ndk官网 网盘下载:andr

    2024年02月07日
    浏览(38)
  • (02)Cartographer源码无死角解析-(78) ROS数据发布→2D点云数据、tf、机器人tracking frame轨迹发布

    讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/127350885   文末正下方中心提供了本人 联系方式, 点击本人照片

    2024年02月12日
    浏览(43)
  • FFMPEG源码之ffmpeg.c解析

    下面是对每个步骤的功能的详细解释: 初始化动态加载。 调用init_dynload函数,用于初始化动态加载库的相关资源,以便在需要时加载需要的库。 注册退出回调函数。 调用register_exit函数,将ffmpeg_cleanup函数注册为在程序退出时被调用的回调函数。 设置stderr的缓冲模式。 调用

    2024年02月15日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包