39#define MAX_SUPPORTED_WIDTH 1950
40#define MAX_SUPPORTED_HEIGHT 1100
43#include "libavutil/hwcontext_vaapi.h"
45typedef struct VAAPIDecodeContext {
47 VAEntrypoint va_entrypoint;
49 VAContextID va_context;
51#if FF_API_STRUCT_VAAPI_CONTEXT
54 struct vaapi_context *old_context;
55 AVBufferRef *device_ref;
59 AVHWDeviceContext *device;
60 AVVAAPIDeviceContext *hwctx;
62 AVHWFramesContext *frames;
63 AVVAAPIFramesContext *hwfc;
65 enum AVPixelFormat surface_format;
85 :
path(
path), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
86 pStream(NULL), aStream(NULL), packet(NULL), pFrame(NULL), is_open(false), is_duration_known(false),
87 check_interlace(false), check_fps(false), duration_strategy(duration_strategy), previous_packet_location{-1, 0},
88 is_seeking(false), seeking_pts(0), seeking_frame(0), is_video_seek(true), seek_count(0),
89 seek_audio_frame_found(0), seek_video_frame_found(0), last_seek_max_frame(-1), seek_stagnant_count(0),
90 last_frame(0), largest_frame_processed(0), current_video_frame(0), audio_pts(0), video_pts(0),
91 hold_packet(false), pts_offset_seconds(0.0), audio_pts_seconds(0.0), video_pts_seconds(0.0),
92 NO_PTS_OFFSET(-99999), source_width(0), source_height(0), source_rotation(0.0), enable_seek(true) {
99 pts_offset_seconds = NO_PTS_OFFSET;
100 video_pts_seconds = NO_PTS_OFFSET;
101 audio_pts_seconds = NO_PTS_OFFSET;
110 if (inspect_reader) {
132 if (abs(diff) <= amount)
143static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
145 const enum AVPixelFormat *p;
150 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
152#if defined(__linux__)
154 case AV_PIX_FMT_VAAPI:
161 case AV_PIX_FMT_VDPAU:
171 case AV_PIX_FMT_DXVA2_VLD:
178 case AV_PIX_FMT_D3D11:
186#if defined(__APPLE__)
188 case AV_PIX_FMT_VIDEOTOOLBOX:
197 case AV_PIX_FMT_CUDA:
217 return AV_PIX_FMT_NONE;
220int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
224 case AV_CODEC_ID_H264:
225 case AV_CODEC_ID_MPEG2VIDEO:
226 case AV_CODEC_ID_VC1:
227 case AV_CODEC_ID_WMV1:
228 case AV_CODEC_ID_WMV2:
229 case AV_CODEC_ID_WMV3:
242 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
250 hw_decode_failed =
false;
251 hw_decode_error_count = 0;
252 hw_decode_succeeded =
false;
257 if (avformat_open_input(&pFormatCtx,
path.c_str(), NULL, NULL) != 0)
261 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
268 packet_status.
reset(
true);
271 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
273 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
280 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
287 if (videoStream == -1 && audioStream == -1)
291 if (videoStream != -1) {
296 pStream = pFormatCtx->streams[videoStream];
302 const AVCodec *pCodec = avcodec_find_decoder(codecId);
303 AVDictionary *
opts = NULL;
304 int retry_decode_open = 2;
309 if (
hw_de_on && (retry_decode_open==2)) {
311 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
314 retry_decode_open = 0;
319 if (pCodec == NULL) {
320 throw InvalidCodec(
"A valid video codec could not be found for this file.",
path);
324 av_dict_set(&
opts,
"strict",
"experimental", 0);
328 int i_decoder_hw = 0;
330 char *adapter_ptr = NULL;
336 pCodecCtx->get_format = get_hw_dec_format;
338 if (adapter_num < 3 && adapter_num >=0) {
339#if defined(__linux__)
340 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
341 adapter_ptr = adapter;
343 switch (i_decoder_hw) {
345 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
348 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
351 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
354 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
357 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
364 switch (i_decoder_hw) {
366 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
369 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
372 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
375 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
378 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
381#elif defined(__APPLE__)
384 switch (i_decoder_hw) {
386 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
389 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
392 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
402#if defined(__linux__)
403 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
405 if( adapter_ptr != NULL ) {
406#elif defined(__APPLE__)
407 if( adapter_ptr != NULL ) {
416 hw_device_ctx = NULL;
418 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
419 const char* hw_name = av_hwdevice_get_type_name(hw_de_av_device_type);
420 std::string hw_msg =
"HW decode active: ";
421 hw_msg += (hw_name ? hw_name :
"unknown");
423 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
466 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
470 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
471 if (avcodec_return < 0) {
472 std::stringstream avcodec_error_msg;
473 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
479 AVHWFramesConstraints *constraints = NULL;
480 void *hwconfig = NULL;
481 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
485 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
486 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
489 if (pCodecCtx->coded_width < constraints->min_width ||
490 pCodecCtx->coded_height < constraints->min_height ||
491 pCodecCtx->coded_width > constraints->max_width ||
492 pCodecCtx->coded_height > constraints->max_height) {
495 retry_decode_open = 1;
498 av_buffer_unref(&hw_device_ctx);
499 hw_device_ctx = NULL;
504 Logger::Instance()->
AppendDebugMethod(
"\nDecode hardware acceleration is used\n",
"Min width :", constraints->min_width,
"Min Height :", constraints->min_height,
"MaxWidth :", constraints->max_width,
"MaxHeight :", constraints->max_height,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
505 retry_decode_open = 0;
507 av_hwframe_constraints_free(&constraints);
520 if (pCodecCtx->coded_width < 0 ||
521 pCodecCtx->coded_height < 0 ||
522 pCodecCtx->coded_width > max_w ||
523 pCodecCtx->coded_height > max_h ) {
524 Logger::Instance()->
AppendDebugMethod(
"DIMENSIONS ARE TOO LARGE for hardware acceleration\n",
"Max Width :", max_w,
"Max Height :", max_h,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
526 retry_decode_open = 1;
529 av_buffer_unref(&hw_device_ctx);
530 hw_device_ctx = NULL;
534 Logger::Instance()->
AppendDebugMethod(
"\nDecode hardware acceleration is used\n",
"Max Width :", max_w,
"Max Height :", max_h,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
535 retry_decode_open = 0;
543 retry_decode_open = 0;
545 }
while (retry_decode_open);
554 if (audioStream != -1) {
559 aStream = pFormatCtx->streams[audioStream];
565 const AVCodec *aCodec = avcodec_find_decoder(codecId);
571 bool audio_opened =
false;
572 if (aCodec != NULL) {
574 AVDictionary *
opts = NULL;
575 av_dict_set(&
opts,
"strict",
"experimental", 0);
578 audio_opened = (avcodec_open2(aCodecCtx, aCodec, &
opts) >= 0);
589 const bool invalid_audio_info =
594 (aCodecCtx->sample_fmt == AV_SAMPLE_FMT_NONE);
595 if (invalid_audio_info) {
597 "FFmpegReader::Open (Disable invalid audio stream)",
602 "sample_fmt",
static_cast<int>(aCodecCtx ? aCodecCtx->sample_fmt : AV_SAMPLE_FMT_NONE));
608 if (avcodec_is_open(aCodecCtx)) {
609 avcodec_flush_buffers(aCodecCtx);
619 "FFmpegReader::Open (Audio codec unavailable; disabling audio)",
620 "audioStream", audioStream);
636 "FFmpegReader::Open (Invalid FPS detected; applying fallback)",
644 "FFmpegReader::Open (Invalid video_timebase detected; applying fallback)",
651 AVDictionaryEntry *tag = NULL;
652 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
653 QString str_key = tag->key;
654 QString str_value = tag->value;
655 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
659 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
660 AVStream* st = pFormatCtx->streams[i];
661 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
662 size_t side_data_size = 0;
663 const uint8_t *displaymatrix = ffmpeg_stream_get_side_data(
664 st, AV_PKT_DATA_DISPLAYMATRIX, &side_data_size);
666 side_data_size >= 9 *
sizeof(int32_t) &&
668 double rotation = -av_display_rotation_get(
669 reinterpret_cast<const int32_t *
>(displaymatrix));
670 if (std::isnan(rotation))
675 const uint8_t *spherical = ffmpeg_stream_get_side_data(
676 st, AV_PKT_DATA_SPHERICAL, &side_data_size);
677 if (spherical && side_data_size >=
sizeof(AVSphericalMapping)) {
680 const AVSphericalMapping *map =
681 reinterpret_cast<const AVSphericalMapping *
>(spherical);
682 const char *proj_name = av_spherical_projection_name(map->projection);
683 info.
metadata[
"spherical_projection"] = proj_name ? proj_name :
"unknown";
685 auto to_deg = [](int32_t v) {
686 return static_cast<double>(v) / 65536.0;
688 info.
metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
689 info.
metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
690 info.
metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
695 UpdateOrientedVideoInfo();
698 previous_packet_location.
frame = -1;
730 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
738 AVPacket *recent_packet = packet;
747 RemoveAVPacket(recent_packet);
752 if(avcodec_is_open(pCodecCtx)) {
753 avcodec_flush_buffers(pCodecCtx);
759 av_buffer_unref(&hw_device_ctx);
760 hw_device_ctx = NULL;
764 if (img_convert_ctx) {
765 sws_freeContext(img_convert_ctx);
766 img_convert_ctx =
nullptr;
768 if (pFrameRGB_cached) {
775 if(avcodec_is_open(aCodecCtx)) {
776 avcodec_flush_buffers(aCodecCtx);
788 working_cache.
Clear();
791 avformat_close_input(&pFormatCtx);
792 av_freep(&pFormatCtx);
799 largest_frame_processed = 0;
800 seek_audio_frame_found = 0;
801 seek_video_frame_found = 0;
802 current_video_frame = 0;
803 first_video_frame = 0;
804 last_video_frame.reset();
805 last_final_video_frame.reset();
809bool FFmpegReader::HasAlbumArt() {
813 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
814 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
817double FFmpegReader::PickDurationSeconds()
const {
818 auto has_value = [](
double value) {
return value > 0.0; };
820 switch (duration_strategy) {
822 if (has_value(video_stream_duration_seconds))
823 return video_stream_duration_seconds;
824 if (has_value(audio_stream_duration_seconds))
825 return audio_stream_duration_seconds;
826 if (has_value(format_duration_seconds))
827 return format_duration_seconds;
830 if (has_value(audio_stream_duration_seconds))
831 return audio_stream_duration_seconds;
832 if (has_value(video_stream_duration_seconds))
833 return video_stream_duration_seconds;
834 if (has_value(format_duration_seconds))
835 return format_duration_seconds;
840 double longest = 0.0;
841 if (has_value(video_stream_duration_seconds))
842 longest = std::max(longest, video_stream_duration_seconds);
843 if (has_value(audio_stream_duration_seconds))
844 longest = std::max(longest, audio_stream_duration_seconds);
845 if (has_value(format_duration_seconds))
846 longest = std::max(longest, format_duration_seconds);
847 if (has_value(longest))
853 if (has_value(format_duration_seconds))
854 return format_duration_seconds;
855 if (has_value(inferred_duration_seconds))
856 return inferred_duration_seconds;
861void FFmpegReader::ApplyDurationStrategy() {
863 const double chosen_seconds = PickDurationSeconds();
865 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
868 is_duration_known =
false;
872 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
876 is_duration_known =
false;
881 info.
duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
882 is_duration_known =
true;
885void FFmpegReader::UpdateAudioInfo() {
895 AVChannelLayout audio_ch_layout = ffmpeg_get_valid_channel_layout(
897 if (audio_ch_layout.nb_channels > 0) {
900 codec_channels = audio_ch_layout.nb_channels;
912 auto record_duration = [](
double &target,
double seconds) {
914 target = std::max(target, seconds);
919 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
950 if (aStream->duration > 0) {
953 if (pFormatCtx->duration > 0) {
955 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
987 ApplyDurationStrategy();
990 AVDictionaryEntry *tag = NULL;
991 while ((tag = av_dict_get(aStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
992 QString str_key = tag->key;
993 QString str_value = tag->value;
994 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
997 av_channel_layout_uninit(&audio_ch_layout);
1001void FFmpegReader::UpdateVideoInfo() {
1008 UpdateOrientedVideoInfo();
1013 auto record_duration = [](
double &target,
double seconds) {
1015 target = std::max(target, seconds);
1020 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
1029 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
1039 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::UpdateVideoInfo (pStream->avg_frame_rate)",
"num", pStream->avg_frame_rate.num,
"den", pStream->avg_frame_rate.den);
1041 if (pStream->sample_aspect_ratio.num != 0) {
1064 if (!check_interlace) {
1065 check_interlace =
true;
1067 switch(field_order) {
1068 case AV_FIELD_PROGRESSIVE:
1081 case AV_FIELD_UNKNOWN:
1083 check_interlace =
false;
1098 if (pFormatCtx->duration >= 0) {
1100 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1110 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1111 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1113 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1117 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1118 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1119 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1123 ApplyDurationStrategy();
1129 const bool likely_still_codec =
1130 codec_id == AV_CODEC_ID_MJPEG ||
1131 codec_id == AV_CODEC_ID_PNG ||
1132 codec_id == AV_CODEC_ID_BMP ||
1133 codec_id == AV_CODEC_ID_TIFF ||
1134 codec_id == AV_CODEC_ID_WEBP ||
1135 codec_id == AV_CODEC_ID_JPEG2000;
1136 const bool likely_image_demuxer =
1137 pFormatCtx && pFormatCtx->iformat && pFormatCtx->iformat->name &&
1138 strstr(pFormatCtx->iformat->name,
"image2");
1139 const bool has_attached_pic = HasAlbumArt();
1140 const bool single_frame_stream =
1141 (pStream && pStream->nb_frames > 0 && pStream->nb_frames <= 1);
1144 const bool is_still_image_video =
1146 ((single_frame_stream || single_frame_clip) &&
1147 (likely_still_codec || likely_image_demuxer));
1149 if (is_still_image_video) {
1154 if (audioStream < 0) {
1155 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1158 ApplyDurationStrategy();
1163 AVDictionaryEntry *tag = NULL;
1164 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1165 QString str_key = tag->key;
1166 QString str_value = tag->value;
1167 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1170 UpdateOrientedVideoInfo();
1173void FFmpegReader::UpdateOrientedVideoInfo() {
1177 if (source_width <= 0 || source_height <= 0) {
1181 if (source_width <= 0 || source_height <= 0)
1184 source_rotation = 0.0;
1187 source_rotation = strtod(rotate_meta->second.c_str(),
nullptr);
1189 const double radians = source_rotation * M_PI / 180.0;
1190 const int oriented_width =
static_cast<int>(std::round(
1191 std::fabs(source_width * std::cos(radians)) +
1192 std::fabs(source_height * std::sin(radians))));
1193 const int oriented_height =
static_cast<int>(std::round(
1194 std::fabs(source_width * std::sin(radians)) +
1195 std::fabs(source_height * std::cos(radians))));
1197 if (oriented_width > 0 && oriented_height > 0) {
1208void FFmpegReader::ApplyFrameOrientation(std::shared_ptr<openshot::Frame> frame) {
1212 auto image = frame->GetImage();
1213 if (!image || image->isNull())
1216 QImage oriented = image->transformed(QTransform().rotate(source_rotation), Qt::SmoothTransformation);
1217 frame->AddImage(std::make_shared<QImage>(oriented));
1221 return this->is_duration_known;
1225 last_seek_max_frame = -1;
1226 seek_stagnant_count = 0;
1229 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.",
path);
1232 if (requested_frame < 1)
1233 requested_frame = 1;
1238 throw InvalidFile(
"Could not detect the duration of the video or audio stream.",
path);
1253 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1266 int64_t diff = requested_frame - last_frame;
1267 if (diff >= 1 && diff <= 20) {
1269 frame = ReadStream(requested_frame);
1274 Seek(requested_frame);
1283 frame = ReadStream(requested_frame);
1291std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1293 bool check_seek =
false;
1294 int packet_error = -1;
1295 int64_t no_progress_count = 0;
1296 int64_t prev_packets_read = packet_status.
packets_read();
1299 double prev_video_pts_seconds = video_pts_seconds;
1309 CheckWorkingFrames(requested_frame);
1314 if (is_cache_found) {
1318 if (!hold_packet || !packet) {
1320 packet_error = GetNextPacket();
1321 if (packet_error < 0 && !packet) {
1332 check_seek = CheckSeek();
1344 if ((
info.
has_video && packet && packet->stream_index == videoStream) ||
1348 ProcessVideoPacket(requested_frame);
1349 if (ReopenWithoutHardwareDecode(requested_frame)) {
1354 if ((
info.
has_audio && packet && packet->stream_index == audioStream) ||
1358 ProcessAudioPacket(requested_frame);
1363 if ((!
info.
has_video && packet && packet->stream_index == videoStream) ||
1364 (!
info.
has_audio && packet && packet->stream_index == audioStream)) {
1366 if (packet->stream_index == videoStream) {
1368 }
else if (packet->stream_index == audioStream) {
1374 RemoveAVPacket(packet);
1384 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::ReadStream (force EOF)",
"packets_read", packet_status.
packets_read(),
"packets_decoded", packet_status.
packets_decoded(),
"packets_eof", packet_status.
packets_eof,
"video_eof", packet_status.
video_eof,
"audio_eof", packet_status.
audio_eof,
"end_of_file", packet_status.
end_of_file);
1397 const bool has_progress =
1401 (video_pts_seconds != prev_video_pts_seconds);
1404 no_progress_count = 0;
1406 no_progress_count++;
1407 if (no_progress_count >= 2000
1412 "requested_frame", requested_frame,
1413 "no_progress_count", no_progress_count,
1427 prev_video_pts_seconds = video_pts_seconds;
1435 "largest_frame_processed", largest_frame_processed,
1436 "Working Cache Count", working_cache.
Count());
1445 CheckWorkingFrames(requested_frame);
1461 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1462 if (frame->has_image_data) {
1463 f->AddImage(std::make_shared<QImage>(frame->GetImage()->copy()));
1467 if (!frame->has_image_data) {
1472 f->AddAudioSilence(samples_in_frame);
1478 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1479 if (last_final_video_frame && last_final_video_frame->has_image_data
1480 && last_final_video_frame->number <= requested_frame) {
1481 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
1482 }
else if (last_video_frame && last_video_frame->has_image_data
1483 && last_video_frame->number <= requested_frame) {
1484 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
1488 f->AddAudioSilence(samples_in_frame);
1496int FFmpegReader::GetNextPacket() {
1497 int found_packet = 0;
1498 AVPacket *next_packet;
1499 next_packet =
new AVPacket();
1500 found_packet = av_read_frame(pFormatCtx, next_packet);
1504 RemoveAVPacket(packet);
1507 if (found_packet >= 0) {
1509 packet = next_packet;
1512 if (packet->stream_index == videoStream) {
1514 }
else if (packet->stream_index == audioStream) {
1523 return found_packet;
1527bool FFmpegReader::GetAVFrame() {
1528 int frameFinished = 0;
1529 auto note_hw_decode_failure = [&](
int err,
const char* stage) {
1531 if (!
hw_de_on || !hw_de_supported || force_sw_decode) {
1534 if (err == AVERROR_INVALIDDATA && packet_status.
video_decoded == 0) {
1535 hw_decode_error_count++;
1537 std::string(
"FFmpegReader::GetAVFrame (hardware decode failure candidate during ") + stage +
")",
1538 "error_count", hw_decode_error_count,
1540 if (hw_decode_error_count >= 3) {
1541 hw_decode_failed =
true;
1554 int send_packet_err = 0;
1555 int64_t send_packet_pts = 0;
1556 if ((packet && packet->stream_index == videoStream) || !packet) {
1557 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1559 if (packet && send_packet_err >= 0) {
1560 send_packet_pts = GetPacketPTS();
1561 hold_packet =
false;
1562 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet succeeded)",
"send_packet_err", send_packet_err,
"send_packet_pts", send_packet_pts);
1571 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1572 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: Not sent [" + av_err2string(send_packet_err) +
"])",
"send_packet_err", send_packet_err,
"send_packet_pts", send_packet_pts);
1573 note_hw_decode_failure(send_packet_err,
"send_packet");
1574 if (send_packet_err == AVERROR(EAGAIN)) {
1576 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1578 if (send_packet_err == AVERROR(EINVAL)) {
1579 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush",
"send_packet_pts", send_packet_pts);
1581 if (send_packet_err == AVERROR(ENOMEM)) {
1582 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(ENOMEM): failed to add packet to internal queue, or legitimate decoding errors",
"send_packet_pts", send_packet_pts);
1589 int receive_frame_err = 0;
1590 AVFrame *decoded_frame = next_frame;
1591 AVFrame *next_frame2;
1599 next_frame2 = next_frame;
1602 while (receive_frame_err >= 0) {
1603 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1605 if (receive_frame_err != 0) {
1606 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: frame not ready yet from decoder [\" + av_err2string(receive_frame_err) + \"])",
"receive_frame_err", receive_frame_err,
"send_packet_pts", send_packet_pts);
1607 note_hw_decode_failure(receive_frame_err,
"receive_frame");
1609 if (receive_frame_err == AVERROR_EOF) {
1611 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1612 avcodec_flush_buffers(pCodecCtx);
1615 if (receive_frame_err == AVERROR(EINVAL)) {
1617 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1618 avcodec_flush_buffers(pCodecCtx);
1620 if (receive_frame_err == AVERROR(EAGAIN)) {
1622 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EAGAIN): output is not available in this state - user must try to send new input)",
"send_packet_pts", send_packet_pts);
1624 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1626 "FFmpegReader::GetAVFrame (receive frame: AVERROR_INPUT_CHANGED: current decoded frame has changed parameters with respect to first decoded frame)",
"send_packet_pts", send_packet_pts);
1637 if (next_frame2->format == hw_de_av_pix_fmt) {
1638 if ((err = av_hwframe_transfer_data(next_frame, next_frame2, 0)) < 0) {
1640 "FFmpegReader::GetAVFrame (Failed to transfer data to output frame)",
1643 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_transfer");
1646 if ((err = av_frame_copy_props(next_frame, next_frame2)) < 0) {
1648 "FFmpegReader::GetAVFrame (Failed to copy props to output frame)",
1651 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_copy_props");
1654 if (next_frame->format == AV_PIX_FMT_NONE) {
1655 next_frame->format = pCodecCtx->sw_pix_fmt;
1657 if (next_frame->width <= 0) {
1658 next_frame->width = next_frame2->width;
1660 if (next_frame->height <= 0) {
1661 next_frame->height = next_frame2->height;
1663 decoded_frame = next_frame;
1666 decoded_frame = next_frame2;
1672 decoded_frame = next_frame2;
1675 if (!decoded_frame->data[0]) {
1677 "FFmpegReader::GetAVFrame (Decoded frame missing image data)",
1678 "format", decoded_frame->format,
1679 "width", decoded_frame->width,
1680 "height", decoded_frame->height);
1681 note_hw_decode_failure(AVERROR_INVALIDDATA,
"decoded_frame_empty");
1688 hw_decode_error_count = 0;
1690 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
1691 hw_decode_succeeded =
true;
1697 AVPixelFormat decoded_pix_fmt = (AVPixelFormat)(decoded_frame->format);
1698 if (decoded_pix_fmt == AV_PIX_FMT_NONE)
1699 decoded_pix_fmt = (AVPixelFormat)(pStream->codecpar->format);
1700 const int decoded_width = decoded_frame->width > 0 ? decoded_frame->width :
info.
width;
1701 const int decoded_height = decoded_frame->height > 0 ? decoded_frame->height :
info.
height;
1702 if (
AV_ALLOCATE_IMAGE(pFrame, decoded_pix_fmt, decoded_width, decoded_height) <= 0) {
1705 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)decoded_frame->data, decoded_frame->linesize,
1706 decoded_pix_fmt, decoded_width, decoded_height);
1707 pFrame->format = decoded_pix_fmt;
1708 pFrame->width = decoded_width;
1709 pFrame->height = decoded_height;
1710 pFrame->color_range = decoded_frame->color_range;
1711 pFrame->colorspace = decoded_frame->colorspace;
1712 pFrame->color_primaries = decoded_frame->color_primaries;
1713 pFrame->color_trc = decoded_frame->color_trc;
1714 pFrame->chroma_location = decoded_frame->chroma_location;
1720 if (decoded_frame->pts != AV_NOPTS_VALUE) {
1723 video_pts = decoded_frame->pts;
1724 }
else if (decoded_frame->pkt_dts != AV_NOPTS_VALUE) {
1726 video_pts = decoded_frame->pkt_dts;
1730 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1736 if (
hw_de_on && hw_de_supported && next_frame2 != next_frame) {
1741 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1747 if (frameFinished) {
1751 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.
width,
1760 return frameFinished;
1763bool FFmpegReader::ReopenWithoutHardwareDecode(int64_t requested_frame) {
1765 if (!hw_decode_failed || force_sw_decode) {
1770 "FFmpegReader::ReopenWithoutHardwareDecode (falling back to software decode)",
1771 "requested_frame", requested_frame,
1772 "video_packets_read", packet_status.
video_read,
1774 "hw_decode_error_count", hw_decode_error_count);
1776 force_sw_decode =
true;
1777 hw_decode_failed =
false;
1778 hw_decode_error_count = 0;
1782 Seek(requested_frame);
1785 (void) requested_frame;
1792 return hw_decode_succeeded;
1799bool FFmpegReader::CheckSeek() {
1802 const int64_t kSeekRetryMax = 5;
1803 const int kSeekStagnantMax = 2;
1807 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1815 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1817 if (max_seeked_frame == last_seek_max_frame) {
1818 seek_stagnant_count++;
1820 last_seek_max_frame = max_seeked_frame;
1821 seek_stagnant_count = 0;
1825 if (max_seeked_frame >= seeking_frame) {
1828 "is_video_seek", is_video_seek,
1829 "max_seeked_frame", max_seeked_frame,
1830 "seeking_frame", seeking_frame,
1831 "seeking_pts", seeking_pts,
1832 "seek_video_frame_found", seek_video_frame_found,
1833 "seek_audio_frame_found", seek_audio_frame_found);
1836 if (seek_count < kSeekRetryMax) {
1837 Seek(seeking_frame - (10 * seek_count * seek_count));
1838 }
else if (seek_stagnant_count >= kSeekStagnantMax) {
1840 Seek(seeking_frame - (10 * kSeekRetryMax * kSeekRetryMax));
1843 Seek(seeking_frame - (10 * seek_count * seek_count));
1848 "is_video_seek", is_video_seek,
1849 "packet->pts", GetPacketPTS(),
1850 "seeking_pts", seeking_pts,
1851 "seeking_frame", seeking_frame,
1852 "seek_video_frame_found", seek_video_frame_found,
1853 "seek_audio_frame_found", seek_audio_frame_found);
1867void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1870 int frame_finished = GetAVFrame();
1873 if (!frame_finished) {
1876 RemoveAVFrame(pFrame);
1882 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1885 if (!seek_video_frame_found && is_seeking)
1886 seek_video_frame_found = current_frame;
1892 working_cache.
Add(CreateFrame(requested_frame));
1898 AVPixelFormat decoded_pix_fmt = (pFrame && pFrame->format != AV_PIX_FMT_NONE)
1899 ?
static_cast<AVPixelFormat
>(pFrame->format)
1901 bool src_full_range = (pFrame && pFrame->color_range == AVCOL_RANGE_JPEG);
1903 int src_width = (pFrame && pFrame->width > 0) ? pFrame->width :
info.width;
1904 int src_height = (pFrame && pFrame->height > 0) ? pFrame->height :
info.height;
1905 int height = src_height;
1906 int width = src_width;
1908 AVFrame *pFrameRGB = pFrameRGB_cached;
1911 if (pFrameRGB ==
nullptr)
1913 pFrameRGB_cached = pFrameRGB;
1916 uint8_t *buffer =
nullptr;
1937 max_width = std::max(
float(max_width), max_width * max_scale_x);
1938 max_height = std::max(
float(max_height), max_height * max_scale_y);
1944 QSize width_size(max_width * max_scale_x,
1947 max_height * max_scale_y);
1949 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1950 max_width = std::max(max_width, width_size.width());
1951 max_height = std::max(max_height, width_size.height());
1953 max_width = std::max(max_width, height_size.width());
1954 max_height = std::max(max_height, height_size.height());
1961 float preview_ratio = 1.0;
1968 max_width =
info.
width * max_scale_x * preview_ratio;
1969 max_height =
info.
height * max_scale_y * preview_ratio;
1977 QSize bounded_size(max_width, max_height);
1979 if (bounded_size.width() > max_decode_size.width() ||
1980 bounded_size.height() > max_decode_size.height()) {
1981 bounded_size.scale(max_decode_size, Qt::KeepAspectRatio);
1982 max_width = bounded_size.width();
1983 max_height = bounded_size.height();
1990 const int quarter_turn = ((
static_cast<int>(std::round(source_rotation / 90.0)) * 90) % 360 + 360) % 360;
1992 std::swap(max_width, max_height);
1996 int original_height = src_height;
1997 if (max_width > 0 && max_height > 0 && (max_width < width || max_height < height)) {
1999 float ratio = float(width) / float(height);
2000 int possible_width = round(max_height * ratio);
2001 int possible_height = round(max_width / ratio);
2003 if (possible_width <= max_width) {
2005 width = possible_width;
2006 height = max_height;
2010 height = possible_height;
2014 if (src_width >= 4 && src_height >= 4) {
2016 width = aligned.width();
2017 height = aligned.height();
2022 const int bytes_per_pixel = 4;
2023 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
2026 constexpr size_t ALIGNMENT = 32;
2027 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
2028 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
2033 int scale_mode = SWS_FAST_BILINEAR;
2035 scale_mode = SWS_BICUBIC;
2037 img_convert_ctx = sws_getCachedContext(img_convert_ctx, src_width, src_height, src_pix_fmt, width, height,
PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL);
2038 if (!img_convert_ctx)
2040 const int *src_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
2041 const int *dst_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
2042 const int dst_full_range = 1;
2043 sws_setColorspaceDetails(img_convert_ctx, src_coeff, src_full_range ? 1 : 0,
2044 dst_coeff, dst_full_range, 0, 1 << 16, 1 << 16);
2046 if (!pFrame || !pFrame->data[0] || pFrame->linesize[0] <= 0) {
2048 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
2049 hw_decode_failed =
true;
2051 "FFmpegReader::ProcessVideoPacket (Invalid source frame; forcing software fallback)",
2052 "requested_frame", requested_frame,
2053 "current_frame", current_frame,
2054 "src_pix_fmt", src_pix_fmt,
2055 "src_width", src_width,
2056 "src_height", src_height);
2060 RemoveAVFrame(pFrame);
2067 const int scaled_lines = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
2068 original_height, pFrameRGB->data, pFrameRGB->linesize);
2069 if (scaled_lines <= 0) {
2071 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
2072 hw_decode_failed =
true;
2074 "FFmpegReader::ProcessVideoPacket (sws_scale failed; forcing software fallback)",
2075 "requested_frame", requested_frame,
2076 "current_frame", current_frame,
2077 "scaled_lines", scaled_lines,
2078 "src_pix_fmt", src_pix_fmt,
2079 "src_width", src_width,
2080 "src_height", src_height);
2085 RemoveAVFrame(pFrame);
2091 std::shared_ptr<Frame> f = CreateFrame(current_frame);
2094 if (!ffmpeg_has_alpha(src_pix_fmt)) {
2096 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
2099 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
2101 ApplyFrameOrientation(f);
2102 if (first_video_frame == 0 && !is_seeking) {
2103 first_video_frame = current_frame;
2107 working_cache.
Add(f);
2110 last_video_frame = f;
2116 RemoveAVFrame(pFrame);
2122 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
2126void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
2129 if (packet && packet->pts != AV_NOPTS_VALUE) {
2131 location = GetAudioPTSLocation(packet->pts);
2134 if (!seek_audio_frame_found && is_seeking)
2135 seek_audio_frame_found = location.
frame;
2142 working_cache.
Add(CreateFrame(requested_frame));
2146 "requested_frame", requested_frame,
2147 "target_frame", location.
frame,
2151 int frame_finished = 0;
2155 int packet_samples = 0;
2159 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
2160 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
2164 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
2165 if (receive_frame_err >= 0) {
2168 if (receive_frame_err == AVERROR_EOF) {
2172 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
2174 avcodec_flush_buffers(aCodecCtx);
2176 if (receive_frame_err != 0) {
2181 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
2184 if (frame_finished) {
2190 audio_pts = audio_frame->pts;
2193 location = GetAudioPTSLocation(audio_pts);
2196 int plane_size = -1;
2202 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
2206 packet_samples = audio_frame->nb_samples * nb_channels;
2215 int pts_remaining_samples = packet_samples /
info.
channels;
2218 if (pts_remaining_samples == 0) {
2220 "packet_samples", packet_samples,
2222 "pts_remaining_samples", pts_remaining_samples);
2226 while (pts_remaining_samples) {
2231 int samples = samples_per_frame - previous_packet_location.
sample_start;
2232 if (samples > pts_remaining_samples)
2233 samples = pts_remaining_samples;
2236 pts_remaining_samples -= samples;
2238 if (pts_remaining_samples > 0) {
2240 previous_packet_location.
frame++;
2249 "packet_samples", packet_samples,
2257 audio_converted->nb_samples = audio_frame->nb_samples;
2258 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
2265 AVChannelLayout input_layout = ffmpeg_get_valid_channel_layout(
2267 AVChannelLayout output_layout = ffmpeg_get_valid_channel_layout(
2269 int in_layout_err = av_opt_set_chlayout(avr,
"in_chlayout", &input_layout, 0);
2270 int out_layout_err = av_opt_set_chlayout(avr,
"out_chlayout", &output_layout, 0);
2278 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
2283 av_channel_layout_uninit(&input_layout);
2284 av_channel_layout_uninit(&output_layout);
2285 if (in_layout_err < 0 || out_layout_err < 0 || swr_init_err < 0) {
2287 throw InvalidChannels(
"Could not initialize FFmpeg audio channel layout or resampler.",
path);
2290 if (swr_init_err < 0) {
2300 audio_converted->data,
2301 audio_converted->linesize[0],
2302 audio_converted->nb_samples,
2304 audio_frame->linesize[0],
2305 audio_frame->nb_samples);
2308 int64_t starting_frame_number = -1;
2309 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++) {
2311 starting_frame_number = location.
frame;
2312 int channel_buffer_size = nb_samples;
2313 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
2317 int remaining_samples = channel_buffer_size;
2318 while (remaining_samples > 0) {
2323 int samples = std::fmin(samples_per_frame - start, remaining_samples);
2326 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
2329 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
2333 "frame", starting_frame_number,
2336 "channel", channel_filter,
2337 "samples_per_frame", samples_per_frame);
2340 working_cache.
Add(f);
2343 remaining_samples -= samples;
2346 if (remaining_samples > 0)
2347 channel_buffer += samples;
2350 starting_frame_number++;
2358 av_free(audio_converted->data[0]);
2367 "requested_frame", requested_frame,
2368 "starting_frame", location.
frame,
2369 "end_frame", starting_frame_number - 1,
2370 "audio_pts_seconds", audio_pts_seconds);
2376void FFmpegReader::Seek(int64_t requested_frame) {
2378 if (requested_frame < 1)
2379 requested_frame = 1;
2382 if (requested_frame > largest_frame_processed && packet_status.
end_of_file) {
2389 "requested_frame", requested_frame,
2390 "seek_count", seek_count,
2391 "last_frame", last_frame);
2394 working_cache.
Clear();
2398 video_pts_seconds = NO_PTS_OFFSET;
2400 audio_pts_seconds = NO_PTS_OFFSET;
2401 hold_packet =
false;
2403 current_video_frame = 0;
2405 first_video_frame = -1;
2406 largest_frame_processed = 0;
2407 last_final_video_frame.reset();
2412 packet_status.
reset(
false);
2418 int buffer_amount = 12;
2419 if (requested_frame - buffer_amount < 20) {
2433 if (seek_count == 1) {
2436 seeking_pts = ConvertFrameToVideoPTS(1);
2438 seek_audio_frame_found = 0;
2439 seek_video_frame_found = 0;
2443 bool seek_worked =
false;
2444 int64_t seek_target = 0;
2448 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
2450 Logger::Instance()->
Log(std::string(pFormatCtx->AV_FILENAME) +
": error while seeking video stream");
2453 is_video_seek =
true;
2460 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
2462 Logger::Instance()->
Log(std::string(pFormatCtx->AV_FILENAME) +
": error while seeking audio stream");
2465 is_video_seek =
false;
2474 avcodec_flush_buffers(aCodecCtx);
2478 avcodec_flush_buffers(pCodecCtx);
2481 previous_packet_location.
frame = -1;
2486 if (seek_count == 1) {
2488 seeking_pts = seek_target;
2489 seeking_frame = requested_frame;
2491 seek_audio_frame_found = 0;
2492 seek_video_frame_found = 0;
2520int64_t FFmpegReader::GetPacketPTS() {
2522 int64_t current_pts = packet->pts;
2523 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2524 current_pts = packet->dts;
2530 return AV_NOPTS_VALUE;
2535void FFmpegReader::UpdatePTSOffset() {
2536 if (pts_offset_seconds != NO_PTS_OFFSET) {
2540 pts_offset_seconds = 0.0;
2541 double video_pts_offset_seconds = 0.0;
2542 double audio_pts_offset_seconds = 0.0;
2544 bool has_video_pts =
false;
2547 has_video_pts =
true;
2549 bool has_audio_pts =
false;
2552 has_audio_pts =
true;
2556 while (!has_video_pts || !has_audio_pts) {
2558 if (GetNextPacket() < 0)
2563 int64_t pts = GetPacketPTS();
2566 if (!has_video_pts && packet->stream_index == videoStream) {
2572 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2573 has_video_pts =
true;
2576 else if (!has_audio_pts && packet->stream_index == audioStream) {
2582 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2583 has_audio_pts =
true;
2593 pts_offset_seconds = video_pts_offset_seconds;
2595 pts_offset_seconds = audio_pts_offset_seconds;
2596 }
else if (has_video_pts && has_audio_pts) {
2598 pts_offset_seconds = video_pts_offset_seconds;
2603int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2605 int64_t previous_video_frame = current_video_frame;
2607 const double video_timebase_value =
2613 double video_seconds = (double(pts) * video_timebase_value) + pts_offset_seconds;
2616 int64_t frame = round(video_seconds * fps_value) + 1;
2619 if (current_video_frame == 0)
2620 current_video_frame = frame;
2624 if (frame == previous_video_frame) {
2629 current_video_frame++;
2638int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2640 const double video_timebase_value =
2646 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2649 int64_t video_pts = round(seconds / video_timebase_value);
2656int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2658 const double audio_timebase_value =
2664 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2667 int64_t audio_pts = round(seconds / audio_timebase_value);
2674AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2675 const double audio_timebase_value =
2682 double audio_seconds = (double(pts) * audio_timebase_value) + pts_offset_seconds;
2685 double frame = (audio_seconds * fps_value) + 1;
2688 int64_t whole_frame = int64_t(frame);
2691 double sample_start_percentage = frame - double(whole_frame);
2697 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2700 if (whole_frame < 1)
2702 if (sample_start < 0)
2709 if (previous_packet_location.
frame != -1) {
2710 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2711 int64_t orig_frame = location.
frame;
2716 location.
frame = previous_packet_location.
frame;
2719 Logger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAudioPTSLocation (Audio Gap Detected)",
"Source Frame", orig_frame,
"Source Audio Sample", orig_start,
"Target Frame", location.
frame,
"Target Audio Sample", location.
sample_start,
"pts", pts);
2728 previous_packet_location = location;
2735std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2737 std::shared_ptr<Frame> output = working_cache.
GetFrame(requested_frame);
2741 output = working_cache.
GetFrame(requested_frame);
2742 if(output)
return output;
2750 working_cache.
Add(output);
2753 if (requested_frame > largest_frame_processed)
2754 largest_frame_processed = requested_frame;
2761bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2764 bool seek_trash =
false;
2765 int64_t max_seeked_frame = seek_audio_frame_found;
2766 if (seek_video_frame_found > max_seeked_frame) {
2767 max_seeked_frame = seek_video_frame_found;
2769 if ((
info.
has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2770 (
info.
has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2778void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2781 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2784 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.
GetFrames();
2785 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2788 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2791 std::shared_ptr<Frame> f = *working_itr;
2794 if (!f || f->number > requested_frame) {
2800 double frame_pts_seconds = (double(f->number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2801 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2804 bool is_video_ready =
false;
2805 bool is_audio_ready =
false;
2806 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2807 if ((frame_pts_seconds <= video_pts_seconds)
2808 || (recent_pts_diff > 1.5)
2812 is_video_ready =
true;
2814 "frame_number", f->number,
2815 "frame_pts_seconds", frame_pts_seconds,
2816 "video_pts_seconds", video_pts_seconds,
2817 "recent_pts_diff", recent_pts_diff);
2823 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2824 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2828 if (!f->has_image_data
2829 && last_final_video_frame
2830 && last_final_video_frame->has_image_data
2831 && last_final_video_frame->number <= f->number) {
2832 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2836 if (!f->has_image_data
2838 && last_video_frame->has_image_data
2839 && last_video_frame->number <= f->number) {
2840 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2844 if (!f->has_image_data) {
2846 "FFmpegReader::CheckWorkingFrames (no previous image found; using black frame)",
2847 "frame_number", f->number);
2848 f->AddColor(
"#000000");
2853 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2854 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2855 || (recent_pts_diff > 1.5)
2860 is_audio_ready =
true;
2862 "frame_number", f->number,
2863 "frame_pts_seconds", frame_pts_seconds,
2864 "audio_pts_seconds", audio_pts_seconds,
2865 "audio_pts_diff", audio_pts_diff,
2866 "recent_pts_diff", recent_pts_diff);
2868 bool is_seek_trash = IsPartialFrame(f->number);
2876 "frame_number", f->number,
2877 "is_video_ready", is_video_ready,
2878 "is_audio_ready", is_audio_ready,
2885 && !packet_status.
end_of_file && !is_seek_trash) {
2890 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2891 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2893 if (!f->has_image_data
2894 && last_final_video_frame
2895 && last_final_video_frame->has_image_data
2896 && last_final_video_frame->number <= f->number) {
2897 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2899 if (!f->has_image_data
2901 && last_video_frame->has_image_data
2902 && last_video_frame->number <= f->number) {
2903 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2910 if (!f->has_image_data && is_video_ready && is_audio_ready) {
2912 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2913 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2915 if (!f->has_image_data
2916 && last_final_video_frame
2917 && last_final_video_frame->has_image_data
2918 && last_final_video_frame->number <= f->number) {
2919 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2921 if (!f->has_image_data
2923 && last_video_frame->has_image_data
2924 && last_video_frame->number <= f->number) {
2925 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2932 if (!f->has_image_data && is_video_ready && is_audio_ready
2933 && !is_seeking && first_video_frame > f->number) {
2934 f->AddColor(
"#000000");
2939 if (!f->has_image_data) {
2943 if ((!packet_status.
end_of_file && is_video_ready && is_audio_ready) || packet_status.
end_of_file || is_seek_trash) {
2946 "requested_frame", requested_frame,
2947 "f->number", f->number,
2948 "is_seek_trash", is_seek_trash,
2949 "Working Cache Count", working_cache.
Count(),
2953 if (!is_seek_trash) {
2956 if (f->has_image_data) {
2957 last_final_video_frame = f;
2961 working_cache.
Remove(f->number);
2964 last_frame = f->number;
2967 working_cache.
Remove(f->number);
2974 working_frames.clear();
2975 working_frames.shrink_to_fit();
2979void FFmpegReader::CheckFPS() {
2987 int frames_per_second[3] = {0,0,0};
2988 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
2991 int all_frames_detected = 0;
2992 int starting_frames_detected = 0;
2997 if (GetNextPacket() < 0)
3002 if (packet->stream_index == videoStream) {
3005 fps_index = int(video_seconds);
3008 if (fps_index >= 0 && fps_index < max_fps_index) {
3010 starting_frames_detected++;
3011 frames_per_second[fps_index]++;
3015 all_frames_detected++;
3020 float avg_fps = 30.0;
3021 if (starting_frames_detected > 0 && fps_index > 0) {
3022 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
3026 if (avg_fps < 8.0) {
3035 if (all_frames_detected > 0) {
3049void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
3053 av_freep(&remove_frame->data[0]);
3061void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
3066 delete remove_packet;
3081 root[
"type"] =
"FFmpegReader";
3082 root[
"path"] =
path;
3083 switch (duration_strategy) {
3085 root[
"duration_strategy"] =
"VideoPreferred";
3088 root[
"duration_strategy"] =
"AudioPreferred";
3092 root[
"duration_strategy"] =
"LongestStream";
3109 catch (
const std::exception& e) {
3111 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
3122 if (!root[
"path"].isNull())
3123 path = root[
"path"].asString();
3124 if (!root[
"duration_strategy"].isNull()) {
3125 const std::string strategy = root[
"duration_strategy"].asString();
3126 if (strategy ==
"VideoPreferred") {
3128 }
else if (strategy ==
"AudioPreferred") {
Shared helpers for Crop effect scaling logic.
Header file for all Exception classes.
AVPixelFormat NormalizeDeprecatedPixFmt(AVPixelFormat pix_fmt, bool &is_full_range)
AVPixelFormat hw_de_av_pix_fmt_global
AVHWDeviceType hw_de_av_device_type_global
Header file for FFmpegReader class.
Header file for FFmpegUtilities.
#define AV_FREE_CONTEXT(av_context)
#define AV_FREE_FRAME(av_frame)
#define SWR_CONVERT(ctx, out, linesize, out_count, in, linesize2, in_count)
#define AV_GET_CODEC_TYPE(av_stream)
#define AV_GET_CODEC_PIXEL_FORMAT(av_stream, av_context)
#define AV_GET_CODEC_CONTEXT(av_stream, av_codec)
#define AV_FIND_DECODER_CODEC_ID(av_stream)
#define AV_ALLOCATE_FRAME()
#define AV_COPY_PICTURE_DATA(av_frame, buffer, pix_fmt, width, height)
#define AV_FREE_PACKET(av_packet)
#define AVCODEC_REGISTER_ALL
#define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context)
#define AV_ALLOCATE_IMAGE(av_frame, pix_fmt, width, height)
#define AV_GET_SAMPLE_FORMAT(av_stream, av_context)
#define AV_RESET_FRAME(av_frame)
Cross-platform helper to encourage returning freed memory to the OS.
#define FF_VIDEO_NUM_PROCESSORS
#define OPEN_MP_NUM_PROCESSORS
#define FF_AUDIO_NUM_PROCESSORS
Header file for Timeline class.
void SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels)
Set maximum bytes to a different amount based on a ReaderInfo struct.
int64_t Count()
Count the frames in the queue.
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
std::vector< std::shared_ptr< openshot::Frame > > GetFrames()
Get an array of all Frames.
void Remove(int64_t frame_number)
Remove a specific frame.
void Clear()
Clear the cache of all frames.
This class represents a clip (used to arrange readers on the timeline)
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
openshot::TimelineBase * ParentTimeline() override
Get the associated Timeline pointer (if any)
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
openshot::ScaleType scale
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
void Open() override
Open File - which is called by the constructor automatically.
bool HardwareDecodeSuccessful() const override
Return true if hardware decode was requested and successfully produced at least one frame.
FFmpegReader(const std::string &path, bool inspect_reader=true)
Constructor for FFmpegReader.
Json::Value JsonValue() const override
Generate Json::Value for this object.
bool GetIsDurationKnown()
Return true if frame can be read with GetFrame()
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
CacheMemory final_cache
Final cache object used to hold final frames.
virtual ~FFmpegReader()
Destructor.
std::string Json() const override
Generate JSON string of this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t requested_frame) override
void Close() override
Close File.
void SetJson(const std::string value) override
Load JSON string into this object.
This class represents a fraction.
int num
Numerator for the fraction.
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Fraction Reciprocal() const
Return the reciprocal as a Fraction.
int den
Denominator for the fraction.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Exception when an invalid # of audio channels are detected.
Exception when no valid codec is found for a file.
Exception for files that can not be found or opened.
Exception for invalid JSON.
Point GetMaxPoint() const
Get max point (by Y coordinate)
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
void Log(std::string message, Level level=LevelDebug)
static Logger * Instance()
Exception when no streams are found in the file.
Exception when memory could not be allocated.
Coordinate co
This is the primary coordinate.
openshot::ReaderInfo info
Information about the current media file.
int MaxDecodeWidth() const
Return the current maximum decoded frame width (0 when unlimited).
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
int MaxDecodeHeight() const
Return the current maximum decoded frame height (0 when unlimited).
bool ApplyOrientationMetadata() const
Return whether readers apply source orientation metadata to returned frames.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
bool HasMaxDecodeSize() const
Return true when a maximum decoded frame size is active.
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Exception when a reader is closed, and a frame is requested.
int DE_LIMIT_WIDTH_MAX
Maximum columns that hardware decode can handle.
int HW_DE_DEVICE_SET
Which GPU to use to decode (0 is the first)
int DE_LIMIT_HEIGHT_MAX
Maximum rows that hardware decode can handle.
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
int HARDWARE_DECODER
Use video codec for faster video decoding (if supported)
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
This class represents a timeline.
This namespace is the default namespace for all code in the openshot library.
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
DurationStrategy
This enumeration determines which duration source to favor.
@ VideoPreferred
Prefer the video stream's duration, fallback to audio then container.
@ LongestStream
Use the longest value from video, audio, or container.
@ AudioPreferred
Prefer the audio stream's duration, fallback to video then container.
void ApplyCropResizeScale(Clip *clip, int source_width, int source_height, int &max_width, int &max_height)
Scale the requested max_width / max_height based on the Crop resize amount, capped by source size.
QSize AlignPreviewSize(const QSize &size)
const Json::Value stringToJson(const std::string value)
This struct holds the associated video frame and starting sample # for an audio packet.
bool is_near(AudioLocation location, int samples_per_frame, int64_t amount)
int64_t packets_decoded()
int audio_bit_rate
The bit rate of the audio stream (in bytes)
int video_bit_rate
The bit rate of the video stream (in bytes)
bool has_single_image
Determines if this file only contains a single image.
float duration
Length of time (in seconds)
openshot::Fraction audio_timebase
The audio timebase determines how long each audio packet should be played.
int width
The width of the video (in pixesl)
int channels
The number of audio channels used in the audio stream.
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
openshot::Fraction display_ratio
The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3)
int height
The height of the video (in pixels)
int pixel_format
The pixel format (i.e. YUV420P, RGB24, etc...)
int64_t video_length
The number of frames in the video stream.
std::string acodec
The name of the audio codec used to encode / decode the video stream.
std::map< std::string, std::string > metadata
An optional map/dictionary of metadata for this reader.
std::string vcodec
The name of the video codec used to encode / decode the video stream.
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square)
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
bool has_video
Determines if this file has a video stream.
bool has_audio
Determines if this file has an audio stream.
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
int video_stream_index
The index of the video stream.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
int audio_stream_index
The index of the audio stream.
int64_t file_size
Size of file (in bytes)