【FFmpeg实战】解复用实战

news2024/9/30 11:28:37

原文链接:https://blog.csdn.net/u014078003/article/details/128554153

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.解复用流程

img

编辑切换为居中

添加图片注释,不超过 140 字(可选)

注意: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);
    }

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

>>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
>>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群: 739729163  领取

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

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

相关文章

【无标题】NXP i.MX 6ULL工业核心板硬件说明书( ARM Cortex-A7,主频792MHz)

1 硬件资源 创龙科技SOM-TLIMX6U是一款基于NXP i.MX 6ULL的ARM Cortex-A7高性能低功耗处理器设计的低成本工业级核心板&#xff0c;主频792MHz&#xff0c;通过邮票孔连接方式引出Ethernet、UART、CAN、LCD、USB等接口。核心板经过专业的PCB Layout和高低温测试验证&…

怎么才能提高自动化测试的覆盖率,华为大佬教你一招!

前言 自动化测试一直是测试人员的核心技能&#xff0c;也是测试的重要手段之一。尤其是在今年所谓的互联网寒冬的行情下&#xff0c;各大企业对测试人员的技术水平要求的很高&#xff0c;而测试人员的技术水平主要集中在三大自动化测试领域&#xff0c;再加测试辅助脚本的编写…

智慧园区能源管理系统建设方案

随着能源资源的日益紧缺和环境保护意识的不断提高&#xff0c;智慧园区能源管理系统建设成为了当前能源管理的热点话题。智慧园区能源管理系统是一种集成化的能源管理平台&#xff0c;可以实现对园区内各种能源的实时监测、分析和管理&#xff0c;从而达到优化能源配置、提高能…

Python 学习之NumPy(一)

文章目录 1.为什么要学习NumPy2.NumPy的数组变换以及索引访问3.NumPy筛选使用介绍筛选出上面nb数组中能被3整除的所有数筛选出数组中小于9的所有数提取出数组中所有的奇数数组中所有的奇数替换为-1二维数组交换2列生成数值5—10&#xff0c;shape 为(3,5)的二维随机浮点数 NumP…

对一大厂游戏测试员的访谈实录,带你了解游戏测试

今天采访了一个在游戏行业做测试的同学&#xff0c;他所在的游戏公司是做大型多人在线角色扮演类的游戏&#xff0c;类似传奇游戏。他所在的公司目前有1200多人&#xff0c;是上市公司&#xff0c;目前游戏产品在国内海外都有市场。 因为我是一个对游戏无感的人&#xff0c;所…

【C++ 程序设计】第 7 章:输入/输出流

目录 一、流类简介 二、标准流对象 三、控制I/O格式 &#xff08;1&#xff09;流操纵符 &#xff08;2&#xff09;标志字 四、调用cout的成员函数【示例一】 五、调用 cin 的成员函数 &#xff08;1&#xff09;get() 函数 &#xff08;2&#xff09;getline()…

高考选什么专业好?适合考公务员的10大热门专业,了解一下!

高考是人生的分水岭&#xff0c;它是青春和未来的交汇处。高考成绩的优劣将对考生未来的发展产生深远的影响。作为学生们人生中重要的一站&#xff0c;高考不仅考验着学生的学业能力&#xff0c;也考验着他们的心理素质和思维能力。 高考结束后&#xff0c;众多考生面临的一个重…

FFmpeg视频转码参数详解

1 固定码率因子crf&#xff08;Constant Rate Factor&#xff09; 固定码率因子&#xff08;CRF&#xff09;是 x264 和 x265 编码器的默认质量&#xff08;和码率控制&#xff09;设置。取值范围是 0 到 51&#xff0c;这其中越低的值&#xff0c;结果质量越好&#xff0c;同…

阿里云docker启动xxljob,部署自己的定时任务

本次安装版本xxl-job-admin:2.3.0 一&#xff1a;创建xxl-job数据库的各种表 作者官方地址 下载sql执行 二&#xff1a;docker拉取xxl-job镜像 docker pull xuxueli/xxl-job-admin:2.3.0 三&#xff1a;docker启动xxl-job服务 docker run -e PARAMS"--spring.datasour…

用C语言实现经典游戏——贪吃蛇

