美文网首页程序员大前端开发
C++,ffmpeg 音视频编程入门

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

作者: 汉堡克 | 来源:发表于2024-04-11 14:57 被阅读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;

    相关文章

      网友评论

        本文标题:C++,ffmpeg 音视频编程入门

        本文链接:https://www.haomeiwen.com/subject/fzpcxjtx.html