1、音视频解封装流程---解复用

对于一个视频文件(mp4格式/flv格式),audio_pkt或者video_pkt是其最基本的数据单元,即视频文件是由独立的视频编码包或者音频编码包组成的。
解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件,那么如何得知packet是视频包还是音频包呢?有这样一个结构体:

typedef struct AVPacket {
    AVBufferRef *buf;      // 指向数据缓冲区的指针
    int64_t pts;           // 显示时间戳
    int64_t dts;           // 解码时间戳
    uint8_t *data;         // 指向数据缓冲区的指针
    int size;              // 数据缓冲区大小
    int stream_index;      // 数据包所属的流标签
    int flags;             // 数据包的标志位
    AVPacketSideData *side_data; // 侧数据数组
    int side_data_elems;   // 侧数据数组的元素数量
    int64_t duration;      // 数据包的持续时间
    int64_t pos;           // 数据包在输入文件中的位置
    int64_t convergence_duration; // 数据包的收敛持续时间(弃用)
} AVPacket;

AVPacket中的stream_index标记了该包是属于音频流还是视频流,stream_index对应什么值的时候是属于音频流/视频流呢?那就需要解析flv/mp4文件,我们可以通过以下方式获得视频流的相关信息:

	char* in_filename = "/home/yx/media_file/believe.flv";	// 定义媒体流路径

    AVFormatContext *in_file_ctx = NULL;    // 媒体流上下文
    int videoindex = -1;                    // 视频索引
    int audioindex = -1;                    // 音频索引

    int result = avformat_open_input(&in_file_ctx,in_filename,NULL,NULL);   // 打开媒体流(将输入文件与媒体流相关)

    result = avformat_find_stream_info(in_file_ctx,NULL);                   // 查找媒体流信息

    printf("stream number:%d\n",in_file_ctx->nb_streams);                   // 打印媒体流中流种类个数,一般只有两个:音频/视频

    for(uint32_t i = 0;i < in_file_ctx->nb_streams; i++)                    // 遍历两个流
    {
        AVStream* in_stream = in_file_ctx->streams[i];                      // 指定视频流文件中第i个流
        if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            printf("**********音频流**********\n");
            printf("samplerate:%dHz\n",in_stream->codecpar->sample_rate);   // 采样率
            printf("index:%d\n",in_stream->index);                          // 媒体流标签
            printf("channel number:%d\n",in_stream->codecpar->channels);    // 声道数

            if(in_stream->codecpar->format == AV_SAMPLE_FMT_FLTP)           // 采样格式
                printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
            else if(in_stream->codecpar->format == AV_SAMPLE_FMT_S16P)
                printf("sampleformat:AV_SAMPLE_FMT_S16P\n");

            if(in_stream->codecpar->codec_id == AV_CODEC_ID_AAC)            // 打印音频流编码格式
                printf("audio codec:AV_CODEC_ID_AAC\n");
            else if(in_stream->codecpar->codec_id == AV_CODEC_ID_MP3)
                printf("audio codec:AV_CODEC_ID_MP3\n");
            else
                printf("audio codec:%d\n",in_stream->codecpar->codec_id);

            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_audio = (in_stream->duration)*av_q2d(in_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\n");

            audioindex = i;                                                 // 获得音频标签													
        }
        else if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            printf("**********视频流**********\n");
            printf("fps:%lffps\n",av_q2d(in_stream->avg_frame_rate));       // 帧率
            printf("index:%d\n",in_stream->index);                          // 媒体流标签
            printf("width:%d,height:%d\n",in_stream->codecpar->width,in_stream->codecpar->height);    // 声道数

            if(in_stream->codecpar->codec_id = AV_CODEC_ID_MPEG4)
                printf("video codec:MPEG4\n");
            else if(in_stream->codecpar->codec_id = AV_CODEC_ID_H264)
                printf("video codec:H264\n");
            else
                printf("video codec:%d\n",in_stream->codecpar->codec_id);

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

            videoindex = i;                                                 // 获得视频标签
        }
    }

此时我们就获得了解复用最关键的信息:视频流标签和音频流标签,接下来只需要依次读取视频流中的packet,依次判断AVPacket中的stream_index来区分音频或者视频,这里先读取20个packet进行分析:

