// 编译命令: 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> ×tampList, 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;
}
帧图获取ffmpeg
最新推荐文章于 2024-08-09 10:25:03 发布