程序员大前端开发

C++,ffmpeg 音视频编程入门

2024-04-11  本文已影响0人  汉堡克

在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;

上一篇下一篇

猜你喜欢

热点阅读