基于Qt、FFMpeg的音视频播放器设计二(FFMpeg视频处理)

2023-05-16

在上一篇中我们介绍了如何在VS2013中配置文件以及FFMpeg的开发环境准备,本篇我们说下视频处理的原理以及实现。对于视频的处理我们这里对它分开总结,不然看起来会显得很冗余复杂,不易理解,主要分为以下几方面。

1、打开视频获取视频信息

2、读取视频分析视频包

3、打开视频解码器

4、视频解码并分析H264解码

5、打开格式转换和缩放

6、视频转RGB并缩放

一、打开视频获取视频信息

#include "aginexplay.h"
#include <QtWidgets/QApplication>

//调用FFMpeg的lib库
#pragma comment(lib,"avformat.lib")

extern "C"
{
//调用FFMpeg的头文件
#include <libavformat/avformat.h>

}
int main(int argc, char *argv[])
{

    //打开视频获取视频信息
	av_register_all();//注册FFMpeg的库

	char *path = "1080.mp4";
	AVFormatContext *ic = NULL;//解封装上下文
	int re = avformat_open_input(&ic,path,0,0);//打开解封装器
	if (re == 0)
	{
		int totalSec = ic->duration / (AV_TIME_BASE);//获取视频的总时间
		printf("file totalSec is %d-%d\n",totalSec/60,totalSec%60);//以分秒计时
		avformat_close_input(&ic);//释放解封装的空间,以防空间被快速消耗完
	}

	QApplication a(argc, argv);
	agineXplay w;
	w.show();
	return a.exec();
}

打开视频获取视频时加入解封装的头文件#include <libavformat/avformat.h>和它的链接库#pragma comment(lib,"avformat.lib"),这一整个过程就是解封装获取视频信息,需要注意的是获取信息后一定要记得释放解封装空间,原因如上。

二、读取视频分析视频包

上一个只是用来测试的打开视频获取信息的过程,现在我们需要读取解封装后的视频进行分析,在未读取完之前先不着急释放其中的解封装的空间,代码也做相应的调整

#include "aginexplay.h"
#include <QtWidgets/QApplication>

//调用FFMpeg的lib库
#pragma comment(lib,"avformat.lib")
#pragma  comment(lib,"avutil.lib")
#pragma  comment(lib,"avcodec.lib")
extern "C"
{
	//调用FFMpeg的头文件
#include <libavformat/avformat.h>

}
int main(int argc, char *argv[])
{
     //打开视频获取视频信息

	av_register_all();//注册FFMpeg的库

	char *path = "1080.mp4";
	AVFormatContext *ic = NULL;//解封装上下文
	int re = avformat_open_input(&ic, path, 0, 0);//打开解封装器
	if (re != 0)//打开错误时
	{
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf));
		printf("open %s failed :%s\n", path, buf);
		getchar();
		return -1;
	}
	int totalSec = ic->duration / (AV_TIME_BASE);//获取视频的总时间
	printf("file totalSec is %d-%d\n", totalSec / 60, totalSec % 60);//以分秒计时


      //读取视频并分析视频包

	for (;;)//读取视频中的所有帧
	{
		AVPacket pkt;
		re = av_read_frame(ic, &pkt);//读取解封装后的数据以及信息放入到pkt中
		if (re != 0) break;
		printf("pts = %d",pkt.pts);//打印它的pts,用来显示的时间戳,后面用来做同步。
		av_packet_unref(&pkt);//重新置0
	}


	avformat_close_input(&ic);//释放解封装器的空间,以防空间被快速消耗完




	QApplication a(argc, argv);
	agineXplay w;
	w.show();
	return a.exec();
}

此时需要处理错误信息和后面的解码过程,引入链接库#pragma  comment(lib,"avutil.lib")
#pragma  comment(lib,"avcodec.lib"),对于解封装后的视频,我们需要它的视频中的每一帧,帧有I、B、P帧,关键帧在后面视频得播放和快进时都要使用到的,而对于pts它的显示时间做同步,可以百度了解下。

三、打开视频解码器

上面两部分已获取了视频信息以及它的视频帧等信息,现在我们需要对获得的消息进行解码,先进行视频解码,音频的解码在后面再说明。对于一个视频打开后有视频流以及音频流stream,先处理视频流,在处理视频时先打开解码器,在打开视频获取视频信息的后面和读取视频并分析视频包前添加打开视频解码器的代码

