34 #define ENABLE_VAAPI 0
37 #define MAX_SUPPORTED_WIDTH 1950
38 #define MAX_SUPPORTED_HEIGHT 1100
41 #include "libavutil/hwcontext_vaapi.h"
43 typedef struct VAAPIDecodeContext {
45 VAEntrypoint va_entrypoint;
47 VAContextID va_context;
49 #if FF_API_STRUCT_VAAPI_CONTEXT
52 struct vaapi_context *old_context;
53 AVBufferRef *device_ref;
57 AVHWDeviceContext *device;
58 AVVAAPIDeviceContext *hwctx;
60 AVHWFramesContext *frames;
61 AVVAAPIFramesContext *hwfc;
63 enum AVPixelFormat surface_format;
80 static AVPixelFormat NormalizeDeprecatedPixFmt(AVPixelFormat pix_fmt,
bool& is_full_range) {
82 case AV_PIX_FMT_YUVJ420P:
84 return AV_PIX_FMT_YUV420P;
85 case AV_PIX_FMT_YUVJ422P:
87 return AV_PIX_FMT_YUV422P;
88 case AV_PIX_FMT_YUVJ444P:
90 return AV_PIX_FMT_YUV444P;
91 case AV_PIX_FMT_YUVJ440P:
93 return AV_PIX_FMT_YUV440P;
94 #ifdef AV_PIX_FMT_YUVJ411P
95 case AV_PIX_FMT_YUVJ411P:
97 return AV_PIX_FMT_YUV411P;
108 :
path(
path), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
109 pStream(NULL), aStream(NULL), packet(NULL), pFrame(NULL), is_open(false), is_duration_known(false),
110 check_interlace(false), check_fps(false), duration_strategy(duration_strategy), previous_packet_location{-1, 0},
111 is_seeking(false), seeking_pts(0), seeking_frame(0), is_video_seek(true), seek_count(0),
112 seek_audio_frame_found(0), seek_video_frame_found(0), last_seek_max_frame(-1), seek_stagnant_count(0),
113 last_frame(0), largest_frame_processed(0), current_video_frame(0), audio_pts(0), video_pts(0),
114 hold_packet(false), pts_offset_seconds(0.0), audio_pts_seconds(0.0), video_pts_seconds(0.0),
115 NO_PTS_OFFSET(-99999), source_width(0), source_height(0), source_rotation(0.0), enable_seek(true) {
122 pts_offset_seconds = NO_PTS_OFFSET;
123 video_pts_seconds = NO_PTS_OFFSET;
124 audio_pts_seconds = NO_PTS_OFFSET;
133 if (inspect_reader) {
155 if (abs(diff) <= amount)
166 static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
168 const enum AVPixelFormat *p;
173 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
175 #if defined(__linux__)
177 case AV_PIX_FMT_VAAPI:
184 case AV_PIX_FMT_VDPAU:
194 case AV_PIX_FMT_DXVA2_VLD:
201 case AV_PIX_FMT_D3D11:
209 #if defined(__APPLE__)
211 case AV_PIX_FMT_VIDEOTOOLBOX:
220 case AV_PIX_FMT_CUDA:
240 return AV_PIX_FMT_NONE;
243 int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
247 case AV_CODEC_ID_H264:
248 case AV_CODEC_ID_MPEG2VIDEO:
249 case AV_CODEC_ID_VC1:
250 case AV_CODEC_ID_WMV1:
251 case AV_CODEC_ID_WMV2:
252 case AV_CODEC_ID_WMV3:
267 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
273 hw_decode_failed =
false;
274 hw_decode_error_count = 0;
275 hw_decode_succeeded =
false;
280 if (avformat_open_input(&pFormatCtx,
path.c_str(), NULL, NULL) != 0)
284 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
291 packet_status.
reset(
true);
294 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
296 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
303 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
310 if (videoStream == -1 && audioStream == -1)
314 if (videoStream != -1) {
319 pStream = pFormatCtx->streams[videoStream];
325 const AVCodec *pCodec = avcodec_find_decoder(codecId);
326 AVDictionary *
opts = NULL;
327 int retry_decode_open = 2;
332 if (
hw_de_on && (retry_decode_open==2)) {
334 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
337 retry_decode_open = 0;
342 if (pCodec == NULL) {
343 throw InvalidCodec(
"A valid video codec could not be found for this file.",
path);
347 av_dict_set(&
opts,
"strict",
"experimental", 0);
351 int i_decoder_hw = 0;
353 char *adapter_ptr = NULL;
359 pCodecCtx->get_format = get_hw_dec_format;
361 if (adapter_num < 3 && adapter_num >=0) {
362 #if defined(__linux__)
363 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
364 adapter_ptr = adapter;
366 switch (i_decoder_hw) {
368 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
371 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
374 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
377 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
380 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
384 #elif defined(_WIN32)
387 switch (i_decoder_hw) {
389 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
392 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
395 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
398 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
401 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
404 #elif defined(__APPLE__)
407 switch (i_decoder_hw) {
409 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
412 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
415 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
425 #if defined(__linux__)
426 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
427 #elif defined(_WIN32)
428 if( adapter_ptr != NULL ) {
429 #elif defined(__APPLE__)
430 if( adapter_ptr != NULL ) {
439 hw_device_ctx = NULL;
441 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
442 const char* hw_name = av_hwdevice_get_type_name(hw_de_av_device_type);
443 std::string hw_msg =
"HW decode active: ";
444 hw_msg += (hw_name ? hw_name :
"unknown");
446 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
489 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
493 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
494 if (avcodec_return < 0) {
495 std::stringstream avcodec_error_msg;
496 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
502 AVHWFramesConstraints *constraints = NULL;
503 void *hwconfig = NULL;
504 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
508 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
509 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
512 if (pCodecCtx->coded_width < constraints->min_width ||
513 pCodecCtx->coded_height < constraints->min_height ||
514 pCodecCtx->coded_width > constraints->max_width ||
515 pCodecCtx->coded_height > constraints->max_height) {
518 retry_decode_open = 1;
521 av_buffer_unref(&hw_device_ctx);
522 hw_device_ctx = NULL;
527 ZmqLogger::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);
528 retry_decode_open = 0;
530 av_hwframe_constraints_free(&constraints);
543 if (pCodecCtx->coded_width < 0 ||
544 pCodecCtx->coded_height < 0 ||
545 pCodecCtx->coded_width > max_w ||
546 pCodecCtx->coded_height > max_h ) {
547 ZmqLogger::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);
549 retry_decode_open = 1;
552 av_buffer_unref(&hw_device_ctx);
553 hw_device_ctx = NULL;
557 ZmqLogger::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);
558 retry_decode_open = 0;
566 retry_decode_open = 0;
568 }
while (retry_decode_open);
577 if (audioStream != -1) {
582 aStream = pFormatCtx->streams[audioStream];
588 const AVCodec *aCodec = avcodec_find_decoder(codecId);
594 bool audio_opened =
false;
595 if (aCodec != NULL) {
597 AVDictionary *
opts = NULL;
598 av_dict_set(&
opts,
"strict",
"experimental", 0);
601 audio_opened = (avcodec_open2(aCodecCtx, aCodec, &
opts) >= 0);
612 const bool invalid_audio_info =
617 (aCodecCtx->sample_fmt == AV_SAMPLE_FMT_NONE);
618 if (invalid_audio_info) {
620 "FFmpegReader::Open (Disable invalid audio stream)",
625 "sample_fmt",
static_cast<int>(aCodecCtx ? aCodecCtx->sample_fmt : AV_SAMPLE_FMT_NONE));
631 if (avcodec_is_open(aCodecCtx)) {
632 avcodec_flush_buffers(aCodecCtx);
642 "FFmpegReader::Open (Audio codec unavailable; disabling audio)",
643 "audioStream", audioStream);
659 "FFmpegReader::Open (Invalid FPS detected; applying fallback)",
667 "FFmpegReader::Open (Invalid video_timebase detected; applying fallback)",
674 AVDictionaryEntry *tag = NULL;
675 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
676 QString str_key = tag->key;
677 QString str_value = tag->value;
678 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
682 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
683 AVStream* st = pFormatCtx->streams[i];
684 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
685 size_t side_data_size = 0;
686 const uint8_t *displaymatrix = ffmpeg_stream_get_side_data(
687 st, AV_PKT_DATA_DISPLAYMATRIX, &side_data_size);
689 side_data_size >= 9 *
sizeof(int32_t) &&
691 double rotation = -av_display_rotation_get(
692 reinterpret_cast<const int32_t *
>(displaymatrix));
693 if (std::isnan(rotation))
698 const uint8_t *spherical = ffmpeg_stream_get_side_data(
699 st, AV_PKT_DATA_SPHERICAL, &side_data_size);
700 if (spherical && side_data_size >=
sizeof(AVSphericalMapping)) {
703 const AVSphericalMapping *map =
704 reinterpret_cast<const AVSphericalMapping *
>(spherical);
705 const char *proj_name = av_spherical_projection_name(map->projection);
706 info.
metadata[
"spherical_projection"] = proj_name ? proj_name :
"unknown";
708 auto to_deg = [](int32_t v) {
709 return static_cast<double>(v) / 65536.0;
711 info.
metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
712 info.
metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
713 info.
metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
718 UpdateOrientedVideoInfo();
721 previous_packet_location.
frame = -1;
755 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
761 AVPacket *recent_packet = packet;
766 int max_attempts = 128;
771 "attempts", attempts);
783 RemoveAVPacket(recent_packet);
788 if(avcodec_is_open(pCodecCtx)) {
789 avcodec_flush_buffers(pCodecCtx);
795 av_buffer_unref(&hw_device_ctx);
796 hw_device_ctx = NULL;
800 if (img_convert_ctx) {
801 sws_freeContext(img_convert_ctx);
802 img_convert_ctx =
nullptr;
804 if (pFrameRGB_cached) {
811 if(avcodec_is_open(aCodecCtx)) {
812 avcodec_flush_buffers(aCodecCtx);
824 working_cache.
Clear();
827 avformat_close_input(&pFormatCtx);
828 av_freep(&pFormatCtx);
835 largest_frame_processed = 0;
836 seek_audio_frame_found = 0;
837 seek_video_frame_found = 0;
838 current_video_frame = 0;
839 last_video_frame.reset();
840 last_final_video_frame.reset();
844 bool FFmpegReader::HasAlbumArt() {
848 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
849 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
852 double FFmpegReader::PickDurationSeconds()
const {
853 auto has_value = [](
double value) {
return value > 0.0; };
855 switch (duration_strategy) {
857 if (has_value(video_stream_duration_seconds))
858 return video_stream_duration_seconds;
859 if (has_value(audio_stream_duration_seconds))
860 return audio_stream_duration_seconds;
861 if (has_value(format_duration_seconds))
862 return format_duration_seconds;
865 if (has_value(audio_stream_duration_seconds))
866 return audio_stream_duration_seconds;
867 if (has_value(video_stream_duration_seconds))
868 return video_stream_duration_seconds;
869 if (has_value(format_duration_seconds))
870 return format_duration_seconds;
875 double longest = 0.0;
876 if (has_value(video_stream_duration_seconds))
877 longest = std::max(longest, video_stream_duration_seconds);
878 if (has_value(audio_stream_duration_seconds))
879 longest = std::max(longest, audio_stream_duration_seconds);
880 if (has_value(format_duration_seconds))
881 longest = std::max(longest, format_duration_seconds);
882 if (has_value(longest))
888 if (has_value(format_duration_seconds))
889 return format_duration_seconds;
890 if (has_value(inferred_duration_seconds))
891 return inferred_duration_seconds;
896 void FFmpegReader::ApplyDurationStrategy() {
898 const double chosen_seconds = PickDurationSeconds();
900 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
903 is_duration_known =
false;
907 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
911 is_duration_known =
false;
916 info.
duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
917 is_duration_known =
true;
920 void FFmpegReader::UpdateAudioInfo() {
930 AVChannelLayout audio_ch_layout = ffmpeg_get_valid_channel_layout(
932 if (audio_ch_layout.nb_channels > 0) {
935 codec_channels = audio_ch_layout.nb_channels;
947 auto record_duration = [](
double &target,
double seconds) {
949 target = std::max(target, seconds);
954 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
985 if (aStream->duration > 0) {
988 if (pFormatCtx->duration > 0) {
990 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1022 ApplyDurationStrategy();
1025 AVDictionaryEntry *tag = NULL;
1026 while ((tag = av_dict_get(aStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1027 QString str_key = tag->key;
1028 QString str_value = tag->value;
1029 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1032 av_channel_layout_uninit(&audio_ch_layout);
1036 void FFmpegReader::UpdateVideoInfo() {
1043 UpdateOrientedVideoInfo();
1048 auto record_duration = [](
double &target,
double seconds) {
1050 target = std::max(target, seconds);
1055 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
1064 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
1076 if (pStream->sample_aspect_ratio.num != 0) {
1099 if (!check_interlace) {
1100 check_interlace =
true;
1102 switch(field_order) {
1103 case AV_FIELD_PROGRESSIVE:
1116 case AV_FIELD_UNKNOWN:
1118 check_interlace =
false;
1133 if (pFormatCtx->duration >= 0) {
1135 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1145 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1146 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1148 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1152 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1153 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1154 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1158 ApplyDurationStrategy();
1164 const bool likely_still_codec =
1165 codec_id == AV_CODEC_ID_MJPEG ||
1166 codec_id == AV_CODEC_ID_PNG ||
1167 codec_id == AV_CODEC_ID_BMP ||
1168 codec_id == AV_CODEC_ID_TIFF ||
1169 codec_id == AV_CODEC_ID_WEBP ||
1170 codec_id == AV_CODEC_ID_JPEG2000;
1171 const bool likely_image_demuxer =
1172 pFormatCtx && pFormatCtx->iformat && pFormatCtx->iformat->name &&
1173 strstr(pFormatCtx->iformat->name,
"image2");
1174 const bool has_attached_pic = HasAlbumArt();
1175 const bool single_frame_stream =
1176 (pStream && pStream->nb_frames > 0 && pStream->nb_frames <= 1);
1179 const bool is_still_image_video =
1181 ((single_frame_stream || single_frame_clip) &&
1182 (likely_still_codec || likely_image_demuxer));
1184 if (is_still_image_video) {
1189 if (audioStream < 0) {
1190 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1193 ApplyDurationStrategy();
1198 AVDictionaryEntry *tag = NULL;
1199 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1200 QString str_key = tag->key;
1201 QString str_value = tag->value;
1202 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1205 UpdateOrientedVideoInfo();
1208 void FFmpegReader::UpdateOrientedVideoInfo() {
1212 if (source_width <= 0 || source_height <= 0) {
1216 if (source_width <= 0 || source_height <= 0)
1219 source_rotation = 0.0;
1222 source_rotation = strtod(rotate_meta->second.c_str(),
nullptr);
1224 const double radians = source_rotation * M_PI / 180.0;
1225 const int oriented_width =
static_cast<int>(std::round(
1226 std::fabs(source_width * std::cos(radians)) +
1227 std::fabs(source_height * std::sin(radians))));
1228 const int oriented_height =
static_cast<int>(std::round(
1229 std::fabs(source_width * std::sin(radians)) +
1230 std::fabs(source_height * std::cos(radians))));
1232 if (oriented_width > 0 && oriented_height > 0) {
1243 void FFmpegReader::ApplyFrameOrientation(std::shared_ptr<openshot::Frame> frame) {
1247 auto image = frame->GetImage();
1248 if (!image || image->isNull())
1251 QImage oriented = image->transformed(QTransform().rotate(source_rotation), Qt::SmoothTransformation);
1252 frame->AddImage(std::make_shared<QImage>(oriented));
1256 return this->is_duration_known;
1260 last_seek_max_frame = -1;
1261 seek_stagnant_count = 0;
1264 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.",
path);
1267 if (requested_frame < 1)
1268 requested_frame = 1;
1273 throw InvalidFile(
"Could not detect the duration of the video or audio stream.",
path);
1288 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1301 int64_t diff = requested_frame - last_frame;
1302 if (diff >= 1 && diff <= 20) {
1304 frame = ReadStream(requested_frame);
1309 Seek(requested_frame);
1318 frame = ReadStream(requested_frame);
1326 std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1328 bool check_seek =
false;
1329 int packet_error = -1;
1330 int64_t no_progress_count = 0;
1331 int64_t prev_packets_read = packet_status.
packets_read();
1334 double prev_video_pts_seconds = video_pts_seconds;
1344 CheckWorkingFrames(requested_frame);
1349 if (is_cache_found) {
1353 if (!hold_packet || !packet) {
1355 packet_error = GetNextPacket();
1356 if (packet_error < 0 && !packet) {
1367 check_seek = CheckSeek();
1379 if ((
info.
has_video && packet && packet->stream_index == videoStream) ||
1383 ProcessVideoPacket(requested_frame);
1384 if (ReopenWithoutHardwareDecode(requested_frame)) {
1389 if ((
info.
has_audio && packet && packet->stream_index == audioStream) ||
1393 ProcessAudioPacket(requested_frame);
1398 if ((!
info.
has_video && packet && packet->stream_index == videoStream) ||
1399 (!
info.
has_audio && packet && packet->stream_index == audioStream)) {
1401 if (packet->stream_index == videoStream) {
1403 }
else if (packet->stream_index == audioStream) {
1409 RemoveAVPacket(packet);
1419 ZmqLogger::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);
1432 const bool has_progress =
1436 (video_pts_seconds != prev_video_pts_seconds);
1439 no_progress_count = 0;
1441 no_progress_count++;
1442 if (no_progress_count >= 2000
1447 "requested_frame", requested_frame,
1448 "no_progress_count", no_progress_count,
1462 prev_video_pts_seconds = video_pts_seconds;
1470 "largest_frame_processed", largest_frame_processed,
1471 "Working Cache Count", working_cache.
Count());
1480 CheckWorkingFrames(requested_frame);
1496 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1497 if (frame->has_image_data) {
1498 f->AddImage(std::make_shared<QImage>(frame->GetImage()->copy()));
1502 if (!frame->has_image_data) {
1507 f->AddAudioSilence(samples_in_frame);
1513 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1514 if (last_final_video_frame && last_final_video_frame->has_image_data
1515 && last_final_video_frame->number <= requested_frame) {
1516 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
1517 }
else if (last_video_frame && last_video_frame->has_image_data
1518 && last_video_frame->number <= requested_frame) {
1519 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
1523 f->AddAudioSilence(samples_in_frame);
1531 int FFmpegReader::GetNextPacket() {
1532 int found_packet = 0;
1533 AVPacket *next_packet;
1534 next_packet =
new AVPacket();
1535 found_packet = av_read_frame(pFormatCtx, next_packet);
1539 RemoveAVPacket(packet);
1542 if (found_packet >= 0) {
1544 packet = next_packet;
1547 if (packet->stream_index == videoStream) {
1549 }
else if (packet->stream_index == audioStream) {
1558 return found_packet;
1562 bool FFmpegReader::GetAVFrame() {
1563 int frameFinished = 0;
1564 auto note_hw_decode_failure = [&](
int err,
const char* stage) {
1566 if (!
hw_de_on || !hw_de_supported || force_sw_decode) {
1569 if (err == AVERROR_INVALIDDATA && packet_status.
video_decoded == 0) {
1570 hw_decode_error_count++;
1572 std::string(
"FFmpegReader::GetAVFrame (hardware decode failure candidate during ") + stage +
")",
1573 "error_count", hw_decode_error_count,
1575 if (hw_decode_error_count >= 3) {
1576 hw_decode_failed =
true;
1589 int send_packet_err = 0;
1590 int64_t send_packet_pts = 0;
1591 if ((packet && packet->stream_index == videoStream) || !packet) {
1592 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1594 if (packet && send_packet_err >= 0) {
1595 send_packet_pts = GetPacketPTS();
1596 hold_packet =
false;
1606 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1607 ZmqLogger::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);
1608 note_hw_decode_failure(send_packet_err,
"send_packet");
1609 if (send_packet_err == AVERROR(EAGAIN)) {
1611 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1613 if (send_packet_err == AVERROR(EINVAL)) {
1614 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush",
"send_packet_pts", send_packet_pts);
1616 if (send_packet_err == AVERROR(ENOMEM)) {
1617 ZmqLogger::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);
1624 int receive_frame_err = 0;
1625 AVFrame *decoded_frame = next_frame;
1626 AVFrame *next_frame2;
1634 next_frame2 = next_frame;
1637 while (receive_frame_err >= 0) {
1638 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1640 if (receive_frame_err != 0) {
1641 ZmqLogger::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);
1642 note_hw_decode_failure(receive_frame_err,
"receive_frame");
1644 if (receive_frame_err == AVERROR_EOF) {
1646 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1647 avcodec_flush_buffers(pCodecCtx);
1650 if (receive_frame_err == AVERROR(EINVAL)) {
1652 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1653 avcodec_flush_buffers(pCodecCtx);
1655 if (receive_frame_err == AVERROR(EAGAIN)) {
1657 "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);
1659 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1661 "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);
1672 if (next_frame2->format == hw_de_av_pix_fmt) {
1673 if ((err = av_hwframe_transfer_data(next_frame, next_frame2, 0)) < 0) {
1675 "FFmpegReader::GetAVFrame (Failed to transfer data to output frame)",
1678 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_transfer");
1681 if ((err = av_frame_copy_props(next_frame, next_frame2)) < 0) {
1683 "FFmpegReader::GetAVFrame (Failed to copy props to output frame)",
1686 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_copy_props");
1689 if (next_frame->format == AV_PIX_FMT_NONE) {
1690 next_frame->format = pCodecCtx->sw_pix_fmt;
1692 if (next_frame->width <= 0) {
1693 next_frame->width = next_frame2->width;
1695 if (next_frame->height <= 0) {
1696 next_frame->height = next_frame2->height;
1698 decoded_frame = next_frame;
1701 decoded_frame = next_frame2;
1707 decoded_frame = next_frame2;
1710 if (!decoded_frame->data[0]) {
1712 "FFmpegReader::GetAVFrame (Decoded frame missing image data)",
1713 "format", decoded_frame->format,
1714 "width", decoded_frame->width,
1715 "height", decoded_frame->height);
1716 note_hw_decode_failure(AVERROR_INVALIDDATA,
"decoded_frame_empty");
1723 hw_decode_error_count = 0;
1725 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
1726 hw_decode_succeeded =
true;
1732 AVPixelFormat decoded_pix_fmt = (AVPixelFormat)(decoded_frame->format);
1733 if (decoded_pix_fmt == AV_PIX_FMT_NONE)
1734 decoded_pix_fmt = (AVPixelFormat)(pStream->codecpar->format);
1735 const int decoded_width = decoded_frame->width > 0 ? decoded_frame->width :
info.
width;
1736 const int decoded_height = decoded_frame->height > 0 ? decoded_frame->height :
info.
height;
1737 if (
AV_ALLOCATE_IMAGE(pFrame, decoded_pix_fmt, decoded_width, decoded_height) <= 0) {
1740 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)decoded_frame->data, decoded_frame->linesize,
1741 decoded_pix_fmt, decoded_width, decoded_height);
1742 pFrame->format = decoded_pix_fmt;
1743 pFrame->width = decoded_width;
1744 pFrame->height = decoded_height;
1745 pFrame->color_range = decoded_frame->color_range;
1746 pFrame->colorspace = decoded_frame->colorspace;
1747 pFrame->color_primaries = decoded_frame->color_primaries;
1748 pFrame->color_trc = decoded_frame->color_trc;
1749 pFrame->chroma_location = decoded_frame->chroma_location;
1755 if (decoded_frame->pts != AV_NOPTS_VALUE) {
1758 video_pts = decoded_frame->pts;
1759 }
else if (decoded_frame->pkt_dts != AV_NOPTS_VALUE) {
1761 video_pts = decoded_frame->pkt_dts;
1765 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1771 if (
hw_de_on && hw_de_supported && next_frame2 != next_frame) {
1776 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1782 if (frameFinished) {
1786 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.
width,
1795 return frameFinished;
1798 bool FFmpegReader::ReopenWithoutHardwareDecode(int64_t requested_frame) {
1800 if (!hw_decode_failed || force_sw_decode) {
1805 "FFmpegReader::ReopenWithoutHardwareDecode (falling back to software decode)",
1806 "requested_frame", requested_frame,
1807 "video_packets_read", packet_status.
video_read,
1809 "hw_decode_error_count", hw_decode_error_count);
1811 force_sw_decode =
true;
1812 hw_decode_failed =
false;
1813 hw_decode_error_count = 0;
1817 Seek(requested_frame);
1820 (void) requested_frame;
1827 return hw_decode_succeeded;
1834 bool FFmpegReader::CheckSeek() {
1837 const int64_t kSeekRetryMax = 5;
1838 const int kSeekStagnantMax = 2;
1842 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1850 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1852 if (max_seeked_frame == last_seek_max_frame) {
1853 seek_stagnant_count++;
1855 last_seek_max_frame = max_seeked_frame;
1856 seek_stagnant_count = 0;
1860 if (max_seeked_frame >= seeking_frame) {
1863 "is_video_seek", is_video_seek,
1864 "max_seeked_frame", max_seeked_frame,
1865 "seeking_frame", seeking_frame,
1866 "seeking_pts", seeking_pts,
1867 "seek_video_frame_found", seek_video_frame_found,
1868 "seek_audio_frame_found", seek_audio_frame_found);
1871 if (seek_count < kSeekRetryMax) {
1872 Seek(seeking_frame - (10 * seek_count * seek_count));
1873 }
else if (seek_stagnant_count >= kSeekStagnantMax) {
1875 Seek(seeking_frame - (10 * kSeekRetryMax * kSeekRetryMax));
1878 Seek(seeking_frame - (10 * seek_count * seek_count));
1883 "is_video_seek", is_video_seek,
1884 "packet->pts", GetPacketPTS(),
1885 "seeking_pts", seeking_pts,
1886 "seeking_frame", seeking_frame,
1887 "seek_video_frame_found", seek_video_frame_found,
1888 "seek_audio_frame_found", seek_audio_frame_found);
1902 void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1905 int frame_finished = GetAVFrame();
1908 if (!frame_finished) {
1911 RemoveAVFrame(pFrame);
1917 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1920 if (!seek_video_frame_found && is_seeking)
1921 seek_video_frame_found = current_frame;
1927 working_cache.
Add(CreateFrame(requested_frame));
1933 AVPixelFormat decoded_pix_fmt = (pFrame && pFrame->format != AV_PIX_FMT_NONE)
1934 ?
static_cast<AVPixelFormat
>(pFrame->format)
1936 bool src_full_range = (pFrame && pFrame->color_range == AVCOL_RANGE_JPEG);
1937 AVPixelFormat src_pix_fmt = NormalizeDeprecatedPixFmt(decoded_pix_fmt, src_full_range);
1938 int src_width = (pFrame && pFrame->width > 0) ? pFrame->width :
info.
width;
1939 int src_height = (pFrame && pFrame->height > 0) ? pFrame->height :
info.
height;
1940 int height = src_height;
1941 int width = src_width;
1943 AVFrame *pFrameRGB = pFrameRGB_cached;
1946 if (pFrameRGB ==
nullptr)
1948 pFrameRGB_cached = pFrameRGB;
1951 uint8_t *buffer =
nullptr;
1972 max_width = std::max(
float(max_width), max_width * max_scale_x);
1973 max_height = std::max(
float(max_height), max_height * max_scale_y);
1979 QSize width_size(max_width * max_scale_x,
1982 max_height * max_scale_y);
1984 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1985 max_width = std::max(max_width, width_size.width());
1986 max_height = std::max(max_height, width_size.height());
1988 max_width = std::max(max_width, height_size.width());
1989 max_height = std::max(max_height, height_size.height());
1996 float preview_ratio = 1.0;
2003 max_width =
info.
width * max_scale_x * preview_ratio;
2004 max_height =
info.
height * max_scale_y * preview_ratio;
2012 QSize bounded_size(max_width, max_height);
2014 if (bounded_size.width() > max_decode_size.width() ||
2015 bounded_size.height() > max_decode_size.height()) {
2016 bounded_size.scale(max_decode_size, Qt::KeepAspectRatio);
2017 max_width = bounded_size.width();
2018 max_height = bounded_size.height();
2025 const int quarter_turn = ((
static_cast<int>(std::round(source_rotation / 90.0)) * 90) % 360 + 360) % 360;
2027 std::swap(max_width, max_height);
2031 int original_height = src_height;
2032 if (max_width != 0 && max_height != 0 && max_width < width && max_height < height) {
2034 float ratio = float(width) / float(height);
2035 int possible_width = round(max_height * ratio);
2036 int possible_height = round(max_width / ratio);
2038 if (possible_width <= max_width) {
2040 width = possible_width;
2041 height = max_height;
2045 height = possible_height;
2050 const int bytes_per_pixel = 4;
2051 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
2054 constexpr
size_t ALIGNMENT = 32;
2055 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
2056 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
2061 int scale_mode = SWS_FAST_BILINEAR;
2063 scale_mode = SWS_BICUBIC;
2065 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);
2066 if (!img_convert_ctx)
2068 const int *src_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
2069 const int *dst_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
2070 const int dst_full_range = 1;
2071 sws_setColorspaceDetails(img_convert_ctx, src_coeff, src_full_range ? 1 : 0,
2072 dst_coeff, dst_full_range, 0, 1 << 16, 1 << 16);
2074 if (!pFrame || !pFrame->data[0] || pFrame->linesize[0] <= 0) {
2076 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
2077 hw_decode_failed =
true;
2079 "FFmpegReader::ProcessVideoPacket (Invalid source frame; forcing software fallback)",
2080 "requested_frame", requested_frame,
2081 "current_frame", current_frame,
2082 "src_pix_fmt", src_pix_fmt,
2083 "src_width", src_width,
2084 "src_height", src_height);
2088 RemoveAVFrame(pFrame);
2095 const int scaled_lines = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
2096 original_height, pFrameRGB->data, pFrameRGB->linesize);
2097 if (scaled_lines <= 0) {
2099 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
2100 hw_decode_failed =
true;
2102 "FFmpegReader::ProcessVideoPacket (sws_scale failed; forcing software fallback)",
2103 "requested_frame", requested_frame,
2104 "current_frame", current_frame,
2105 "scaled_lines", scaled_lines,
2106 "src_pix_fmt", src_pix_fmt,
2107 "src_width", src_width,
2108 "src_height", src_height);
2113 RemoveAVFrame(pFrame);
2119 std::shared_ptr<Frame> f = CreateFrame(current_frame);
2122 if (!ffmpeg_has_alpha(src_pix_fmt)) {
2124 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
2127 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
2129 ApplyFrameOrientation(f);
2132 working_cache.
Add(f);
2135 last_video_frame = f;
2141 RemoveAVFrame(pFrame);
2147 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
2151 void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
2154 if (packet && packet->pts != AV_NOPTS_VALUE) {
2156 location = GetAudioPTSLocation(packet->pts);
2159 if (!seek_audio_frame_found && is_seeking)
2160 seek_audio_frame_found = location.
frame;
2167 working_cache.
Add(CreateFrame(requested_frame));
2171 "requested_frame", requested_frame,
2172 "target_frame", location.
frame,
2176 int frame_finished = 0;
2180 int packet_samples = 0;
2184 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
2185 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
2189 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
2190 if (receive_frame_err >= 0) {
2193 if (receive_frame_err == AVERROR_EOF) {
2197 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
2199 avcodec_flush_buffers(aCodecCtx);
2201 if (receive_frame_err != 0) {
2206 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
2209 if (frame_finished) {
2215 audio_pts = audio_frame->pts;
2218 location = GetAudioPTSLocation(audio_pts);
2221 int plane_size = -1;
2227 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
2231 packet_samples = audio_frame->nb_samples * nb_channels;
2240 int pts_remaining_samples = packet_samples /
info.
channels;
2243 if (pts_remaining_samples == 0) {
2245 "packet_samples", packet_samples,
2247 "pts_remaining_samples", pts_remaining_samples);
2251 while (pts_remaining_samples) {
2256 int samples = samples_per_frame - previous_packet_location.
sample_start;
2257 if (samples > pts_remaining_samples)
2258 samples = pts_remaining_samples;
2261 pts_remaining_samples -= samples;
2263 if (pts_remaining_samples > 0) {
2265 previous_packet_location.
frame++;
2274 "packet_samples", packet_samples,
2282 audio_converted->nb_samples = audio_frame->nb_samples;
2283 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
2290 AVChannelLayout input_layout = ffmpeg_get_valid_channel_layout(
2292 AVChannelLayout output_layout = ffmpeg_get_valid_channel_layout(
2294 int in_layout_err = av_opt_set_chlayout(avr,
"in_chlayout", &input_layout, 0);
2295 int out_layout_err = av_opt_set_chlayout(avr,
"out_chlayout", &output_layout, 0);
2303 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
2308 av_channel_layout_uninit(&input_layout);
2309 av_channel_layout_uninit(&output_layout);
2310 if (in_layout_err < 0 || out_layout_err < 0 || swr_init_err < 0) {
2312 throw InvalidChannels(
"Could not initialize FFmpeg audio channel layout or resampler.",
path);
2315 if (swr_init_err < 0) {
2325 audio_converted->data,
2326 audio_converted->linesize[0],
2327 audio_converted->nb_samples,
2329 audio_frame->linesize[0],
2330 audio_frame->nb_samples);
2333 int64_t starting_frame_number = -1;
2334 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++) {
2336 starting_frame_number = location.
frame;
2337 int channel_buffer_size = nb_samples;
2338 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
2342 int remaining_samples = channel_buffer_size;
2343 while (remaining_samples > 0) {
2348 int samples = std::fmin(samples_per_frame - start, remaining_samples);
2351 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
2354 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
2358 "frame", starting_frame_number,
2361 "channel", channel_filter,
2362 "samples_per_frame", samples_per_frame);
2365 working_cache.
Add(f);
2368 remaining_samples -= samples;
2371 if (remaining_samples > 0)
2372 channel_buffer += samples;
2375 starting_frame_number++;
2383 av_free(audio_converted->data[0]);
2392 "requested_frame", requested_frame,
2393 "starting_frame", location.
frame,
2394 "end_frame", starting_frame_number - 1,
2395 "audio_pts_seconds", audio_pts_seconds);
2401 void FFmpegReader::Seek(int64_t requested_frame) {
2403 if (requested_frame < 1)
2404 requested_frame = 1;
2407 if (requested_frame > largest_frame_processed && packet_status.
end_of_file) {
2414 "requested_frame", requested_frame,
2415 "seek_count", seek_count,
2416 "last_frame", last_frame);
2419 working_cache.
Clear();
2423 video_pts_seconds = NO_PTS_OFFSET;
2425 audio_pts_seconds = NO_PTS_OFFSET;
2426 hold_packet =
false;
2428 current_video_frame = 0;
2429 largest_frame_processed = 0;
2430 last_final_video_frame.reset();
2435 packet_status.
reset(
false);
2441 int buffer_amount = 12;
2442 if (requested_frame - buffer_amount < 20) {
2456 if (seek_count == 1) {
2459 seeking_pts = ConvertFrameToVideoPTS(1);
2461 seek_audio_frame_found = 0;
2462 seek_video_frame_found = 0;
2466 bool seek_worked =
false;
2467 int64_t seek_target = 0;
2471 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
2476 is_video_seek =
true;
2483 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
2488 is_video_seek =
false;
2497 avcodec_flush_buffers(aCodecCtx);
2501 avcodec_flush_buffers(pCodecCtx);
2504 previous_packet_location.
frame = -1;
2509 if (seek_count == 1) {
2511 seeking_pts = seek_target;
2512 seeking_frame = requested_frame;
2514 seek_audio_frame_found = 0;
2515 seek_video_frame_found = 0;
2543 int64_t FFmpegReader::GetPacketPTS() {
2545 int64_t current_pts = packet->pts;
2546 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2547 current_pts = packet->dts;
2553 return AV_NOPTS_VALUE;
2558 void FFmpegReader::UpdatePTSOffset() {
2559 if (pts_offset_seconds != NO_PTS_OFFSET) {
2563 pts_offset_seconds = 0.0;
2564 double video_pts_offset_seconds = 0.0;
2565 double audio_pts_offset_seconds = 0.0;
2567 bool has_video_pts =
false;
2570 has_video_pts =
true;
2572 bool has_audio_pts =
false;
2575 has_audio_pts =
true;
2579 while (!has_video_pts || !has_audio_pts) {
2581 if (GetNextPacket() < 0)
2586 int64_t pts = GetPacketPTS();
2589 if (!has_video_pts && packet->stream_index == videoStream) {
2595 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2596 has_video_pts =
true;
2599 else if (!has_audio_pts && packet->stream_index == audioStream) {
2605 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2606 has_audio_pts =
true;
2616 pts_offset_seconds = video_pts_offset_seconds;
2618 pts_offset_seconds = audio_pts_offset_seconds;
2619 }
else if (has_video_pts && has_audio_pts) {
2621 pts_offset_seconds = video_pts_offset_seconds;
2626 int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2628 int64_t previous_video_frame = current_video_frame;
2630 const double video_timebase_value =
2636 double video_seconds = (double(pts) * video_timebase_value) + pts_offset_seconds;
2639 int64_t frame = round(video_seconds * fps_value) + 1;
2642 if (current_video_frame == 0)
2643 current_video_frame = frame;
2647 if (frame == previous_video_frame) {
2652 current_video_frame++;
2661 int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2663 const double video_timebase_value =
2669 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2672 int64_t video_pts = round(seconds / video_timebase_value);
2679 int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2681 const double audio_timebase_value =
2687 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2690 int64_t audio_pts = round(seconds / audio_timebase_value);
2697 AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2698 const double audio_timebase_value =
2705 double audio_seconds = (double(pts) * audio_timebase_value) + pts_offset_seconds;
2708 double frame = (audio_seconds * fps_value) + 1;
2711 int64_t whole_frame = int64_t(frame);
2714 double sample_start_percentage = frame - double(whole_frame);
2720 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2723 if (whole_frame < 1)
2725 if (sample_start < 0)
2732 if (previous_packet_location.
frame != -1) {
2733 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2734 int64_t orig_frame = location.
frame;
2739 location.
frame = previous_packet_location.
frame;
2742 ZmqLogger::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);
2751 previous_packet_location = location;
2758 std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2760 std::shared_ptr<Frame> output = working_cache.
GetFrame(requested_frame);
2764 output = working_cache.
GetFrame(requested_frame);
2765 if(output)
return output;
2773 working_cache.
Add(output);
2776 if (requested_frame > largest_frame_processed)
2777 largest_frame_processed = requested_frame;
2784 bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2787 bool seek_trash =
false;
2788 int64_t max_seeked_frame = seek_audio_frame_found;
2789 if (seek_video_frame_found > max_seeked_frame) {
2790 max_seeked_frame = seek_video_frame_found;
2792 if ((
info.
has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2793 (
info.
has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2801 void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2804 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2807 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.
GetFrames();
2808 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2811 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2814 std::shared_ptr<Frame> f = *working_itr;
2817 if (!f || f->number > requested_frame) {
2823 double frame_pts_seconds = (double(f->number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2824 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2827 bool is_video_ready =
false;
2828 bool is_audio_ready =
false;
2829 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2830 if ((frame_pts_seconds <= video_pts_seconds)
2831 || (recent_pts_diff > 1.5)
2835 is_video_ready =
true;
2837 "frame_number", f->number,
2838 "frame_pts_seconds", frame_pts_seconds,
2839 "video_pts_seconds", video_pts_seconds,
2840 "recent_pts_diff", recent_pts_diff);
2846 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2847 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2851 if (!f->has_image_data
2852 && last_final_video_frame
2853 && last_final_video_frame->has_image_data
2854 && last_final_video_frame->number <= f->number) {
2855 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2859 if (!f->has_image_data
2861 && last_video_frame->has_image_data
2862 && last_video_frame->number <= f->number) {
2863 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2867 if (!f->has_image_data) {
2869 "FFmpegReader::CheckWorkingFrames (no previous image found; using black frame)",
2870 "frame_number", f->number);
2871 f->AddColor(
"#000000");
2876 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2877 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2878 || (recent_pts_diff > 1.5)
2883 is_audio_ready =
true;
2885 "frame_number", f->number,
2886 "frame_pts_seconds", frame_pts_seconds,
2887 "audio_pts_seconds", audio_pts_seconds,
2888 "audio_pts_diff", audio_pts_diff,
2889 "recent_pts_diff", recent_pts_diff);
2891 bool is_seek_trash = IsPartialFrame(f->number);
2899 "frame_number", f->number,
2900 "is_video_ready", is_video_ready,
2901 "is_audio_ready", is_audio_ready,
2908 && !packet_status.
end_of_file && !is_seek_trash) {
2913 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2914 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2916 if (!f->has_image_data
2917 && last_final_video_frame
2918 && last_final_video_frame->has_image_data
2919 && last_final_video_frame->number <= f->number) {
2920 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2922 if (!f->has_image_data
2924 && last_video_frame->has_image_data
2925 && last_video_frame->number <= f->number) {
2926 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2933 if (!f->has_image_data && is_video_ready && is_audio_ready) {
2935 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2936 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2938 if (!f->has_image_data
2939 && last_final_video_frame
2940 && last_final_video_frame->has_image_data
2941 && last_final_video_frame->number <= f->number) {
2942 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2944 if (!f->has_image_data
2946 && last_video_frame->has_image_data
2947 && last_video_frame->number <= f->number) {
2948 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2954 if (!f->has_image_data) {
2958 if ((!packet_status.
end_of_file && is_video_ready && is_audio_ready) || packet_status.
end_of_file || is_seek_trash) {
2961 "requested_frame", requested_frame,
2962 "f->number", f->number,
2963 "is_seek_trash", is_seek_trash,
2964 "Working Cache Count", working_cache.
Count(),
2968 if (!is_seek_trash) {
2971 if (f->has_image_data) {
2972 last_final_video_frame = f;
2976 working_cache.
Remove(f->number);
2979 last_frame = f->number;
2982 working_cache.
Remove(f->number);
2989 working_frames.clear();
2990 working_frames.shrink_to_fit();
2994 void FFmpegReader::CheckFPS() {
3002 int frames_per_second[3] = {0,0,0};
3003 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
3006 int all_frames_detected = 0;
3007 int starting_frames_detected = 0;
3012 if (GetNextPacket() < 0)
3017 if (packet->stream_index == videoStream) {
3020 fps_index = int(video_seconds);
3023 if (fps_index >= 0 && fps_index < max_fps_index) {
3025 starting_frames_detected++;
3026 frames_per_second[fps_index]++;
3030 all_frames_detected++;
3035 float avg_fps = 30.0;
3036 if (starting_frames_detected > 0 && fps_index > 0) {
3037 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
3041 if (avg_fps < 8.0) {
3050 if (all_frames_detected > 0) {
3064 void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
3068 av_freep(&remove_frame->data[0]);
3076 void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
3081 delete remove_packet;
3096 root[
"type"] =
"FFmpegReader";
3097 root[
"path"] =
path;
3098 switch (duration_strategy) {
3100 root[
"duration_strategy"] =
"VideoPreferred";
3103 root[
"duration_strategy"] =
"AudioPreferred";
3107 root[
"duration_strategy"] =
"LongestStream";
3124 catch (
const std::exception& e) {
3126 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
3137 if (!root[
"path"].isNull())
3138 path = root[
"path"].asString();
3139 if (!root[
"duration_strategy"].isNull()) {
3140 const std::string strategy = root[
"duration_strategy"].asString();
3141 if (strategy ==
"VideoPreferred") {
3143 }
else if (strategy ==
"AudioPreferred") {
Shared helpers for Crop effect scaling logic.
Header file for all Exception classes.
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.
Header file for ZeroMQ-based Logger 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
The scale determines how a clip should be resized to fit its parent.
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)
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.
void Log(std::string message)
Log message to all subscribers of this logger (if any)
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)
Append debug information.
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
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.
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)