FFmpeg 解复用实战

news2024/9/28 23:34:16

1.封装格式相关函数

  • avformat_alloc_context():负责申请一个AVFormatContext结构的内存,并进行简单初始化,这个函数可以不用手动调用,内部会自动调用。

  • avformat_free_context():释放该结构里的所有东西以及该结构本身,如果不手动alloc,这个也可以不用调用。

  • avformat_open_input():打开输入视频文件,如果传入的AVFormatContext未进行初始化,内部会进行初始化。

    int avformat_open_input(AVFormatContext **ps, const char *filename,
                            ff_const59 AVInputFormat *fmt, AVDictionary **options)
    {
        AVFormatContext *s = *ps;
        int i, ret = 0;
        AVDictionary *tmp = NULL;
        ID3v2ExtraMeta *id3v2_extra_meta = NULL;
    
        if (!s && !(s = avformat_alloc_context()))
            return AVERROR(ENOMEM);
        if (!s->av_class) {
            av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
            return AVERROR(EINVAL);
        }
        ...
    }
    
  • avformat_close_input():关闭解复用器。内部会自动调用avformat_free_context 进行释放。

void avformat_close_input(AVFormatContext **ps)
{
    AVFormatContext *s;
    AVIOContext *pb;

    if (!ps || !*ps)
        return;

    s  = *ps;
    pb = s->pb;

    if ((s->iformat && strcmp(s->iformat->name, "image2") && s->iformat->flags & AVFMT_NOFILE) ||
        (s->flags & AVFMT_FLAG_CUSTOM_IO))
        pb = NULL;

    flush_packet_queue(s);

    if (s->iformat)
        if (s->iformat->read_close)
            s->iformat->read_close(s);

    avformat_free_context(s);

    *ps = NULL;

    avio_close(pb);
}
  • avformat_find_stream_info():获取视频文件信息

  • av_read_frame(): 读取音视频包,得到的是AVPacket

  • avformat_seek_file():定位文件

  • av_seek_frame():定位文件

2.解复用流程

在这里插入图片描述

注意:flv封装格式的视频,如果不调用avformat_find_stream_info解复用拿不到音视频帧的信息,mp4能拿到

由于需要读取数据包,avformat_find_stream_info接口会带来很大的延迟。

3.得到音频视频流index