int totalSec = ic->duration / (AV_TIME_BASE);//获取视频的总时间
	printf("file totalSec is %d-%d\n", totalSec / 60, totalSec % 60);//以分秒计时

int videoStream = 0;
	for (int i = 0; i < ic->nb_streams; i++)
	{
		AVCodecContext *enc = ic->streams[i]->codec;//解码上下文
		if (enc->codec_type == AVMEDIA_TYPE_VIDEO)//判断是否为视频
		{
			videoStream = i;
			AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器
			if (!codec)//未找到解码器
			{
				printf("video code not find\n");
				return -1;
			}
			int err = avcodec_open2(enc, codec, NULL);//打开解码器
			if (err!= 0)//未打开解码器
			{
				char buf[1024] = { 0 };
				av_strerror(err, buf, sizeof(buf));
				printf(buf);
				return -2;
			}
			printf("open codec success!\n");
		}
	}



      //读取视频并分析视频包

	for (;;)//读取视频中的所有帧

四、视频解码并分析H264解码

现在我们进行视频得解码过程,在上面我们已经获得了视频的帧,对于每次获得的视频帧我们需要对其解码,但解码之后我们要将这些视频帧放入到AVFrame,正如我们知道的此时解码后的视频帧是YUV格式,但最后我们显示的是RGB格式,这种转换我们后面介绍,现在我们对于读取视频并分析视频包的处理过程加入解码视频过程。


 //读取视频并分析视频包
AVFrame *yuv = av_frame_alloc();//分配的只是AVFrame的对象空间
	for (;;)//读取视频中的所有帧
	{
		AVPacket pkt;
		re = av_read_frame(ic, &pkt);//读取解封装后的数据以及信息放入到pkt中
		if (re != 0) break;
		if (pkt.stream_index != videoStream) continue;
		int pts = pkt.pts * r2d(ic->streams[pkt.stream_index]->time_base) *1000;
		int got_picture = 0;//从这里
		int re = avcodec_decode_video2(videoCtx, yuv,&got_picture,&pkt);//解码视频过程
		if (got_picture)
		{
			printf("[%d]",re);
		}//到这里替换解码过程
		printf("pts = %d\n",pts);//打印它的pts,用来显示的时间戳,后面用来做同步。
		av_packet_unref(&pkt);//重新置0
	}

上面可以实现解码过程,但目前新版本还有一个解码方式,将上面的//从这里.......//到这里替换解码过程的这段代码替换成如下代码

       int re = avcodec_send_packet(videoCtx ,&pkt);//发送视频帧
		if (re != 0)
		{
			av_packet_unref(&pkt);//不成功就释放这个pkt
			continue;
		}
		re = avcodec_receive_frame(videoCtx, yuv);//接受后对视频帧进行解码
		if (re != 0)
		{
			av_packet_unref(&pkt);//不成功就释放这个pkt
			continue;
		}
		printf("D\n");
		

五、打开格式转换和缩放

我们知道在完成了解码之后的值是个YUV格式,是将亮度和色彩分离的一种存储方式,这种方式优势是黑白画面,只取Y是黑白画面,UV是色彩,但我们的程序和显示器并不接受此格式,所以我们需要将它转化为RGB格式,我们需要使用转码器,在头文件中加入库#include <libswscale/swscale.h>和链接库#pragma  comment(lib,"swscale.lib"),首先我们初始化一个SwsContext用来转码,对于解码后的每帧视频我们进行转码,在转码之前我们先打开转码器,在如下位置添加代码。

