ffmpeg视音频解码器启动

本文介绍了如何使用FFmpeg库进行音视频解码器的封装和调用,包括视频文件的解封装、多媒体流信息查看、解码器查找与启动。代码示例展示了从打开视频文件到初始化解码器上下文的完整过程,适用于音视频处理的工程实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实ffmpeg音视频的解码器调用流程在上一篇文章中已经讲述完成了,本文主要是将已有的程序进行了一定的封装处理,令其更符合工程规范。

先上代码:

#include<iostream>

extern "C"
{
#include "libavcodec/avcodec.h"
#include "include/libavformat/avformat.h"
#include "include/libswscale/swscale.h"
#include "include/libavdevice/avdevice.h"
}
using namespace std;

AVFormatContext* myFFMpegDemux(const char* path, AVFormatContext* ic);//打开视频文件
void myFFMpegShow(AVFormatContext* ic);//显示视频文件信息
const AVCodec* myFFMpegFindVideoCodec(AVFormatContext* ic, const AVCodec* vcodec, int* videoindex);//寻找视频流解码器
const AVCodec* myFFMpegFindAudioCodec(AVFormatContext* ic, const AVCodec* acodec, int* audioindex);//寻找音频流解码器
void myFFMpegOpenVideoCodec(AVFormatContext* ic, AVCodecContext *vc, int videoindex);//启动视频流解码器
void myFFMpegOpenAudioCodec(AVFormatContext* ic, AVCodecContext *ac, int audioindex);//启动音频流解码器

int main(int argc, char *argv[])
{
	const char* path = "ds.mov";//记录视频源文件的路径,这里视频文件ds.mov直接放在项目工程里面了,所以可以直接用视频名称
	//如果视频不在项目工程里面,路径的书写格式举例:D:\\code of visual studio\\ffmpegTest\\ffmpegTest\\ds.mov
	//路径要使用“\\”,不然会被视为转义字符
	cout << "TEST DEMUX" << endl;

	//初始化封装库
	//在新版本中av_register_all()被弃用了,可以根据代码里有无此函数判断ffmpeg版本

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

	//解封装上下文
	AVFormatContext* ic = nullptr;//将其地址做为输入,会申请一块空间,将这块空间的地址赋给ic
	//解封装上下文AVFormatContext,是存储音视频封装格式中包含信息的结构体。

	//对视频进行解封装
	ic = myFFMpegDemux(path,ic);

	//获取并显示视频流与音频流的信息
	myFFMpegShow(ic);

	//初始化解码器信息
	const AVCodec* vcodec = nullptr;
	const AVCodec* acodec = nullptr;

	//寻找合适的视频解码器与音频解码器
	int videoindex = 0;
	int audioindex = 0;
	vcodec = myFFMpegFindVideoCodec(ic, vcodec,&videoindex);
	acodec = myFFMpegFindAudioCodec(ic, acodec, &audioindex);
	
	//创建解码器上下文
	AVCodecContext *vc = avcodec_alloc_context3(vcodec);
	myFFMpegOpenVideoCodec(ic, vc, videoindex);
	AVCodecContext *ac = avcodec_alloc_context3(acodec);
	myFFMpegOpenAudioCodec(ic, ac, audioindex);

	//资源回收
	if (ic) {//如果封装上下文仍存在
		avformat_close_input(&ic);//释放资源,指针置零
		ic = nullptr;
		vcodec = nullptr;
		acodec = nullptr;
		vc = nullptr;
		ac = nullptr;
	}
	return 0;
}

AVFormatContext* myFFMpegDemux(const char* path,AVFormatContext* ic) {
	//对视频文件进行解封装操作
	int re = avformat_open_input(&ic, path, 0, nullptr);//0表示自动选择解封装器,设置一个返回值知道有无错误
	if (re != 0)//如果返回值不是0,说明打开出现错误
	{
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);//记录错误
		cout << "open" << path << "failed!:" << buf << endl;//提示错误
		return nullptr;
	}
	cout << "open " << path << " success!" << endl;//提示成功
	return ic;
}