AVPacket* pkt = av_packet_alloc();
    int pkt_count = 0;                                  // 当前是第0个包
    int print_count = 20;                               // 最大打印十个包的信息

    while(pkt_count<=20)                                // 只解析20个包
    {
        result = av_read_frame(in_file_ctx,pkt);        // 依次从输入视频来读取包
        if(result < 0)
        {
            printf("av_read_frame fail\n");
            break;
        }

        if(pkt_count++ < print_count)
        {
            if(pkt->stream_index == audioindex)
            {
                printf("audioindex:%d\n",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(in_file_ctx->streams[audioindex]->time_base));
            }
            else if(pkt->stream_index == videoindex)
            {
                printf("videoindex:%d\n",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(in_file_ctx->streams[videoindex]->time_base));

            }
        }
        av_packet_unref(pkt);                           // 解析完引用计数-1,自动释放
    }

这里我们读取到视频包或者音频包后,打印包的详细信息:
pts:编码时间戳,dts:解码时间戳,size:包的大小,pos:包当前的位置。
每一个包的相关信息读取之后,调用 av_packet_unref(pkt)使引用计数–,当计数减为0,系统会自动释放该部分空间。
在这里插入图片描述

完整代码如下:

#include <stdio.h>
#include "libavformat/avformat.h"
void demux_flv()
{
    char* in_filename = "/home/yx/media_file/believe.flv";
    printf("输入文件路径%s\n",in_filename);

    AVFormatContext *in_file_ctx = NULL;    // 媒体流上下文
    int videoindex = -1;                    // 视频索引
    int audioindex = -1;                    // 音频索引

    int result = avformat_open_input(&in_file_ctx,in_filename,NULL,NULL);   // 打开媒体流(将输入文件与媒体流相关)
    if(result < 0)
        printf("open file fail\n");

    result = avformat_find_stream_info(in_file_ctx,NULL);                   // 查找媒体流信息
    if(result < 0)
        printf("find stream info fail\n");
    av_dump_format(in_file_ctx,0,in_filename,0);                            // 打印输出媒体流的信息,第1个0表示输出所有流
    printf("media name:%s\n",in_file_ctx->url);
    printf("stream number:%d\n",in_file_ctx->nb_streams);                   // 只有两个流:视频流或者音频流
    printf("media average radio:%lldkps\n",(int64_t)(in_file_ctx->bit_rate/1024));

    int total_seconds,hour,minute,second;
    total_seconds = (in_file_ctx->duration)/AV_TIME_BASE;
    hour = total_seconds/3600;
    minute = (total_seconds % 3600)/60;
    second = (total_seconds % 60);
    printf("total duration: %02d:%02d:%02d\n",hour,minute,second);

    for(uint32_t i = 0;i < in_file_ctx->nb_streams; i++)                    // 遍历两个流
    {
        AVStream* in_stream = in_file_ctx->streams[i];                      // 指定视频流文件中第i个流
        if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            printf("**********音频流**********\n");
            printf("samplerate:%dHz\n",in_stream->codecpar->sample_rate);   // 采样率
            printf("index:%d\n",in_stream->index);                          // 媒体流标签
            printf("channel number:%d\n",in_stream->codecpar->channels);    // 声道数

            if(in_stream->codecpar->format == AV_SAMPLE_FMT_FLTP)           // 采样格式
                printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
            else if(in_stream->codecpar->format == AV_SAMPLE_FMT_S16P)
                printf("sampleformat:AV_SAMPLE_FMT_S16P\n");

            if(in_stream->codecpar->codec_id == AV_CODEC_ID_AAC)            // 打印音频流编码格式
                printf("audio codec:AV_CODEC_ID_AAC\n");
            else if(in_stream->codecpar->codec_id == AV_CODEC_ID_MP3)
                printf("audio codec:AV_CODEC_ID_MP3\n");
            else
                printf("audio codec:%d\n",in_stream->codecpar->codec_id);

            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_audio = (in_stream->duration)*av_q2d(in_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\n");

            audioindex = i;                                                 // 获得音频标签
        }
        else if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            printf("**********视频流**********\n");
            printf("fps:%lffps\n",av_q2d(in_stream->avg_frame_rate));       // 帧率
            printf("index:%d\n",in_stream->index);                          // 媒体流标签
            printf("width:%d,height:%d\n",in_stream->codecpar->width,in_stream->codecpar->height);    // 声道数

            if(in_stream->codecpar->codec_id = AV_CODEC_ID_MPEG4)
                printf("video codec:MPEG4\n");
            else if(in_stream->codecpar->codec_id = AV_CODEC_ID_H264)
                printf("video codec:H264\n");
            else
                printf("video codec:%d\n",in_stream->codecpar->codec_id);

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

            videoindex = i;                                                 // 获得视频标签
        }
    }
    printf("====================================\n");
    AVPacket* pkt = av_packet_alloc();
    int pkt_count = 0;                                  // 当前是第0个包
    int print_count = 20;                               // 最大打印十个包的信息

    while(pkt_count<=20)                                // 只解析20个包
    {
        result = av_read_frame(in_file_ctx,pkt);        // 依次从输入视频来读取包
        if(result < 0)
        {
            printf("av_read_frame fail\n");
            break;
        }

        if(pkt_count++ < print_count)
        {
            if(pkt->stream_index == audioindex)
            {
                printf("audioindex:%d\n",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(in_file_ctx->streams[audioindex]->time_base));
            }
            else if(pkt->stream_index == videoindex)
            {
                printf("videoindex:%d\n",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(in_file_ctx->streams[videoindex]->time_base));

            }
        }
        av_packet_unref(pkt);                           // 解析完引用计数-1,自动释放
    }
}

