FFmpeg学习之解码

avcodec_find_decoder()

  • 音视频解码流程:

        1. 注册所有解码器

        avcodec_register_all()

        2. 找到合适的解码器(获取解码器信息)

        AVCodec * avcodec_find_decoder(enum AVCodecID id);        或

        AVCodec * avcodec_find_decoder_by_name(const char *name);

        例:avcodec_find_decoder_by_name("h264_mediacodec");

        3. 创建解码器上下文(获取当次解码信息)

        AVCodecContext  * avcodec_alloc_context3(const AVCodec *codec);

        4. 配置解码器上下文(一般参数从封装上下文中获取)

        int avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par);

        5. 打开解码器上下文

        int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

       6. 发送packet到线程解码(发送完立刻返回,最开始会发完几包数据才能拿到数据,而发完最后一次有效数据并不能立刻拿到对应解码后的数据,所以发完最后的有效数据后可以再发一次NULL)

        int avcodec_send_packet(AVCodecContext *codec, const AVPacket *avpkt);

        7. 从线程中拿到解码后的帧数据(一次发送可能会对应多个帧数据,所以发完一次会一直去拿,直到拿不到数据,再去发送下一次包解码)

        int avcodec_receive_frame(AVCodecContext *codec, AVFrame *frame);

        8. 结束解码时需要释放AVFrame

        void av_frame_free(AVFrame **frame);

       9. 释放解码器上下文

        void avcodec_free_context(AVCodecContext  **avctx);

  • 代码展示
#include <iostream>
#include <thread>

using namespace std;

//测试解封装
extern "C"{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}

//引用库
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")

//static修饰的分数转浮点函数,避免功能性函数重定义
static double r2d(AVRational r)
{
	return (r.den == 0) ? 0 : (double)r.num/(double)r.den;
}

void XSleep(int ms)
{
	// c++11
	chrono::milliseconds du(ms);
	this_thread::sleep_for(du);
}

