当前位置: 首页 > article >正文

RV1126解码—ffmpeg_read_thread线程的讲解

fmpeg_read_thread线程的用处

        解码这一块,在第一篇的时候就看到了,有三个线程,这篇先介绍一个线程的作用是怎么样的。

        ffmpeg_read_thread线程的主要作用是读取每一帧流媒体文件,这个流媒体文件可以是复合流文件。如:TS文件、MP4文件等等,也可以包括网络流媒体文件:如RTMP、RTSP等等。然后抽取每一帧视频帧插入到队列里面。所以就是作用就是读取每一帧视频帧入队。

         下面代码要看一下。

        第一步:avformat_open_input() 打开输入的流媒体文件

#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1

//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
    int ret;
    //线程分离
    pthread_detach(pthread_self());

    //定义出一个大管家
    AVFormatContext* ctx = nullptr;

    //该函数用于打开多媒体数据并且获得一些相关的信息
    ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0); 
    if(ret < 0)
    {
        printf("复合文件或者网络文件打开失败\n");
        return 0;
    }
    printf("==> 流媒体文件输入成功!\n");
    
    
}

        第二步:avformat_find_stream_info() :可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。

#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1

//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
    int ret;
    //线程分离
    pthread_detach(pthread_self());

    //定义出一个大管家
    AVFormatContext* ctx = nullptr;

    //该函数用于打开多媒体数据并且获得一些相关的信息
    ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0); 
    if(ret < 0)
    {
        printf("复合文件或者网络文件打开失败\n");
        return 0;
    }
    printf("==> 流媒体文件输入成功!\n");

    // 可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据
    avformat_find_stream_info(m_pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");

  // 通过av_find_best_stream()函数来获取流的索引 “AVMEDIA_TYPE_VIDEO”找视频的索引
    int nVideo_indx = av_find_best_stream(m_pInFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (nVideo_indx < 0)
    {
      //todo......
    }
    printf("==> av_find_best_stream!\n");
    
    
}

        第一步和第二步就相当于初始化工作已经完成了,接下来就是这个线程最重要的一个函数“av_read_frame()”,

        先来说一下这个“av_read_frame()”,函数的一些底层的逻辑,看看是怎么工作的,

        假设下图是H264的一个连续的视频码流,视频源源不断通摄像头那边传输过来,除非停止。

         假设我把这些连续的码流整体塞给解码器,和下图一样,加密器能否正常解析出来,因为这些码流最终都要送到解码器里面解析的,但是,一下子全部给它能否工作?答案是不行的,在解码器里面是没有办法解析一个整体的数据,整体的数据就是左边的这个东西。因为解码器没有办法提取每一帧数据。

        所以我们要想它解码器能正常工作,我们要在码流中提取一帧一帧数据,送到解码器这里,那怎么提取,就是与“00 00 00 01”为切割符,是一帧数据的起始符。

        所以这也也就是av_read_frame的作用,就是提取视频里中的每一帧数据,这个时候解码器就可以工作了。

av_read_frame:这个API的具体作用是读取码流中提取一帧完整的视频帧或者音频帧。对于编解码器来说,开发者需要获取一帧完整的数据帧才能够对其进行数据的编解码

#define LOCAL_TS "test_local.ts" //本地文件地址./ymx,也可以是网络地址例如:rtsp://172.0.0.1:8554/rtsp1

//读取每一帧流文件,然后抽取每一帧视频帧插入到队列里面
void* ffmpeg_read_thread(void* args)
{
    int ret;
    //线程分离
    pthread_detach(pthread_self());

    //定义出一个大管家
    AVFormatContext* ctx = nullptr;

    //该函数用于打开多媒体数据并且获得一些相关的信息
    ret = avformat_open_input(&ctx, LOCAL_TS, 0, 0); 
    if(ret < 0)
    {
        printf("复合文件或者网络文件打开失败\n");
        return 0;
    }
    printf("==> 流媒体文件输入成功!\n");

    // 可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据
    avformat_find_stream_info(m_pInFmtCtx, nullptr);
    printf("===========Input Information==========\n");

  // 通过av_find_best_stream()函数来获取流的索引 “AVMEDIA_TYPE_VIDEO”找视频的索引
    int nVideo_indx = av_find_best_stream(m_pInFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (nVideo_indx < 0)
    {
      //todo......
    }
    printf("==> av_find_best_stream!\n");

    AVPacket *packet = av_packet_alloc();

    // 通过av_read_frame获取每一帧视频帧
    while (av_read_frame(m_pInFmtCtx, packet) >= 0)
    {
      if (packet->stream_index == nVideo_indx)//找到这个索引看看是视频的还是音频
      {
        video_data_packet_t *video_data_packet = (video_data_packet_t 
  *)malloc(sizeof(video_data_packet_t));
        memcpy(video_data_packet->buffer, packet->data, packet->size);
        video_data_packet->video_frame_size = packet->size;
        //入队
        video_queue->putVideoPacketQueue(video_data_packet);
    }
  }

  return NULL;
    
    
}

         


http://www.kler.cn/a/554541.html

相关文章:

  • 什么是pytest.ini及如何在Pytest中应用以提升配置效率
  • 万能头文件
  • Profinet 转 EtherNet/IP 如何解锁西门子 1500 与罗克韦尔 PLC 通讯潜能
  • Emacs折腾日记(十三)——函数、宏以及命令
  • java每日精进 2.13 MySql迁移人大金仓
  • uniapp 使用unplugin-auto-import 后, vue文件报红问题
  • 一文讲清 AIO BIO NIO的区别
  • hive迁移补数脚本细粒度 表名-分区唯一键
  • Linux内核实时机制7 - 实时改造机理 -软中断优化上
  • php 系统命令执行及绕过
  • shell编程总结
  • 树莓派4基于Debian GNU/Linux 12 (Bookworm)添加多个静态ipv4网络
  • 设计模式教程:享元模式(Flyweight Pattern)
  • 实时股票行情接口与WebSocket行情接口的应用
  • python中格式化输出知识点汇总
  • 使用API有效率地管理Dynadot域名,为域名部署DNS安全拓展(DNSSEC)
  • 视觉目标检测之小目标检测技术调研与实验
  • 网络安全技术pat实验 网络安全 实验
  • 当滑动组件连续触发回调函数的三种解决办法
  • 相比于WebSocket,SSE更适合轻量级