c++ ffmpeg播放器项目实现记录 Qt5.12和VS2019

目录

类的设计

类接口设计部分

在这里插入图片描述

类名及功能概述

xDemux : 解封装 获取音视频的信息然后进行seek 操作,静态的处理
xDwmuxThread : 线程启动 开始读取 与xDemux 是组合关系 不是继承

面向对象的五大原则

在这里插入图片描述
替换原则 : 父类实现的东西子类也可以实现
接口隔离: 客户不需要,隔离开来
依赖倒置 ;该功能方便

正式开始 抄代码了 哈哈哈哈哈

1 、创建Qt gui 工程 这里命名未XPlay2 (教程是这么写的 ) 记得加入 OpenGL 和OpenGL 的扩展库
坑 1 无法打开ui …h 头文件
解决方案参考链接
2 、添加第一个类 比较详细后面参照这个就好了
第一步
在这里插入图片描述
在这里插入图片描述
主要用到的部分源码

#pragma once
#include<mutex>

//没有用到全部不用引入头文件
// 结构体或者类名声明一次
struct AVFormatContext;
struct AVPacket;
struct AVCodecParameters;
class XDemux
{
public:
	// 打开媒体文件 或者流媒体 rtmp  http
	// 设置为虚函数
	virtual bool Open(const char* url);

	// 读线程和解码线程 如何交替控制 随意我们复制一份 
	// 不是真的复制 记数+1 
	//需要空间调用者释放,释放AVPacket 对象空间 ,和数据空间
	// av_packet_free
	virtual AVPacket* Read();


	//获取视频参数  返回的空间需要清理  avcodec_parameters_free
	virtual AVCodecParameters* CopyVpara();

	//获取音频参数  返回的空间需要清理 avcodec_parameters_free
	virtual AVCodecParameters* CopyApara();


	// 播放位置调整 pos[0 ~1] 
	virtual bool Seek(double pos);

	// 清空缓存
	virtual void Clear();

	//关闭功能

	virtual void Close();

	XDemux();
	virtual ~XDemux();
	//媒体总时长(毫秒)
	int totalMs = 0;
protected:
	// 互斥变量 防止同时打开多个时线程手出现问题
	std::mutex mux;
	//解封装上下文
	AVFormatContext* ic = NULL;
	//音视频索引,读取时区分音视频
	int videoStream = 0;
	int audioStream = 1;
};


#include "XDemux.h"
#include <iostream>

extern "C" {
#include "libavformat/avformat.h"
}
using namespace std;
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")


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

bool XDemux::Open(const char* url)
{  
	Close();
	//参数设置
	AVDictionary* opts = NULL;
	//设置rtsp流已tcp协议打开
	av_dict_set(&opts, "rtsp_transport", "tcp", 0);

	//网络延时时间
	av_dict_set(&opts, "max_delay", "500", 0);

	mux.lock();  // 最后释放 
	int re = avformat_open_input(
		&ic,
		url,
		0,  // 0表示自动选择解封器
		&opts //参数设置,比如rtsp的延时时间
	);
	if (re != 0)
	{
		mux.unlock();//如果出错则今早释放
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		cout << "open " << url << " failed! :" << buf << endl;
		return false;
	}
	cout << "open " << url << " success! " << endl;

	//获取流信息 
	re = avformat_find_stream_info(ic, 0);

	//总时长 毫秒
	int totalMs = ic->duration / (AV_TIME_BASE / 1000);
	cout << "totalMs = " << totalMs << endl;

	//打印视频流详细信息
	av_dump_format(ic, 0, url, 0);


	/***********
	* 改掉之前的遍历程序 换成函数
	*/
	//获取视频流
	videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	AVStream* as = ic->streams[videoStream];

	cout << "=======================================================" << endl;
	cout << videoStream << "视频信息" << endl;
	cout << "codec_id = " << as->codecpar->codec_id << endl;
	cout << "format = " << as->codecpar->format << endl;
	cout << "width=" << as->codecpar->width << endl;
	cout << "height=" << as->codecpar->height << endl;
	//帧率 fps 分数转换

	cout << "video fps = " << r2d(as->avg_frame_rate) << endl;

	cout << "=======================================================" << endl;
	cout << audioStream << "音频信息" << endl;
	//获取音频流
	audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	as = ic->streams[audioStream];
	cout << "codec_id = " << as->codecpar->codec_id << endl;
	cout << "format = " << as->codecpar->format << endl;
	cout << "sample_rate = " << as->codecpar->sample_rate << endl;
	//AVSampleFormat;
	cout << "channels = " << as->codecpar->channels << endl;
	//一帧数据?? 单通道样本数 
	cout << "frame_size = " << as->codecpar->frame_size << endl;
	//1024 * 2 * 2 = 4096  fps = sample_rate/frame_size
	mux.unlock();


	return true;
}


