pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
pCodecCtx->width = in_w;
pCodecCtx->height = in_h;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->bit_rate = 400000;
pCodecCtx->gop_size=250;
//H264
//pCodecCtx->me\_range = 16;
//pCodecCtx->max\_qdiff = 4;
//pCodecCtx->qcompress = 0.6;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
// 可选参数
pCodecCtx->max_b_frames=3;
// 设置选项
AVDictionary \*param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
av\_dict\_set(¶m, "preset", "slow", 0);
av\_dict\_set(¶m, "tune", "zerolatency", 0);
//av\_dict\_set(¶m, "profile", "main", 0);
}
//H.265
if(pCodecCtx->codec_id == AV_CODEC_ID_H265){
av\_dict\_set(¶m, "preset", "ultrafast", 0);
av\_dict\_set(¶m, "tune", "zero-latency", 0);
}
//Show some Information
av\_dump\_format(pFormatCtx, 0, out_file, 1); // av\_dump\_format()是一个手工调试的函数,能使我们看到pFormatCtx->streams里面有什么内容。
pCodec = avcodec\_find\_encoder(pCodecCtx->codec_id); // 查找编码器
if (!pCodec){
printf("Can not find encoder! \n");
return -1;
}
if (avcodec\_open2(pCodecCtx, pCodec,¶m) < 0){ // 打开编码器
printf("Failed to open encoder! \n");
return -1;
}
pFrame = av\_frame\_alloc(); // AVFrame结构,av\_frame\_alloc申请内存,av\_frame\_free释放内存
picture_size = avpicture\_get\_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); //计算这个格式的图片,需要多少字节来存储
picture_buf = (uint8\_t \*)av\_malloc(picture_size);
// 这个函数是为已经分配的空间的结构体AVPicture挂上一段用于保存数据的空间
avpicture\_fill((AVPicture \*)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// 写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
avformat\_write\_header(pFormatCtx,NULL);
av\_new\_packet(&pkt,picture_size); // 分配数据包的有效size并初始化
y_size = pCodecCtx->width \* pCodecCtx->height;
// 一帧一帧循环操作
for (int i=0; i<framenum; i++){
// Read raw YUV data
if (fread(picture_buf, 1, y_size\*3/2, in_file) <= 0){ // fread函数,从文件流中读取数据,如果不成功或读到文件末尾返回 0
printf("Failed to read raw data! \n");
return -1;
}else if(feof(in_file)){ // 判断文件是否结束
break;
}
pFrame->data[0] = picture_buf; // Y
pFrame->data[1] = picture_buf+ y_size; // U
pFrame->data[2] = picture_buf+ y_size\*5/4; // V
// PTS
pFrame->pts=i; // pts : 以时间为基本单位的表示时间戳(应该向用户显示帧的时间)。
int got_picture=0;
// 编码一帧视频。即将AVFrame(存储YUV像素数据)编码为AVPacket(存储H.264等格式的码流数据)。
// 成功时返回0,失败时返回负错误代码 失败时返回错误返回码
int ret = avcodec\_encode\_video2(pCodecCtx, &pkt,pFrame, &got_picture);
if(ret < 0){
printf("Failed to encode! \n");
return -1;
}
if (got_picture==1){
printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);
framecnt++;
pkt.stream_index = video_st->index;
ret = av\_write\_frame(pFormatCtx, &pkt); // 将编码后的视频码流写入文件,
av\_free\_packet(&pkt); // free
}
}
// Flush Encoder
int ret = flush\_encoder(pFormatCtx,0); // 输入的像素数据读取完成后调用此函数,用于输出编码器中剩余的AVPacket
if (ret < 0) {
printf("Flushing encoder failed\n");
return -1;
}
// 写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)
av\_write\_trailer(pFormatCtx);
// Clean
if (video_st){
avcodec\_close(video_st->codec);
av\_free(pFrame);
av\_free(picture_buf);
}
avio\_close(pFormatCtx->pb);
avformat\_free\_context(pFormatCtx);
fclose(in_file);
return 0;
}
## 三、ffmpeg 视频解码 (解码为YUV)
老规矩,看一下 视频解码 的流程图
图下紧接着每个步骤的中文含义

