C++,ffmpeg 音视频编程入门
在C++编程中与FFmpeg相关的开发涵盖了视频、音频处理的多种应用场景,包括编码、解码、转码、流媒体处理、滤镜应用、混音等。
FFmpeg库简介
FFmpeg是一套开源的多媒体框架,包含了一系列用于处理音频、视频的工具和库。其主要组件包括:
libavcodec: 提供音频/视频编解码器库,支持多种格式的编码与解码。
libavformat: 处理媒体容器格式,如MP4、MKV、FLV等,负责封装和解析文件头信息、时间戳等。
libavfilter: 提供丰富的音视频滤镜库,用于实现特效、剪裁、缩放、去噪等功能。
libavutil: 包含通用实用函数和数据结构,如数学运算、随机数生成、时间与内存管理等。
libswscale: 图像像素格式转换库,用于颜色空间转换、大小缩放等。
libswresample: 音频重采样库,处理不同采样率、声道数和音频格式之间的转换。
FFmpeg官方文档:
FFmpeg Documentation:官方提供的完整文档集,包括API参考手册、FAQ、指南等。
FFmpeg Wiki:社区维护的Wiki,包含大量实用教程、示例代码和疑难解答。
官方示例代码:
FFmpeg Examples:官方提供的C语言示例,虽非C++但可以直接移植使用。
FFmpeg Filters Documentation:滤镜库的详细文档,附带示例。
Dranger's FFmpeg Tutorial:经典的FFmpeg入门教程,包含C语言示例,适合初学者。
Using FFmpeg with C++:CodeProject上的C++教程,介绍FFmpeg的基本使用。
书籍:
Mastering FFmpeg:深入讲解FFmpeg的各个方面,适合有一定基础的开发者。
The FFmpeg/Libav Handbook:详细阐述FFmpeg库的使用,包含大量代码示例。
在线课程:
FFmpeg Video Processing with C++:Udemy上的付费课程,涵盖FFmpeg在C++中的实战应用。
硬件加速:
FFmpeg Hardware Acceleration:FFmpeg Wiki页面,介绍如何利用硬件加速进行编解码。
实时流处理:
Streaming with FFmpeg:FFmpeg Wiki页面,涵盖RTMP、HLS、DASH等流媒体协议的处理。
滤镜与特效:
FFmpeg Filters Documentation:官方滤镜库文档,详述每个滤镜的功能与参数。
环境配置与安装
下载与编译FFmpeg
访问FFmpeg官网(https://ffmpeg.org/download.html)下载最新源码包。
解压源码包,进入解压后的目录。
配置编译选项,推荐使用以下命令以生成动态链接库:
./configure--prefix=/usr/local --enable-shared
这里--prefix指定安装路径,--enable-shared生成动态库。根据需要,可以添加其他编译选项,如支持特定编解码器、协议等。
运行make编译源码,然后执行make install安装FFmpeg。
环境变量设置
Linux/Mac系统下,将FFmpeg库路径添加到LD_LIBRARY_PATH环境变量中:
exportLD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
Windows系统下,将FFmpeg库路径添加到系统或项目环境变量PATH中。
C++项目集成
包含头文件与链接库
在C++项目中,需要包含FFmpeg头文件并链接相应的库。以下以CMake为例:
CMakeLists.txt
find_package(PkgConfig REQUIRED)
pkg_check_modules(FFMPEG REQUIRED libavformat libavcodec libavutil)
include_directories(${FFMPEG_INCLUDE_DIRS})
target_link_libraries(your_project ${FFMPEG_LIBRARIES})
如果使用其他构建系统,需要手动指定头文件目录(通常为/usr/local/include)和链接库(如avformat,avcodec,avutil等)。
基础编程步骤
初始化与清理
#include <libavformat/avformat.h>
int main() {
av_register_all(); // 注册所有编解码器和协议
// ... 其他操作
return 0;
}
在程序结束前,确保释放所有FFmpeg分配的资源:
avformat_close_input(&ifmt_ctx); // 关闭输入文件
avcodec_free_context(&dec_ctx); // 释放解码器上下文
打开输入/输出文件
AVFormatContext *ifmt_ctx = nullptr;
if (avformat_open_input(&ifmt_ctx, input_filename, nullptr, nullptr) < 0) {
// 错误处理
}
AVOutputFormat *ofmt = nullptr;
AVFormatContext *ofmt_ctx = nullptr;
if (avformat_alloc_output_context2(&ofmt_ctx, ofmt, NULL, output_filename) < 0) {
// 错误处理
}
读取/写入流信息
if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0) {
// 错误处理
}
AVStream *in_stream = ifmt_ctx->streams[video_stream_idx];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
// 错误处理
}
解码/编码
AVCodec *decoder = avcodec_find_decoder(in_stream->codec->codec_id);
AVCodecContext *dec_ctx = avcodec_alloc_context3(decoder);
if (avcodec_parameters_to_context(dec_ctx, in_stream->codecpar) < 0) {
// 错误处理
}
if (avcodec_open2(dec_ctx, decoder, nullptr) < 0) {
// 错误处理
}
AVPacket packet;
AVFrame *frame = av_frame_alloc();
while (av_read_frame(ifmt_ctx, &packet) >= 0) {
if (packet.stream_index == video_stream_idx) {
int ret = avcodec_send_packet(dec_ctx, &packet);
if (ret < 0) {
// 错误处理
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
// 错误处理
}
// 处理解码后的帧(如编码、显示、保存等)
av_frame_unref(frame);
}
}
av_packet_unref(&packet);
}
av_frame_free(&frame);
高级功能
滤镜处理
使用libavfilter实现视频滤镜效果:
AVFilterGraph *filter_graph = avfilter_graph_alloc();
AVFilterContext *buffersrc_ctx = nullptr, *buffersink_ctx = nullptr;
const char *filter_descr = "scale=w=320:h=240:force_original_aspect_ratio=decrease";
AVFilter *buffersrc = avfilter_get_by_name("buffer");
AVFilter *buffersink = avfilter_get_by_name("buffersink");
AVDictionary *opts = nullptr;
av_dict_set(&opts, "pix_fmts", "yuv420p", 0);
if (avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, opts, filter_graph) < 0) {
// 错误处理
}
if (avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph) < 0) {
// 错误处理
}
if (avfilter_link(buffersrc_ctx, 0, buffersink_ctx, 0) != 0) {
// 错误处理
}
if (avfilter_graph_config(filter_graph, nullptr) < 0) {
// 错误处理
}
// 使用filter_graph进行滤镜处理
音频混音
使用libswresample进行音频重采样和混音:
SwrContext *swr_ctx = swr_alloc_set_opts(nullptr,
out_ch_layout, out_sample_fmt, out_sample_rate,
in_ch_layout, in_sample_fmt, in_sample_rate,
0, nullptr);
if (!swr_ctx || swr_init(swr_ctx) < 0) {
// 错误处理
}
// 读取音频帧
// ...
swr_convert(swr_ctx, &output, out_nb_samples, &input, in_nb_samples);
// 清理swr_ctx
swr_free(&swr_ctx);
错误处理与调试
错误处理
if (ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, errbuf, sizeof(errbuf));
std::cerr << "Error: " << errbuf << std::endl;
// 错误处理
}
日志系统
av_log_set_level(AV_LOG_DEBUG); // 设置日志级别
资源管理与性能优化
使用FFmpeg提供的内存管理函数,如av_malloc(), av_free()等,避免内存泄漏。
合理设置缓冲区大小,减少内存分配次数。
根据硬件特性选择合适的编解码器、滤镜等,利用硬件加速(如GPU、DSP)提升性能。
FFmpeg编程全收集
完整示例代码:
#include <iostream>
#include <string>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
// 定义全局变量
AVFormatContext *ifmt_ctx = nullptr;
AVFormatContext *ofmt_ctx = nullptr;
AVCodecContext *dec_ctx = nullptr;
AVCodecContext *enc_ctx = nullptr;
AVStream *in_stream = nullptr;
AVStream *out_stream = nullptr;
SwsContext *sws_ctx = nullptr;
// 解码与编码过程中的错误处理回调
static void log_callback(void *ptr, int level, const char *fmt, va_list vargs) {
vfprintf(stderr, fmt, vargs);
}
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " input_file output_file" << std::endl;
return 1;
}
const char *input_filename = argv[1];
const char *output_filename = argv[2];
// 初始化FFmpeg库
av_register_all();
av_log_set_level(AV_LOG_INFO);
av_log_set_callback(log_callback);
// 打开输入文件
if (avformat_open_input(&ifmt_ctx, input_filename, nullptr, nullptr) < 0) {
std::cerr << "Could not open input file" << std::endl;
return 1;
}
// 获取输入文件信息
if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0) {
std::cerr << "Failed to retrieve input stream information" << std::endl;
return 1;
}
// 查找视频流
for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
in_stream = ifmt_ctx->streams[i];
break;
}
}
if (!in_stream) {
std::cerr << "No video stream found in input file" << std::endl;
return 1;
}
// 创建解码器上下文
dec_ctx = avcodec_alloc_context3(nullptr);
if (!dec_ctx) {
std::cerr << "Failed to allocate decoder context" << std::endl;
return 1;
}
if (avcodec_parameters_to_context(dec_ctx, in_stream->codecpar) < 0) {
std::cerr << "Failed to copy codec parameters to decoder context" << std::endl;
return 1;
}
AVCodec *decoder = avcodec_find_decoder(dec_ctx->codec_id);
if (!decoder) {
std::cerr << "Failed to find decoder for codec ID " << dec_ctx->codec_id << std::endl;
return 1;
}
if (avcodec_open2(dec_ctx, decoder, nullptr) < 0) {
std::cerr << "Failed to open decoder" << std::endl;
return 1;
}
// 创建输出文件
AVOutputFormat *ofmt = av_guess_format(nullptr, output_filename, nullptr);
if (!ofmt) {
std::cerr << "Could not deduce output format from file extension" << std::endl;
return 1;
}
avformat_alloc_output_context2(&ofmt_ctx, ofmt, nullptr, output_filename);
if (!ofmt_ctx) {
std::cerr << "Failed to create output format context" << std::endl;
return 1;
}
// 添加输出视频流
out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream) {
std::cerr << "Failed to allocate output stream" << std::endl;
return 1;
}
if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
std::cerr << "Failed to copy codec parameters to output stream" << std::endl;
return 1;
}
if (avformat_write_header(ofmt_ctx, nullptr) < 0) {
std::cerr << "Failed to write header to output file" << std::endl;
return 1;
}
// 创建编码器上下文
enc_ctx = avcodec_alloc_context3(decoder);
if (!enc_ctx) {
std::cerr << "Failed to allocate encoder context" << std::endl;
return 1;
}
if (avcodec_parameters_to_context(enc_ctx, out_stream->codecpar) < 0) {
std::cerr << "Failed to copy codec parameters to encoder context" << std::endl;
return 1;
}
if (avcodec_open2(enc_ctx, decoder, nullptr) < 0) {
std::cerr << "Failed to open encoder" << std::endl;
return 1;
}
// 创建像素格式转换上下文
sws_ctx = sws_getContext(
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
enc_ctx->width, enc_ctx->height, enc_ctx->pix_fmt,
SWS_BILINEAR, nullptr, nullptr, nullptr
);
if (!sws_ctx) {
std::cerr << "Failed to create SwsContext" << std::endl;
return 1;
}
// 读取、解码、编码、写入视频帧
AVPacket packet;
AVFrame *input_frame = av_frame_alloc();
AVFrame *output_frame = av_frame_alloc();
uint8_t *output_buffer = static_cast<uint8_t*>(av_malloc(av_image_get_buffer_size(
enc_ctx->pix_fmt, enc_ctx->width, enc_ctx->height, 1
)));
av_image_fill_arrays(output_frame->data, output_frame->linesize, output_buffer, enc_ctx->pix_fmt, enc_ctx->width, enc_ctx->height, 1);
while (true) {
if (av_read_frame(ifmt_ctx, &packet) < 0) {
break;
}
if (packet.stream_index == in_stream->index) {
int ret = avcodec_send_packet(dec_ctx, &packet);
if (ret < 0) {
std::cerr << "Error sending a packet for decoding" << std::endl;
return 1;
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, input_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
std::cerr << "Error during decoding" << std::endl;
return 1;
}
sws_scale(sws_ctx, input_frame->data, input_frame->linesize, 0, dec_ctx->height, output_frame->data, output_frame->linesize);
AVPacket enc_packet;
av_init_packet(&enc_packet);
enc_packet.data = nullptr;
enc_packet.size = 0;
ret = avcodec_send_frame(enc_ctx, output_frame);
if (ret < 0) {
std::cerr << "Error sending a frame for encoding" << std::endl;
return 1;
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, &enc_packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
std::cerr << "Error during encoding" << std::endl;
return 1;
}
enc_packet.stream_index = out_stream->index;
av_interleaved_write_frame(ofmt_ctx, &enc_packet);
av_packet_unref(&enc_packet);
}
}
}
av_packet_unref(&packet);
}
// 写入尾部信息并关闭输出文件
av_write_trailer(ofmt_ctx);
// 释放资源
avcodec_free_context(&dec_ctx);
avcodec_free_context(&enc_ctx);
av_frame_free(&input_frame);
av_frame_free(&output_frame);
av_freep(&output_buffer);
sws_freeContext(sws_ctx);
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&ofmt_ctx->pb);
}
avformat_free_context(ofmt_ctx);
return 0;
}
视频转码
命令行
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset veryfast output.mp4
c++主要代码
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
int main() {
// 初始化FFmpeg库
av_register_all();
// 打开输入文件
AVFormatContext* ifmt_ctx = nullptr;
if (avformat_open_input(&ifmt_ctx, "input.mp4", nullptr, nullptr) < 0) {
std::cerr << "Failed to open input file." << std::endl;
return 1;
}
// 查找视频流并初始化解码器
AVStream* in_stream = nullptr;
AVCodecContext* dec_ctx = nullptr;
for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
in_stream = ifmt_ctx->streams[i];
break;
}
}
if (!in_stream) {
std::cerr << "No video stream found in input file." << std::endl;
return 1;
}
dec_ctx = avcodec_alloc_context3(nullptr);
if (!dec_ctx || avcodec_parameters_to_context(dec_ctx, in_stream->codecpar) < 0) {
std::cerr << "Failed to initialize decoder context." << std::endl;
return 1;
}
AVCodec* decoder = avcodec_find_decoder(dec_ctx->codec_id);
if (!decoder || avcodec_open2(dec_ctx, decoder, nullptr) < 0) {
std::cerr << "Failed to open decoder." << std::endl;
return 1;
}
// 创建输出文件与编码器
AVFormatContext* ofmt_ctx = nullptr;
AVStream* out_stream = nullptr;
AVCodecContext* enc_ctx = nullptr;
if (avformat_alloc_output_context2(&ofmt_ctx, nullptr, "mp4", "output.mp4") < 0) {
std::cerr << "Failed to create output format context." << std::endl;
return 1;
}
out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream || avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
std::cerr << "Failed to set output stream parameters." << std::endl;
return 1;
}
enc_ctx = avcodec_alloc_context3(nullptr);
if (!enc_ctx || avcodec_parameters_to_context(enc_ctx, out_stream->codecpar) < 0) {
std::cerr << "Failed to initialize encoder context." << std::endl;
return 1;
}
enc_ctx->codec_id = AV_CODEC_ID_H264;
enc_ctx->bit_rate = in_stream->codecpar->bit_rate;
enc_ctx->width = in_stream->codecpar->width;
enc_ctx->height = in_stream->codecpar->height;
enc_ctx->time_base = {1, 24}; // Assuming 24 FPS
// H.264 specific settings
enc_ctx->gop_size = 250; // GOP size
enc_ctx->max_b_frames = 3;
enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
AVCodec* encoder = avcodec_find_encoder(enc_ctx->codec_id);
if (!encoder || avcodec_open2(enc_ctx, encoder, nullptr) < 0) {
std::cerr << "Failed to open encoder." << std::endl;
return 1;
}
// Read, decode, encode, and write frames
// ... (omitted for brevity; see完整示例代码)
// Clean up resources
// ... (omitted for brevity; see完整示例代码)
return 0;
}
视频裁剪
命令行
ffmpeg -i input.mp4 -vf "crop=in_w:in_h-100" output.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 添加裁剪过滤器
AVFilterGraph* filter_graph = avfilter_graph_alloc();
AVFilterContext* in_filter_ctx = nullptr, *out_filter_ctx = nullptr;
char args[512];
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
dec_ctx->time_base.num, dec_ctx->time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
inputs->name = av_strdup("in");
inputs->filter_ctx = in_filter_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
outputs->name = av_strdup("out");
outputs->filter_ctx = out_filter_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
if (avfilter_graph_parse_ptr(filter_graph, "crop=in_w:in_h-100", &inputs, &outputs, nullptr) < 0 ||
avfilter_graph_config(filter_graph, nullptr) < 0) {
std::cerr << "Failed to configure filter graph." << std::endl;
return 1;
}
// Read, decode, filter, encode, and write frames
// ... (omitted for brevity; see完整示例代码)
// Clean up resources
// ... (omitted for brevity; see完整示例代码)
return 0;
视频合并
命令行
ffmpeg -i "concat:input1.mp4|input2.mp4|input3.mp4" -c copy output.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 创建输入文件列表
std::vector<std::string> input_files = {"input1.mp4", "input2.mp4", "input3.mp4"};
std::string concat_list = "concat:" + boost::algorithm::join(input_files, "|");
// 打开合并输入文件
if (avformat_open_input(&ifmt_ctx, concat_list.c_str(), nullptr, nullptr) < 0) {
std::cerr << "Failed to open input files for concatenation." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
// 由于使用-c copy,不需要重新编码,只需复制流参数到输出文件
for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream* in_stream = ifmt_ctx->streams[i];
AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream || avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
std::cerr << "Failed to copy stream parameters." << std::endl;
return 1;
}
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频抽帧(提取关键帧)
命令行
ffmpeg -i input.mp4 -vf "select='eq(pict_type,I)'" -vsync 0 keyframes_%03d.png
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置抽帧过滤器
AVFilterGraph* filter_graph = avfilter_graph_alloc();
AVFilterContext* in_filter_ctx = nullptr, *out_filter_ctx = nullptr;
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
dec_ctx->time_base.num, dec_ctx->time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
inputs->name = av_strdup("in");
inputs->filter_ctx = in_filter_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
outputs->name = av_strdup("out");
outputs->filter_ctx = out_filter_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
if (avfilter_graph_parse_ptr(filter_graph, "select='eq(pict_type,I)'", &inputs, &outputs, nullptr) < 0 ||
avfilter_graph_config(filter_graph, nullptr) < 0) {
std::cerr << "Failed to configure filter graph." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略帧处理循环,但此处应保存为图像文件而非编码为视频)
// Clean up resources
// ... (omitted for brevity; see完整示例代码)
return 0;
视频旋转
命令行
ffmpeg -i input.mp4 -vf "transpose=1" rotated.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置旋转过滤器
snprintf(args, sizeof(args), "transpose=1");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0
视频缩放
命令行
ffmpeg -i input.mp4 -vf "scale=640:480" scaled.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置缩放过滤器
snprintf(args, sizeof(args), "scale=640:480");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频去水印
命令行
ffmpeg -i input.mp4 -vf "delogo=x=100:y=100:w=200:h=100" no_logo.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置去水印过滤器
snprintf(args, sizeof(args), "delogo=x=100:y=100:w=200:h=100");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加文字水印
命令行
ffmpeg -i input.mp4 -vf "drawtext=text='My Watermark':fontfile=/path/to/font.ttf:fontsize=24:fontcolor=white@0.5:x=(w-text_w)/2:y=(h-text_h)/2" watermarked.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置文字水印过滤器
snprintf(args, sizeof(args), "drawtext=text='My Watermark':fontfile=/path/to/font.ttf:fontsize=24:fontcolor=white@0.5:x=(w-text_w)/2:y=(h-text_h)/2");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
提取音频流
命令行
ffmpeg -i input.mp4 -vn -acodec copy output.mp3
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 仅保留音频流,不处理视频流
for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream* in_stream = ifmt_ctx->streams[i];
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream || avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
std::cerr << "Failed to copy audio stream parameters." << std::endl;
return 1;
}
}
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加背景音乐
命令行
ffmpeg -i video.mp4 -i background_music.mp3 -map 0:v -map 1:a -c:v copy -shortest output.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 打开背景音乐文件
AVFormatContext* bgm_fmt_ctx = nullptr;
if (avformat_open_input(&bgm_fmt_ctx, "background_music.mp3", nullptr, nullptr) < 0) {
std::cerr << "Failed to open background music file." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
// 保留视频流和背景音乐流
for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream* in_stream = ifmt_ctx->streams[i];
if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream || avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
std::cerr << "Failed to copy video stream parameters." << std::endl;
return 1;
}
}
}
for (unsigned int i = 0; i < bgm_fmt_ctx->nb_streams; i++) {
AVStream* bgm_stream = bgm_fmt_ctx->streams[i];
if (bgm_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);
if (!out_stream || avcodec_parameters_copy(out_stream->codecpar, bgm_stream->codecpar) < 0) {
std::cerr << "Failed to copy audio stream parameters." << std::endl;
return 1;
}
}
}
// ... (与视频转码示例相同的部分,省略)
// 在帧处理循环中,同时读取并处理视频流和背景音乐流的帧
// 注意调整时间戳以保持同步,并根据实际情况选择合适的音频混合方法
return 0;
视频截图
命令行
ffmpeg -i input.mp4 -ss 00:01:30 -vframes 1 screenshot.jpg
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置开始时间
double start_time = 90.0; // 1 minute 30 seconds in seconds
av_seek_frame(ifmt_ctx, -1, start_time * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);
// 只处理一帧
int frame_count = 1;
// ... (与视频转码示例相同的部分,省略帧处理循环,但此处应保存为图像文件而非编码为视频)
return 0;
视频转GIF
命令行
ffmpeg -i input.mp4 -vf "fps=15,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gif
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置GIF生成过滤器链
snprintf(args, sizeof(args), "fps=15,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频速度调整
命令行
ffmpeg -i input.mp4 -filter:v "setpts=PTS/2" slow.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置速度调整过滤器
snprintf(args, sizeof(args), "setpts=PTS/2");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加噪声
命令行
ffmpeg -i input.mp4 -vf "noise=alls=10:allf=t+u" noisy.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置噪声添加过滤器
snprintf(args, sizeof(args), "noise=alls=10:allf=t+u");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加模糊效果
命令行
ffmpeg -i input.mp4 -vf "boxblur=luma_radius=5:luma_power=1:chroma_radius=2:chroma_power=1" blurry.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置模糊效果过滤器
snprintf(args, sizeof(args), "boxblur=luma_radius=5:luma_power=1:chroma_radius=2:chroma_power=1");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加马赛克效果
命令行
ffmpeg -i input.mp4 -vf "delogo=x=100:y=100:w=200:h=100,mpdecimate,setpts=N/FRAME_RATE/TB" mosaic.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置马赛克效果过滤器链
snprintf(args, sizeof(args), "delogo=x=100:y=100:w=200:h=100,mpdecimate,setpts=N/FRAME_RATE/TB");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加色彩校正
命令行
ffmpeg -i input.mp4 -vf "colorbalance=rs=-10:gs=-⅓:bs=+5" color_corrected.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置色彩校正过滤器
snprintf(args, sizeof(args), "colorbalance=rs=-10:gs=-⅓:bs=+5");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加文字滚动字幕
命令行
ffmpeg -i input.mp4 -vf "scroll=text='Scrolling Text':fontfile=/path/to/font.ttf:fontcolor=white@0.5:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=h-line_h-10" scrolling_subtitle.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置文字滚动字幕过滤器
snprintf(args, sizeof(args), "scroll=text='Scrolling Text':fontfile=/path/to/font.ttf:fontcolor=white@0.5:fontsize=24:box=1:boxcolor=black@0.5:boxborderw=5:x=(w-text_w)/2:y=h-line_h-10");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加时间戳
命令行
ffmpeg -i input.mp4 -vf "drawtext=fontfile=/path/to/font.ttf:fontcolor=white@0.5:fontsize=24:text='%{localtime}':x=(w-text_w)/2:y=h-line_h-10" timestamped.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置时间戳过滤器
snprintf(args, sizeof(args), "drawtext=fontfile=/path/to/font.ttf:fontcolor=white@0.5:fontsize=24:text='%{localtime}':x=(w-text_w)/2:y=h-line_h-10");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加边框
命令行
ffmpeg -i input.mp4 -vf "pad=w=640:h=480:x=(ow-iw)/2:y=(oh-ih)/2:color=black@0.5" bordered.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置边框过滤器
snprintf(args, sizeof(args), "pad=w=640:h=480:x=(ow-iw)/2:y=(oh-ih)/2:color=black@0.5");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加老电影效果
命令行
ffmpeg -i input.mp4 -vf "curves=vintage,deband" vintage.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置老电影效果过滤器链
snprintf(args, sizeof(args), "curves=vintage,deband");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加黑边填充
命令行
ffmpeg -i input.mp4 -vf "pad=w=1280:h=720:x=0:y=0:color=black" padded.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置黑边填充过滤器
snprintf(args, sizeof(args), "pad=w=1280:h=720:x=0:y=0:color=black");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加淡入淡出效果
命令行
ffmpeg -i input.mp4 -vf "fade=in:duration=2,fade=out:st=10:d=2" faded.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置淡入淡出效果过滤器链
snprintf(args, sizeof(args), "fade=in:duration=2,fade=out:st=10:d=2");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加颜色渐变背景
命令行
ffmpeg -i input.mp4 -vf "color=cyan@0.5:duration=15,format=yuva420p,colorchannelmixer=aa=0.5" gradient_background.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置颜色渐变背景过滤器链
snprintf(args, sizeof(args), "color=cyan@0.5:duration=15,format=yuva420p,colorchannelmixer=aa=0.5");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加色度键(绿幕)效果
命令行
ffmpeg -i input.mp4 -vf "chromakey=green" chroma_keyed.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置色度键(绿幕)效果过滤器
snprintf(args, sizeof(args), "chromakey=green");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加色度分离(YUV到RGB)效果
命令行
ffmpeg -i input.mp4 -vf "format=yuv420p,split[v][u];[v]hue=s=0,format=yuv420p[v1];[u]hue=s=120,format=yuv420p[u1];[v1][u1]overlay=format=yuv420p" color_separated.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置色度分离(YUV到RGB)效果过滤器链
snprintf(args, sizeof(args), "format=yuv420p,split[v][u];[v]hue=s=0,format=yuv420p[v1];[u]hue=s=120,format=yuv420p[u1];[v1][u1]overlay=format=yuv420p");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加动态模糊效果
命令行
ffmpeg -i input.mp4 -vf "motionblur=duration=5" motion_blurred.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 设置动态模糊效果过滤器
snprintf(args, sizeof(args), "motionblur=duration=5");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;
视频添加图像叠加效果
命令行
ffmpeg -i input.mp4 -i overlay.png -filter_complex "[0:v][1:v]overlay=W-w-10:H-h-10" image_overlayed.mp4
c++主要代码
// ... (与视频转码示例相同的部分,省略)
// 打开叠加图片文件
AVFormatContext* img_fmt_ctx = nullptr;
if (avformat_open_input(&img_fmt_ctx, "overlay.png", nullptr, nullptr) < 0) {
std::cerr << "Failed to open overlay image file." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
// 设置图像叠加效果过滤器链
snprintf(args, sizeof(args), "[0:v][1:v]overlay=W-w-10:H-h-10");
if (avfilter_graph_create_filter(&in_filter_ctx, avfilter_get_by_name("buffer"), "in", args, nullptr, filter_graph) < 0 ||
avfilter_graph_create_filter(&out_filter_ctx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, filter_graph) < 0) {
std::cerr << "Failed to create filter contexts." << std::endl;
return 1;
}
// ... (与视频转码示例相同的部分,省略)
return 0;