for (;;)//读取视频中的所有帧
	{
		AVPacket pkt;
		re = av_read_frame(ic, &pkt);//读取解封装后的数据以及信息放入到pkt中
		if (re != 0) break;
		if (pkt.stream_index != videoStream) continue;
		int pts = pkt.pts * r2d(ic->streams[pkt.stream_index]->time_base) *1000;
		int re = avcodec_send_packet(videoCtx ,&pkt);//发送视频帧
		if (re != 0)
		{
			av_packet_unref(&pkt);//不成功就释放这个pkt
			continue;
		}
		re = avcodec_receive_frame(videoCtx, yuv);//接受后对视频帧进行解码
		if (re != 0)
		{
			av_packet_unref(&pkt);//不成功就释放这个pkt
			continue;
		}
		printf("D\n");
		
		cCtx = sws_getCachedContext(cCtx, videoCtx->width,//初始化一个SwsContext
			videoCtx->height,
			videoCtx->pix_fmt, //输入格式
			outwidth, outheight,
			AV_PIX_FMT_BGRA,//输出格式
			SWS_BICUBIC,//转码的算法
			NULL, NULL, NULL);//打开转码器

	   if (!cCtx)
		{
			printf("sws_getCachedContext  failed!\n");
			break;
		}

		/*
		int got_picture = 0;
		int re = avcodec_decode_video2(videoCtx, yuv,&got_picture,&pkt);//解码视频过程
		if (got_picture)
		{
			printf("[%d]",re);
		}
		*/
		printf("pts = %d\n",pts);//打印它的pts,用来显示的时间戳,后面用来做同步。
		av_packet_unref(&pkt);//重新置0
	}
	if (cCtx)
	{
		sws_freeContext(cCtx);
		cCtx = NULL;
	}





	avformat_close_input(&ic);//释放解封装器的空间,以防空间被快速消耗完

转码器在最后需要释放其上下文空间,并重新进行初始化为NULL。

六、视频转RGB并缩放

上一节打开了转码器,现在我们开始对视频进行转码,在打开转码器的下方添加视频转码的代码

 if (!cCtx)
		{
			printf("sws_getCachedContext  failed!\n");
			break;
		}
	    uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
	   data[0] = (uint8_t *)rgb;//第一位输出RGB
	   int linesize[AV_NUM_DATA_POINTERS] = { 0 };

	   linesize[0] = outwidth * 4;//一行的宽度,32位4个字节
	   int h = sws_scale(cCtx, yuv->data, //当前处理区域的每个通道数据指针
		   yuv->linesize,//每个通道行字节数
		   0,videoCtx->height,//原视频帧的高度
		   data,//输出的每个通道数据指针	
		   linesize//每个通道行字节数

		   );//开始转码

	   if (h > 0)
	   {
		   printf("(%d)", h);
	   }
		/*
		int got_picture = 0;
		int re = avcodec_decode_video2(videoCtx, yuv,&got_picture,&pkt);//解码视频过程
		if (got_picture)
		{
			printf("[%d]",re);
		}
		*/

其中的data 和linesize是我们输出的每个通道数据指针和所转数据的行的字节数,其中的  data[0] = (uint8_t *)rgb中的rgb是我们所要转化为RGB的格式,在RGB中,我们设置了为outheight*outwidth,有outheight*outwidth个像素,像素格式在sws_getCachedContext中设置为AV_PIX_FMT_BGRA,是32位四个字节,所有这里的的总大小为outheight*outwidth*4,所以这里申请的rgb为char *rgb = new char[outheight*outwidth*4],同理每一行的字节数linesize 为outwidth *4,可以参考代码部分,至此YUV转码为RGB过程结束。

从上面我们可以看到代码基本上是堆出来的,自然它的可复用性相对来说比较差,对后面的扩展也有一定限制,所以在下一篇中我们对它进行类的封装啊!

