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

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");elseprintf("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));}elseprintf("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");elseprintf("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));}elseprintf("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");elseprintf("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));}elseprintf("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");elseprintf("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));}elseprintf("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;
}

相关文章:

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

对于一个视频文件(mp4格式/flv格式)&#xff0c;audio_pkt或者video_pkt是其最基本的数据单元&#xff0c;即视频文件是由独立的视频编码包或者音频编码包组成的。 解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件&#xff0c;那么如何得知packet是视频包还是…...

centos7升级gcc到7.3.0

1、下载gcc-7.3.0源码 wget ftp.gnu.org/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.gz 2、解压gcc-7.3.0 tar -xvf gcc-7.3.0.tar.gz3、安装依赖 cd gcc-7.3.0 ./contrib/download_prerequisites ./contrib/download_prerequisites会下载对应的依赖包&#xff0c;如果下载不了的话&a…...

系统运维面试题总结(网络基础类)

系统运维面试题总结&#xff08;网络基础类&#xff09; 网络基础类第七层&#xff1a;应用层第六层&#xff1a;表示层第五层&#xff1a;会话层第四层&#xff1a;传输层第三层&#xff1a;网络层第二层&#xff1a;数据链路层第一层&#xff1a;物理层 类似面试题1、TCP/IP四…...

PO模式登录测试

项目实践 登陆项目测试 get_driver import page from selenium import webdriverclass GetDriver:driver Noneclassmethoddef get_driver(cls):if cls.driver is None:cls.driver webdriver.Edge()cls.driver.maximize_window()cls.driver.get(page.url)return cls.drivercl…...

X86 +PC104+支持WinCE5.0,WinCE6.0,DOS,WinXP, QNX等操作系统,工业控制数据采集核心模块板卡定制

CPU 模块 是一款基于RDC 3306的SOM Express模块。RDC 3306这款X86架构的CPU是一款性能高、稳定性强的处理器。 它是一款灵活精巧的主板&#xff08;尺寸为91.8mm68.6mm&#xff09;&#xff0c;可以灵活的运用于用户的底板&#xff0c;节约开发成本。模块的接插件使用插针形式…...

视频监控汇聚和融合平台的特点、功能、接入方式、应用场景

目录 一、产品概述 二、主要特点 1、多协议支持 2、高度集成与兼容性 3、高性能与可扩展性 4、智能化分析 5、安全可靠 三、功能概述 1. 视频接入与汇聚 2. 视频存储与回放 3. 实时监控与预警 4. 信息共享与联动 5. 远程管理与控制 四、接入方式 1、直接接入 2…...

实习总结 --- 其他业务

一. 回归测试&#xff1a;回归测试与测新是对应的&#xff0c;当需求准入交付测试的时候首先要进行的就是测新&#xff0c;也就是对新功能对测试&#xff0c;一般是在sim环境下测试的&#xff1b;当测新通过后才会进行回归测试&#xff0c;回归测试的目的是为了保证老功能的正确…...

2024年上半年典型网络攻击事件汇总

