34#define MAX_SUPPORTED_WIDTH 1950
35#define MAX_SUPPORTED_HEIGHT 1100
38#include "libavutil/hwcontext_vaapi.h"
40typedef struct VAAPIDecodeContext {
42 VAEntrypoint va_entrypoint;
44 VAContextID va_context;
46#if FF_API_STRUCT_VAAPI_CONTEXT
49 struct vaapi_context *old_context;
50 AVBufferRef *device_ref;
54 AVHWDeviceContext *device;
55 AVVAAPIDeviceContext *hwctx;
57 AVHWFramesContext *frames;
58 AVVAAPIFramesContext *hwfc;
60 enum AVPixelFormat surface_format;
79 : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), NO_PTS_OFFSET(-99999),
80 path(path), is_video_seek(true), check_interlace(false), check_fps(false),
enable_seek(true), is_open(false),
81 seek_audio_frame_found(0), seek_video_frame_found(0),is_duration_known(false), largest_frame_processed(0),
82 current_video_frame(0), packet(NULL), duration_strategy(duration_strategy),
83 audio_pts(0), video_pts(0), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
84 pStream(NULL), aStream(NULL), pFrame(NULL), previous_packet_location{-1,0},
92 pts_offset_seconds = NO_PTS_OFFSET;
93 video_pts_seconds = NO_PTS_OFFSET;
94 audio_pts_seconds = NO_PTS_OFFSET;
97 working_cache.SetMaxBytesFromInfo(
info.fps.ToDouble() * 2,
info.width,
info.height,
info.sample_rate,
info.channels);
101 if (inspect_reader) {
123 if (abs(diff) <= amount)
134static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
136 const enum AVPixelFormat *p;
138 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
140#if defined(__linux__)
142 case AV_PIX_FMT_VAAPI:
147 case AV_PIX_FMT_VDPAU:
155 case AV_PIX_FMT_DXVA2_VLD:
160 case AV_PIX_FMT_D3D11:
166#if defined(__APPLE__)
168 case AV_PIX_FMT_VIDEOTOOLBOX:
175 case AV_PIX_FMT_CUDA:
191 return AV_PIX_FMT_NONE;
194int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
198 case AV_CODEC_ID_H264:
199 case AV_CODEC_ID_MPEG2VIDEO:
200 case AV_CODEC_ID_VC1:
201 case AV_CODEC_ID_WMV1:
202 case AV_CODEC_ID_WMV2:
203 case AV_CODEC_ID_WMV3:
218 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
228 if (avformat_open_input(&pFormatCtx, path.c_str(), NULL, NULL) != 0)
229 throw InvalidFile(
"File could not be opened.", path);
232 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
239 packet_status.reset(
true);
242 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
244 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
246 packet_status.video_eof =
false;
247 packet_status.packets_eof =
false;
248 packet_status.end_of_file =
false;
251 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
253 packet_status.audio_eof =
false;
254 packet_status.packets_eof =
false;
255 packet_status.end_of_file =
false;
258 if (videoStream == -1 && audioStream == -1)
259 throw NoStreamsFound(
"No video or audio streams found in this file.", path);
262 if (videoStream != -1) {
264 info.video_stream_index = videoStream;
267 pStream = pFormatCtx->streams[videoStream];
273 const AVCodec *pCodec = avcodec_find_decoder(codecId);
274 AVDictionary *
opts = NULL;
275 int retry_decode_open = 2;
280 if (
hw_de_on && (retry_decode_open==2)) {
282 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
285 retry_decode_open = 0;
290 if (pCodec == NULL) {
291 throw InvalidCodec(
"A valid video codec could not be found for this file.", path);
295 av_dict_set(&
opts,
"strict",
"experimental", 0);
299 int i_decoder_hw = 0;
301 char *adapter_ptr = NULL;
304 fprintf(stderr,
"Hardware decoding device number: %d\n", adapter_num);
307 pCodecCtx->get_format = get_hw_dec_format;
309 if (adapter_num < 3 && adapter_num >=0) {
310#if defined(__linux__)
311 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
312 adapter_ptr = adapter;
314 switch (i_decoder_hw) {
316 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
319 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
322 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
325 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
328 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
335 switch (i_decoder_hw) {
337 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
340 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
343 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
346 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
349 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
352#elif defined(__APPLE__)
355 switch (i_decoder_hw) {
357 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
360 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
363 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
373#if defined(__linux__)
374 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
376 if( adapter_ptr != NULL ) {
377#elif defined(__APPLE__)
378 if( adapter_ptr != NULL ) {
387 hw_device_ctx = NULL;
389 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
390 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
391 throw InvalidCodec(
"Hardware device reference create failed.", path);
422 throw InvalidCodec(
"Hardware device create failed.", path);
432 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
436 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
437 if (avcodec_return < 0) {
438 std::stringstream avcodec_error_msg;
439 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
445 AVHWFramesConstraints *constraints = NULL;
446 void *hwconfig = NULL;
447 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
451 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
452 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
455 if (pCodecCtx->coded_width < constraints->min_width ||
456 pCodecCtx->coded_height < constraints->min_height ||
457 pCodecCtx->coded_width > constraints->max_width ||
458 pCodecCtx->coded_height > constraints->max_height) {
461 retry_decode_open = 1;
464 av_buffer_unref(&hw_device_ctx);
465 hw_device_ctx = NULL;
470 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);
471 retry_decode_open = 0;
473 av_hwframe_constraints_free(&constraints);
486 if (pCodecCtx->coded_width < 0 ||
487 pCodecCtx->coded_height < 0 ||
488 pCodecCtx->coded_width > max_w ||
489 pCodecCtx->coded_height > max_h ) {
490 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);
492 retry_decode_open = 1;
495 av_buffer_unref(&hw_device_ctx);
496 hw_device_ctx = NULL;
500 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);
501 retry_decode_open = 0;
509 retry_decode_open = 0;
511 }
while (retry_decode_open);
520 if (audioStream != -1) {
522 info.audio_stream_index = audioStream;
525 aStream = pFormatCtx->streams[audioStream];
531 const AVCodec *aCodec = avcodec_find_decoder(codecId);
537 if (aCodec == NULL) {
538 throw InvalidCodec(
"A valid audio codec could not be found for this file.", path);
542 AVDictionary *
opts = NULL;
543 av_dict_set(&
opts,
"strict",
"experimental", 0);
546 if (avcodec_open2(aCodecCtx, aCodec, &
opts) < 0)
547 throw InvalidCodec(
"An audio codec was found, but could not be opened.", path);
557 AVDictionaryEntry *tag = NULL;
558 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
559 QString str_key = tag->key;
560 QString str_value = tag->value;
561 info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
565 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
566 AVStream* st = pFormatCtx->streams[i];
567 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
569#if (LIBAVFORMAT_VERSION_MAJOR < 62)
570 for (
int j = 0; j < st->nb_side_data; j++) {
571 AVPacketSideData *sd = &st->side_data[j];
573 for (
int j = 0; j < st->codecpar->nb_coded_side_data; j++) {
574 AVPacketSideData *sd = &st->codecpar->coded_side_data[j];
577 if (sd->type == AV_PKT_DATA_DISPLAYMATRIX &&
578 sd->size >= 9 *
sizeof(int32_t) &&
579 !
info.metadata.count(
"rotate"))
581 double rotation = -av_display_rotation_get(
582 reinterpret_cast<int32_t *
>(sd->data));
583 if (std::isnan(rotation)) rotation = 0;
584 info.metadata[
"rotate"] = std::to_string(rotation);
587 else if (sd->type == AV_PKT_DATA_SPHERICAL) {
589 info.metadata[
"spherical"] =
"1";
592 const AVSphericalMapping* map =
593 reinterpret_cast<const AVSphericalMapping*
>(sd->data);
596 const char* proj_name = av_spherical_projection_name(map->projection);
597 info.metadata[
"spherical_projection"] = proj_name
602 auto to_deg = [](int32_t v){
603 return (
double)v / 65536.0;
605 info.metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
606 info.metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
607 info.metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
615 previous_packet_location.frame = -1;
616 previous_packet_location.sample_start = 0;
619 working_cache.SetMaxBytesFromInfo(
info.fps.ToDouble() * 2,
info.width,
info.height,
info.sample_rate,
info.channels);
627 if (
info.fps.ToFloat() > 240.0f || (
info.fps.num <= 0 ||
info.fps.den <= 0) ||
info.video_length <= 0) {
647 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
653 AVPacket *recent_packet = packet;
658 int max_attempts = 128;
659 while (packet_status.packets_decoded() < packet_status.packets_read() && attempts < max_attempts) {
661 "packets_read", packet_status.packets_read(),
662 "packets_decoded", packet_status.packets_decoded(),
663 "attempts", attempts);
664 if (packet_status.video_decoded < packet_status.video_read) {
665 ProcessVideoPacket(
info.video_length);
667 if (packet_status.audio_decoded < packet_status.audio_read) {
668 ProcessAudioPacket(
info.video_length);
675 RemoveAVPacket(recent_packet);
679 if (
info.has_video) {
680 if(avcodec_is_open(pCodecCtx)) {
681 avcodec_flush_buffers(pCodecCtx);
687 av_buffer_unref(&hw_device_ctx);
688 hw_device_ctx = NULL;
692 if (img_convert_ctx) {
693 sws_freeContext(img_convert_ctx);
694 img_convert_ctx =
nullptr;
696 if (pFrameRGB_cached) {
702 if (
info.has_audio) {
703 if(avcodec_is_open(aCodecCtx)) {
704 avcodec_flush_buffers(aCodecCtx);
716 working_cache.Clear();
719 avformat_close_input(&pFormatCtx);
720 av_freep(&pFormatCtx);
728 largest_frame_processed = 0;
729 seek_audio_frame_found = 0;
730 seek_video_frame_found = 0;
731 current_video_frame = 0;
732 last_video_frame.reset();
736bool FFmpegReader::HasAlbumArt() {
740 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
741 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
744double FFmpegReader::PickDurationSeconds()
const {
745 auto has_value = [](
double value) {
return value > 0.0; };
747 switch (duration_strategy) {
749 if (has_value(video_stream_duration_seconds))
750 return video_stream_duration_seconds;
751 if (has_value(audio_stream_duration_seconds))
752 return audio_stream_duration_seconds;
753 if (has_value(format_duration_seconds))
754 return format_duration_seconds;
757 if (has_value(audio_stream_duration_seconds))
758 return audio_stream_duration_seconds;
759 if (has_value(video_stream_duration_seconds))
760 return video_stream_duration_seconds;
761 if (has_value(format_duration_seconds))
762 return format_duration_seconds;
767 double longest = 0.0;
768 if (has_value(video_stream_duration_seconds))
769 longest = std::max(longest, video_stream_duration_seconds);
770 if (has_value(audio_stream_duration_seconds))
771 longest = std::max(longest, audio_stream_duration_seconds);
772 if (has_value(format_duration_seconds))
773 longest = std::max(longest, format_duration_seconds);
774 if (has_value(longest))
780 if (has_value(format_duration_seconds))
781 return format_duration_seconds;
782 if (has_value(inferred_duration_seconds))
783 return inferred_duration_seconds;
788void FFmpegReader::ApplyDurationStrategy() {
789 const double fps_value =
info.fps.ToDouble();
790 const double chosen_seconds = PickDurationSeconds();
792 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
793 info.duration = 0.0f;
794 info.video_length = 0;
795 is_duration_known =
false;
799 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
801 info.duration = 0.0f;
802 info.video_length = 0;
803 is_duration_known =
false;
807 info.video_length = frames;
808 info.duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
809 is_duration_known =
true;
812void FFmpegReader::UpdateAudioInfo() {
822 if (
info.sample_rate > 0) {
827 auto record_duration = [](
double &target,
double seconds) {
829 target = std::max(target, seconds);
833 info.has_audio =
true;
834 info.file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
835 info.acodec = aCodecCtx->codec->name;
845 if (
info.channel_layout == 0) {
846 if (
info.channels == 1) {
848 }
else if (
info.channels == 2) {
855 if (
info.audio_bit_rate <= 0) {
857 info.audio_bit_rate = pFormatCtx->bit_rate;
861 info.audio_timebase.num = aStream->time_base.num;
862 info.audio_timebase.den = aStream->time_base.den;
865 if (aStream->duration > 0) {
866 record_duration(audio_stream_duration_seconds, aStream->duration *
info.audio_timebase.ToDouble());
868 if (pFormatCtx->duration > 0) {
870 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
874 if (
info.duration <= 0.0f &&
info.video_bit_rate > 0 &&
info.file_size > 0) {
876 record_duration(inferred_duration_seconds,
static_cast<double>(
info.file_size) /
info.video_bit_rate);
880 if (!
info.has_video) {
884 info.video_timebase.num = 1;
885 info.video_timebase.den = 30;
890 Clip *parent =
static_cast<Clip *
>(
ParentClip());
900 ApplyDurationStrategy();
903 AVDictionaryEntry *tag = NULL;
904 while ((tag = av_dict_get(aStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
905 QString str_key = tag->key;
906 QString str_value = tag->value;
907 info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
911void FFmpegReader::UpdateVideoInfo() {
912 if (
info.vcodec.length() > 0) {
917 auto record_duration = [](
double &target,
double seconds) {
919 target = std::max(target, seconds);
923 info.has_video =
true;
924 info.file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
927 info.vcodec = pCodecCtx->codec->name;
928 info.video_bit_rate = (pFormatCtx->bit_rate / 8);
931 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
933 info.fps.num = framerate.num;
934 info.fps.den = framerate.den;
943 if (pStream->sample_aspect_ratio.num != 0) {
944 info.pixel_ratio.num = pStream->sample_aspect_ratio.num;
945 info.pixel_ratio.den = pStream->sample_aspect_ratio.den;
950 info.pixel_ratio.num = 1;
951 info.pixel_ratio.den = 1;
956 Fraction size(
info.width *
info.pixel_ratio.num,
info.height *
info.pixel_ratio.den);
962 info.display_ratio.num = size.num;
963 info.display_ratio.den = size.den;
966 if (!check_interlace) {
967 check_interlace =
true;
969 switch(field_order) {
970 case AV_FIELD_PROGRESSIVE:
971 info.interlaced_frame =
false;
975 info.interlaced_frame =
true;
976 info.top_field_first =
true;
980 info.interlaced_frame =
true;
981 info.top_field_first =
false;
983 case AV_FIELD_UNKNOWN:
985 check_interlace =
false;
993 info.video_timebase.num = pStream->time_base.num;
994 info.video_timebase.den = pStream->time_base.den;
997 record_duration(video_stream_duration_seconds, pStream->duration *
info.video_timebase.ToDouble());
1000 if (pFormatCtx->duration >= 0) {
1002 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1006 if (
info.video_bit_rate > 0 &&
info.file_size > 0) {
1008 record_duration(inferred_duration_seconds,
static_cast<double>(
info.file_size) /
info.video_bit_rate);
1012 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1013 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1015 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1016 info.has_single_image =
true;
1019 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1020 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1021 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1022 info.has_single_image =
true;
1025 ApplyDurationStrategy();
1028 AVDictionaryEntry *tag = NULL;
1029 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1030 QString str_key = tag->key;
1031 QString str_value = tag->value;
1032 info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1037 return this->is_duration_known;
1043 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.", path);
1046 if (requested_frame < 1)
1047 requested_frame = 1;
1048 if (requested_frame >
info.video_length && is_duration_known)
1049 requested_frame =
info.video_length;
1050 if (
info.has_video &&
info.video_length == 0)
1052 throw InvalidFile(
"Could not detect the duration of the video or audio stream.", path);
1058 std::shared_ptr<Frame> frame =
final_cache.GetFrame(requested_frame);
1068 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1082 int64_t diff = requested_frame - last_frame;
1083 if (diff >= 1 && diff <= 20) {
1085 frame = ReadStream(requested_frame);
1090 Seek(requested_frame);
1099 frame = ReadStream(requested_frame);
1107std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1109 bool check_seek =
false;
1110 int packet_error = -1;
1120 CheckWorkingFrames(requested_frame);
1125 if (is_cache_found) {
1129 if (!hold_packet || !packet) {
1131 packet_error = GetNextPacket();
1132 if (packet_error < 0 && !packet) {
1134 packet_status.packets_eof =
true;
1139 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ReadStream (GetNextPacket)",
"requested_frame", requested_frame,
"packets_read", packet_status.packets_read(),
"packets_decoded", packet_status.packets_decoded(),
"is_seeking", is_seeking);
1143 check_seek = CheckSeek(
false);
1155 if ((
info.has_video && packet && packet->stream_index == videoStream) ||
1156 (
info.has_video && packet_status.video_decoded < packet_status.video_read) ||
1157 (
info.has_video && !packet && !packet_status.video_eof)) {
1159 ProcessVideoPacket(requested_frame);
1162 if ((
info.has_audio && packet && packet->stream_index == audioStream) ||
1163 (
info.has_audio && !packet && packet_status.audio_decoded < packet_status.audio_read) ||
1164 (
info.has_audio && !packet && !packet_status.audio_eof)) {
1166 ProcessAudioPacket(requested_frame);
1171 if ((!
info.has_video && packet && packet->stream_index == videoStream) ||
1172 (!
info.has_audio && packet && packet->stream_index == audioStream)) {
1174 if (packet->stream_index == videoStream) {
1175 packet_status.video_decoded++;
1176 }
else if (packet->stream_index == audioStream) {
1177 packet_status.audio_decoded++;
1182 RemoveAVPacket(packet);
1188 packet_status.end_of_file = packet_status.packets_eof && packet_status.video_eof && packet_status.audio_eof;
1189 if ((packet_status.packets_eof && packet_status.packets_read() == packet_status.packets_decoded()) || packet_status.end_of_file) {
1192 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);
1193 if (!packet_status.video_eof) {
1194 packet_status.video_eof =
true;
1196 if (!packet_status.audio_eof) {
1197 packet_status.audio_eof =
true;
1199 packet_status.end_of_file =
true;
1206 "packets_read", packet_status.packets_read(),
1207 "packets_decoded", packet_status.packets_decoded(),
1208 "end_of_file", packet_status.end_of_file,
1209 "largest_frame_processed", largest_frame_processed,
1210 "Working Cache Count", working_cache.Count());
1213 if (!packet_status.end_of_file && requested_frame >=
info.video_length) {
1215 packet_status.end_of_file =
true;
1217 if (packet_status.end_of_file) {
1219 CheckWorkingFrames(requested_frame);
1223 std::shared_ptr<Frame> frame =
final_cache.GetFrame(requested_frame);
1230 frame =
final_cache.GetFrame(largest_frame_processed);
1235 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1238 if (!frame->has_image_data) {
1240 f->AddColor(
info.width,
info.height,
"#000");
1243 frame->AddAudioSilence(samples_in_frame);
1248 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1249 f->AddColor(
info.width,
info.height,
"#000");
1250 f->AddAudioSilence(samples_in_frame);
1258int FFmpegReader::GetNextPacket() {
1259 int found_packet = 0;
1260 AVPacket *next_packet;
1261 next_packet =
new AVPacket();
1262 found_packet = av_read_frame(pFormatCtx, next_packet);
1266 RemoveAVPacket(packet);
1269 if (found_packet >= 0) {
1271 packet = next_packet;
1274 if (packet->stream_index == videoStream) {
1275 packet_status.video_read++;
1276 }
else if (packet->stream_index == audioStream) {
1277 packet_status.audio_read++;
1285 return found_packet;
1289bool FFmpegReader::GetAVFrame() {
1290 int frameFinished = 0;
1296 int send_packet_err = 0;
1297 int64_t send_packet_pts = 0;
1298 if ((packet && packet->stream_index == videoStream) || !packet) {
1299 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1301 if (packet && send_packet_err >= 0) {
1302 send_packet_pts = GetPacketPTS();
1303 hold_packet =
false;
1313 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1314 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);
1315 if (send_packet_err == AVERROR(EAGAIN)) {
1317 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1319 if (send_packet_err == AVERROR(EINVAL)) {
1320 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);
1322 if (send_packet_err == AVERROR(ENOMEM)) {
1323 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);
1330 int receive_frame_err = 0;
1331 AVFrame *next_frame2;
1339 next_frame2 = next_frame;
1342 while (receive_frame_err >= 0) {
1343 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1345 if (receive_frame_err != 0) {
1346 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);
1348 if (receive_frame_err == AVERROR_EOF) {
1350 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1351 avcodec_flush_buffers(pCodecCtx);
1352 packet_status.video_eof =
true;
1354 if (receive_frame_err == AVERROR(EINVAL)) {
1356 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1357 avcodec_flush_buffers(pCodecCtx);
1359 if (receive_frame_err == AVERROR(EAGAIN)) {
1361 "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);
1363 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1365 "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);
1376 if (next_frame2->format == hw_de_av_pix_fmt) {
1377 next_frame->format = AV_PIX_FMT_YUV420P;
1378 if ((err = av_hwframe_transfer_data(next_frame,next_frame2,0)) < 0) {
1381 if ((err = av_frame_copy_props(next_frame,next_frame2)) < 0) {
1389 next_frame = next_frame2;
1395 packet_status.video_decoded++;
1399 throw OutOfMemory(
"Failed to allocate image buffer", path);
1401 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)next_frame->data, next_frame->linesize,
1402 (AVPixelFormat)(pStream->codecpar->format),
info.width,
info.height);
1408 if (next_frame->pts != AV_NOPTS_VALUE) {
1411 video_pts = next_frame->pts;
1412 }
else if (next_frame->pkt_dts != AV_NOPTS_VALUE) {
1414 video_pts = next_frame->pkt_dts;
1418 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1429 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1435 if (frameFinished) {
1438 avpicture_alloc((AVPicture *) pFrame, pCodecCtx->pix_fmt,
info.width,
info.height);
1439 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.width,
1448 return frameFinished;
1452bool FFmpegReader::CheckSeek(
bool is_video) {
1457 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1461 if ((
info.has_video && !seek_video_frame_found) || (
info.has_audio && !seek_audio_frame_found))
1465 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1468 if (max_seeked_frame >= seeking_frame) {
1471 "is_video_seek", is_video_seek,
1472 "max_seeked_frame", max_seeked_frame,
1473 "seeking_frame", seeking_frame,
1474 "seeking_pts", seeking_pts,
1475 "seek_video_frame_found", seek_video_frame_found,
1476 "seek_audio_frame_found", seek_audio_frame_found);
1479 Seek(seeking_frame - (10 * seek_count * seek_count));
1483 "is_video_seek", is_video_seek,
1484 "packet->pts", GetPacketPTS(),
1485 "seeking_pts", seeking_pts,
1486 "seeking_frame", seeking_frame,
1487 "seek_video_frame_found", seek_video_frame_found,
1488 "seek_audio_frame_found", seek_audio_frame_found);
1502void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1505 int frame_finished = GetAVFrame();
1508 if (!frame_finished) {
1511 RemoveAVFrame(pFrame);
1517 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1520 if (!seek_video_frame_found && is_seeking)
1521 seek_video_frame_found = current_frame;
1527 working_cache.Add(CreateFrame(requested_frame));
1534 int height =
info.height;
1535 int width =
info.width;
1536 int64_t video_length =
info.video_length;
1539 AVFrame *pFrameRGB = pFrameRGB_cached;
1542 if (pFrameRGB ==
nullptr)
1543 throw OutOfMemory(
"Failed to allocate frame buffer", path);
1544 pFrameRGB_cached = pFrameRGB;
1547 uint8_t *buffer =
nullptr;
1554 int max_width =
info.width;
1555 int max_height =
info.height;
1557 Clip *parent =
static_cast<Clip *
>(
ParentClip());
1568 max_width = std::max(
float(max_width), max_width * max_scale_x);
1569 max_height = std::max(
float(max_height), max_height * max_scale_y);
1575 QSize width_size(max_width * max_scale_x,
1576 round(max_width / (
float(
info.width) /
float(
info.height))));
1577 QSize height_size(round(max_height / (
float(
info.height) /
float(
info.width))),
1578 max_height * max_scale_y);
1580 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1581 max_width = std::max(max_width, width_size.width());
1582 max_height = std::max(max_height, width_size.height());
1584 max_width = std::max(max_width, height_size.width());
1585 max_height = std::max(max_height, height_size.height());
1592 float preview_ratio = 1.0;
1599 max_width =
info.width * max_scale_x * preview_ratio;
1600 max_height =
info.height * max_scale_y * preview_ratio;
1608 int original_height = height;
1609 if (max_width != 0 && max_height != 0 && max_width < width && max_height < height) {
1611 float ratio = float(width) / float(height);
1612 int possible_width = round(max_height * ratio);
1613 int possible_height = round(max_width / ratio);
1615 if (possible_width <= max_width) {
1617 width = possible_width;
1618 height = max_height;
1622 height = possible_height;
1627 const int bytes_per_pixel = 4;
1628 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
1631 constexpr size_t ALIGNMENT = 32;
1632 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
1633 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
1638 int scale_mode = SWS_FAST_BILINEAR;
1640 scale_mode = SWS_BICUBIC;
1642 img_convert_ctx = sws_getCachedContext(img_convert_ctx,
info.width,
info.height,
AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, height,
PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL);
1643 if (!img_convert_ctx)
1644 throw OutOfMemory(
"Failed to initialize sws context", path);
1647 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
1648 original_height, pFrameRGB->data, pFrameRGB->linesize);
1651 std::shared_ptr<Frame> f = CreateFrame(current_frame);
1656 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
1659 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
1663 working_cache.Add(f);
1666 last_video_frame = f;
1672 RemoveAVFrame(pFrame);
1675 video_pts_seconds = (double(video_pts) *
info.video_timebase.ToDouble()) + pts_offset_seconds;
1678 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
1682void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
1683 AudioLocation location;
1685 if (packet && packet->pts != AV_NOPTS_VALUE) {
1687 location = GetAudioPTSLocation(packet->pts);
1690 if (!seek_audio_frame_found && is_seeking)
1691 seek_audio_frame_found = location.
frame;
1698 working_cache.Add(CreateFrame(requested_frame));
1702 "requested_frame", requested_frame,
1703 "target_frame", location.
frame,
1707 int frame_finished = 0;
1711 int packet_samples = 0;
1715 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
1716 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1720 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
1721 if (receive_frame_err >= 0) {
1724 if (receive_frame_err == AVERROR_EOF) {
1726 packet_status.audio_eof =
true;
1728 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
1730 avcodec_flush_buffers(aCodecCtx);
1732 if (receive_frame_err != 0) {
1737 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
1740 if (frame_finished) {
1741 packet_status.audio_decoded++;
1746 audio_pts = audio_frame->pts;
1749 location = GetAudioPTSLocation(audio_pts);
1752 int plane_size = -1;
1758 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
1762 packet_samples = audio_frame->nb_samples * nb_channels;
1771 int pts_remaining_samples = packet_samples /
info.channels;
1774 if (pts_remaining_samples == 0) {
1776 "packet_samples", packet_samples,
1777 "info.channels",
info.channels,
1778 "pts_remaining_samples", pts_remaining_samples);
1782 while (pts_remaining_samples) {
1787 int samples = samples_per_frame - previous_packet_location.sample_start;
1788 if (samples > pts_remaining_samples)
1789 samples = pts_remaining_samples;
1792 pts_remaining_samples -= samples;
1794 if (pts_remaining_samples > 0) {
1796 previous_packet_location.frame++;
1797 previous_packet_location.sample_start = 0;
1800 previous_packet_location.sample_start += samples;
1805 "packet_samples", packet_samples,
1806 "info.channels",
info.channels,
1807 "info.sample_rate",
info.sample_rate,
1813 audio_converted->nb_samples = audio_frame->nb_samples;
1814 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
1826 av_opt_set_int(avr,
"in_channels",
info.channels, 0);
1827 av_opt_set_int(avr,
"out_channels",
info.channels, 0);
1830 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
1831 av_opt_set_int(avr,
"in_sample_rate",
info.sample_rate, 0);
1832 av_opt_set_int(avr,
"out_sample_rate",
info.sample_rate, 0);
1839 audio_converted->data,
1840 audio_converted->linesize[0],
1841 audio_converted->nb_samples,
1843 audio_frame->linesize[0],
1844 audio_frame->nb_samples);
1847 int64_t starting_frame_number = -1;
1848 for (
int channel_filter = 0; channel_filter <
info.channels; channel_filter++) {
1850 starting_frame_number = location.
frame;
1851 int channel_buffer_size = nb_samples;
1852 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
1856 int remaining_samples = channel_buffer_size;
1857 while (remaining_samples > 0) {
1862 int samples = std::fmin(samples_per_frame - start, remaining_samples);
1865 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
1868 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
1872 "frame", starting_frame_number,
1875 "channel", channel_filter,
1876 "samples_per_frame", samples_per_frame);
1879 working_cache.Add(f);
1882 remaining_samples -= samples;
1885 if (remaining_samples > 0)
1886 channel_buffer += samples;
1889 starting_frame_number++;
1897 av_free(audio_converted->data[0]);
1902 audio_pts_seconds = (double(audio_pts) *
info.audio_timebase.ToDouble()) + pts_offset_seconds;
1906 "requested_frame", requested_frame,
1907 "starting_frame", location.
frame,
1908 "end_frame", starting_frame_number - 1,
1909 "audio_pts_seconds", audio_pts_seconds);
1915void FFmpegReader::Seek(int64_t requested_frame) {
1917 if (requested_frame < 1)
1918 requested_frame = 1;
1919 if (requested_frame >
info.video_length)
1920 requested_frame =
info.video_length;
1921 if (requested_frame > largest_frame_processed && packet_status.end_of_file) {
1928 "requested_frame", requested_frame,
1929 "seek_count", seek_count,
1930 "last_frame", last_frame);
1933 working_cache.Clear();
1937 video_pts_seconds = NO_PTS_OFFSET;
1939 audio_pts_seconds = NO_PTS_OFFSET;
1940 hold_packet =
false;
1942 current_video_frame = 0;
1943 largest_frame_processed = 0;
1944 bool has_audio_override =
info.has_audio;
1945 bool has_video_override =
info.has_video;
1948 packet_status.reset(
false);
1954 int buffer_amount = 12;
1955 if (requested_frame - buffer_amount < 20) {
1964 info.has_audio = has_audio_override;
1965 info.has_video = has_video_override;
1969 if (seek_count == 1) {
1972 seeking_pts = ConvertFrameToVideoPTS(1);
1974 seek_audio_frame_found = 0;
1975 seek_video_frame_found = 0;
1979 bool seek_worked =
false;
1980 int64_t seek_target = 0;
1983 if (!seek_worked &&
info.has_video && !HasAlbumArt()) {
1984 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
1985 if (av_seek_frame(pFormatCtx,
info.video_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) {
1986 fprintf(stderr,
"%s: error while seeking video stream\n", pFormatCtx->AV_FILENAME);
1989 is_video_seek =
true;
1995 if (!seek_worked &&
info.has_audio) {
1996 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
1997 if (av_seek_frame(pFormatCtx,
info.audio_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) {
1998 fprintf(stderr,
"%s: error while seeking audio stream\n", pFormatCtx->AV_FILENAME);
2001 is_video_seek =
false;
2010 avcodec_flush_buffers(aCodecCtx);
2014 avcodec_flush_buffers(pCodecCtx);
2017 previous_packet_location.frame = -1;
2018 previous_packet_location.sample_start = 0;
2022 if (seek_count == 1) {
2024 seeking_pts = seek_target;
2025 seeking_frame = requested_frame;
2027 seek_audio_frame_found = 0;
2028 seek_video_frame_found = 0;
2049 info.has_audio = has_audio_override;
2050 info.has_video = has_video_override;
2056int64_t FFmpegReader::GetPacketPTS() {
2058 int64_t current_pts = packet->pts;
2059 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2060 current_pts = packet->dts;
2066 return AV_NOPTS_VALUE;
2071void FFmpegReader::UpdatePTSOffset() {
2072 if (pts_offset_seconds != NO_PTS_OFFSET) {
2076 pts_offset_seconds = 0.0;
2077 double video_pts_offset_seconds = 0.0;
2078 double audio_pts_offset_seconds = 0.0;
2080 bool has_video_pts =
false;
2081 if (!
info.has_video) {
2083 has_video_pts =
true;
2085 bool has_audio_pts =
false;
2086 if (!
info.has_audio) {
2088 has_audio_pts =
true;
2092 while (!has_video_pts || !has_audio_pts) {
2094 if (GetNextPacket() < 0)
2099 int64_t pts = GetPacketPTS();
2102 if (!has_video_pts && packet->stream_index == videoStream) {
2104 video_pts_offset_seconds = 0.0 - (video_pts *
info.video_timebase.ToDouble());
2108 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2109 has_video_pts =
true;
2112 else if (!has_audio_pts && packet->stream_index == audioStream) {
2114 audio_pts_offset_seconds = 0.0 - (pts *
info.audio_timebase.ToDouble());
2118 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2119 has_audio_pts =
true;
2125 if (has_video_pts && has_audio_pts) {
2137 pts_offset_seconds = std::max(video_pts_offset_seconds, audio_pts_offset_seconds);
2142int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2144 int64_t previous_video_frame = current_video_frame;
2147 double video_seconds = (double(pts) *
info.video_timebase.ToDouble()) + pts_offset_seconds;
2150 int64_t frame = round(video_seconds *
info.fps.ToDouble()) + 1;
2153 if (current_video_frame == 0)
2154 current_video_frame = frame;
2158 if (frame == previous_video_frame) {
2163 current_video_frame++;
2172int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2174 double seconds = (double(frame_number - 1) /
info.fps.ToDouble()) + pts_offset_seconds;
2177 int64_t video_pts = round(seconds /
info.video_timebase.ToDouble());
2184int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2186 double seconds = (double(frame_number - 1) /
info.fps.ToDouble()) + pts_offset_seconds;
2189 int64_t audio_pts = round(seconds /
info.audio_timebase.ToDouble());
2196AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2198 double audio_seconds = (double(pts) *
info.audio_timebase.ToDouble()) + pts_offset_seconds;
2201 double frame = (audio_seconds *
info.fps.ToDouble()) + 1;
2204 int64_t whole_frame = int64_t(frame);
2207 double sample_start_percentage = frame - double(whole_frame);
2213 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2216 if (whole_frame < 1)
2218 if (sample_start < 0)
2222 AudioLocation location = {whole_frame, sample_start};
2225 if (previous_packet_location.frame != -1) {
2226 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2227 int64_t orig_frame = location.
frame;
2231 location.
sample_start = previous_packet_location.sample_start;
2232 location.
frame = previous_packet_location.frame;
2235 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);
2244 previous_packet_location = location;
2251std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2253 std::shared_ptr<Frame> output = working_cache.GetFrame(requested_frame);
2257 output = working_cache.GetFrame(requested_frame);
2258 if(output)
return output;
2262 output->SetPixelRatio(
info.pixel_ratio.num,
info.pixel_ratio.den);
2263 output->ChannelsLayout(
info.channel_layout);
2264 output->SampleRate(
info.sample_rate);
2266 working_cache.Add(output);
2269 if (requested_frame > largest_frame_processed)
2270 largest_frame_processed = requested_frame;
2277bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2280 bool seek_trash =
false;
2281 int64_t max_seeked_frame = seek_audio_frame_found;
2282 if (seek_video_frame_found > max_seeked_frame) {
2283 max_seeked_frame = seek_video_frame_found;
2285 if ((
info.has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2286 (
info.has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2294void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2297 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2300 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.GetFrames();
2301 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2304 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2307 std::shared_ptr<Frame> f = *working_itr;
2310 if (!f || f->number > requested_frame) {
2316 double frame_pts_seconds = (double(f->number - 1) /
info.fps.ToDouble()) + pts_offset_seconds;
2317 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2320 bool is_video_ready =
false;
2321 bool is_audio_ready =
false;
2322 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2323 if ((frame_pts_seconds <= video_pts_seconds)
2324 || (recent_pts_diff > 1.5)
2325 || packet_status.video_eof || packet_status.end_of_file) {
2328 is_video_ready =
true;
2330 "frame_number", f->number,
2331 "frame_pts_seconds", frame_pts_seconds,
2332 "video_pts_seconds", video_pts_seconds,
2333 "recent_pts_diff", recent_pts_diff);
2334 if (
info.has_video && !f->has_image_data) {
2337 for (int64_t previous_frame = requested_frame - 1; previous_frame > 0; previous_frame--) {
2338 std::shared_ptr<Frame> previous_frame_instance =
final_cache.GetFrame(previous_frame);
2339 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2341 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2346 if (last_video_frame && !f->has_image_data) {
2348 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2349 }
else if (!f->has_image_data) {
2350 f->AddColor(
"#000000");
2355 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2356 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2357 || (recent_pts_diff > 1.5)
2358 || packet_status.audio_eof || packet_status.end_of_file) {
2362 is_audio_ready =
true;
2364 "frame_number", f->number,
2365 "frame_pts_seconds", frame_pts_seconds,
2366 "audio_pts_seconds", audio_pts_seconds,
2367 "audio_pts_diff", audio_pts_diff,
2368 "recent_pts_diff", recent_pts_diff);
2370 bool is_seek_trash = IsPartialFrame(f->number);
2373 if (!
info.has_video) is_video_ready =
true;
2374 if (!
info.has_audio) is_audio_ready =
true;
2378 "frame_number", f->number,
2379 "is_video_ready", is_video_ready,
2380 "is_audio_ready", is_audio_ready,
2381 "video_eof", packet_status.video_eof,
2382 "audio_eof", packet_status.audio_eof,
2383 "end_of_file", packet_status.end_of_file);
2386 if ((!packet_status.end_of_file && is_video_ready && is_audio_ready) || packet_status.end_of_file || is_seek_trash) {
2389 "requested_frame", requested_frame,
2390 "f->number", f->number,
2391 "is_seek_trash", is_seek_trash,
2392 "Working Cache Count", working_cache.Count(),
2394 "end_of_file", packet_status.end_of_file);
2396 if (!is_seek_trash) {
2401 working_cache.Remove(f->number);
2404 last_frame = f->number;
2407 working_cache.Remove(f->number);
2414 working_frames.clear();
2415 working_frames.shrink_to_fit();
2419void FFmpegReader::CheckFPS() {
2427 int frames_per_second[3] = {0,0,0};
2428 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
2431 int all_frames_detected = 0;
2432 int starting_frames_detected = 0;
2437 if (GetNextPacket() < 0)
2442 if (packet->stream_index == videoStream) {
2444 double video_seconds = (double(GetPacketPTS()) *
info.video_timebase.ToDouble()) + pts_offset_seconds;
2445 fps_index = int(video_seconds);
2448 if (fps_index >= 0 && fps_index < max_fps_index) {
2450 starting_frames_detected++;
2451 frames_per_second[fps_index]++;
2455 all_frames_detected++;
2460 float avg_fps = 30.0;
2461 if (starting_frames_detected > 0 && fps_index > 0) {
2462 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
2466 if (avg_fps < 8.0) {
2472 info.fps = Fraction(
int(avg_fps), 1);
2475 if (all_frames_detected > 0) {
2477 info.video_length = all_frames_detected;
2478 info.duration = all_frames_detected / avg_fps;
2481 info.video_length =
info.duration * avg_fps;
2489void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
2493 av_freep(&remove_frame->data[0]);
2501void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
2506 delete remove_packet;
2521 root[
"type"] =
"FFmpegReader";
2522 root[
"path"] = path;
2523 switch (duration_strategy) {
2525 root[
"duration_strategy"] =
"VideoPreferred";
2528 root[
"duration_strategy"] =
"AudioPreferred";
2532 root[
"duration_strategy"] =
"LongestStream";
2549 catch (
const std::exception& e) {
2551 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
2562 if (!root[
"path"].isNull())
2563 path = root[
"path"].asString();
2564 if (!root[
"duration_strategy"].isNull()) {
2565 const std::string strategy = root[
"duration_strategy"].asString();
2566 if (strategy ==
"VideoPreferred") {
2568 }
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 FF_AUDIO_NUM_PROCESSORS
Header file for Timeline class.
Header file for ZeroMQ-based Logger class.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
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).
void Open() override
Open File - which is called by the constructor automatically.
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.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number).
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.
Coordinate co
This is the primary coordinate.
openshot::ReaderInfo info
Information about the current media file.
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.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
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,...
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.
bool TrimMemoryToOS(bool force) noexcept
Attempt to return unused heap memory to the operating system.
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)
bool is_near(AudioLocation location, int samples_per_frame, int64_t amount)
int width
The width of the video (in pixesl).