AVPacket* XDemux::Read()
{  
	mux.lock(); //读是一个线程 open 是一个线程 ic可能被释放掉
	if (!ic) // 容错 如果没有打开 直接返回false
	{
		mux.unlock();
		return 0;
	}

	// pack是要返回去的 不能定义未对象 AVPacket ss 
	// 局部对象在本程序段内结束后会释放 定义未指针
	
	AVPacket* pkt = av_packet_alloc();  // 分配的是对象空间 ,不是数据空间
	// 读取一帧 并分配一帧数据空间
	int re = av_read_frame(ic, pkt); // 返回0 代表成功

	if (re!=0)
	{
		mux.unlock();
		av_packet_free(&pkt); // 读取失败 首先释放对象资源
		return 0;
	}

	// 成功继续进行下一步操作
	// 将pts转换为毫秒  读进来的是秒
	pkt->pts = pkt->pts * (1000 * (r2d(ic->streams[pkt->stream_index]->time_base)));
	pkt->dts = pkt->dts * (1000 * (r2d(ic->streams[pkt->stream_index]->time_base)));
	mux.unlock();
	cout << pkt->pts << "" << flush;
	return pkt; // 返回pack
}


//seek 位置 pos 0.0 ~1.0
bool XDemux::Seek(double pos)
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return false;
	}
	// 清理读取缓冲
	avformat_flush(ic);
	
	long long seekPos = 0; 
	seekPos=ic->streams[videoStream]->duration* pos;  //duration 总的时常
	int re = av_seek_frame(ic, videoStream, seekPos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
	mux.unlock();
	if (re < 0) return false;
	return true;
}

// 清空缓存
void XDemux::Clear()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return;
	}
	avformat_flush(ic);
	mux.unlock();
}

//关闭功能
void XDemux::Close()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return;
	}
	avformat_close_input(&ic);
	// 媒体总时长(毫秒)
	totalMs = 0;
	mux.unlock();
}



//获取视频参数 
AVCodecParameters* XDemux::CopyVpara()
{   
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return NULL;
	}
	//创建空间 
	AVCodecParameters* pv = avcodec_parameters_alloc();
	// 源地址复制到目标地址
	avcodec_parameters_copy(pv, ic->streams[videoStream]->codecpar);

	mux.unlock();
	return pv;

}

//获取音频参数
AVCodecParameters* XDemux::CopyApara()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return NULL;
	}
	//创建空间 
	AVCodecParameters* pa = avcodec_parameters_alloc();
	// 源地址复制到目标地址
	avcodec_parameters_copy(pa, ic->streams[audioStream]->codecpar);

	mux.unlock();
	return pa;
}
XDemux::XDemux()
{   //虽然会初始化一次 我们还是判断以下 
	static bool is_First = true;

	// 为了解决两个线程同时调用时冲突  设置一个互斥量
	static std::mutex dmux;
	dmux.lock();
	if (is_First)
	{
		//初始化封装库
		av_register_all();
		// 初始化网络
		avformat_network_init();
		is_First = false;

	}
	dmux.unlock();
	
}
XDemux:: ~XDemux()
{

}

主函数限额是代码

#include "XPlay2.h"
#include <QtWidgets/QApplication>
#include <iostream>
using namespace std;
#include "XDemux.h" 
int main(int argc, char *argv[])
{  


	// 测试打开bin 下test.mp4  
	//cout << " demux.Open()=" << demux.Open("test.mp4");


	
	XDemux demux;
	//流视频测试 (可以打开亲测)
	char* url = "test.mp4";
	cout << " demux.Open()=" << demux.Open(url);
	demux.Read();// 读一帧
	demux.Clear();
	demux.Close();
	cout << " demux.Open()=" << demux.Open(url);

	cout << " demux.CopyVpara=" << demux.CopyVpara()<<endl;
	cout << " demux.CopyApara=" << demux.CopyApara() << endl;

	cout << "seek=" << demux.Seek(0.9) << endl;
	
	for (;;)
	{
		AVPacket* pkt = demux.Read();
		if (!pkt) break;
	}
	
	QApplication a(argc, argv);
	XPlay2 w;
	w.show();
	return a.exec();
}

创建第二个类了 XDecode
创建 Qt 上的显示界面 提升为XVideoWidget 类

下面开始绘制图像
解码出来是一个AVFrame ,要在XVideoWidget 接收这个函数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值