int main()
{
    demux_flv();
    printf("Hello World!\n");
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

账号和权限的管理1

文章目录 修改用户账号的属性usermod格式常用选项 用户账号的初始化配置文件文件来源主要的用户初始配置文件 组账号文件添加组账号groupadd格式常用选项其他选项 删除组账号groupdel格式 查询账号信息groups格式 id格式 finger格式 W、who、users格式 文件/目录的权限和归属访…

Linux实用命令练习

目录 一、常用命令 二、系统命令 三、用户和组 四、权限 五、文件相关命令 六、查找 七、正则表达式 八、输入输出重定向 九、进程控制 十、其他命令 1、远程文件复制&#xff1a;scp 2、locate查找 3、which命令 4、设置或显示环境变量&#xff1a;export 5、修…

Free Pascal语言基础学习:定义变量、数据类型、循环语句、case语句、条件判断、with语句、运算符

Pascal是一种结构化编程语言&#xff0c;而Free Pascal作为其现代编译器&#xff0c;不仅支持跨多种操作系统和处理器架构&#xff0c;还提供了高效的内存使用和函数重载等先进功能。Free Pascal继承了Pascal语言的核心特性&#xff0c;同时进行了扩展和优化&#xff0c;使其成…

最流行的文件同步软件

PanguFlow是一款免费的文件同步软件&#xff0c;他支持文件的全量同步、支持文件的增量同步、支持文件的实时备份&#xff0c;支持双向同步&#xff0c;支持三向同步甚至多向同步&#xff0c;支持无人值守运行。 PanguFlow数据同步软件下载地址https://pan.baidu.com/s/1GLjFR…

python实现网页自动化(自动登录需要验证的网页)

引言: python作为实现网页自动化的一个重要工具,其强大的各种封装的库使得程序运行更加简洁,只需要下载相应的库,然后调用库中的函数就可以简便的实现我们想要的网页相关操作。 正文: 我的前几篇文章写了关于初学爬虫中比较容易上手的功能,例如爬取静态网页的数据、动…

【Elasticsearch】linux使用supervisor常驻Elasticsearch,centos6.10安装 supervisor

背景&#xff1a; linux服务器&#xff0c;CentOS 6操作系统&#xff0c;默认版本python2.6.6&#xff0c;避免安装过多的依赖不升级python 在网上查的资料python2.6.6兼容supervisor版本 3.1.3 安装supervisor 手动在python官网下载supervisor&#xff0c;并上传到服务器 下…

解锁横向招聘:创新您的人才搜索

技能差距仍然是面试官面临的问题之一。在这些空缺职位中&#xff0c;很难找到合适的技能候选人&#xff0c;特别是高级职位或以上职位。另一方面&#xff0c;申请人也发现很难找到一份适合自己的工作&#xff0c;因为他们抱怨工作要求太窄或太具体。在具有挑战性的职位招聘环境…

扛鼎中国AI搜索,天工凭什么?

人类的创作不会没有瓶颈&#xff0c;但AI的热度可不会消停。 大模型之战依旧精彩&#xff0c;OpenAI选择在Google前一天举行发布会&#xff0c;两家AI企业之间的拉扯赚足了热度。 反观国内&#xff0c;百模大战激发了大家对于科技变革的热切期盼&#xff0c;而如今行业已逐渐…

生成独立的zedboard+ad9361起始项目

文件分享 链接&#xff1a;https://pan.baidu.com/s/17wB_9xVWjO7HhxNvmmZyuA 提取码&#xff1a;94zz 首先下载HDL和NO-OS项目 git clone --recursive https://github.com/analogdevicesinc/hdl git clone --recursive https://github.com/analogdevicesinc/no-OS下载…

用人工智能大模型预报气象,中国气象局示范计划公开征集火热报名中

近日&#xff0c;中国气象局发布了人工智能气象预报大模型示范计划&#xff08;以下简称“示范计划”&#xff09;&#xff0c;推进气象大模型标准规范和有序发展&#xff0c;引导解决预报业务实际难题&#xff0c;促进人工智能气象预报大模型业务的应用转化、准入&#xff0c;…

【Linux】初识操作系统

一、冯•诺依曼体系结构 在学习操作系统之前&#xff0c;我们先来认识一下冯•诺依曼体系结构&#xff0c;我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识的计算机&am…

Navicat上新啦

前言 Navicat&#xff0c;在数据库界&#xff0c;几乎是一个神奇的存在&#xff0c;似乎统治了数据库开发工具的“一片天”。且看下图&#xff1a; 红的蓝的绿的橙的…&#xff0c;可以说&#xff0c;留给它的color不多了。 那么商业BI到服务监控、从云托管到云协作&#xff…

VUE3-Elementplus-form表单-笔记

1. 结构相关 el-row表示一行&#xff0c;一行分成24份 el-col表示列 (1) :span"12" 代表在一行中&#xff0c;占12份 (50%) (2) :span"6" 表示在一行中&#xff0c;占6份 (25%) (3) :offset"3" 代表在一行中&#xff0c;左侧margin份数 el…

5G NR PUSCH物理层过程

物理层过程 加扰 假设要在单个码字q上传输的bit块为 b ( q ) ( 0 ) , . . . , b ( q ) ( M b i t ( q ) − 1 ) b^{(q)}(0),...,b^{(q)}(M_{bit}^{(q)} - 1) b(q)(0),...,b(q)(Mbit(q)​−1) &#xff0c;其中 M b i t ( q ) M_{bit}^{(q)} Mbit(q)​是总比特数&#xff0c;加…

《昇思25天学习打卡营第16天 | 昇思MindSpore基于MobileNetv2的垃圾分类》

16天 本节学习了垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。 MobileNet网络是由Google团队于2017年提出的专注于移动端、嵌入式或IoT设备的轻量级CNN网络&#xff0c;相比于传…

2024.6.30周报

目录 摘要 ABSTRACT 一、文献阅读 一、题目 二、摘要 三、模型架构 四、文章解读 一、Introduction 二、创新点 三、RBM 四、贪心算法 五、实验 六、结论 二、代码复现 总结 摘要 本周我阅读了一篇题目为Generative Pre-Trained Physics-Informed Neural Netwo…

树莓派4B学习笔记16:Python引用自定义模块_简单模块化

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Python 版本3.7.3&#xff1a; 今日学习&#xff1a;Python引用自定义模块 文章提供测试…

【机器学习】机器学习的重要方法——强化学习:理论,方法与实践

目录 一、强化学习的核心概念 二、强化学习算法的分类与示例代码 三.强化学习的优势 四.强化学习的应用与挑战 五、总结与展望 强化学习&#xff1a;理论&#xff0c;方法和实践 在人工智能的广阔领域中&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&…

一文带你了解乐观锁和悲观锁的本质区别!

文章目录 悲观锁是什么&#xff1f;乐观锁是什么&#xff1f;如何实现乐观锁&#xff1f;什么是CAS应用局限性ABA问题是什么&#xff1f; 悲观锁是什么&#xff1f; 悲观锁它总是假设最坏的情况&#xff0c;它会认为共享资源在每次被访问的时候就会出现线程安全问题&#xff0…

SCI二区|北极海鹦优化算法(APO)原理及实现【免费获取Matlab代码】

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2024年&#xff0c;W Wang受到北极海鹦的生存和捕食行为启发&#xff0c;提出了北极海鹦优化算法&#xff08;Arctic Puffin Optimization, APO&#xff09;。 2.算法原理 2.1算法思想 …