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格式),audio_pkt或者video_pkt是其最基本的数据单元,即视频文件是由独立的视频编码包或者音频编码包组成的。 解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件,那么如何得知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会下载对应的依赖包,如果下载不了的话&a…...

系统运维面试题总结(网络基础类)
系统运维面试题总结(网络基础类) 网络基础类第七层:应用层第六层:表示层第五层:会话层第四层:传输层第三层:网络层第二层:数据链路层第一层:物理层 类似面试题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是一款性能高、稳定性强的处理器。 它是一款灵活精巧的主板(尺寸为91.8mm68.6mm),可以灵活的运用于用户的底板,节约开发成本。模块的接插件使用插针形式…...

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

实习总结 --- 其他业务
一. 回归测试:回归测试与测新是对应的,当需求准入交付测试的时候首先要进行的就是测新,也就是对新功能对测试,一般是在sim环境下测试的;当测新通过后才会进行回归测试,回归测试的目的是为了保证老功能的正确…...

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、美客多补单测评黑科技:打造无懈可击的自养号补单环境
不管哪个跨境平台的风控都会做升级,相对的补单技术也需要进行相应的做升级,风控升级后,自己养号补单需要注意以下技术问题,以确保补单的稳定性和安全性: 一、物理环境 1. 硬件参数伪装:平台已经开始通过I…...

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

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

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

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.3-2.4
目录 第三门课 结构化机器学习项目(Structuring Machine Learning Projects)第二周:机器学习策略(2)(ML Strategy (2))2.3 快速搭建你的第一个系统,并进行迭代(Build your first system quickly…...

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

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

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

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

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

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

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

linux nfs的使用
版权声明:来自百度AI,此处记录是方便日后查看,无任何商业用途 linux网络文件共享服务之nfs NFS(Network File System)是一种允许计算机用户或者操作系统通过网络以类似本地的方式访问文件的协议。以下是一个简单的NF…...

eclipse断点调试(用图说话)
eclipse断点调试(用图说话) debug方式启动项目,后端调试bug调试 前端代码调试,请参考浏览器断点调试(用图说话) 1、前端 选中一条数据,点击删除按钮 2、后端接口打断点 断点按钮 介绍 resum…...

vue的学习--day2
如有错误,烦请指正~ 目录 一、什么是单页面应用程序 二、使用工具:node.js 三、工具链 易错点 一、什么是单页面应用程序 多个组件(例如登录、注册等以vue结尾的都叫做组件)在一个页面显示,叫单页面应用…...

html + css 快速实现订单详情的布局demo
突然安排让速写这样的一个布局,重点就是CSS画一条虚线,并且还要灵活设置虚线的宽度和虚线之间的间隔和虚线的颜色。 注:订单里面的金额都是随意写的 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8&…...

居然这么简单就能实现扫雷游戏!
目录 一.思路 1.成果展示 2.思路 二.具体操作 1.创建"棋盘" 2.初始化雷 3.布置雷 4.打印 5.排除雷 三.代码实现 1.test.c文件 2.thunder.h文件 3.thunder.c文件 Hello,大家好,今天我们来实现扫雷游戏,希望这一篇博客能给带给大家一…...

安装Gitlab+Jenkins
GItlab概述 GitLab概述: 是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。 Ruby on Rails 是一个可以使你开发、部署、维护 web 应用程序变得简单的框架。 GitLab拥有与…...

php 命令行模式详解
PHP 的命令行模式(Command Line Interface, CLI)是 PHP 的一个特定版本或运行时配置,它允许 PHP 脚本在没有 Web 服务器的情况下直接在命令行环境中执行。CLI 版本的 PHP 通常不包含 CGI 或者其他 web server 接口,因此更轻量级&a…...

Git 基础-创建版本库 git init、添加到暂存区git add、查看状态git status、查看改动git diff
目录 1.创建版本库 git init 1.创建版本库 git init 在目录中创建新的 Git 仓库。 你可以在任何时候、任何目录中这么做,完全是本地化的。 在目录中执行 git init,就可以创建一个 Git 仓库了。 注意: 没事不要手动修改 .git 目录里面的文件,不…...

Python实现无头浏览器采集应用的反爬虫与反检测功能解析与应对策略
Python实现无头浏览器采集应用的反爬虫与反检测功能解析与应对策略 随着网络数据的快速增长,爬虫技术在数据采集、信息分析和业务发展中扮演着重要的角色。然而,随之而来的反爬虫技术也在不断升级,给爬虫应用的开发和维护带来了挑战。为了应…...
法国工程师IMT联盟 密码学及其应用 2023年期末考试题
1 在 Unix 下的安全性 (30 分钟) 1.1 问题 1 1.1.1 问题 我们注意constat到通过 SMTP 服务器发送“假”电子邮件(垃圾邮件)相对容易。越来越常见的做法是在 SMTP 连接之上部署dployer TLS 协议protocole(即 SMTPS)。这解决了垃圾…...