FFmpeg的sws_scale函数使用示例
以下为读取一个视频,将视频中的YUV格式图片转成RGB并保存到PNG格式的使用示例
- 创建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; }
- 编译执行。
g++ -std=c++17 -I/home/ffmpeg/include -L/home/ffmpeg/lib -lavcodec -lavformat -lavutil -lswscale -lswresample -lavfilter -lavdevice main.cpp -o main && ./main <输入视频文件> <输出目录>
父主题: 安装使用FFmpeg