面向对象开发的五大基本原则
单一职责 ===== 各个模块相对独立,优点一:在修改其中某个模块的时候不会对其他模块造成影响;优点二:可以对各个模块进行单独的测试;例如解封装模块和解码模块相互独立设计。
开闭原则 ===== 对扩展开发,修改封闭。例如增加一个功能时,对之前的业务逻辑不需要变更,只需要添加新的方案。
替换原则 ===== 一个类的派生类可以直接替换基类的方法。保证基类接口功能不变,子类去实现自己的接口方法。
接口隔离 ===== 内部对用户进行隔离,使用户只使用我们提供的公共接口。
依赖倒置 ===== 尽量依靠抽象来编程,外部变成全部是基于抽象接口来编程。
超级播放器UML整体架构
#pragma once
#include <mutex>
struct AVFormatContext;
struct AVPacket;
struct AVCodecParameters;
class XDemux
{
public:
//打开媒体文件,或者流媒体 rtmp http rstp
virtual bool Open(const char *url);
//空间需要调用者释放 ,释放AVPacket对象空间,和数据空间 av_packet_free
virtual AVPacket *Read();
virtual bool IsAudio(AVPacket *pkt);
//获取视频参数 返回的空间需要清理 avcodec_parameters_free
virtual AVCodecParameters *CopyVPara();
//获取音频参数 返回的空间需要清理 avcodec_parameters_free
virtual AVCodecParameters *CopyAPara();
//seek 位置 pos 0.0 ~1.0
virtual bool Seek(double pos);
//清空读取缓存
virtual void Clear();
virtual void Close();
XDemux();
virtual ~XDemux();
//媒体总时长(毫秒)
int totalMs = 0;
int width = 0;
int height = 0;
int sampleRate = 0;
int channels = 0;
protected:
std::mutex mux;
//解封装上下文
AVFormatContext *FormatContext = NULL;
//音视频索引,读取时区分音视频
int videoStream = 0;
int audioStream = 1;
};
#include "XDemux.h"
#include <iostream>
using namespace std;
extern "C" {
#include "libavformat/avformat.h"
}
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(
&FormatContext,
url,
NULL, // 0表示自动选择解封器
NULL //参数设置,比如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(FormatContext, 0);
//总时长 毫秒
int totalMs = FormatContext->duration / (AV_TIME_BASE / 1000);
cout << "totalMs = " << totalMs << endl;
//打印视频流详细信息
av_dump_format(FormatContext, 0, url, 0);
//获取视频流
videoStream = av_find_best_stream(FormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
AVStream *as = FormatContext->streams[videoStream];
width = as->codecpar->width;
height = as->codecpar->height;
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(FormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
as = FormatContext->streams[audioStream];
sampleRate = as->codecpar->sample_rate;
channels = as->codecpar->channels;
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;
}
//清空读取缓存
void XDemux::Clear()
{
mux.lock();
if (!FormatContext)
{
mux.unlock();
return;
}
//清理读取缓冲
avformat_flush(FormatContext);
mux.unlock();
}
void XDemux::Close()
{
mux.lock();
if (!FormatContext)
{
mux.unlock();
return;
}
avformat_close_input(&FormatContext);
//媒体总时长(毫秒)
totalMs = 0;
mux.unlock();
}
//seek 位置 pos 0.0 ~1.0
bool XDemux::Seek(double pos)
{
mux.lock();
if (!FormatContext)
{
mux.unlock();
return false;
}
//清理读取缓冲,防止网络流粘包
avformat_flush(FormatContext);
long long seekPos = 0;
seekPos = FormatContext->streams[videoStream]->duration * pos;
int re = av_seek_frame(FormatContext, videoStream, seekPos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
mux.unlock();
if (re < 0) return false;
return true;
}
//获取视频参数 返回的空间需要清理 avcodec_parameters_free
AVCodecParameters *XDemux::CopyVPara()
{
mux.lock();
if (!FormatContext)
{
mux.unlock();
return NULL;
}
AVCodecParameters *pa = avcodec_parameters_alloc();
avcodec_parameters_copy(pa, FormatContext->streams[videoStream]->codecpar);
mux.unlock();
return pa;
}
//获取音频参数 返回的空间需要清理 avcodec_parameters_free
AVCodecParameters *XDemux::CopyAPara()
{
mux.lock();
if (!FormatContext)
{
mux.unlock();
return NULL;
}
AVCodecParameters *pa = avcodec_parameters_alloc();
avcodec_parameters_copy(pa, FormatContext->streams[audioStream]->codecpar);
mux.unlock();
return pa;
}
bool XDemux::IsAudio(AVPacket *pkt)
{
if (!pkt) return false;
if (pkt->stream_index == videoStream)
return false;
return true;
}
//空间需要调用者释放 ,释放AVPacket对象空间,和数据空间 av_packet_free
AVPacket *XDemux::Read()
{
//防止Open被线程性关掉
mux.lock();
if (!FormatContext) //容错
{
mux.unlock();
return 0;
}
//定义一个临时的AVPacket用于解码使用
AVPacket *pkt = av_packet_alloc();
//读取一帧,并分配空间
int re = av_read_frame(FormatContext, pkt);
if (re != 0)
{
mux.unlock();
av_packet_free(&pkt);
return 0;
}
//pts转换为毫秒
pkt->pts = pkt->pts*(1000 * (r2d(FormatContext->streams[pkt->stream_index]->time_base)));
pkt->dts = pkt->dts*(1000 * (r2d(FormatContext->streams[pkt->stream_index]->time_base)));
mux.unlock();
cout << "**********************************\n";
cout << "PKT DTS: " << pkt->dts << endl;
cout << "PKT PTS: " << pkt->pts << endl;
/*cout << pkt->pts << " " << flush;*/
return pkt;
}
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()
{
}
本项目使用QT5.9 FFmpeg4.2.1