上一篇文章我们学习了如何把视频文件解封装,本篇文章我们来学习如何解码视频数据。文章分段讲解视频解码的各个步骤,接着会贴上完整代码,最后进行测试。
准备工作
首先创建一个新的控制台工程,把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

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

被折叠的 条评论
为什么被折叠?



