当前位置: 首页 > 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…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...