FFmpeg Visual Studio开发(三):视频解码

本文详细介绍了如何使用FFmpeg解码视频文件,包括打开文件、获取视频流、解码视频帧并以YUV格式输出的过程,适合深入理解视频处理技术的开发者。

上一篇文章我们学习了如何把视频文件解封装,本篇文章我们来学习如何解码视频数据。文章分段讲解视频解码的各个步骤,接着会贴上完整代码,最后进行测试。

准备工作

首先创建一个新的控制台工程,把FFmpeg4的库配置好,不熟悉的朋友可以看看第一篇文章。接着跑一下测试程序看看配置是否成功。

#include "stdafx.h"
#include <iostream>

extern "C"
{
   
   
#include "libavformat/avformat.h"
};

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
   
   
	cout << "hello FFmpeg" << endl;
	cout << avcodec_configuration() << endl;
	return 0;
}

打印了配置信息,说明目前是没有问题的了。请添加图片描述
接着我们来认识几个结构体。

结构体 说明
AVFormatContext IO相关的上下文结构体,用于获取音频流、视频流及文件相关操作
AVStream 数据流结构体,可以是音频流或视频流
AVCodecContext 解码器上下文结构体
AVPacket 封装数据帧结构体,用于接收音视频的封装数据
AVFrame 编解码数据帧结构体,用于接收视频编解码数据

最后,我们来了解一下全过程。
1.打开视频文件
2.创建输出文件
3.获取视频流
4.打开解码器
5.循环读取每一个封装视频帧
    5.1.解码视频帧
    5.2.输出yuv帧

打开视频文件

打开视频文件的同时把它的相关数据写入AVFormatContext。

static int openFile(const char* filePath, AVFormatContext **avFormatContext){
   
   
	//打开文件流,读取头信息
	int ret = avformat_open_input(avFormatContext, filePath, NULL, NULL);
	if (ret < 0){
   
   //文件打开失败
		char buff[1024];
		//把具体错误信息写入buff
		av_strerror(ret, buff, sizeof(buff)-1);
		cout << "can't open file" << endl;
		cout << buff << endl;
		//释放AVFormatContext的内存
		avformat_close_input(avFormatContext);
		return -1;
	}
	return 0;
}

创建输出文件

创建一个输出文件用于保存yuv输出数据。

FILE *outputFile = NULL;
fopen_s(&outputFile, outputFilePath, "wb");

获取视频流

视频文件里有音频流和视频流,FFmpeg是通过下标(index)来区分它们的。这里只获取视频流。

static int getVideoStream(int *videoIndex,AVFormatContext* avFormatContext,AVStream **avStream){
   
   
	*videoIndex = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	
	if (videoIndex < 0){
   
   
		cout << av_get_media_type_string(AVMEDIA_TYPE_VIDEO) << endl;
		//释放AVFormatContext的内存
		avformat_close_input(&avFormatContext);
		return -1;
	}

	*avStream = avFormatContext->streams[*videoIndex];
	if (*avStream == NULL){
   
   
		cout << "can't get video stream" << endl;
		return -1;
	}

	return 0;
}

打开解码器

解码器分为音频解码器和视频解码器,这里获取的是视频解码器。

static int openVideoCodec(AVCodecContext **avCodecContext,AVStream *videoStream){
   
   
	int ret;

	//获取视频解码器
	AVCodec *avCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
	if (avCodec == NULL){
   
   
		cout << "can't find codec" << endl;
		return -1;
	}

	//获取视频解码器上下文
	*avCodecContext = avcodec_alloc_context3(avCodec);
	if (avCodecContext == NULL){
   
   
		cout << "can't alloc video codec context" << endl;
		return -1;
	}

	if (avcodec_parameters_to_context(*avCodecContext,videoStream->codecpar) < 0){
   
   
		cout << "can't copy input video codec parms to video decoder context" << endl;
		return -1;
	}

	AVDictionary *opts = NULL;

	//打开视频解码器
	if (avcodec_open2(*avCodecContext, avCodec, &opts) < 0){
   
   
		cout << "can't open video codec" << endl;
		return -1;
	}

	return 0;
}

循环读取每个封装视频帧

视频文件里封装了大量的数据帧,分为音频帧和视频帧。这里每找到一个视频帧就对其进行解码。

...
AVPacket *avPacket = NULL;
	if (createPacket(&avPacket) < 0){
   
   
		return 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值