FFmpeg+qt实现的播放器解析(一)

视音频 专栏收录该内容
6 篇文章 0 订阅

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实现的播放器解析(四)

  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 岁月 设计师:pinMode 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值