Qt+FFmpeg+opengl从零制作视频播放器-3.解封装

news2024/9/23 19:18:15

解封装:如下图所示,就是将FLV、MKV、MP4等文件解封装为视频H.264或H.265压缩数据,音频MP3或AAC的压缩数据,下图为常用的基本操作。

 ffmpeg使用解封装的基本流程如下:

在使用FFmpeg API之前,需要先注册API,然后才能使用API。当然,新版本ffmpeg库不需要再调用下面的方法。

av_register_all()

初始化网络配置

avformat_network_init

设置一些参数,如下图所示,设置了最大延迟、传输协议等参数。

	//参数设置
	AVDictionary *opts = NULL;

	//设置rtsp流已tcp协议打开
	av_dict_set(&opts, "rtsp_transport", "tcp", 0);
	av_dict_set(&opts, "max_delay", "500", 0);
	av_dict_set(&opts, "buffer_size", "1024000", 0);
	av_dict_set(&opts, "probsize", "4096", 0);
	av_dict_set(&opts, "fps", "25", 0);

构建AVFormatContext,声明输入的封装结构体,通过输入文件或者流地址作为封装结构的句柄。

    AVFormatContext* inputFmtCtx = nullptr;
    const char* inputUrl = "test.mp4";
 
    ///打开输入的流,获取数据 begin
    int ret = avformat_open_input(&inputFmtCtx, inputUrl, NULL, NULL);

查找音视频流信息,通过下面的接口与AVFormatContext中建立输入文件对应的流信息。

    //查找;
    if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
    {
        printf("Couldn't find stream information.\n");
        return false;
    }


读取音视频流,采用av_read_frame来读取数据包,读出来的数据存储在AVPacket中,确定其为音频、视频、字幕数据,最后解码,或者存储。

    AVPacket* pkt = NULL;
    pkt = av_packet_alloc();
 
    while (av_read_frame(inputFmtCtx, pkt) >= 0)
    {
        //获取数据包
 
        //.....解码或者存储
 
        //重置
        av_packet_unref(pkt);
    }

上述代码所示,通过循环调用av_read_frame()读取到pkt包,然后可以进行解码或者存储数据,如果读取的数据结束,则退出循环,开始指向结束操作。

每次使用完AVPacket包后,需要重置,否则会内存泄漏。

    //重置
    av_packet_unref(pkt);

执行结束后关闭输入文件,释放资源。

    //关闭
    avformat_close_input(&inputFmtCtx);

    //释放
    avformat_free_context(inputFmtCtx);
 
    //释放资源
    av_packet_free(&pkt);

 新建空白解决方案,如下图所示。

解决方案,右键-添加-新建项目。 

新建Qt控制台程序,3_demuxDemo 

然后,配置ffmpeg的编译环境:看目录4。

ffmpeg环境配置 

解封装源码示例:

#include <QtCore/QCoreApplication>

extern "C" {
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/eval.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/fifo.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/time.h"
#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"  
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"
}

#include <iostream>
using namespace std;

