面向对象开发的五大基本原则
单一职责 ===== 各个模块相对独立,优点一:在修改其中某个模块的时候不会对其他模块造成影响;优点二:可以对各个模块进行单独的测试;例如解封装模块和解码模块相互独立设计。
开闭原则 ===== 对扩展开发,修改封闭。例如增加一个功能时,对之前的业务逻辑不需要变更,只需要添加新的方案。
替换原则 ===== 一个类的派生类可以直接替换基类的方法。保证基类接口功能不变,子类去实现自己的接口方法。
接口隔离 ===== 内部对用户进行隔离,使用户只使用我们提供的公共接口。
依赖倒置 ===== 尽量依靠抽象来编程,外部变成全部是基于抽象接口来编程。
超级播放器UML整体架构
#pragma once
#include <mutex>
struct AVFormatContext;
struct AVPacket;
struct AVCodecParameters;
class XDemux
{
public://打开媒体文件,或者流媒体 rtmp http rstpvirtual bool Open(const char *url);//空间需要调用者释放 ,释放AVPacket对象空间,和数据空间 av_packet_freevirtual AVPacket *Read();virtual bool IsAudio(AVPacket *pkt);//获取视频参数 返回的空间需要清理 avcodec_parameters_freevirtual AVCodecParameters *CopyVPara();//获取音频参数 返回的空间需要清理 avcodec_parameters_freevirtual AVCodecParameters *CopyAPara();//seek 位置 pos 0.0 ~1.0virtual 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_sizemux.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()
{
}
本项目下载链接