鲲鹏社区首页
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

FFmpeg的sws_scale函数使用示例

以下为读取一个视频,将视频中的YUV格式图片转成RGB并保存到PNG格式的使用示例

  1. 创建main.cpp文件,写入以下内容。
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    extern "C" {
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/log.h>
    #include <libavutil/imgutils.h>
    #include <libswscale/swscale.h>
    }
    int main(int argc, char *argv[]) {
        // 检查输入参数
        if (argc < 3) {
            fprintf(stderr, "用法: %s <输入视频文件> <输出目录>\n", argv[0]);
            return -1;
        }
        const char *input_filename = argv[1];
        const char *output_dir = argv[2];
        // 打开视频文件
        AVFormatContext *format_ctx = NULL;
        if (avformat_open_input(&format_ctx, input_filename, NULL, NULL) < 0) {
            fprintf(stderr, "无法打开视频文件: %s\n", input_filename);
            return -1;
        }
        // 获取流信息
        if (avformat_find_stream_info(format_ctx, NULL) < 0) {
            fprintf(stderr, "无法获取流信息\n");
            return -1;
        }
        // 查找视频流
        int video_stream_index = -1;
        for (int i = 0; i < format_ctx->nb_streams; i++) {
            if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                video_stream_index = i;
                break;
            }
        }
        if (video_stream_index == -1) {
            fprintf(stderr, "未找到视频流\n");
            return -1;
        }
        // 获取解码器上下文
        AVCodecParameters *codecpar = format_ctx->streams[video_stream_index]->codecpar;
        const AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
        if (!codec) {
            fprintf(stderr, "未找到解码器\n");
            return -1;
        }
        AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
        avcodec_parameters_to_context(codec_ctx, codecpar);
        // 打开解码器
        if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
            fprintf(stderr, "无法打开解码器\n");
            return -1;
        }
        // 初始化SWS上下文(用于图像格式转换)
        struct SwsContext *sws_ctx = sws_getContext(
            codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
            codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24,
            SWS_BILINEAR, NULL, NULL, NULL
        );
        // 分配帧内存
        AVFrame *frame = av_frame_alloc();
        AVFrame *rgb_frame = av_frame_alloc();
        if (!frame || !rgb_frame) {
            fprintf(stderr, "无法分配帧内存\n");
            return -1;
        }
        // 为RGB帧分配缓冲区
        int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codec_ctx->width, codec_ctx->height, 1);
        uint8_t *buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t));
        av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, buffer, AV_PIX_FMT_RGB24, codec_ctx->width, codec_ctx->height, 1);
        // 读取帧并保存为PNG
        AVPacket packet;
        int frame_count = 0;
        while (av_read_frame(format_ctx, &packet) >= 0) {
            if (packet.stream_index == video_stream_index) {
                // 解码视频帧
                if (avcodec_send_packet(codec_ctx, &packet) == 0) {
                    while (avcodec_receive_frame(codec_ctx, frame) == 0) {
                        // 转换为RGB24格式
                        sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0, codec_ctx->height, rgb_frame->data, rgb_frame->linesize);
                        // 保存为PNG文件
                        char output_filename[256];
                        snprintf(output_filename, sizeof(output_filename), "%s/frame_%04d.png", output_dir, frame_count++);
                        FILE *file = fopen(output_filename, "wb");
                        if (file) {
                            fprintf(file, "P6\n%d %d\n255\n", codec_ctx->width, codec_ctx->height);
                            fwrite(rgb_frame->data[0], 1, num_bytes, file);
                            fclose(file);
                        } else {
                            fprintf(stderr, "无法保存文件: %s\n", output_filename);
                        }
                    }
                }
            }
            av_packet_unref(&packet);
        }
        // 释放资源
        av_free(buffer);
        av_frame_free(&frame);
        av_frame_free(&rgb_frame);
        sws_freeContext(sws_ctx);
        avcodec_free_context(&codec_ctx);
        avformat_close_input(&format_ctx);
        printf("视频帧已保存到目录: %s\n", output_dir);
        return 0;
    }
    
  2. 编译执行。
    g++ -std=c++17 -I/home/ffmpeg/include -L/home/ffmpeg/lib -lavcodec -lavformat -lavutil -lswscale -lswresample -lavfilter -lavdevice main.cpp -o main && ./main <输入视频文件> <输出目录>