ffmpeg 解码视频并将每帧保存为图片

本文介绍了如何使用FFmpeg库解析视频文件,提取每一帧并将其编码为JPEG格式,详细展示了视频流处理、解码、编码过程的关键步骤。
摘要由CSDN通过智能技术生成
//
// Created by Administrator on 2024/4/19.
//
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}

#include <iostream>

char *in_path = "../2023102402.mp4";
char *out_path = "../out/de";

void exit_check(int ret, char *msg) {
    if (ret < 0) {
        std::cout << "ERROR: " << msg << std::endl;
        exit(1);
    }
}

/**
 * 编码器 输出图片保存
 * @param out_enc_ctx 
 * @param out_enc_frame 
 * @param out_enc_packet 
 * @param frame_num 
 */
void encode_jpg(AVCodecContext *out_enc_ctx, AVFrame *in_dec_frame, AVPacket *out_enc_packet, int64_t frame_num) {
    //发送帧到编码器
    int out_ref = avcodec_send_frame(out_enc_ctx, in_dec_frame);
    if (out_ref < 0) {
        return;
    }
    while (true) {
        out_ref = avcodec_receive_packet(out_enc_ctx, out_enc_packet);
        if (out_ref == AVERROR(EAGAIN) || out_ref == AVERROR_EOF)
            break;
        else if (out_ref < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }
        std::string path = out_path;
        path.append(std::to_string(frame_num));
        path.append(".png");
        //输出
        FILE *outFile = fopen(path.c_str(), "wb");
        fwrite(out_enc_packet->data, 1, out_enc_packet->size, outFile);
        fclose(outFile);

        //取消引用
        av_packet_unref(out_enc_packet);
    }
}

/**
 * 功能 ffmpeg 解析视频资源,并将每帧保存为jpg
 *  
 */
int main() {

    //获取视频信息
    AVFormatContext *in_avf_ctx = nullptr;
    int in_ret = avformat_open_input(&in_avf_ctx, in_path, nullptr, nullptr);
    exit_check(in_ret, "avformat_open_input");

    //获取视频流信息
    in_ret = avformat_find_stream_info(in_avf_ctx, nullptr);
    exit_check(in_ret, "avformat_find_stream_info");

    //构建视频解码器 从AVFormatContext中的视频流
    int in_video_idx = av_find_best_stream(in_avf_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    exit_check(in_video_idx, "av_find_best_stream");

    //解码器
    const AVCodec *in_decode = avcodec_find_decoder(in_avf_ctx->streams[in_video_idx]->codecpar->codec_id);
    AVCodecContext *in_dec_ctx = avcodec_alloc_context3(in_decode);

    //从AVFormatContext中的视频流复制编解码参数到解码器
    in_ret = avcodec_parameters_to_context(in_dec_ctx, in_avf_ctx->streams[in_video_idx]->codecpar);
    exit_check(in_ret, "avcodec_parameters_to_context");

    //打开解码器
    in_ret = avcodec_open2(in_dec_ctx, in_decode, nullptr);
    exit_check(in_ret, "avcodec_open2");

    //解码包 解码帧
    AVPacket *in_dec_packet = av_packet_alloc();
    AVFrame *in_dec_frame = av_frame_alloc();

    //编码器
    const AVCodec *out_encode = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
    AVCodecContext *out_enc_ctx = avcodec_alloc_context3(out_encode);
    out_enc_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
    out_enc_ctx->height = in_dec_ctx->height;
    out_enc_ctx->width = in_dec_ctx->width;
    out_enc_ctx->time_base.den = 1;
    out_enc_ctx->time_base.num = 30;
    in_ret = avcodec_open2(out_enc_ctx, out_encode, nullptr);
    exit_check(in_ret, "avcodec_open2");

    //编码包 编码帧
    AVPacket *out_enc_packet = av_packet_alloc();


    //从AVFormatContext中的视频流读取数据包
    while (av_read_frame(in_avf_ctx, in_dec_packet) >= 0) {
        //视频数据包处理
        if (in_dec_packet->stream_index != in_video_idx) {
            // 数据包取消引用
            av_packet_unref(in_dec_packet);
            continue;
        }
        //将数据包放入发送到解码器
        in_ret = avcodec_send_packet(in_dec_ctx, in_dec_packet);
        if (in_ret < 0) {
            break;
        }
        while (in_ret >= 0) {
            //获取帧
            in_ret = avcodec_receive_frame(in_dec_ctx, in_dec_frame);
            if (in_ret == AVERROR(EAGAIN) || in_ret == AVERROR_EOF) {
                break;
            }
            //保存数据
            encode_jpg(out_enc_ctx, in_dec_frame, out_enc_packet, in_dec_ctx->frame_num);
        }
        // 数据包取消引用
        av_packet_unref(in_dec_packet);
    }

    // 关闭
    av_packet_free(&out_enc_packet);
    avcodec_free_context(&out_enc_ctx);
    av_frame_free(&in_dec_frame);
    av_packet_free(&in_dec_packet);
    avcodec_free_context(&in_dec_ctx);
    avformat_close_input(&in_avf_ctx);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值