stuff-from-scratch/src/video/FfmegInterface.cpp

119 lines
3.7 KiB
C++
Raw Normal View History

2022-01-01 18:46:31 +00:00
#include "FfmpegInterface.h"
#include "Image.h"
#include "Video.h"
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/dict.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
std::vector<std::unique_ptr<Image> > FfmpegInterface::decodeToImages(const std::unique_ptr<Video>& video, unsigned maxImages)
{
std::vector<std::unique_ptr<Image> > images;
AVFormatContext* fmt_ctx{nullptr};
auto ret = avformat_open_input(&fmt_ctx, video->GetPath().c_str(), nullptr, nullptr);
if (ret)
{
return images;
}
//AVDictionaryEntry* tag{nullptr};
//while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
//{
// std::cout << "key: " << tag->key << " value: " << tag->value << std::endl;
//}
ret = avformat_find_stream_info(fmt_ctx, nullptr);
AVCodec* decoder;
const auto bestStreamIdx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
//std::cout << "Best stream idx = " << std::to_string(bestStreamIdx) << std::endl;
//std::cout << "Codec is: " << decoder->name << " |" << decoder->long_name << std::endl;
AVCodecContext* decoder_context = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(decoder_context, fmt_ctx->streams[bestStreamIdx]->codecpar);
av_opt_set_int(decoder_context, "refcounted_frames", 1, 0);
avcodec_open2(decoder_context, decoder, nullptr);
AVPacket packet;
AVFrame* frame = av_frame_alloc();
AVFrame* scaled_frame = av_frame_alloc();
int w = decoder_context->width;
int h = decoder_context->height;
scaled_frame->width = w;
scaled_frame->height = h;
scaled_frame->format = AV_PIX_FMT_RGB24;
av_image_alloc(scaled_frame->data, scaled_frame->linesize, w, h, AV_PIX_FMT_RGB24, 32);
auto img_convert_ctx = sws_getContext(w, h,
decoder_context->pix_fmt,
w, h, AV_PIX_FMT_RGB24, SWS_BICUBIC,
NULL, NULL, NULL);
for (unsigned idx=0; idx<100; idx++)
{
auto frame_ret = av_read_frame(fmt_ctx, &packet);
if (frame_ret)
{
break;
}
if (packet.stream_index == bestStreamIdx)
{
avcodec_send_packet(decoder_context, &packet);
ret = 0;
while(ret >= 0)
{
ret = avcodec_receive_frame(decoder_context, frame);
if (ret >= 0)
{
frame->pts = frame->best_effort_timestamp;
sws_scale(img_convert_ctx, frame->data,
frame->linesize, 0,
decoder_context->height,
scaled_frame->data, scaled_frame->linesize);
auto image = Image::Create(frame->width, frame->height);
image->SetNumChannels(3);
auto bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, frame->width, frame->height, 32);
std::vector<unsigned char> data(bufferSize, 0);
auto copyRet = av_image_copy_to_buffer(data.data(), bufferSize,
scaled_frame->data, scaled_frame->linesize,
AV_PIX_FMT_RGB24, frame->width, frame->height, 32);
image->SetData(data);
images.push_back(std::move(image));
}
av_frame_unref(frame);
//av_frame_unref(scaled_frame);
}
}
av_packet_unref(&packet);
}
avcodec_free_context(&decoder_context);
avformat_close_input(&fmt_ctx);
av_frame_free(&frame);
return images;
}