xDemux : 解封装 获取音视频的信息然后进行seek 操作,静态的处理
xDwmuxThread : 线程启动 开始读取 与xDemux 是组合关系 不是继承
替换原则 : 父类实现的东西子类也可以实现
接口隔离: 客户不需要,隔离开来
依赖倒置 ;该功能方便
1 、创建Qt gui 工程 这里命名未XPlay2 (教程是这么写的 ) 记得加入 OpenGL 和OpenGL 的扩展库
坑 1 无法打开ui …h 头文件
解决方案参考链接
2 、添加第一个类 比较详细后面参照这个就好了
第一步
主要用到的部分源码
#pragma once
#include
//没有用到全部不用引入头文件
// 结构体或者类名声明一次
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
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
#include
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();
}
下面开始绘制图像
解码出来是一个AVFrame ,要在XVideoWidget 接收这个函数