//
// 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);
}
ffmpeg 解码视频并将每帧保存为图片
于 2024-04-24 09:59:46 首次发布