帧图获取ffmpeg

// 编译命令: g++ main.cpp -o main -std=c++11 -lavcodec -lavutil -lavformat -lswscale -lavfilter

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}

bool saveThumbnail(const AVFrame *frame, const std::string &outputPath) {
  AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_PNG);
  if (!encoder) {
    std::cerr << "Error: PNG encoder not found" << std::endl;
    return false;
  }

  AVCodecContext *encoderCtx = avcodec_alloc_context3(encoder);
  encoderCtx->width = frame->width;
  encoderCtx->height = frame->height;
  encoderCtx->pix_fmt = static_cast<AVPixelFormat>(frame->format);
  encoderCtx->time_base = {1, 1};

  if (avcodec_open2(encoderCtx, encoder, nullptr) < 0) {
    std::cerr << "Error: Could not open output codec" << std::endl;
    return false;
  }

  AVPacket *pkt = av_packet_alloc();
  if (!pkt) {
    std::cerr << "Error: Could not allocate memory for output packet" << std::endl;
    return false;
  }

  int ret = avcodec_send_frame(encoderCtx, frame);
  if (ret < 0) {
    std::cerr << "Error: Could not send frame to output codec" << std::endl;
    return false;
  }

  ret = avcodec_receive_packet(encoderCtx, pkt);
  if (ret < 0) {
    std::cerr
        << "Error: Could not receive packet from output codec" << std::endl;
    return false;
  }

  FILE *file = fopen(outputPath.c_str(), "wb");
  if (file) {
    fwrite(pkt->data, 1, pkt->size, file);
    fclose(file);
  } else {
    std::cerr << "Error: Could not write thumbnail to " << outputPath
              << std::endl;
    return false;
  }

  av_packet_unref(pkt);
  av_packet_free(&pkt);
  avcodec_free_context(&encoderCtx);

  return true;
}

bool createThumbnail(const std::string &videoPath,
                     const std::vector<int> &timestampList, int outputWidth,
                     int outputHeight) {
  AVFormatContext *formatContext = nullptr;
  int videoStreamIndex = -1;
  AVCodec *decoder = nullptr;
  AVCodecContext *decoderCtx = nullptr;

  av_register_all();
  avformat_open_input(&formatContext, videoPath.c_str(), nullptr, nullptr);
  avformat_find_stream_info(formatContext, nullptr);

  for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
    if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
      videoStreamIndex = i;
      break;
    }
  }

  if (videoStreamIndex == -1) {
    std::cerr << "Error: Couldn't find a video stream in the video file"
              << std::endl;
    return false;
  }

  decoder =
      avcodec_find_decoder(formatContext->streams[videoStreamIndex]->codecpar->codec_id);
  decoderCtx = avcodec_alloc_context3(decoder);
  avcodec_parameters_to_context(decoderCtx,
                                formatContext->streams[videoStreamIndex]->codecpar);
  avcodec_open2(decoderCtx, decoder, nullptr);

  SwsContext *swsCtx = sws_getContext(
      decoderCtx->width, decoderCtx->height, decoderCtx->pix_fmt, outputWidth,
      outputHeight, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);

  AVFrame *frame = av_frame_alloc();
  AVFrame *outputFrame = av_frame_alloc();

  outputFrame->width = outputWidth;
  outputFrame->height = outputHeight;
  outputFrame->format = AV_PIX_FMT_RGB24;

  int bufferSize =
      av_image_get_buffer_size((AVPixelFormat)outputFrame->format,
                               outputFrame->width, outputFrame->height, 1);
  uint8_t *buffer = static_cast<uint8_t *>(av_malloc(bufferSize * sizeof(uint8_t)));
  av_image_fill_arrays(outputFrame->data, outputFrame->linesize, buffer,
                       (AVPixelFormat)outputFrame->format, outputFrame->width,
                       outputFrame->height, 1);

  AVPacket *pkt = av_packet_alloc();
  int64_t duration = formatContext->streams[videoStreamIndex]->duration;
  int64_t startTime = formatContext->streams[videoStreamIndex]->start_time;
  int64_t timeBase = av_q2d(formatContext->streams[videoStreamIndex]->time_base);

  bool success = false;
  while (av_read_frame(formatContext, pkt) >= 0) {
    if (pkt->stream_index == videoStreamIndex) {
      avcodec_send_packet(decoderCtx, pkt);
      if (avcodec_receive_frame(decoderCtx, frame) == 0) {
        int currentTimestamp = av_rescale_q(frame->pts, formatContext->streams[videoStreamIndex]->time_base, {1, 1});
        if (std::find(timestampList.begin(), timestampList.end(), currentTimestamp) !=
            timestampList.end()) {
          sws_scale(swsCtx, frame->data, frame->linesize, 0, frame->height,
                    outputFrame->data, outputFrame->linesize);

          // 生成输出文件名
          std::string outputPath =
              videoPath.substr(0, videoPath.find_last_of(".")) + "_" +
              std::to_string(frame->display_picture_number) + ".png";

          // 保存缩略图
          if (saveThumbnail(outputFrame, outputPath)) {
            std::cout << "Saved thumbnail at " << currentTimestamp
                      << " seconds to: " << outputPath << std::endl;
          }
        }
      }
    }

    av_packet_unref(pkt);
  }

  sws_freeContext(swsCtx);
  avcodec_close(decoderCtx);
  avformat_close_input(&formatContext);

  return success;
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " /path/to/video.mp4" << std::endl;
    return 1;
  }

  std::string videoPath = argv[1];
  std::vector<int> timestampList = {10, 20};
  int outputWidth = 1280;
  int outputHeight = 720;

  createThumbnail(videoPath, timestampList, outputWidth, outputHeight);

  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值