文章目录 前言一、Ivanti VPN 的0 Day攻击(2024年1月)二、微软公司高管账户泄露攻击(2024年1月)三、Change Healthcare网络攻击(2024年2月)四、ConnectWise ScreenConnect漏洞利用攻击(2024年2月)五、XZ Utils软件供应链攻击(2024年3月)六、AT&T数据泄露攻击(20…...

Ozon、美客多补单测评黑科技:打造无懈可击的自养号补单环境

不管哪个跨境平台的风控都会做升级&#xff0c;相对的补单技术也需要进行相应的做升级&#xff0c;风控升级后&#xff0c;自己养号补单需要注意以下技术问题&#xff0c;以确保补单的稳定性和安全性&#xff1a; 一、物理环境 1. 硬件参数伪装&#xff1a;平台已经开始通过I…...

ES报错:解决too_many_clauses: maxClauseCount is set to 1024 报错问题

解决too_many_clauses: maxClauseCount is set to 1024 报错问题 问题场景报错信息问题分析解决1. 优化查询2. 增加maxClauseCount3. 改用其他查询类型修改后的查询示例 问题场景 查询语句&#xff1a;查询clcNo分类号包含分类O的所有文档 {"match_phrase_prefix":…...

完全指南:在Linux上安装和精通Conda

前言 Conda是一个强大的包管理和环境管理工具&#xff0c;特别适用于数据科学和机器学习项目。本文将详细指导你在Linux系统上安装、配置和充分利用Conda的方法。 步骤一&#xff1a;下载和安装Conda 下载安装包&#xff1a; wget https://repo.anaconda.com/miniconda/Minic…...

# linux 系统中,使用 “ ll “ 命令报错 “ bash ll command not found “ 解决方法:

linux 系统中&#xff0c;使用 " ll " 命令报错 " bash ll command not found " 解决方法&#xff1a; 一、错误描述&#xff1a; 报错原因&#xff1a; 1、这个错误表明你尝试在 bash shell 中执行 ll 命令&#xff0c;但是系统找不到这个命令。ll 通常…...

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.3-2.4

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第二周&#xff1a;机器学习策略&#xff08;2&#xff09;(ML Strategy (2))2.3 快速搭建你的第一个系统&#xff0c;并进行迭代&#xff08;Build your first system quickly…...

【软件测试】快速定位bug,编写测试用例

作为一名测试人员如果连常见的系统问题都不知道如何分析&#xff0c;频繁将前端人员问题指派给后端人员&#xff0c;后端人员问题指派给前端人员&#xff0c;那么在团队里你在开发中的地位显而易见 &#xff0c;口碑、升值、加薪那应该是你遥不可及的梦 但是作为测试人员来说&…...

升级springboot3

坑爹的发版流水线&#xff0c;管天管地&#xff0c;springboot2过了维护期&#xff0c;就催着我们升级。 导致必须上jdk17 记录一下升级需要处理的事情 先升级springboot和cloud&#xff0c;这里定下基调&#xff0c;其他的才好跟着升级 https://spring.io/projects/spring-b…...

视频编解码从H.264到H.266:浅析GB28181安防视频汇聚EasyCVR视频压缩技术

随着信息技术的飞速发展&#xff0c;视频编解码技术也在不断革新&#xff0c;以适应高清、超高清甚至8K视频时代的到来。视频编解码技术作为数字多媒体领域的核心技术之一&#xff0c;也在不断地演进和革新。从早期的H.261到现在的H.265、H.266&#xff0c;每一次技术的升级都极…...

vue项目访问 域名/index.html 空页面问题

很大可能是vue前端没做404页面&#xff0c;在路由不匹配时会跳转到空路由页面。 也可以把所有路由不匹配的网址全部跳转到域名首页。防止出现404或者页面错误。 如果使用docker nginx部署项目&#xff0c;配置文件上会有 try_files $uri $uri/ /index.html; 这段配置会尝试…...

区块链开发入门:基础概念与实施技术详解

区块链开发入门&#xff1a;基础概念与实施技术详解 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 引言 随着区块链技术的快速发展&#xff0c;它已经不再局…...

Rust破界:前端革新与Vite重构的深度透视(下)

Rust破界&#xff1a;前端革新与Vite重构的深度透视&#xff08;下&#xff09; 前端开发者&#xff1a;拥抱 Rust 的策略与时机技能树的扩展 结语&#xff1a;跨界融合的未来展望Vite 重构的深远意义 附录&#xff1a;进一步探索 Rust 的资源指南 前端开发者&#xff1a;拥抱 …...

Android 解决 “Module was compiled with an incompatible version of Kotlin“ 问题

解决 “Module was compiled with an incompatible version of Kotlin” 问题 在Android开发中&#xff0c;有时我们会遇到Kotlin版本不兼容的问题。具体来说&#xff0c;你可能会看到如下错误&#xff1a; D:/.gradle/caches/transforms-3/caf5371a15e0d6ffc362b4a5ece9cd49…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...