main.cpp
cpp
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#include <iostream>
#include <string>
int resize_video(const std::string &input_filename, const std::string &output_filename, int width, int height) {
av_register_all();
AVFormatContext *input_format_context = nullptr;
if (avformat_open_input(&input_format_context, input_filename.c_str(), nullptr, nullptr) < 0) {
std::cerr << "Could not open input file." << std::endl;
return -1;
}
if (avformat_find_stream_info(input_format_context, nullptr) < 0) {
std::cerr << "Could not find stream information." << std::endl;
return -1;
}
AVCodec *decoder = nullptr;
int video_stream_index = av_find_best_stream(input_format_context, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
if (video_stream_index < 0) {
std::cerr << "Could not find video stream in the input file." << std::endl;
return -1;
}
AVCodecContext *decoder_context = avcodec_alloc_context3(decoder);
if (!decoder_context) {
std::cerr << "Could not allocate decoder context." << std::endl;
return -1;
}
if (avcodec_parameters_to_context(decoder_context, input_format_context->streams[video_stream_index]->codecpar) < 0) {
std::cerr << "Could not copy decoder parameters to decoder context." << std::endl;
return -1;
}
if (avcodec_open2(decoder_context, decoder, nullptr) < 0) {
std::cerr << "Could not open decoder." << std::endl;
return -1;
}
AVFormatContext *output_format_context = nullptr;
if (avformat_alloc_output_context2(&output_format_context, nullptr, nullptr, output_filename.c_str()) < 0) {
std::cerr << "Could not create output context." << std::endl;
return -1;
}
AVStream *output_stream = avformat_new_stream(output_format_context, nullptr);
if (!output_stream) {
std::cerr << "Failed to allocate output stream." << std::endl;
return -1;
}
AVCodec *encoder = avcodec_find_encoder(decoder_context->codec_id);
if (!encoder) {
std::cerr << "Necessary encoder not found." << std::endl;
return -1;
}
AVCodecContext *encoder_context = avcodec_alloc_context3(encoder);
if (!encoder_context) {
std::cerr << "Could not allocate encoder context." << std::endl;
return -1;
}
encoder_context->height = height;
encoder_context->width = width;
encoder_context->sample_aspect_ratio = decoder_context->sample_aspect_ratio;
encoder_context->pix_fmt = encoder->pix_fmts[0];
encoder_context->time_base = av_inv_q(decoder_context->framerate);
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
encoder_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
if (avcodec_open2(encoder_context, encoder, nullptr) < 0) {
std::cerr << "Could not open encoder." << std::endl;
return -1;
}
if (avcodec_parameters_from_context(output_stream->codecpar, encoder_context) < 0) {
std::cerr << "Could not copy encoder parameters to output stream." << std::endl;
return -1;
}
output_stream->time_base = encoder_context->time_base;
if (!(output_format_context->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&output_format_context->pb, output_filename.c_str(), AVIO_FLAG_WRITE) < 0) {
std::cerr << "Could not open output file." << std::endl;
return -1;
}
}
if (avformat_write_header(output_format_context, nullptr) < 0) {
std::cerr << "Error occurred when opening output file." << std::endl;
return -1;
}
AVFrame *frame = av_frame_alloc();
AVFrame *sws_frame = av_frame_alloc();
if (!frame || !sws_frame) {
std::cerr << "Could not allocate frame." << std::endl;
return -1;
}
sws_frame->format = encoder_context->pix_fmt;
sws_frame->width = encoder_context->width;
sws_frame->height = encoder_context->height;
if (av_image_alloc(sws_frame->data, sws_frame->linesize, width, height, encoder_context->pix_fmt, 32) < 0) {
std::cerr << "Could not allocate destination image." << std::endl;
return -1;
}
SwsContext *sws_context = sws_getContext(
decoder_context->width, decoder_context->height, decoder_context->pix_fmt,
width, height, encoder_context->pix_fmt,
SWS_BILINEAR, nullptr, nullptr, nullptr
);
AVPacket packet;
av_init_packet(&packet);
packet.data = nullptr;
packet.size = 0;
while (av_read_frame(input_format_context, &packet) >= 0) {
if (packet.stream_index == video_stream_index) {
if (avcodec_send_packet(decoder_context, &packet) == 0) {
while (avcodec_receive_frame(decoder_context, frame) == 0) {
sws_scale(sws_context, frame->data, frame->linesize, 0, frame->height, sws_frame->data, sws_frame->linesize);
sws_frame->pts = frame->pts;
if (avcodec_send_frame(encoder_context, sws_frame) == 0) {
AVPacket encoded_packet;
av_init_packet(&encoded_packet);
encoded_packet.data = nullptr;
encoded_packet.size = 0;
while (avcodec_receive_packet(encoder_context, &encoded_packet) == 0) {
av_interleaved_write_frame(output_format_context, &encoded_packet);
av_packet_unref(&encoded_packet);
}
}
}
}
}
av_packet_unref(&packet);
}
av_write_trailer(output_format_context);
avcodec_close(decoder_context);
avcodec_close(encoder_context);
avformat_close_input(&input_format_context);
if (output_format_context && !(output_format_context->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&output_format_context->pb);
}
avformat_free_context(output_format_context);
av_frame_free(&frame);
av_frame_free(&sws_frame);
sws_freeContext(sws_context);
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 5) {
std::cerr << "Usage: " << argv[0] << " <input file> <output file> <width> <height>" << std::endl;
return -1;
}
std::string input_filename = argv[1];
std::string output_filename = argv[2];
int width = std::stoi(argv[3]);
int height = std::stoi(argv[4]);
if (resize_video(input_filename, output_filename, width, height) == 0) {
std::cout << "Video resized successfully." << std::endl;
} else {
std::cout << "Failed to resize video." << std::endl;
}
return 0;
}
代码解释
- 初始化 FFmpeg :使用
av_register_all()
初始化 FFmpeg 库。 - 打开输入文件 :使用
avformat_open_input()
打开输入文件,并获取输入文件的格式上下文。 - 查找视频流 :使用
av_find_best_stream()
查找输入文件中的视频流,并获取其解码器和解码器上下文。 - 打开解码器 :使用
avcodec_open2()
打开解码器。 - 创建输出文件上下文 :使用
avformat_alloc_output_context2()
创建输出文件的格式上下文。 - 创建输出流并初始化编码器:为输出文件创建一个新的流,并为其分配编码器上下文。设置编码器上下文的宽度、高度、像素格式和时间基准。
- 打开编码器 :使用
avcodec_open2()
打开编码器。 - 写入文件头 :使用
avformat_write_header()
将文件头写入输出文件。 - 初始化 SWS 上下文 :使用
sws_getContext()
初始化 SWS 上下文,用于缩放图像。 - 读取和处理帧:循环读取输入文件中的每个帧,缩放帧,并将其发送到编码器,然后将编码后的帧写入输出文件。
- 写入文件尾 :使用
av_write_trailer()
写入输出文件的文件尾。 - 清理资源:关闭解码器和编码器,关闭输入和输出文件,并释放所有分配的资源。