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

//
// 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用FFmpeg将流保存视频的C++示例代码: 首先,您需要包含FFmpeg的头文件和库文件: ```c++ #include <iostream> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> } #pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avformat.lib") #pragma comment(lib, "avutil.lib") #pragma comment(lib, "swscale.lib") ``` 然后,您可以使用以下代码片段将流保存视频: ```c++ // 打开输入流 AVFormatContext* inputFormatCtx = nullptr; if (avformat_open_input(&inputFormatCtx, "input_stream_url", nullptr, nullptr) < 0) { return -1; } // 查找流信息 if (avformat_find_stream_info(inputFormatCtx, nullptr) < 0) { avformat_close_input(&inputFormatCtx); return -1; } // 找到视频流 int videoStreamIndex = -1; AVCodecParameters* codecPar = nullptr; for (int i = 0; i < inputFormatCtx->nb_streams; i++) { if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; codecPar = inputFormatCtx->streams[i]->codecpar; break; } } if (videoStreamIndex == -1) { avformat_close_input(&inputFormatCtx); return -1; } // 初始化解码器和解码器上下文 AVCodec* decoder = avcodec_find_decoder(codecPar->codec_id); if (decoder == nullptr) { avformat_close_input(&inputFormatCtx); return -1; } AVCodecContext* decoderCtx = avcodec_alloc_context3(decoder); if (decoderCtx == nullptr) { avformat_close_input(&inputFormatCtx); return -1; } if (avcodec_parameters_to_context(decoderCtx, codecPar) < 0) { avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); return -1; } if (avcodec_open2(decoderCtx, decoder, nullptr) < 0) { avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); return -1; } // 创建输出格式上下文 AVFormatContext* outputFormatCtx = nullptr; if (avformat_alloc_output_context2(&outputFormatCtx, nullptr, "mp4", "output_video.mp4") < 0) { avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); return -1; } // 创建输出视频流 AVStream* videoStream = avformat_new_stream(outputFormatCtx, nullptr); if (videoStream == nullptr) { avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } AVCodecParameters* videoCodecPar = videoStream->codecpar; if (avcodec_parameters_copy(videoCodecPar, codecPar) < 0) { avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } videoCodecPar->codec_tag = 0; // 初始化编码器和编码器上下文 AVCodec* encoder = avcodec_find_encoder(videoCodecPar->codec_id); if (encoder == nullptr) { avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } AVCodecContext* encoderCtx = avcodec_alloc_context3(encoder); if (encoderCtx == nullptr) { avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } if (avcodec_parameters_to_context(encoderCtx, videoCodecPar) < 0) { avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } if (avcodec_open2(encoderCtx, encoder, nullptr) < 0) { avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } // 初始化图像转换器 SwsContext* swsCtx = sws_getContext(codecPar->width, codecPar->height, decoderCtx->pix_fmt, videoCodecPar->width, videoCodecPar->height, encoderCtx->pix_fmt, SWS_BILINEAR, nullptr, nullptr, nullptr); if (swsCtx == nullptr) { avcodec_close(encoderCtx); avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } // 打开输出文件 if (!(outputFormatCtx->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&outputFormatCtx->pb, "output_video.mp4", AVIO_FLAG_WRITE) < 0) { avcodec_close(encoderCtx); avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } } // 写入文件头 if (avformat_write_header(outputFormatCtx, nullptr) < 0) { avcodec_close(encoderCtx); avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); return -1; } // 处理每一帧 AVPacket inputPacket, outputPacket; av_init_packet(&inputPacket); av_init_packet(&outputPacket); inputPacket.data = nullptr; inputPacket.size = 0; while (av_read_frame(inputFormatCtx, &inputPacket) >= 0) { if (inputPacket.stream_index == videoStreamIndex) { // 解码 int ret = avcodec_send_packet(decoderCtx, &inputPacket); if (ret < 0) { av_packet_unref(&inputPacket); break; } while (ret >= 0) { AVFrame* frame = av_frame_alloc(); ret = avcodec_receive_frame(decoderCtx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_frame_free(&frame); break; } else if (ret < 0) { av_frame_free(&frame); av_packet_unref(&inputPacket); break; } // 转换图像格式 AVFrame* outFrame = av_frame_alloc(); outFrame->format = encoderCtx->pix_fmt; outFrame->width = videoCodecPar->width; outFrame->height = videoCodecPar->height; av_frame_get_buffer(outFrame, 0); sws_scale(swsCtx, frame->data, frame->linesize, 0, codecPar->height, outFrame->data, outFrame->linesize); // 编码 ret = avcodec_send_frame(encoderCtx, outFrame); if (ret < 0) { av_frame_free(&outFrame); break; } while (ret >= 0) { av_init_packet(&outputPacket); ret = avcodec_receive_packet(encoderCtx, &outputPacket); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_packet_unref(&outputPacket); break; } else if (ret < 0) { av_packet_unref(&outputPacket); av_frame_free(&outFrame); break; } // 写入输出文件 av_packet_rescale_ts(&outputPacket, encoderCtx->time_base, videoStream->time_base); outputPacket.stream_index = videoStream->index; av_interleaved_write_frame(outputFormatCtx, &outputPacket); av_packet_unref(&outputPacket); } av_frame_free(&outFrame); av_frame_free(&frame); } } av_packet_unref(&inputPacket); } // 写入文件尾 av_write_trailer(outputFormatCtx); // 释放资源 avcodec_close(encoderCtx); avcodec_free_context(&encoderCtx); avcodec_close(decoderCtx); avcodec_free_context(&decoderCtx); avformat_close_input(&inputFormatCtx); avformat_free_context(outputFormatCtx); sws_freeContext(swsCtx); ``` 需要注意的是,此示例代码仅适用于保存单个视频流。如果您需要保存多个视频流或音频流,请在代码中进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值