1、av\_register\_all():注册所有组件。
2、avformat\_open\_input():打开输入视频文件。
3、avformat\_find\_stream\_info():获取视频文件信息
4、avcodec\_find\_decoder():查找解码器。
5、avcodec\_open2():打开解码器。
6、av\_read\_frame():从输入文件读取一帧压缩数据。
7、avcodec\_decode\_video2():解码一帧压缩数据。
8、avcodec\_close():关闭解码器。
9、avformat\_close\_input():关闭输入视频文件。
建议带着流程看代码
/**
* FFMPEG视频解码流程
* 1、av_register_all():注册所有组件。
* 2、avformat_open_input():打开输入视频文件。
* 3、avformat_find_stream_info():获取视频文件信息
* 4、avcodec_find_decoder():查找解码器。
* 5、avcodec_open2():打开解码器。
* 6、av_read_frame():从输入文件读取一帧压缩数据。
* 7、avcodec_decode_video2():解码一帧压缩数据。
* 8、avcodec_close():关闭解码器。
* 9、avformat_close_input():关闭输入视频文件。
*/
#include “stdafx.h”
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern “C”
{
#include “libavcodec/avcodec.h”
#include “libavformat/avformat.h”
#include “libswscale/swscale.h”
#include “libavutil/imgutils.h”
};
#else
//Linux…
#ifdef __cplusplus
extern “C”
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif
int main()
{
//文件格式上下文
AVFormatContext *pFormatCtx; // 封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装 格式相关信息。
int i = 0, videoindex;
AVCodecContext *pCodecCtx; // 编码器上下文结构体,保存了视频(音频)编解码相关信息。
AVCodec *pCodec; // AVCodec是存储编解码器信息的结构体。
AVFrame *pFrame, *pFrameYUV; // AVFrame是包含码流参数较多的结构体
unsigned char *out_buffer;
AVPacket *packet; // AVPacket是存储压缩编码数据相关信息的结构体
int y_size;
int ret, got_picture;
// struct SwsContext结构体位于libswscale类库中, 该类库主要用于处理图片像素数据, 可以完成图片像素格式的转换, 图片的拉伸等工作.
struct SwsContext \*img_convert_ctx;
char filepath[] = "input.mkv";
FILE \*fp_yuv = fopen("output.yuv", "wb+");
av\_register\_all(); // 注册所有组件
avformat\_network\_init(); // 对网络库进行全局初始化。
pFormatCtx = avformat\_alloc\_context(); // 初始化AVFormatContext结构体指针。使用avformat\_free\_context()释放内存。
if (avformat\_open\_input(&pFormatCtx, filepath, NULL, NULL) != 0) // 打开输入流并读取header。必须使用avformat\_close\_input()接口关闭。
{
printf("Couldn't open input stream.\n");
return -1;
}
//读取一部分视音频数据并且获得一些相关的信息
if (avformat\_find\_stream\_info(pFormatCtx, NULL) < 0) // 读取媒体文件的包以获取流信息
{
printf("Couldn't find stream information.\n");
return -1;
}
//查找视频编码索引
videoindex = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
break;
}
}
if (videoindex == -1)
{
printf("Didn't find a video stream.\n");
return -1;
}
//编解码上下文
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
//查找解码器
pCodec = avcodec\_find\_decoder(pCodecCtx->codec_id); // 查找符合ID的已注册解码器
if (pCodec == NULL)
{
printf("Codec not found.\n");
return -1;
}
//打开解码器
if (avcodec\_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("Could not open codec.\n");
return -1;
}
//申请AVFrame,用于原始视频
pFrame = av\_frame\_alloc();
//申请AVFrame,用于yuv视频
pFrameYUV = av\_frame\_alloc();
//分配内存,用于图像格式转换
out_buffer = (unsigned char \*)av\_malloc(av\_image\_get\_buffer\_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
// 根据指定的图像参数和提供的数组设置参数指针和linesize大小
av\_image\_fill\_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
packet = (AVPacket \*)av\_malloc(sizeof(AVPacket));
//Output Info-----------------------------
printf("--------------- File Information ----------------\n");
//手工调试函数,输出tbn、tbc、tbr、PAR、DAR的含义
av\_dump\_format(pFormatCtx, 0, filepath, 0);
printf("-------------------------------------------------\n");
//申请转换上下文。 sws\_getContext功能:初始化 SwsContext 结构体指针
img_convert_ctx = sws\_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
//读取数据
while (av\_read\_frame(pFormatCtx, packet) >= 0) // 读取码流中的音频若干帧或者视频一帧
{
if (packet->stream_index == videoindex)
{
// avcodec\_decode\_video2 功能:解码一帧视频数据
ret = avc