int main(int argc, char* argv[])
{
	cout << "Test Demux FFmpeg.club" << endl;

	//初始化封装库
	av_register_all();

	//初始化网络库 可以打开rtsp rtmp http 协议的流媒体视频)
	avformat_network_init();

	//注册解码器
	avcodec_register_all();

	//参数设置
	AVFormatContext *ic = NULL;
	const char* path = "abc.mp4";

	AVDictionary *opts = NULL;//NULL表示使用默认配置
	//设置rtsp流,以tcp协议打开
	//av_dict_set(&opts, "rtsp_transport", "tcp", 0);

	//设置网络延时时间
	//av_dict_set(&opts, "max_delay", "500", 0);

	//解封装上下文
	int ret = avformat_open_input(
		&ic,
		path,		//视频源
		0,			//0表示自动选择解封装器
		&opts		//参数设置,比如rtsp的延时时间
		);

	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "open " << path << " failed ! :" << buff << endl;
		getchar();
		return -1;
	}
	cout << "open " << path << " succeed !" << endl;

	//获取流信息
	ret = avformat_find_stream_info(ic, 0);

	//总时长
	int totalTime = ic->duration/AV_TIME_BASE;
	cout << path << " total time is " << totalTime << " s." << endl;

	//打印视频流详细信息
	av_dump_format(ic, 0, path, 0);

	//音视频索引,用于读取时区分音视频数据
	int audioStream = 0;
	int videoStream = 1;

	//获取音视频流信息 (通过遍历,或函数获取)
	//遍历方式
	for (int i = 0; i < ic->nb_streams; i++)
	{
		AVStream *as = ic->streams[i];
		cout << "format = " << as->codecpar->format << endl;
		cout << "codec_id = " << as->codecpar->codec_id << endl;
		//音频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;
			cout << i << "音频信息" << endl;
			cout << "sample_rate = " <<as->codecpar->sample_rate << endl;
			//AVSampleFormat
			cout << "channels = " << as->codecpar->channels << endl;
			//一帧数据:单通道样本数
			cout << "frame size = " << as->codecpar->frame_size << endl;
		}
		//视频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;
			cout << i << "视频信息" << endl;
			//这里边可能没有宽高参数
			cout << "width = " << as->codecpar->width << endl;
			cout << "height = " << as->codecpar->height << endl;
			//帧率 fps 
			cout << "video FPS = " << r2d(as->avg_frame_rate) << endl;
		}
	}
	//函数方式获取
	audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

	///
	//视频解码器打开
	//找到解码器
	AVCodec *vcodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
	if (!vcodec)
	{
		cout << "couldn't found the codec, codec_id is " << ic->streams[videoStream]->codecpar->codec_id << endl;
		getchar();
		return -1;
	}
	cout << "video AVCodec id is :" << ic->streams[videoStream]->codecpar->codec_id << endl;

	//创建解码器上下文
	AVCodecContext *vc = avcodec_alloc_context3(vcodec);

	//配置解码器上下文参数(由streams中的配置复制过来)
	avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);
	//8线程解码
	vc->thread_count = 8;

	//打开解码器上下文
	ret = avcodec_open2(vc, 0, 0);
	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "avcodec_open2 error ! " << buff << endl;
		getchar();
		return -1;
	}
	cout << "video avcodec_open2 succeed ! " << endl;
	getchar();

	///
	//音频解码器打开
	//找到解码器
	AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
	if (!acodec)
	{
		cout << "couldn't found the codec, codec_id is " << ic->streams[audioStream]->codecpar->codec_id << endl;
		getchar();
		return -1;
	}
	cout << "audio AVCodec id is :" << ic->streams[audioStream]->codecpar->codec_id << endl;

	//创建解码器上下文
	AVCodecContext *ac = avcodec_alloc_context3(acodec);

	//配置解码器上下文参数(由streams中的配置复制过来)
	avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);
	//8线程解码
	vc->thread_count = 8;

	//打开解码器上下文
	ret = avcodec_open2(ac, 0, 0);
	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "avcodec_open2 error ! " << buff << endl;
		getchar();
		return -1;
	}
	cout << "audio avcodec_open2 succeed ! " << endl;
	getchar();

	//AVPacket 申请空间、初始化
	AVPacket *pkt = av_packet_alloc();
	//AVFrame 申请空间、初始化
	AVFrame *frame = av_frame_alloc();
	while (1)
	{
		int ret = av_read_frame(ic, pkt);
		if (ret)
		{
			//读到结尾
			cout << "==============end==============" << endl;
			//跳转到3秒位置
			int ms = 3000;//3秒,根据时间基数转换
			int64_t pos = (double)ms / (double)1000 * r2d(ic->streams[pkt->stream_index]->time_base);
			av_seek_frame(ic, 0, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
			cout << " \\ \\ \\ \\ \\ \\ \\ \\ at 3 ms" << endl;
			continue;
		}
		cout << "pkt->size = " << pkt->size << endl;
		//显示的时间
		cout << "pkt->pts = " << pkt->pts << endl;
		cout << "pkt->pts ms = " << pkt->pts * (r2d(ic->streams[pkt->stream_index]->time_base) * 1000) << endl;

		//XSleep(500);

		//解码的时间
		cout << "pkt->dts = " << pkt->dts << endl;

		//解码上下文
		AVCodecContext *cc = 0;
		if (pkt->stream_index == videoStream)
		{
			cc = vc;
			cout << "图像" << endl;
		}
		if (pkt->stream_index == audioStream)
		{
			cc = ac;
			cout << "音频" << endl;
		}

		///解码视频
		//发送packet到线程解码,发完立即返回
		ret = avcodec_send_packet(cc, pkt);

		//释放库内部单次使用的内存,并将引用计数-1
		av_packet_unref(pkt);

		if (ret != 0)
		{
			char buff[1024] = { 0 };
			av_strerror(ret, buff, sizeof(buff) - 1);
			cout << " avcodec_send_packet error, " << buff << endl;
			continue;
		}

		while (1)
		{
			//从线程中拿解码后的数据,一次send可能对应多次receive
			ret = avcodec_receive_frame(cc, frame);
			if (ret != 0)
			{
				break;
			}
			cout << " recv frame " << frame->format << " " << frame->linesize[0] << endl;
		}
	}
	//释放空间
	av_frame_free(&frame);
	av_packet_free(&pkt);

	if (ic)
	{
		//释放封装上下文,并把ic并置0
		avformat_close_input(&ic);
	}

	getchar();
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值