void myFFMpegShow(AVFormatContext* ic) {
	avformat_find_stream_info(ic, 0);
	//自行计算视频的总时长,以毫秒为单位,AV_TIME_BASE为1秒时长
	int total = ic->duration / (AV_TIME_BASE / 1000);
	cout << "total ms = " << total << endl;

	//获取视频流的详细信息,包括视频流与音频流
	av_dump_format(ic, 1, "2", 0);

	for (int i = 0; i < ic->nb_streams; i++)//对视频中所有的流进行遍历
	{
		AVStream* as = ic->streams[i];
		//音频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			cout << i << "音频" << endl;
			cout << "simple_rate" << as->codecpar->sample_rate << endl;
			cout << "format" << as->codecpar->format << endl;
			cout << "channels" << as->codecpar->channels << endl;
			cout << "codec_id" << as->codecpar->codec_id << endl;
		}
		//视频
		else if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			cout << i << "视频" << endl;
			cout << "simple_rate" << as->codecpar->sample_rate << endl;
			cout << "format" << as->codecpar->format << endl;
			cout << "channels" << as->codecpar->channels << endl;
			cout << "codec_id" << as->codecpar->codec_id << endl;
		}
	}
}

const AVCodec* myFFMpegFindVideoCodec(AVFormatContext* ic, const AVCodec* vcodec,int* videoindex) {
	//找到视频流在streams[i]中的编号i
	*videoindex = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, &vcodec, 0);
	//找合适的视频解码器,得到流对应的解码器编号
	vcodec = avcodec_find_decoder(ic->streams[*videoindex]->codecpar->codec_id);
	//如果没有找到流对应的解码器,需要判断一下
	if (!vcodec)
	{
		cout << "can't find the codec id" << ic->streams[*videoindex]->codecpar->codec_id << endl;
		return nullptr;
	}
	cout << " find the video codec id" << ic->streams[*videoindex]->codecpar->codec_id << endl;
	return vcodec;
}

const AVCodec* myFFMpegFindAudioCodec(AVFormatContext* ic,const AVCodec* acodec,int* audioindex) {
	//找到音频流在streams[i]中的编号i
	*audioindex = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, &acodec, 0);
	//找合适的音频解码器,得到流对应的解码器编号
	acodec = avcodec_find_decoder(ic->streams[*audioindex]->codecpar->codec_id);
	//如果没有找到流对应的解码器,需要判断一下
	if (!acodec) {
		cout << "can't find the codec id" << ic->streams[*audioindex]->codecpar->codec_id << endl;
		return nullptr;
	}
	cout << " find the audio codec id" << ic->streams[*audioindex]->codecpar->codec_id << endl;
	return acodec;
}

void myFFMpegOpenVideoCodec(AVFormatContext* ic, AVCodecContext *vc, int videoindex) {
	//配置解码器上下文参数
	avcodec_parameters_to_context(vc, ic->streams[videoindex]->codecpar);
	//八线程解码
	vc->thread_count = 8;
	//打开解码器上下文
	int re = avcodec_open2(vc, 0, 0);
	if (re != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		cout << "open vedio codec failed!" << buf << endl;
		return ;
	}
	cout << "video codec success!" << endl;
}

void myFFMpegOpenAudioCodec(AVFormatContext* ic, AVCodecContext *ac, int audioindex) {
	//配置解码器上下文参数
	avcodec_parameters_to_context(ac, ic->streams[audioindex]->codecpar);
	//八线程解码
	ac->thread_count = 8;
	//打开解码器上下文
	int re = avcodec_open2(ac, 0, 0);
	if (re != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		cout << "open audio codec failed!" << buf << endl;
		return;
	}
	cout << "audio codec success!" << endl;
}

以上的代码实现了视频文件的解封装,多媒体流的信息查看,视频流与音频流各自的解码器调用。

在程序中需要注意的是AVFormatContext* ic是贯穿程序始终的变量,因为它保留了视频文件的上下文全部信息,可以一定程度上视为程序文件本身,因此各个函数的传参中通常都有AVFormatContext* ic。

此外需要注意的是myFFMpegDemux函数实现了对视频文件的打开,在该过程中ic的内容是改变的(其初始值是nullptr),当一个变量在局部函数中进行改变处理时,我们需要将它的更新进行保存。对于ic这个指针而言,在局部函数中的更新方式主要为1.使用双指针2.将ic设为返回值。为减少代码复杂程度,本程序选择将ic设为返回值。其他的函数中ic仅仅是提供数值,本身没有改变,因此不需要考虑此步骤。

从代码中可以看出,视频流解码器的操作与音频流解码器的操作是镜像的,终其原因是ffmpeg对音频视频编码器型号进行了统一的编号,提供相同的接口,在程序中除了判断流的数组下标外其余都相同。

 程序运行结果图如上图所示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值