FFMPEG总结 -- 音视频编解码和转码(全)_ffmpeg音频转码

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(&param, "preset", "slow", 0);
	av\_dict\_set(&param, "tune", "zerolatency", 0);
	//av\_dict\_set(&param, "profile", "main", 0);
}
//H.265
if(pCodecCtx->codec_id == AV_CODEC_ID_H265){
	av\_dict\_set(&param, "preset", "ultrafast", 0);
	av\_dict\_set(&param, "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,&param) < 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)


老规矩,看一下 视频解码 的流程图  
  图下紧接着每个步骤的中文含义  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210207144755502.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h3eDgwMjc0Ng==,size_16,color_FFFFFF,t_70)


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

猜你喜欢

转载自blog.csdn.net/2401_89694064/article/details/144527387
今日推荐