解封装类的实现【3】

解封装的流程整合为类

1、需要的头文件

#include //多线程的互斥锁
#include //C++输入输出流

extern “C”
{
#include “libavformat/avformat.h”//解封装
#include “libavcodec/avcodec.h”//解码
#include “libavutil/avutil.h”//工具
}
#pragma comment(lib,“avformat.lib”)//解封装
#pragma comment(lib,“avutil.lib”)//工具库,包含打印错误信息函数
#pragma comment(lib,“avcodec.lib”)//解码

2、成员变量

开放的变量
public:
int total_time = 0;//媒体总时长ms

int width = 0;//保存视频的宽高
int height = 0;

int sampleRate = 0;//保存音频的采样率和通道数
int channels = 0;

不开放的变量
protected:
std::mutex mux;//互斥对象,互斥锁,保证多线程对变量的正确访问

AVFormatContext* ic = NULL;//解封装上下文,在Open()函数中开辟空间,释放

int videoStream = 0;//视频索引
int audioStream = 1;//音频索引

函数声明

XDemux();//初始化ffmpeg库

virtual ~XDemux();//虚析构函数,保证继承类指针空间正确释放

//打开媒体文件或者流媒体 (rtmp http rstp),打开ic的空间,ic自己释放
virtual bool Open(const char* url);

//读取一帧数据包pkt
//返回的空间需要清理,空间需要调用者释放,分配av_packet_alloc(),
//av_read_frame()。
//释放AVPacket对象空间和其数据空间,使用av_packet_free()
virtual AVPacket* Read();

//获取视频参数,返回的空间需要调用者清理,分配avcodec_parameters_alloc()清理avcodec_parameters_free()
virtual AVCodecParameters* copyVparam();

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

//seek位置 pos:0.0 - 1.0 跳转到媒体流的百分比位置
virtual bool Seek(double pos);

//清空ic读取缓存
virtual void Clear();

//清理ic上下文的内存空间
virtual void Close();

//显示音频和视频的流媒体信息,并且保存音频和视频流的一些参数
virtual void printInformation(const char* url);

//判断是视频包还是音频包
virtual bool isAudio(AVPacket* pkt);

.cpp与.h文件

#pragma once
#include <mutex>
#include <iostream>

extern "C"
{
#include "libavformat/avformat.h"//解封装
#include "libavcodec/avcodec.h"//解码
#include "libavutil/avutil.h"//工具
}
#pragma comment(lib,"avformat.lib")//解封装
#pragma comment(lib,"avutil.lib")//工具库,包含打印错误信息函数
#pragma comment(lib,"avcodec.lib")//解码


class XDemux
{

public:
	XDemux();//初始化ffmpeg库
	virtual ~XDemux();

	//打开媒体文件或者流媒体 rtmp http rstp
	virtual bool Open(const char* url);

	//读取一帧数据
	//返回的空间需要清理,空间需要调用者释放,分配av_packet_alloc(),av_read_frame()。
	//释放AVPacket对象空间和其数据空间,使用av_packet_free()
	virtual AVPacket* Read();

	//获取视频参数,返回的空间需要清理,分配avcodec_parameters_alloc()清理avcodec_parameters_free()
	virtual AVCodecParameters* copyVparam();

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

	//seek位置 pos:0.0 - 1.0 跳转到媒体流的百分比位置
	virtual bool Seek(double pos);

	//清空ic读取缓存
	virtual void Clear();

	//清理ic上下文的内存空间
	virtual void Close();

	//显示音频和视频的流媒体信息,并且保存音频和视频流的一些参数
	virtual void printInformation(const char* url);

	//判断是视频包还是音频包
	virtual bool isAudio(AVPacket* pkt);


public:
	//媒体总时长ms
	int total_time = 0;

	int width = 0;//保存视频的宽高
	int height = 0;

	int sampleRate = 0;//保存音频信息
	int channels = 0;

protected:
	//互斥对象
	std::mutex mux;
	//解封装上下文
	AVFormatContext* ic = NULL;

