FFmpeg+qt实现的播放器解析(一)
FFmpeg+qt实现的播放器解析(二)
FFmpeg+qt实现的播放器解析(三)
FFmpeg+qt实现的播放器解析(四)
本文正式开始拆分使用FFmpeg+qt实现的播放器 *首看一下界面类XDemux(实现解封装)
XDemux 类中使用到的ffmpeg函数:
-
void av_register_all();
//注册所有的格式。包括解封装格式和加封装格式。
- int avformat_network_init(void);// 用于初始化网络。FFmpeg本身也支持解封装RTSP的数据,如果要解封装网络数据格式,则可调用该函数。 -
av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags) //设置打开流所使用的协议,和网络延时等
-
int avformat_open_input(AVFormatContext **ps, const char *url, - - AVInputFormat *fmt, AVDictionary **options);//打开一个文件并解析。可解析的内容包括:视频流、音频流、视频流参数、音频流参数、视频帧索引。
-
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)//,查找格式和索引。有些早期格式它的索引并没有放到头当中,需要你到后面探测,就会用到此函数。
-
int av_find_best_stream(AVFormatContext *ic,enum AVMediaType type,int wanted_stream_nb,int related_stream,AVCodec **decoder_ret,int flags);// 当视频被解封装出来后,需要分开处理音频和视频,需要找到对应的音频流和视频流
-
void av_dump_format(AVFormatContext *ic,int index,const char *url,int is_output);//打印流的详细信息
-
int avformat_flush(AVFormatContext *s);//清理读取的缓存
-
void avformat_close_input(AVFormatContext **s);//关闭打开的流
-
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,int flags);//该函数可以将音/视频seek到指定的位置 实现视频的跳转, 快进,快退,
-
AVCodecParameters *avcodec_parameters_alloc(void);//分配新的 - AVCodecParameters结构
- int avcodec_parameters_copy (AVCodecParameters *dst, const AVCodecParameters *src);//*将src的内容复制到dst。dst中的任何分配字段都将被释放,并且
*替换为src中相应字段的新分配重复项。
- AVPacket *av_packet_alloc(void); // AVPacket空间的创建和初始化。创建一个AVPacket对象,它会在堆上面申请空间,因此还需要去手动释放。
- av_packet_free(&pkt);//释放包
头文件:
#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 AVPacket *ReadVideo();
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;
//解封装上下文
AVFormatContext *ic = NULL;
//音视频索引,读取时区分音视频
int videoStream = 0;
int audioStream = 1;
protected:
std::mutex mux;
};
源文件:
#include "XDemux.h"
#include <iostream>
using namespace std;
extern "C" {
#include "libavformat/avformat.h"
}
#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);
//总时长 毫秒
this->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);
if(videoStream<0) return false;
AVStream *as = ic->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(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
as = ic->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 (!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();
}
//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; //向前seek失败的原因是duration=0
int dur=ic->streams[videoStream]->duration;
seekPos = ic->streams[videoStream]->duration * pos;
//seekPos =ic->duration * pos;
int re = av_seek_frame(ic, 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 (!ic)
{
mux.unlock();
return NULL;
}
AVCodecParameters *pa = avcodec_parameters_alloc();
avcodec_parameters_copy(pa, ic->streams[videoStream]->codecpar);
mux.unlock();
return pa;
}
//获取音频参数 返回的空间需要清理 avcodec_parameters_free
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;
}
bool XDemux::IsAudio(AVPacket *pkt)
{
if (!pkt) return false;
if (pkt->stream_index == videoStream)
return false;
return true
}
AVPacket *XDemux::ReadVideo()
{
mux.lock();
if (!ic) //容错
{
mux.unlock();
return 0;
}
mux.unlock();
AVPacket *pkt = NULL;
//防止阻塞
for (;;)
{
pkt = Read();
if (!pkt)break;
if (pkt->stream_index == videoStream)
{
break;
}
av_packet_free(&pkt);
}
return pkt;
}
//空间需要调用者释放 ,释放AVPacket对象空间,和数据空间 av_packet_free
AVPacket *XDemux::Read()
{
mux.lock();
if (!ic) //容错
{
mux.unlock();
return 0;
}
AVPacket *pkt = av_packet_alloc();
//读取一帧,并分配空间
int re = av_read_frame(ic, pkt);
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;
}
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()
{
}
FFmpeg+qt实现的播放器解析(一)
FFmpeg+qt实现的播放器解析(二)
FFmpeg+qt实现的播放器解析(三)
FFmpeg+qt实现的播放器解析(四)