前言
FFmpeg 是一个开源的多媒体处理库,广泛用于音视频的编码、解码、转码、处理和流媒体传输等任务。
前言将详细讲解如何使用 FFmpeg 库进行编码和解码操作。
1. 初始化 FFmpeg 库
在使用 FFmpeg 库之前,需要先初始化相关的模块。通常在程序的开始部分调用 av_register_all() 和 avformat_network_init() 函数。
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
}
int main()
{
av_register_all();
avformat_network_init();
// 其他代码
return 0;
}
2. 打开输入文件(解码)
使用 avformat_open_input 函数打开输入文件。在这个过程中,你需要创建一个 AVFormatContext 结构体来存储文件格式的信息。
AVFormatContext *formatContext = NULL;
const char *inputFilePath = "input.mp4";
if (avformat_open_input(&formatContext, inputFilePath, NULL, NULL) != 0)
{
fprintf(stderr, "无法打开输入文件\n");
return -1;
}
3. 获取输入文件的流信息(解码)
使用 avformat_find_stream_info 函数获取输入文件的流信息。这一步会填充 formatContext 中的流列表。
if (avformat_find_stream_info(formatContext, NULL) < 0)
{
fprintf(stderr, "无法获取流信息\n");
return -1;
}
4. 查找视频流(解码)
遍历 formatContext 中的流列表,找到视频流。
视频流的 codec_type 通常为 AVMEDIA_TYPE_VIDEO。
int videoStreamIndex = -1;
for (int i = 0; i < formatContext->nb_streams; i++)
{
if(formatContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1)
{
fprintf(stderr, "未找到视频流\n");
return -1;
}
5. 获取编解码器参数(解码)
从找到的视频流中获取编解码器参数。
AVCodecParameters*codecParameters=formatContext->streams[videoStreamIndex]->codecpar;
6. 查找解码器(解码)
使用 avcodec_find_decoder 函数根据编解码器参数找到相应的解码器。
AVCodec*decoder=avcodec_find_decoder(codecParameters->codec_id);
if (!decoder)
{
fprintf(stderr, "未找到解码器\n");
return -1;
}
7. 创建编解码上下文(解码)
使用 avcodec_alloc_context3 函数创建一个编解码上下文,并使用 avcodec_parameters_to_context 函数将编解码器参数复制到编解码上下文中。
AVCodecContext *codecContext = avcodec_alloc_context3(decoder);
if (avcodec_parameters_to_context(codecContext, codecParameters) < 0)
{
fprintf(stderr, "无法将编解码器参数复制到编解码上下文中\n");
return -1;
}
8. 打开解码器(解码)
使用 avcodec_open2 函数打开解码器。这一步会初始化编解码上下文。
if (avcodec_open2(codecContext, decoder, NULL) < 0)
{
fprintf(stderr, "无法打开解码器\n");
return -1;
}
9. 解码数据(解码)
读取视频数据包并解码。使用 av_read_frame 函数读取数据包,然后使用 avcodec_send_packet 和 avcodec_receive_frame 函数进行解码。
AVPacket packet;
AVFrame *frame = av_frame_alloc();
while (av_read_frame(formatContext, &packet) >= 0)
{
if (packet.stream_index == videoStreamIndex)
{
if (avcodec_send_packet(codecContext, &packet) < 0)
{
fprintf(stderr, "无法发送数据包到解码器\n");
break;
}
while (avcodec_receive_frame(codecContext, frame) == 0)
{
// 处理解码后的帧
// 例如,显示帧或进行其他处理
}
}
av_packet_unref(&packet);
}
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
10. 打开输出文件(编码)
使用 avformat_alloc_output_context2 函数创建输出文件的格式上下文。
AVFormatContext *outputFormatContext = NULL;
const char *outputFilePath = "output.mp4";
avformat_alloc_output_context2(&outputFormatContext, NULL, NULL, outputFilePath);
if (!outputFormatContext)
{
fprintf(stderr, "无法分配输出格式上下文\n");
return -1;
}
11. 创建输出视频流(编码)
创建一个新的视频流,并设置其参数。通常需要设置视频的宽、高、帧率、比特率等参数。
AVStream *outStream = avformat_new_stream(outputFormatContext, NULL);
if (!outStream)
{
fprintf(stderr, "无法创建新的视频流\n");
return -1;
}
outStream->id = outputFormatContext->nb_streams - 1;
outStream->time_base = (AVRational){1, 25};
// 设置编码器参数
AVCodecContext *encoderContext = avcodec_alloc_context3(NULL);
if (!encoderContext)
{
fprintf(stderr, "无法分配编码器上下文\n");
return -1;
}
encoderContext->codec_id = AV_CODEC_ID_H264; // 选择一个编码器
encoderContext->bit_rate = 2000000; // 比特率
encoderContext->width = 1280; // 宽度
encoderContext->height = 720; // 高度
encoderContext->time_base = (AVRational){1, 25}; // 帧率
encoderContext->gop_size = 12; // GOP 大小
encoderContext->max_b_frames = 1; // B帧数
encoderContext->pix_fmt = AV_PIX_FMT_YUV420P; // 像素格式
avcodec_parameters_from_context(outStream->codecpar,encoderContext);
12. 查找编码器(编码)
使用 avcodec_find_encoder 函数查找合适的编码器。
AVCodec *encoder = avcodec_find_encoder(encoderContext->codec_id);
if (!encoder)
{
fprintf(stderr, "未找到编码器\n");
return -1;
}
encoderContext->codec_id = encoder->id;
13. 打开编码器(编码)
使用 avcodec_open2 函数打开编码器。
if (avcodec_open2(encoderContext, encoder, NULL) < 0)
{
fprintf(stderr, "无法打开编码器\n");
return -1;
}
14. 写入文件头(编码)
在实际编码之前,需要写入文件头信息。
if(avio_open(&outputFormatContext->pb,outputFilePath,AVIO_FLAG_WRITE) < 0)
{
fprintf(stderr, "无法打开输出文件\n");
return -1;
}
avformat_write_header(outputFormatContext, NULL);
15. 编码数据(编码)
从解码后的帧中读取数据并进行编码。使用 avcodec_send_frame 和 avcodec_receive_packet 函数进行编码。
AVPacket *encodePacket = av_packet_alloc();
while (av_read_frame(formatContext, &packet) >= 0)
{
if (packet.stream_index == videoStreamIndex)
{
if (avcodec_send_packet(codecContext, &packet) < 0)
{
fprintf(stderr, "无法发送数据包到解码器\n");
break;
}
while (avcodec_receive_frame(codecContext, frame) == 0)
{
// 处理解码后的帧
// 例如,显示帧或进行其他处理
if (avcodec_send_frame(encoderContext, frame) < 0)
{
fprintf(stderr, "无法发送帧到编码器\n");
break;
}
while(avcodec_receive_packet(encoderContext,encodePacket) == 0)
{
// 将编码后的数据包写入输出文件
encodePacket->stream_index = outStream->index;
av_packet_rescale_ts(encodePacket, encoderContext->time_base, outStream->time_base);
av_interleaved_write_frame(outputFormatContext, encodePacket);
av_packet_unref(encodePacket);
}
}
}
av_packet_unref(&packet);}
// 写入文件尾
av_write_trailer(outputFormatContext);
av_packet_free(&encodePacket);
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
avio_close(outputFormatContext->pb);
avformat_free_context(outputFormatContext);
continue...
1608

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