	//音视频索引
	int videoStream = 0;
	int audioStream = 1;

};

开辟了ic的空间,自己释放ic空间。开辟了pkt的空间,等待调用者释放。开辟了音频和视频参数Aparam、Vparam的空间,等待调用着释放。保存了视频的宽高参数,保存了音频的采样率和通道数参数。


#include "XDemux.h"

using namespace std;

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//


bool XDemux::isAudio(AVPacket* pkt)
{
	if (!pkt)return false;

	if (pkt->stream_index == videoStream)
		return false;

	return true;
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

XDemux::XDemux()
{
	static bool isFirst = true;
	static std::mutex dmux;//互斥变量
	dmux.lock();//上锁,其它线程等待,防止多次初始化
	if (isFirst)
	{
		//初始化
		//初始化封装库
		av_register_all();

		//初始化网络库(可以打开rtsp rtmp http 协议的流媒体视频)
		avformat_network_init();
		isFirst = false;
	}
	dmux.unlock();//解锁,其它线程执行

}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

XDemux::~XDemux()
{
	
	this->Close();
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//显示信息
void XDemux::printInformation(const char* url)
{
	//获取音视频流信息,包含了视频和音频 
	//mp4在这里flv有所不同
	int res = avformat_find_stream_info(ic, 0);
	//AV_TIME_BASE是时间的计数百万, ic->duration / AV_TIME_BASE是秒。
	//打印总时长 ms
	total_time = ic->duration / (AV_TIME_BASE / 1000);
	cout << "total time : " << total_time << " ms" << endl;

	//打印视频流的详细信息
	cout << "=======================媒流体的所有信息如下:========================" << endl;
	av_dump_format(ic, 0, url, 0);
	cout << "==============================end=======================================" << endl << endl;

	//分别获取音视频索引,并打印音频和视频的相关信息
	//获取音视频流信息(遍历,函数获取),ic->nb_streams是媒体流的数量
	videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

	//音频
	AVStream* stream = ic->streams[audioStream];
	sampleRate = stream->codecpar->sample_rate;//保存音频信息
	channels = stream->codecpar->channels;

	cout << "==========================" << audioStream << " : 音频信息如下:" << "==================" << endl;
	//stream->codecpar->codec_type 中保存流的类别
	cout << "codec_id = " << stream->codecpar->codec_id << endl;//AVCodecID
	cout << "sample_rate = " << stream->codecpar->sample_rate << endl;
	cout << "format = " << stream->codecpar->format << endl;//AVSampleFormat
	cout << "channnels = " << stream->codecpar->channels << endl;
	cout << "audio fps = " << av_q2d(stream->avg_frame_rate) << endl;//存储浮点数是按照分子分母保存的
	//音频一帧数据 是 一定量的样本数 单通道
	cout << "frame_size = " << stream->codecpar->frame_size << endl;//fps = sample_rate/frame_size
	cout << "==============================end=======================================" << endl << endl;

	//视频
	stream = ic->streams[videoStream];
	width = stream->codecpar->width;//将视频的宽给成员变量
	height = stream->codecpar->height;//将视频的高给成员变量

	int vs_duration = (stream->duration) * av_q2d(stream->time_base);//打印时长 av_q2d = r2d
	cout << "==========================" << videoStream << " : 视频信息如下:" << "==================" << endl;
	cout << "codec_id = " << stream->codecpar->codec_id << endl;//AVCodecID
	cout << "sample_rate = " << stream->codecpar->sample_rate << endl;
	cout << "width = " << stream->codecpar->width << endl;
	cout << "height = " << stream->codecpar->height << endl;
	cout << "vedio fps = " << av_q2d(stream->avg_frame_rate) << endl;//帧率 fps 分数转换
	cout << "视频的总时长为: " << vs_duration / 3600 << ":" << vs_duration % 3600 / 60 << ":" << vs_duration % 60 << endl;
	cout << "==============================end=======================================" << endl << endl;
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//打开媒体文件或者流媒体 rtmp http rstp
bool XDemux::Open(const char* url)
{
	//打开前先清空ic的内容,不能在上锁后执行,会死锁
	this->Close();

	const char* path = url;
	//参数设置
	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, //上下文结构体:保存音视频的构成和基本信息
		path, //音视频文件路径
		0, //自动选择解封器
		&opts//参数设置,比如rtsp的延迟设置
	);
	if (re != 0)//0为成功打开文件
	{
		mux.unlock();//解锁,打开失败就解锁,解锁在返回之前
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);//打印错误原因
		cout << " open--" << path << "--failed! : " << buf << endl;
		return false;
	}
	cout << "open--" << path << "--success! : " << endl;

	//打印流媒体信息
	this->printInformation(path);



	mux.unlock();//解锁,使用完了就解锁,解锁在返回之前

	return true;
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//空间需要调用者释放,释放AVPacket对象空间 和 数据空间 av_packet_free
AVPacket* XDemux::Read()
{
	mux.lock();//上锁
	if (!ic)//流媒体上下文不存在
	{
		mux.unlock();//解锁在返回之前
		return NULL;
	}

	AVPacket* pkt = av_packet_alloc();//只是分配对象空间,没有分配数据空间
	
	//可能读取的为视频帧,也可能为音频帧
	int re = av_read_frame(ic, pkt);//读取一帧,并且分配了数据的空间

	if (re != 0)//一帧数据不存在 0代表成功
	{
		mux.unlock();//解锁在返回之前
		av_packet_free(&pkt);
		return NULL;
	}
	//pts同一转换为毫秒
	pkt->pts = pkt->pts * av_q2d(ic->streams[pkt->stream_index]->time_base) * 1000;
	pkt->dts = pkt->dts * av_q2d(ic->streams[pkt->stream_index]->time_base) * 1000;

	mux.unlock();//解锁在返回之前

	cout << "时间戳 pts = " << pkt->pts << endl;

	return pkt;

}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//获取视频参数,返回的空间需要清理,分配avcodec_parameters_alloc()清理avcodec_parameters_free()
AVCodecParameters* XDemux::copyVparam()
{
	mux.lock();//上锁

	if (!ic)
	{
		mux.unlock();//解锁在返回之前
		return NULL;
	}
	//分配参数的内存
	AVCodecParameters* Vparam = avcodec_parameters_alloc();
	//把参数里面的数据复制一份,不是赋值,赋值只是地址,程序结束了被会清空
	avcodec_parameters_copy(Vparam, ic->streams[videoStream]->codecpar);

	mux.unlock();//解锁在返回之前
	return Vparam;
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//获取音频参数,返回的空间需要清理
AVCodecParameters* XDemux::copyAparam()
{
	mux.lock();//上锁

	if (!ic)
	{
		mux.unlock();//解锁在返回之前
		return NULL;
	}
	//分配参数的内存
	AVCodecParameters* Aparam = avcodec_parameters_alloc();
	//把参数里面的数据复制一份,不是赋值,赋值只是地址,程序结束了被会清空
	avcodec_parameters_copy(Aparam, ic->streams[audioStream]->codecpar);

	mux.unlock();//解锁在返回之前
	return Aparam;
}

//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//

//seek位置 pos:0.0 - 1.0
bool XDemux::Seek(double pos)
{

	mux.lock();
	if (!ic)
	{
		mux.unlock();//解锁在返回之前
		return NULL;
	}

	//清理读取缓冲,必须单独清除,不能调用清除函数,会死锁
	avformat_flush(ic);

	long long seekPos = 0;

	seekPos = ic->streams[videoStream]->duration* pos;

	//long long pos = (double)ms / 1000 * r2d(ic->streams[pkt->stream_index]->time_base);
	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();//如果还有一个线程要打开,因为打开的ic还没解锁
	if (!ic)//如果ic为空,还没有打开
	{
		mux.unlock();//解锁在返回之前
		return;
	}

	//清理ic 清空媒体流上下文的内容
	avformat_close_input(&ic);
	//媒体总时长
	total_time = 0;


	mux.unlock();//解锁在返回之前
}
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-//


  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值