新版本的ffmpeg可以通过av_find_best_stream来得到相应的index

   int video_index = av_find_best_stream(av_format_cxontext, AVMEDIA_TYPE_VIDEO,
                                          -1,-1, NULL, 0);
    int audio_index=av_find_best_stream(av_format_cxontext,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
    printf("使用av_find_best_stream方式 video_index=%d,audio_index=%d \n",video_index,audio_index);

旧版本的ffmpeg也可以通过遍历streams的方式来拿到

 for(int i=0;i<av_format_cxontext->nb_streams;i++){
        AVStream* av_stream= av_format_cxontext->streams[i];  //可能为音频流 、视频流 、字幕流

        if(AVMEDIA_TYPE_AUDIO==av_stream->codecpar->codec_type){
            printf("-----audio info -----\n");
            //index 是解服用之后每个流的唯一身份标识
            printf("audio index is %d \n",av_stream->index);
            audioindex = i; // 获取音频的索引
        }else if(AVMEDIA_TYPE_VIDEO==av_stream->codecpar->codec_type){
            printf("----- 视频信息 -----\n");
            videoindex = i;
        }
    }

4.ffmpeg实战解封装

 // AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *av_format_cxontext = NULL;  // 输入文件的demux
    int videoindex = -1;  // 视频索引
    int audioindex = -1;  // 音频索引

    // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
    int ret = avformat_open_input(&av_format_cxontext, filename, NULL, NULL);
    if (ret < 0) {
        char buf[1024] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("open %s failed:%s\n", filename, buf);
        goto failed;
    } else {
        printf("avformat_open_input 成功 ret is %d\n", ret);
    }
    //查找流信息
    ret = avformat_find_stream_info(av_format_cxontext,NULL);
    if(ret<0){ //失败
        char buf[1024]={0};
        av_strerror(ret,buf,sizeof (buf)-1);
        printf("avformat_find_stream_info %s failed:%s\n",filename,buf);
        goto failed;
    }else{
        printf("avformat_find_stream_info 成功 %d  \n",ret);
    }

    printf("\n ====av_dump_format filename is %s====\n",filename);
    av_dump_format(av_format_cxontext,0,filename,0);
    printf("\n====av_dump_format finish ====\n\n");

    // url: 调用avformat_open_input读取到的媒体文件的路径/名字
    printf("media name is %s\n",av_format_cxontext->url);

    printf("stream number is %d \n",av_format_cxontext->nb_streams);

    printf("media average ratio:%lldkbps \n",(int64_t)av_format_cxontext->bit_rate/1024);


    int total_seconds,hour,minute,second;

    //duration:媒体文件的长度  单位微妙
    total_seconds=av_format_cxontext->duration/AV_TIME_BASE;
    hour=total_seconds/3600;
    minute=(total_seconds%3600)/60;
    second=(total_seconds%60);

    printf("total duration is : %02d:%02d:%02d\n\n",hour,minute,second);

    //    av_find_best_stream() 这个是新版本的ffmpeg 获取流的方式
    //老的方式还是通过遍历的方式获取音频流以及视频流

    int video_index = av_find_best_stream(av_format_cxontext, AVMEDIA_TYPE_VIDEO,
                                          -1,-1, NULL, 0);

    int audio_index=av_find_best_stream(av_format_cxontext,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);

    printf("使用av_find_best_stream方式 video_index=%d,audio_index=%d \n",video_index,audio_index);

    for(int i=0;i<av_format_cxontext->nb_streams;i++){
        AVStream* av_stream= av_format_cxontext->streams[i];  //可能为音频流 、视频流 、字幕流

        if(AVMEDIA_TYPE_AUDIO==av_stream->codecpar->codec_type){
            printf("-----audio info -----\n");
            //index 是解服用之后每个流的唯一身份标识
            printf("audio index is %d \n",av_stream->index);

            printf("sample rate %dHZ \n",av_stream->codecpar->sample_rate);

            printf("av_stream->codecpar->format is %d \n",av_stream->codecpar->format);
            switch(av_stream->codecpar->format){
            case AV_SAMPLE_FMT_FLTP:
                printf("audio format is AV_SAMPLE_SLTP \n");
                break;
            case AV_SAMPLE_FMT_S16:
                printf("audio format is AV_SAMPLE_FMT_S16 \n");
                break;
            case AV_SAMPLE_FMT_S32:
                printf("audio format is AV_SAMPLE_FMT_S32 \n");
                break;
            case AV_SAMPLE_FMT_S16P:
                printf("audio format is AV_SAMPLE_FMT_S16P \n");
                break;
            default:
                printf("audio format is other \n");
                break;
            }

            //音频通道数量
            printf("channel number is %d \n",av_stream->codecpar->channels);



            switch(av_stream->codecpar->codec_id){
            case AV_CODEC_ID_AAC:
                printf("音频压缩编码格式 是AV_CODEC_ID_AAC \n");
                break;
            case AV_CODEC_ID_MP3:
                printf("音频压缩编码格式 AV_CODEC_ID_MP3 \n");
                break;
            default:
                printf("音频压缩编码格式 %d \n",av_stream->codecpar->codec_id);
                break;
            }

            //音频总时长单位是秒  注释如果把时间单位放大为毫秒或者微妙  音频总时长根音频总时长不一定相等

            if(av_stream->duration!=AV_NOPTS_VALUE){

                int duration_audio=(av_stream->duration)*av_q2d(av_stream->time_base);
                printf("audio duration: %02d:%02d:%02d\n",
                       duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
            }else{
                printf("audio duration unknown");
            }

            audioindex = i; // 获取音频的索引

        }else if(AVMEDIA_TYPE_VIDEO==av_stream->codecpar->codec_type){
            printf("----- 视频信息 -----\n");
            printf("视频流index:%d\n", av_stream->index);
            printf("fps is %lffps \n",av_q2d(av_stream->avg_frame_rate));

            switch(av_stream->codecpar->codec_id){
            case AV_CODEC_ID_H264:
                printf("video codec:H264\n");
                break;
            case AV_CODEC_ID_MPEG4:
                 printf("video codec:MPEG4\n");
                break;
            default:
                printf("video codec_id:%d\n", av_stream->codecpar->codec_id);
                break;
            }

            printf("视频width:%d,height:%d\n",av_stream->codecpar->width,av_stream->codecpar->height);
            if(av_stream->duration!=AV_NOPTS_VALUE){
                int video_duration=av_stream->duration*av_q2d(av_stream->time_base);

                printf("video duration: %02d:%02d:%02d\n",
                       video_duration / 3600,
                       (video_duration % 3600) / 60,
                       (video_duration % 60)); //将视频总时长转换为时分秒的格式打印到控制台上         }else{
               printf("video duration unknown");
            }

            videoindex = i;

        }

    }

    AVPacket* pkt=av_packet_alloc();
    int pkt_count=0;
    int print_max_count = 6;
    printf("------start av_read_frame------\n");
    while(1){
        ret= av_read_frame(av_format_cxontext,pkt);
        if(ret<0){
            printf("av read frame end\n");
            break;
        }

        if(pkt_count++<print_max_count){
            if(pkt->stream_index==audioindex){
                printf("audio pts:%lld\n",pkt->pts);
                printf("audio dts:%lld\n",pkt->dts);
                printf("audio size:%d\n",pkt->size);
                printf("audio pos:%lld\n",pkt->pos);
                printf("audio duration::%lf\n\n",pkt->duration*av_q2d(av_format_cxontext->streams[audioindex]->time_base));
            }else if(pkt->stream_index==videoindex){
                printf("video pts:%lld\n",pkt->pts);
                printf("video dts:%lld\n",pkt->dts);
                printf("video size:%d\n",pkt->size);
                printf("video pos:%lld\n",pkt->pos);
                printf("video duration: %lf\n\n",
                       pkt->duration * av_q2d(av_format_cxontext->streams[videoindex]->time_base));
            }else{
                printf("unknown stream_index:%d\n",pkt->stream_index);
            }
        }
        av_packet_unref(pkt);
    }
    if(pkt){
        av_packet_free(pkt);
    }
failed:
    if(av_format_cxontext){
        avformat_close_input(&av_format_cxontext);
    }

通过解封装可以得到音频视频流相关的信息以及解码器相关的信息,这些信息会影响视频、音频的解码以及显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

再学C语言24:分支和跳转——逻辑运算符和条件运算符

一、多重选择else if 功能&#xff1a;在两个以上的语句中作出选择 示例代码&#xff1a; #include <stdio.h> int main(void) {float score;printf("Please enter your score: \n");scanf("%f", &score);if(score < 60)printf("不及…

SSM基础整合

SSM基础整合 文章目录SSM基础整合[toc]SSM基础整合SSM整合流程表现层数据封装设置统一数据返回结果类定义code类优化Controller优化后的返还结果异常处理器异常处理项目异常处理方案项目异常分类处理SSM基础整合 SSM整合流程 创建工程 SSM整合 Spring SpringConfig Configur…

CAS 的使用场景 CAS的ABA问题的优化 以及 synchronized 的三大优化

目录 &#x1f388;专栏链接:多线程相关知识详解 一.什么是CAS 二.CAS最常用的两个场景 Ⅰ.实现原子类 Ⅱ.实现自旋锁 三.CAS的ABA问题 四.优化解决CAS的ABA问题 五.synchronized的优化 Ⅰ.锁升级/锁膨胀 Ⅱ.锁消除 Ⅲ.锁粗化 一.什么是CAS CAS&#xff08;Compar…

p2深度学习基本概念简介下笔记

3. 第 1 讲&#xff1a;深度学习基本概念简介下_哔哩哔哩_bilibili 线性模型太简单&#xff0c;我们需要比较复杂的模型。 不管怎样弄&#xff0c;蓝色描述不出红色的线 强调model bias 无法模拟真实的状况 写出一个有未知参数更复杂的model 观察红色的曲线 &#xff1a;可…

你知道数据在内存中是如何存储的嘛?

&#x1f996;作者&#xff1a;学写代码的恐龙 &#x1f996;博客主页&#xff1a;学写代码的恐龙博客主页 &#x1f996;专栏&#xff1a;【初级c语言】 &#x1f996;语录&#xff1a;❀未来的你&#xff0c;一定会感谢现在努力奋斗的自己❀ 深度剖析数据在内存中的存储一、…

数据结构精讲——排序(二)

数据结构精讲——排序&#xff08;二&#xff09; 排序的分类 上节课我们已经了解过插入排序和选择排序&#xff0c;这节课主要讲的是快排排序&#xff0c;冒泡排序应该都很熟悉&#xff0c;少提一下吧。 冒泡排序 冒泡排序是我们刚接触编程时就学习的排序方法&#xff08;还…

达梦数据库实施、运维与使用经验(一)

一、使用Xshell输入Insert语句&#xff0c;回车执行系统没反应 问题解决思路 查下数据库的活动会话数并登录数据库服务器看数据库的资源负载 select * from v$sessions where stateACTIVE;在数据库Xshell中输入语句查询数据库磁盘是否满了 df -lh上图中的第一个命令是用来查…

C++的IO流

目录 1. 流是什么 2. CIO流 2.1 C标准IO流 2.2 C文件IO流 2.2.1 简单读取文件流 2.2.2 C读取文件巧妙之处 3. stringstream的简单介绍 1. 流是什么 “流”即是流动的意思&#xff0c;是物质从一处向另一处流动的过程&#xff0c;是对一种有序连续且具有方向性的数据&am…

【我的渲染技术进阶之旅】关于OpenGL纹理压缩的相关资料

文章目录一、为啥要了解压缩纹理1.1 为啥要使用压缩纹理1.2 如何自定义压缩纹理以及使用压缩纹理的效果1.2.1 使用压缩纹理节省显存1.2.2 自定义压缩纹理&#xff1a;将压缩好的纹理数据保存在本地1.2.3 使用自定义的压缩纹理1.2.3 示例原理二、纹理压缩相关知识2.0 什么是压缩…

FOLOLane论文阅读

论文地址&#xff1a;https://arxiv.org/pdf/2105.13680.pdf 一、论文创新点 主流的车道线检测方法使用语义分割和聚类实现&#xff0c;但像素级的输出存在信息冗余&#xff0c;同时会带来大量噪声。而该论文使用两个分支&#xff0c;一个输出heatmap用于判断像素是否是关键点…

干货 | 初窥 Pytest 测试框架,基础薄弱也能轻松 hold 住

pytest 是一个成熟的全功能Python测试工具&#xff0c;可以帮助您编写更好的程序。它与 python 自带的 unittest 测试框架类似&#xff0c;但 pytest 使用起来更简洁和高效&#xff0c;并且兼容 unittest 框架。pytest 能够支持简单的单元测试和复杂的功能测试&#xff0c;pyte…

java中修改List的对象元素时碰到的坑

坑 case1 case1:Data class Person {Integer age;String name;public Person(Integer age, String name) {this.age age;this.name name;} } List<Person> v1List new ArrayList<>(); v1List.add(new Person(11, "小刚")); v1List.add(new Person(1…

前端基础(五)_运算符(算术运算符、赋值运算符、比较运算符、逻辑运算符、三目运算符、运算符优先级和结合性、数据类型的隐式转换)

一、算术运算符 算术运算符即&#xff1a;加&#xff08;&#xff09;减&#xff08;-&#xff09;乘&#xff08;*&#xff09;除&#xff08;/&#xff09;取余&#xff08;%&#xff09;加加&#xff08;&#xff09;减减&#xff08;–&#xff09; 算术运算符里比较特殊的…

CleanMyMacX软件有哪些优缺点?值不值得下载

CleanMyMac X 2023是一款可靠且功能强大的Mac清洁工具工具&#xff0c;他可以让你随时检查Mac电脑的健康情况&#xff0c;并删除电脑中的垃圾文件&#xff0c;来腾出存储空间&#xff0c;保持Mac系统的整洁。至问世以来&#xff0c;CleanMyMac 系统倍受国内外用户推崇&#xff…

【4】SCI易中期刊推荐——神经科学研究(中科院4区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…

C#使用迷宫地图来模拟新冠疫情的传播速度(一)

国家开始发布疫情放开政策&#xff0c;本人于2022-12-21开始感染并发高烧。 最近才康复。有感于此 我们用初始感染源来影响九宫网格来查看新冠的传播速度 小游戏规则如下&#xff1a; 一个感染源 可以传播附近相邻的8个网格【类似于扫雷】&#xff0c;假如每个感染源一天只…

Vue--》setup、ref、reactive函数使用讲解

目录 setup ref函数 reactive函数 Vue3中的响应式原理 setup Vue3中的一个新的配置项&#xff0c;值为一个函数。组件中所用到的数据、方法等等&#xff0c;均要配置在setup中。setup函数的两种返回值&#xff0c;如下&#xff1a; 若返回一个对象&#xff0c;则对象中的…

使用Docker快速搭建Hfish蜜罐

HFish简介 HFish是一款社区型免费蜜罐&#xff0c;侧重企业安全场景&#xff0c;从内网失陷检测、外网威胁感知、威胁情报生产三个场景出发&#xff0c;为用户提供可独立操作且实用的功能&#xff0c;通过安全、敏捷、可靠的中低交互蜜罐增加用户在失陷感知和威胁情报领域的能…

源码讲解ThreadLocal父子线程通信问题(图+文+源码)

1 缘起 在复习ThreadLocal相关应用的知识&#xff0c; 有一个老生常谈的问题&#xff1a;父子线程通信&#xff0c; 起初&#xff0c;对于父子线程通信&#xff0c;仅了解ThreadLocal无法通过子线程获取线程数据&#xff0c; 并不了解为什么会这样&#xff1f;以及为什么Inher…

15.4 宏任务和微任务

宏任务和微任务 start 如果彻底理解了事件循环&#xff0c;其实大多数 JS 执行的逻辑都能理解了但是在 ES6 中引入了 Promise, 就引出了两个新概念&#xff0c;宏任务和微任务。 1.宏任务和微任务 1.1 名词 宏任务&#xff1a;macrotask 微任务&#xff1a;microtask 在 E…