static double r2d(AVRational r)
{
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

	//av_register_all();
	avformat_network_init();

	AVDictionary* options = NULL;

	av_dict_set(&options, "buffer_size", "1024000", 0);
	av_dict_set(&options, "max_delay", "500000", 0);
	av_dict_set(&options, "stimeout", "2000000", 0);
	av_dict_set(&options, "rtsp_transport", "tcp", 0);

	AVFormatContext* inputFmtCtx = nullptr;
	const char* inputUrl = "F:/1920x1080.mp4";

	///打开输入的流,获取数据 begin
	int ret = avformat_open_input(&inputFmtCtx, inputUrl, NULL, NULL);
	if (ret != 0)
	{
		printf("Couldn't open input stream.\n");
		return -1;
	}

	int vIndex = -1;
	int aIndex = -1;

	//查找;
	if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
	{
		printf("Couldn't find stream information.\n");
		return false;
	}


	for (int i = 0; i < inputFmtCtx->nb_streams/*视音频流的个数*/; i++)
	{
		//查找视频
		if (inputFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			vIndex = i;
		}
		else if (inputFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			aIndex = i;
		}
	}

	//===================video=================
	//视频宽
	int width = inputFmtCtx->streams[vIndex]->codecpar->width;

	//视频高
	int height = inputFmtCtx->streams[vIndex]->codecpar->height;

	//视频总时长s
	int64_t m_totalTime = static_cast<double>(inputFmtCtx->duration) / AV_TIME_BASE;

	//获取帧率;
	int fps = r2d(inputFmtCtx->streams[vIndex]->avg_frame_rate);
	if (fps == 0)
	{
		fps = 25;
	}

	int iHour, iMinute, iSecond, iTotalSeconds;//HH:MM:SS
	//打印结构体信息
	puts("AVFormatContext信息:");
	puts("---------------------------------------------");
	iTotalSeconds = (int)inputFmtCtx->duration/*微秒*/ / 1000000;
	iHour = iTotalSeconds / 3600;//小时
	iMinute = iTotalSeconds % 3600 / 60;//分钟
	iSecond = iTotalSeconds % 60;//秒
	printf("持续时间:%02d:%02d:%02d\n", iHour, iMinute, iSecond);
	printf("平均混合码率:%d kb/s\n", inputFmtCtx->bit_rate / 1000);
	printf("视音频个数:%d\n", inputFmtCtx->nb_streams);
	puts("---------------------------------------------");

	puts("AVInputFormat信息:");
	puts("---------------------------------------------");
	printf("封装格式名称:%s\n", inputFmtCtx->iformat->name);
	printf("封装格式长名称:%s\n", inputFmtCtx->iformat->long_name);
	printf("封装格式扩展名:%s\n", inputFmtCtx->iformat->extensions);
	printf("封装格式ID:%d\n", inputFmtCtx->iformat->raw_codec_id);
	puts("---------------------------------------------");

	puts("AVStream信息:");
	puts("---------------------------------------------");
	printf("视频流标识符:%d\n", inputFmtCtx->streams[vIndex]->index);
	printf("音频流标识符:%d\n", inputFmtCtx->streams[aIndex]->index);
	printf("视频流长度:%d微秒\n", inputFmtCtx->streams[vIndex]->duration);
	printf("音频流长度:%d微秒\n", inputFmtCtx->streams[aIndex]->duration);
	puts("---------------------------------------------");

	printf("视频时长:%d微秒\n", inputFmtCtx->streams[vIndex]->duration);
	printf("音频时长:%d微秒\n", inputFmtCtx->streams[aIndex]->duration);
	printf("视频宽:%d\n", inputFmtCtx->streams[vIndex]->codecpar->width);
	printf("视频高:%d\n", inputFmtCtx->streams[vIndex]->codecpar->height);
	printf("音频采样率:%d\n", inputFmtCtx->streams[aIndex]->codecpar->sample_rate);
	printf("音频信道数目:%d\n", inputFmtCtx->streams[aIndex]->codecpar->channels);

	puts("AVFormatContext元数据:");
	puts("---------------------------------------------");
	AVDictionaryEntry *dict = NULL;
	while (dict = av_dict_get(inputFmtCtx->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
	{
		printf("[%s] = %s\n", dict->key, dict->value);
	}
	puts("---------------------------------------------");

	puts("AVStream视频元数据:");
	puts("---------------------------------------------");
	dict = NULL;
	while (dict = av_dict_get(inputFmtCtx->streams[vIndex]->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
	{
		printf("[%s] = %s\n", dict->key, dict->value);
	}
	puts("---------------------------------------------");

	puts("AVStream音频元数据:");
	puts("---------------------------------------------");
	dict = NULL;
	while (dict = av_dict_get(inputFmtCtx->streams[aIndex]->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
	{
		printf("[%s] = %s\n", dict->key, dict->value);
	}
	puts("---------------------------------------------");


	av_dump_format(inputFmtCtx, -1, inputUrl, 0);
	printf("\n\n编译信息:\n%s\n\n", avcodec_configuration());

	AVPacket* pkt = NULL;
	pkt = av_packet_alloc();

	while (av_read_frame(inputFmtCtx, pkt) >= 0)
	{
		//获取数据包

		//.....解码或者存储

		//重置
		av_packet_unref(pkt);
	}


	//关闭
	avformat_close_input(&inputFmtCtx);

	//释放
	avformat_free_context(inputFmtCtx);

	//释放资源
	av_packet_free(&pkt);


    return a.exec();
}

运行截图:

完整工程:

https://download.csdn.net/download/wzz953200463/88959152icon-default.png?t=N7T8https://download.csdn.net/download/wzz953200463/88959152

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

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

相关文章

离线安装数据库 mysql 5.7 linux

离线安装数据库 mysql 5.7 linux 方法一 参考链接Linux(Debian10.2)安装MySQL5.7.24环境 赋予文件执行权限chmod x 文件名 使用root用户sudo su解压文件tar xvf mysql-5.7.42-linux-glibc2.12-x86_64.tar.gz重命名mv mysql-5.7.42-linux-glibc2.12-x86_64 mysql将桌面的mys…

探索5个启发人心的网页设计案例,助您打造独特个人作品集!

对于网页设计师来说&#xff0c;网页设计作品集不仅是网页的门面&#xff0c;也是个人专业素养的体现。那么我们就不能掉以轻心地设计作品集了。无论是制作简单大方的作品集还是表现力极强的优秀作品集&#xff0c;设计师都必须非常努力地参考大量的设计作品来获得灵感。 国产…

Docker 安装部署 SqlServer 数据库

Docker 安装部署 SqlServer 数据库 背景&#xff1a; ​ 最近在开发数据中台数据集成模块&#xff0c;需要对接大量的数据做测试&#xff0c; 由于SqlServer 下载安装会耗费大量时间&#xff0c;所以采用 Docker 安装 Sqlserver 的方式部署数据库。 1、拉去 sqlserver 镜像 …

Linux系统下基于VSCode和Cmake进行C++开发

目录 简介一、GCC编译器1.1创建cpp文件1.2编译过程1.3g重要编译参数 二、GDB调试器三、IDE-VScode3.1 VSCode常用快捷键3.2 swap测试 四、CMake4.1CMake介绍4.2 CMake语法特性介绍4.3 CMake重要指令和常用变量4.4 CMake编译流程4.5CMake代码实践 五、使用VSCode进行完整项目开发…

Js输入输出语句

输入语法 prompt("您想输入的是&#xff1f;")输出语法: 语法1: document.write(‘要出的内容’&#xff09; <body><script>document.write("你好")document.write("<h1>我是<h1>")</script> </body>作…

基于单片机的事务管理系统

基于单片机的事务管理系统 摘 要 所谓事务管理系统就是主要用来做提醒&#xff0c;辅助以计时、秒表等的一个小系统。利用MCS51单片机即可完成系统硬件需要&#xff0c;成本低廉&#xff0c;程序简单&#xff0c;功能丰富实用&#xff0c;使用率广。根据题目的设计要求&#…

Github上哪些好用的安全工具1

专注于web漏洞挖掘、内网渗透、免杀和代码审计&#xff0c;感谢各位师傅的关注&#xff01;网安之路漫长&#xff0c;与君共勉&#xff01; URLFinder 一款快速提取网页信息的工具。该项目可以快速爬取网页上的 URL 地址、JS 文件里的 API 接口等信息&#xff0c;支持批量抓取…

QTextToSpeech的使用——Qt

前言 之前随便看了几眼QTextToSpeech的帮助就封装使用了&#xff0c;达到了效果就没再管了&#xff0c;最近需要在上面加功能&#xff08;变换语速&#xff09;&#xff0c;就写了个小Demo后&#xff0c;发现不对劲了。 出现的问题 场景 写了个队列添加到语音播放子线程中&a…

Day34:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法

目录 Java-反射-Class对象类获取 Java-反射-Field成员变量类获取 Java-反射-Method成员方法类获取 Java-反射-Constructor构造方法类获取 Java-反射-不安全命令执行&反序列化链构造 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;…

Redis:使用redis-dump导出、导入、还原数据实例

redis的备份和还原&#xff0c;借助了第三方的工具&#xff0c;redis-dump 1、安装必要环境 yum -y install zlib-devel openssl-devel2、安装redis-dump 安装ruby&#xff1a; ruby下载地址&#xff1a;https://www.ruby-lang.org/zh_cn/downloads/ 我下载的是 2.5.0 版本…

GPT-SoVITS开源音色克隆框架的训练与调试

GPT-SoVITS开源框架的报错与调试 遇到的问题解决办法 GPT-SoVITS是一款创新的跨语言音色克隆工具&#xff0c;同时也是一个非常棒的少样本中文声音克隆项目。 它是是一个开源的TTS项目&#xff0c;只需要1分钟的音频文件就可以克隆声音&#xff0c;支持将汉语、英语、日语三种…

Linux ping可以上内网,不能上外网(系统重启DNS不生效)(直接更改/etc/resolv.conf修改nameserver重启被重置)

在Linux上可以连内网&#xff0c;不能连外网 ping内网可以&#xff0c;外网不行 可能的问题&#xff1a; 1.可能设置了网络防火墙&#xff0c;阻止了服务器访问外网的请求 2.DNS解析问题&#xff1a;检查服务器的DNS设置是否正确&#xff0c;能够正确解析外部域名。如果DNS解析…

案例分析篇15:软件开发方法考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

Vue首屏优化方案

在Vue项目中&#xff0c;引入到工程中的所有js、css文件&#xff0c;编译时都会被打包进vendor.js&#xff0c;浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多&#xff0c;那么vendor.js文件体积将会相当的大&#xff0c;影响首屏的体验。可以看个例子&#xff1a;…

Linux搭建我的世界(MC)整合包服务器,All the Mods 9(ATM9)整合包开服教程

Linux使用MCSM面板搭建我的世界(Minecraft)整合包服务器&#xff0c;MC开服教程&#xff0c;All the Mods 9(ATM9)整合包搭建服务器的教程。 本教程使用Docker来运行mc服&#xff0c;可以方便切换不同Java版本&#xff0c;方便安装多个mc服版本。 视频教程&#xff1a;https:…

基于SpringBoot+Vue的电商应用系统的设计与实现

1 绪论 1.1研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大环境让那些止步不前&…

工具篇--从零开始学Git

一、git概述 1.1版本控制方式 集中式版本控制工具 集中式版本控制工具&#xff0c;版本库是集中存放在中央服务器的&#xff0c; team 里每个人 work 时从中央服务器下载代码&#xff0c;是必须联网才能工作&#xff0c; 局域网或互联网&#xff0c;个人修改之…

NB-IoT模块

目录 一. NB-IoT模块实物图 二. BC20/NB-IoT模块产品规格 三. 指令顺序 1. AT判断BC20模组是否正常 2. ATE0返回OK&#xff0c;已经返回回显 3. ATCSQ 4. AT_CEREG? 5. ATCGATT? 6. ATCGATT? 四. OneNet 连接 1. AT 查看 NB(当前NB)&#xff0c;云平台根据这两个…

Three 材质纹理 (总结三)

THREE.MeshLambertMaterial&#xff08;网格 Lambert 材质&#xff09; 该材质使用基于非物理的Lambertian模型来计算反射率。可以用来创建暗淡的并不光亮的表面&#xff0c;该材质非常易用&#xff0c;而且会与场景中的光源产生反应。 MeshLambertMaterial属性 # .color : …

OpenCASCADE开发指南:专栏内容介绍及目录

1、专栏介绍 1.1 几何内核 在三维建模软件和仿真软件中&#xff0c;我们经常能听到到“几何内核”一词。那么几何内核是究竟什么是几何建模内核&#xff1f;先说结论&#xff1a;几何没有内核&#xff0c;就没有CAD软件&#xff01; 从软件的历史中可以清楚地看到&#xff0c;…