119 lines
3.7 KiB
C++
119 lines
3.7 KiB
C++
|
#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;
|
||
|
}
|