下一篇链接:https://blog.csdn.net/hfuu1504011020/article/details/82661783

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Qt、FFMpeg的音视频播放器设计二(FFMpeg视频处理) 的相关文章

  • SpringBoot集成阿里easyexcel(四)Converter导入导出数据转换器

    SpringBoot集成阿里easyexcel xff08 四 xff09 Converter导入导出数据转换器 通过com alibaba excel converters Converter转换器实现Excel导入导出时Java数据与E
  • SpringBoot集成Ehcache缓存

    SpringBoot集成Ehcache缓存 Ehcache有两种缓存方式 xff0c 分别是堆内存 磁盘 xff08 非堆内存 xff09 一 堆内存缓存 也就是MemoryStore xff0c 速度最快 xff0c 不适合存放大量数据
  • Spring的切面编程(AOP)概念与使用AOP实现日志记录

    Spring的切面编程 xff08 AOP xff09 概念与使用 一 面向切面编程 定义 面向切面编程 xff08 AOP xff09 是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术 作用 xff1a 利用AOP对业务
  • 关于intrins.h头文件的介绍

    在单片机中应用最多的当然就是移位函数 xff0c 利用移位函数可以更简便的实现流水灯等效果 移位函数 移位函数名 左移 span class token function crol span span class token punctua
  • 大批量数据分批批量插入或更新(Mybatis+MySQL)

    大批量数据分批批量插入或更新 在MySQL数据库的前提下 xff0c 插入或更新大批量数据 首先批量插入需要考虑到以下几个因素 xff1a 数据库一次可以承受多大或者多少条数据的插入批量插入是否会占用Mysql资源太久 xff0c 影响系统
  • VSCode配置C++开发环境

    更新细节 2020 7 3 更新细节及排版 2022 6 9 昨天从下午一直研究到晚上十一点 xff0c 查阅了很多博客资料 xff0c 还是没配置好VSCode的C 43 43 开发环境 xff0c 今天早上又弄了一下 xff0c 现在O
  • stm32模拟输出PPM信号

    PPM信号周期为20ms xff0c 分成10分代表10个通道信号 xff0c 也就是2ms代表一个信号 0 5ms代表一个通道信号的开始 xff0c 所以0 5ms 2ms为通道范围控制 LED p1 39 A 39 8 IO口初始化 x
  • 使用JSON.parse,解决ie6-7上JSON未定义问题

    使用JSON parse时出现JSON未定义问题 xff0c JSON不是标准的javascript类型 xff0c 一些高级的浏览器支持 xff0c 但一些老一点的浏览器不支持JSON 如ie6 7 若需要 ie6 7 支持JSON只需要
  • C语言中的大小端转换与高低位颠倒

    在说大小端高低位之前 xff0c 肯定要说明数据在计算机内是如何存储的 在计算机中 xff0c 我们将数据分割成了一个一个的字节 xff08 byte xff09 xff0c 而每个字节又有8位 xff08 bit xff09 一个字节 x
  • C语言库函数中的Strcat函数

    一 Strcat函数的参数 Strcat函数所引用的头文件是 lt string h gt char strcat char strDestination const char strSource 参数说明 xff1a strDestina
  • SLAM中的marginalization 和 Schur complement

    在视觉SLAM的很多论文中 xff0c 会大量或者偶尔出现marginalization这个词 翻译为边缘化 xff0c 有的论文是特地要用它 xff0c 比如sliding window slam 2 okvis 3 dso 4 而有的论
  • 数据结构之单链表循环

    单链表循环代码如下 xff1a include lt stdio h gt include lt stdlib h gt typedef struct node int data struct node next sqlist sqlist
  • 数据结构之双链表循环

    定义是 xff1a 每个数据结点都有两个指针 xff0c 分别指向直接后继和直接前驱 因此双向链表中单任意一个结点开始 xff0c 都可以很方便的访问它的前驱结点和后继结点 循环链表指 xff1a 最后一个结点next指向头结点 xff0c
  • linux学习之进程

    进程概念 xff1a 活跃度程序 xff0c 占用系统资源 xff0c 在内存中执行产生一个进程 孤儿进程 xff1a 父进程先于子进程结束 xff0c 则子进程称为孤儿进程 xff0c 并且这个子进程被init进程回收 include l
  • 使用libcurl实现http通信——post上传数据并获取response

    接口释义 使用libcurl实现http通信 get获取response 代码实现 size t span class token function responseStr span span class token punctuation
  • C语言学习之sprintf

    sprintf函数介绍 xff1a 该函数原型为 xff1a int sprintf char str const char format 该函数的功能为 xff1a 本该输出到显示上的数据 xff0c 改为输出到str所指导内存空间中 x
  • linux学习之存储器

    存储器的种类 xff1a 分为易失性存储器和非易失性存储器 易失性存储器 掉电数据会丢失 读写速度快 xff1a RAM random access memory 随机存储内存 又分为DRAM xff08 动态随机存储内存 xff09 和S
  • HTTP请求报文(请求行、请求头、请求体)

    HTTP协议 1 简介 HTTP协议 xff08 Hyper Text Transfer Protocol xff0c 超文本传输协议 xff09 是用于从万维网 xff08 WWW World Wide Web xff09 服务器传输超文
  • UART详解

    UART 通用异步收发传输器 xff08 Universal Asynchronous Receiver Transmitter xff0c 通常称作UART 是一种异步全双工串行通信协议 xff0c 它将要传输的资料在串行通信与并行通信之
  • 基于STM32的NB-IoT模块驱动

    一 概述 目前这个NB IoT在国内还是比较火的 xff0c 最近刚好使用了一款NB IoT模块 将驱动程序贡献给大家 希望能对用到的朋友有点帮助 二 NB IoT模块介绍 本次采用的是有人物联网公司的WHNB75模块 xff08 支持电信

随机推荐