播放器实战15 xdemux与avcodecparameters

1.xdemux
图片来源:https://jiedi.ke.qq.com/
在这里插入图片描述

int main(int argc, char *argv[])
{
    xdemux demux;
    const char* path = "E:\\ffmpeg\\test.flv";
    const char* url = "rtmp://192.168.1.10/live?vhost=ossrs.net/livestream'<br>url6 = ";
    //url = "v1080.mp4";
    cout << "demux.Open = " << demux.Open(path)<<endl;
     cout << "CopyVPara = " << demux.CopyVPara( ) << endl;
    cout << "CopyAPara = " << demux.CopyAPara() << endl;
    for (;;)
    {
        AVPacket* pkt = demux.read();
        if (!pkt)break;
    }
    QApplication a(argc, argv);
    Xplay2 w;
    w.show();
    return a.exec();
}

xdemux.h

#pragma once
#include <mutex>
struct AVFormatContext;
struct AVPacket;
struct AVCodecParameters;
class xdemux
{
public:

	//打开媒体文件,或者流媒体 rtmp http rstp
	virtual bool Open(const char* url);

	xdemux();
	virtual ~xdemux();
	virtual AVPacket* read();
	//媒体总时长(毫秒)
	int totalMs = 0;
protected:
	std::mutex mux;
	//解封装上下文
	AVFormatContext* ic = NULL;
	//音视频索引,读取时区分音视频
	int videoStream = 0;
	int audioStream = 1;
};

xdemux.cpp

#include "XDemux.h"
#include <iostream>
#include<mutex>
extern "C" {
#include "libavformat/avformat.h"
}
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
using namespace std;
static double r2d(AVRational r)
{
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}
xdemux::xdemux()
{
	static bool isFirst = true;
	mutex mux;
	mux.lock();
	if (isFirst)
	{
		//初始化封装库  被弃用了
		//av_register_all();

		//初始化网络库 (可以打开rtsp rtmp http 协议的流媒体视频)
		avformat_network_init();
		isFirst = false;
	}
	mux.unlock();
}
xdemux:: ~xdemux()
{
}

通过实例化xdemux的对象调用xdemux的构造函数,在这里面会进行网络库等初始化


bool xdemux::Open(const char* url)
{
	//参数设置
	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;
	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;
}

2.xdemux:: read()

然后通过xdemux的open方法去解封装,在这个函数中将解封装后的信息放到ic中
解封装后要去读每一个packet:AVPacket* pkt = demux.read();

AVPacket*  xdemux:: read()
{
	mux.lock();
	if (!ic)//打开的话ic是0,否则是1
		//做容错,虽然在main函数中看起来是read在初始化之后被调用,但假如初始化很慢,会多进程调用read,这时候ic就是空的
	{
		mux.unlock();
		return 0;
	}

读和打开不能同时进行,ic在读时初始化,若读一半ic变了的话会发生错误
if (!ic)//打开的话ic是0,否则是1
做容错,虽然在main函数中看起来是read在初始化之后被调用,但假如初始化很慢,会多进程调用read,这时候ic就是空的

	AVPacket* pkt=av_packet_alloc();//只是对象空间,并没有分配数据空间
	int re=av_read_frame(ic, pkt);//分配数据空间
	if (re!=0)//打开的话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->dts  << flush;
	return pkt;
}

将解封装后的信息以ic做为桥梁传进int re=av_read_frame(ic, pkt);将读出的信息放在AVPacket中并用pkt指向(标记)出这块空间,读完一帧会自动读下一帧
在这里插入图片描述
下面显示的是dts(解码时间戳),因为是通过packet打印的,每个packet里有很多帧的信息,所有每个时间戳之间存在一定的间隔

3.avcodecparameters

解封装获得的信息需要传给decode()
在这里插入图片描述

结构体AVCodecParameters中有个指针uint8_t *extradata,
*Extra binary data needed for initializing the decoder, codec - dependent.
*Must be allocated with av_malloc() and will be freed by
avcodec_parameters_free().
//结构体中其他成员可以直接复制,但当结构体释放的时候指针指向的成员也会被释放,直接复制为浅拷贝,就会宕掉
//动态链接库中申请和释放

AVCodecParameters* xdemux::CopyVPara()
{
	mux.lock();//用ic了
	if (!ic)//可能没打开
	{
		mux.unlock();
		return NULL;
	}
	AVCodecParameters* pa = avcodec_parameters_alloc();
	avcodec_parameters_copy(ic->streams[videoStream]->codecpar, pa);
	mux.unlock();
	return pa;
}
AVCodecParameters* xdemux::CopyAPara()
{
	mux.lock();
	if (!ic)//可能没打开
	{
		mux.unlock();
		return NULL;
	}
	AVCodecParameters* pa = avcodec_parameters_alloc();
	avcodec_parameters_copy(ic->streams[audioStream]->codecpar, pa);
	return pa;
}

复制视频和音频信息
先做容错判断,
再开辟空间
再调用复制函数,将ic中的信息放到pa中

主函数调用方法:
cout << "CopyVPara = " << demux.CopyVPara( ) << endl;
cout << "CopyAPara = " << demux.CopyAPara() << endl;

打印了pa的地址
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值