目录 1.游戏实现思想 &#xff08;1&#xff09;定义蛇对象 &#xff08;2&#xff09;食物对象 &#xff08;3&#xff09;分数&#xff1a; &#xff08;4&#xff09;初始化蛇 &#xff08;5&#xff09;初始化食物 &#xff08;6&#xff09;修改控制台光标位置 &…

Spring Data JPA 报 HOUR_OF_DAY: 0 -> 1异常的解决过程和方案

在进行数据查询时&#xff0c;控制台报了Caused by: com.mysql.cj.exceptions.WrongArgumentException: HOUR_OF_DAY: 0 -> 1异常&#xff0c;查询得知&#xff1a;这是由于查mysql库&#xff0c;转换类型为datetime类型的字段引起的。 网上的解决方案有多种&#xff0c;大…

坐标系转换QGIS插件GeoHey

最近要将面要素&#xff08;GCJ02火星坐标系&#xff09;转WGS84&#xff0c;用程序转太麻烦了&#xff0c;找了半天没找到合适的。 插件非常好用&#xff01;&#xff01;&#xff01; 在QGIS中&#xff0c;由极海&#xff08;GeoHey&#xff09;团队提供GeoHey Toolbox插件…

Linux_清理docker容器的log

最近发现服务器硬盘空间满了&#xff0c;就排查了一番&#xff0c;发现有docker容器的log文件占用太多&#xff0c;所以要做一下清理。 首先是要找到docker容器log文件的储存位置。 1、首先在执行了一下 df -Th 命令&#xff0c;发现根目录满了。 2、然后去到根目录下&#xff…

Android项目中接入 Lint代码规范

一、概述 Android Studio 提供了一个名为 Lint 的代码扫描工具,可帮助开发者发现并更正代码结构质量方面的问题,并且无需您实际执行应用,也不必编写测试用例。系统会报告该工具检测到的每个问题并提供问题的描述消息和严重级别,以便开发者可以快速确定需要优先进行的关键改…

Linux下vim的常见命令操作(快速复查)

目录 前言1、Vim常用操作1.1、环境参数1.2、方向1.3、插入命令1.4、定位命令1.5、删除命令1.6、复制和剪切命令1.7、替换和取消命令1.8、搜索和搜索替换命令1.9、保存和退出命令1.10、其他命令1.11、可视模式 前言 本篇文章不面向新手&#xff0c;全文几乎都是命令&#xff0c;…

【Redis】多级缓存之缓存数据同步策略与Canal

目录 一、数据同步策略 1.设置有效期 2.同步双写 3.异步通知 二、Canal 三、监听Canal 一、数据同步策略 缓存数据同步的常见方式有三种&#xff1a; 1.设置有效期 给缓存设置有效期&#xff0c;到期后自动删除。再次查询时更新&#xff0c;他简单、方便&#xff0c;但…

H5学习 (一)--创建工程

文章目录 一、下载安装VS Code二、创建新文件1. 使用cmd N&#xff0c;创建一个文件2. 点击 Select a language&#xff0c;改变文件的编码类型3. 选择HTML 语言模式4. 输入 !按回车键&#xff0c;就会自动生成一个HTML模版5. 右击项目&#xff0c;选择 “Open In Default Bro…

交叉熵、Focal Loss以及其Pytorch实现

交叉熵、Focal Loss以及其Pytorch实现 本文参考链接&#xff1a;https://towardsdatascience.com/focal-loss-a-better-alternative-for-cross-entropy-1d073d92d075 文章目录 交叉熵、Focal Loss以及其Pytorch实现一、交叉熵二、Focal loss三、Pytorch1.[交叉熵](https://pyto…

浅谈商业智能BI的过去、现在和未来

互联网的大发展也在引领各行各业的改变&#xff0c;包括商业智能BI&#xff0c;商业智能BI就是在数字化时代下飞速发展的。商业智能BI与互联网发展的同时&#xff0c;人工智能、大数据、区块链、云计算等新一代信息化、数字化技术也开始进行加速商业智能BI发展及应用&#xff0…

vmareWorkstation-提取vmdk-文件系统

参考博文-CSDN-BugM&#xff08;作者&#xff09;-将vmdk作为硬盘挂载到另一个linux系统的虚拟机上 一、以管理员身份运行wmware-workstation 二、将目的vmdk文件映射到一个linux虚拟机上 选择左下方的添加按钮 添加的文件的路径可以查看需要添加的虚拟机的情况&#xff0c;如…