35#define MAX_SUPPORTED_WIDTH 1950
36#define MAX_SUPPORTED_HEIGHT 1100
39#include "libavutil/hwcontext_vaapi.h"
41typedef struct VAAPIDecodeContext {
43 VAEntrypoint va_entrypoint;
45 VAContextID va_context;
47#if FF_API_STRUCT_VAAPI_CONTEXT
50 struct vaapi_context *old_context;
51 AVBufferRef *device_ref;
55 AVHWDeviceContext *device;
56 AVVAAPIDeviceContext *hwctx;
58 AVHWFramesContext *frames;
59 AVVAAPIFramesContext *hwfc;
61 enum AVPixelFormat surface_format;
78static AVPixelFormat NormalizeDeprecatedPixFmt(AVPixelFormat pix_fmt,
bool& is_full_range) {
80 case AV_PIX_FMT_YUVJ420P:
82 return AV_PIX_FMT_YUV420P;
83 case AV_PIX_FMT_YUVJ422P:
85 return AV_PIX_FMT_YUV422P;
86 case AV_PIX_FMT_YUVJ444P:
88 return AV_PIX_FMT_YUV444P;
89 case AV_PIX_FMT_YUVJ440P:
91 return AV_PIX_FMT_YUV440P;
92#ifdef AV_PIX_FMT_YUVJ411P
93 case AV_PIX_FMT_YUVJ411P:
95 return AV_PIX_FMT_YUV411P;
106 : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), NO_PTS_OFFSET(-99999),
107 path(
path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), is_open(false),
108 seek_audio_frame_found(0), seek_video_frame_found(0),
109 last_seek_max_frame(-1), seek_stagnant_count(0),
110 is_duration_known(false), largest_frame_processed(0),
111 current_video_frame(0), packet(NULL), duration_strategy(duration_strategy),
112 audio_pts(0), video_pts(0), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
113 pStream(NULL), aStream(NULL), pFrame(NULL), previous_packet_location{-1,0},
121 pts_offset_seconds = NO_PTS_OFFSET;
122 video_pts_seconds = NO_PTS_OFFSET;
123 audio_pts_seconds = NO_PTS_OFFSET;
132 if (inspect_reader) {
154 if (abs(diff) <= amount)
165static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
167 const enum AVPixelFormat *p;
172 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
174#if defined(__linux__)
176 case AV_PIX_FMT_VAAPI:
183 case AV_PIX_FMT_VDPAU:
193 case AV_PIX_FMT_DXVA2_VLD:
200 case AV_PIX_FMT_D3D11:
208#if defined(__APPLE__)
210 case AV_PIX_FMT_VIDEOTOOLBOX:
219 case AV_PIX_FMT_CUDA:
239 return AV_PIX_FMT_NONE;
242int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
246 case AV_CODEC_ID_H264:
247 case AV_CODEC_ID_MPEG2VIDEO:
248 case AV_CODEC_ID_VC1:
249 case AV_CODEC_ID_WMV1:
250 case AV_CODEC_ID_WMV2:
251 case AV_CODEC_ID_WMV3:
266 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
272 hw_decode_failed =
false;
273 hw_decode_error_count = 0;
274 hw_decode_succeeded =
false;
279 if (avformat_open_input(&pFormatCtx,
path.c_str(), NULL, NULL) != 0)
283 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
290 packet_status.
reset(
true);
293 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
295 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
302 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
309 if (videoStream == -1 && audioStream == -1)
313 if (videoStream != -1) {
318 pStream = pFormatCtx->streams[videoStream];
324 const AVCodec *pCodec = avcodec_find_decoder(codecId);
325 AVDictionary *
opts = NULL;
326 int retry_decode_open = 2;
331 if (
hw_de_on && (retry_decode_open==2)) {
333 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
336 retry_decode_open = 0;
341 if (pCodec == NULL) {
342 throw InvalidCodec(
"A valid video codec could not be found for this file.",
path);
346 av_dict_set(&
opts,
"strict",
"experimental", 0);
350 int i_decoder_hw = 0;
352 char *adapter_ptr = NULL;
358 pCodecCtx->get_format = get_hw_dec_format;
360 if (adapter_num < 3 && adapter_num >=0) {
361#if defined(__linux__)
362 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
363 adapter_ptr = adapter;
365 switch (i_decoder_hw) {
367 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
370 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
373 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
376 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
379 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
386 switch (i_decoder_hw) {
388 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
391 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
394 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
397 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
400 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
403#elif defined(__APPLE__)
406 switch (i_decoder_hw) {
408 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
411 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
414 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
424#if defined(__linux__)
425 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
427 if( adapter_ptr != NULL ) {
428#elif defined(__APPLE__)
429 if( adapter_ptr != NULL ) {
438 hw_device_ctx = NULL;
440 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
441 const char* hw_name = av_hwdevice_get_type_name(hw_de_av_device_type);
442 std::string hw_msg =
"HW decode active: ";
443 hw_msg += (hw_name ? hw_name :
"unknown");
445 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
488 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
492 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
493 if (avcodec_return < 0) {
494 std::stringstream avcodec_error_msg;
495 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
501 AVHWFramesConstraints *constraints = NULL;
502 void *hwconfig = NULL;
503 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
507 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
508 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
511 if (pCodecCtx->coded_width < constraints->min_width ||
512 pCodecCtx->coded_height < constraints->min_height ||
513 pCodecCtx->coded_width > constraints->max_width ||
514 pCodecCtx->coded_height > constraints->max_height) {
517 retry_decode_open = 1;
520 av_buffer_unref(&hw_device_ctx);
521 hw_device_ctx = NULL;
526 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);
527 retry_decode_open = 0;
529 av_hwframe_constraints_free(&constraints);
542 if (pCodecCtx->coded_width < 0 ||
543 pCodecCtx->coded_height < 0 ||
544 pCodecCtx->coded_width > max_w ||
545 pCodecCtx->coded_height > max_h ) {
546 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);
548 retry_decode_open = 1;
551 av_buffer_unref(&hw_device_ctx);
552 hw_device_ctx = NULL;
556 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);
557 retry_decode_open = 0;
565 retry_decode_open = 0;
567 }
while (retry_decode_open);
576 if (audioStream != -1) {
581 aStream = pFormatCtx->streams[audioStream];
587 const AVCodec *aCodec = avcodec_find_decoder(codecId);
593 bool audio_opened =
false;
594 if (aCodec != NULL) {
596 AVDictionary *
opts = NULL;
597 av_dict_set(&
opts,
"strict",
"experimental", 0);
600 audio_opened = (avcodec_open2(aCodecCtx, aCodec, &
opts) >= 0);
611 const bool invalid_audio_info =
616 (aCodecCtx->sample_fmt == AV_SAMPLE_FMT_NONE);
617 if (invalid_audio_info) {
619 "FFmpegReader::Open (Disable invalid audio stream)",
624 "sample_fmt",
static_cast<int>(aCodecCtx ? aCodecCtx->sample_fmt : AV_SAMPLE_FMT_NONE));
630 if (avcodec_is_open(aCodecCtx)) {
631 avcodec_flush_buffers(aCodecCtx);
641 "FFmpegReader::Open (Audio codec unavailable; disabling audio)",
642 "audioStream", audioStream);
658 "FFmpegReader::Open (Invalid FPS detected; applying fallback)",
666 "FFmpegReader::Open (Invalid video_timebase detected; applying fallback)",
673 AVDictionaryEntry *tag = NULL;
674 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
675 QString str_key = tag->key;
676 QString str_value = tag->value;
677 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
681 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
682 AVStream* st = pFormatCtx->streams[i];
683 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
685 for (
int j = 0; j < st->nb_side_data; j++) {
686 AVPacketSideData *sd = &st->side_data[j];
689 if (sd->type == AV_PKT_DATA_DISPLAYMATRIX &&
690 sd->size >= 9 *
sizeof(int32_t) &&
693 double rotation = -av_display_rotation_get(
694 reinterpret_cast<int32_t *
>(sd->data));
695 if (std::isnan(rotation)) rotation = 0;
699 else if (sd->type == AV_PKT_DATA_SPHERICAL) {
704 const AVSphericalMapping* map =
705 reinterpret_cast<const AVSphericalMapping*
>(sd->data);
708 const char* proj_name = av_spherical_projection_name(map->projection);
714 auto to_deg = [](int32_t v){
715 return (
double)v / 65536.0;
717 info.
metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
718 info.
metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
719 info.
metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
727 previous_packet_location.
frame = -1;
761 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
767 AVPacket *recent_packet = packet;
772 int max_attempts = 128;
777 "attempts", attempts);
789 RemoveAVPacket(recent_packet);
794 if(avcodec_is_open(pCodecCtx)) {
795 avcodec_flush_buffers(pCodecCtx);
801 av_buffer_unref(&hw_device_ctx);
802 hw_device_ctx = NULL;
806 if (img_convert_ctx) {
807 sws_freeContext(img_convert_ctx);
808 img_convert_ctx =
nullptr;
810 if (pFrameRGB_cached) {
817 if(avcodec_is_open(aCodecCtx)) {
818 avcodec_flush_buffers(aCodecCtx);
830 working_cache.
Clear();
833 avformat_close_input(&pFormatCtx);
834 av_freep(&pFormatCtx);
841 largest_frame_processed = 0;
842 seek_audio_frame_found = 0;
843 seek_video_frame_found = 0;
844 current_video_frame = 0;
845 last_video_frame.reset();
846 last_final_video_frame.reset();
850bool FFmpegReader::HasAlbumArt() {
854 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
855 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
858double FFmpegReader::PickDurationSeconds()
const {
859 auto has_value = [](
double value) {
return value > 0.0; };
861 switch (duration_strategy) {
863 if (has_value(video_stream_duration_seconds))
864 return video_stream_duration_seconds;
865 if (has_value(audio_stream_duration_seconds))
866 return audio_stream_duration_seconds;
867 if (has_value(format_duration_seconds))
868 return format_duration_seconds;
871 if (has_value(audio_stream_duration_seconds))
872 return audio_stream_duration_seconds;
873 if (has_value(video_stream_duration_seconds))
874 return video_stream_duration_seconds;
875 if (has_value(format_duration_seconds))
876 return format_duration_seconds;
881 double longest = 0.0;
882 if (has_value(video_stream_duration_seconds))
883 longest = std::max(longest, video_stream_duration_seconds);
884 if (has_value(audio_stream_duration_seconds))
885 longest = std::max(longest, audio_stream_duration_seconds);
886 if (has_value(format_duration_seconds))
887 longest = std::max(longest, format_duration_seconds);
888 if (has_value(longest))
894 if (has_value(format_duration_seconds))
895 return format_duration_seconds;
896 if (has_value(inferred_duration_seconds))
897 return inferred_duration_seconds;
902void FFmpegReader::ApplyDurationStrategy() {
904 const double chosen_seconds = PickDurationSeconds();
906 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
909 is_duration_known =
false;
913 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
917 is_duration_known =
false;
922 info.
duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
923 is_duration_known =
true;
926void FFmpegReader::UpdateAudioInfo() {
927 const int codec_channels =
936 if (codec_channels > 0 &&
949 auto record_duration = [](
double &target,
double seconds) {
951 target = std::max(target, seconds);
956 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
987 if (aStream->duration > 0) {
990 if (pFormatCtx->duration > 0) {
992 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();
1033void FFmpegReader::UpdateVideoInfo() {
1039 auto record_duration = [](
double &target,
double seconds) {
1041 target = std::max(target, seconds);
1046 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
1053 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
1065 if (pStream->sample_aspect_ratio.num != 0) {
1088 if (!check_interlace) {
1089 check_interlace =
true;
1091 switch(field_order) {
1092 case AV_FIELD_PROGRESSIVE:
1105 case AV_FIELD_UNKNOWN:
1107 check_interlace =
false;
1122 if (pFormatCtx->duration >= 0) {
1124 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1134 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1135 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1137 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1141 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1142 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1143 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1147 ApplyDurationStrategy();
1153 const bool likely_still_codec =
1154 codec_id == AV_CODEC_ID_MJPEG ||
1155 codec_id == AV_CODEC_ID_PNG ||
1156 codec_id == AV_CODEC_ID_BMP ||
1157 codec_id == AV_CODEC_ID_TIFF ||
1158 codec_id == AV_CODEC_ID_WEBP ||
1159 codec_id == AV_CODEC_ID_JPEG2000;
1160 const bool likely_image_demuxer =
1161 pFormatCtx && pFormatCtx->iformat && pFormatCtx->iformat->name &&
1162 strstr(pFormatCtx->iformat->name,
"image2");
1163 const bool has_attached_pic = HasAlbumArt();
1164 const bool single_frame_stream =
1165 (pStream && pStream->nb_frames > 0 && pStream->nb_frames <= 1);
1168 const bool is_still_image_video =
1170 ((single_frame_stream || single_frame_clip) &&
1171 (likely_still_codec || likely_image_demuxer));
1173 if (is_still_image_video) {
1178 if (audioStream < 0) {
1179 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1182 ApplyDurationStrategy();
1187 AVDictionaryEntry *tag = NULL;
1188 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1189 QString str_key = tag->key;
1190 QString str_value = tag->value;
1191 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1196 return this->is_duration_known;
1200 last_seek_max_frame = -1;
1201 seek_stagnant_count = 0;
1204 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.",
path);
1207 if (requested_frame < 1)
1208 requested_frame = 1;
1213 throw InvalidFile(
"Could not detect the duration of the video or audio stream.",
path);
1228 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1241 int64_t diff = requested_frame - last_frame;
1242 if (diff >= 1 && diff <= 20) {
1244 frame = ReadStream(requested_frame);
1249 Seek(requested_frame);
1258 frame = ReadStream(requested_frame);
1266std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1268 bool check_seek =
false;
1269 int packet_error = -1;
1270 int64_t no_progress_count = 0;
1271 int64_t prev_packets_read = packet_status.
packets_read();
1274 double prev_video_pts_seconds = video_pts_seconds;
1284 CheckWorkingFrames(requested_frame);
1289 if (is_cache_found) {
1293 if (!hold_packet || !packet) {
1295 packet_error = GetNextPacket();
1296 if (packet_error < 0 && !packet) {
1307 check_seek = CheckSeek();
1319 if ((
info.
has_video && packet && packet->stream_index == videoStream) ||
1323 ProcessVideoPacket(requested_frame);
1324 if (ReopenWithoutHardwareDecode(requested_frame)) {
1329 if ((
info.
has_audio && packet && packet->stream_index == audioStream) ||
1333 ProcessAudioPacket(requested_frame);
1338 if ((!
info.
has_video && packet && packet->stream_index == videoStream) ||
1339 (!
info.
has_audio && packet && packet->stream_index == audioStream)) {
1341 if (packet->stream_index == videoStream) {
1343 }
else if (packet->stream_index == audioStream) {
1349 RemoveAVPacket(packet);
1359 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);
1372 const bool has_progress =
1376 (video_pts_seconds != prev_video_pts_seconds);
1379 no_progress_count = 0;
1381 no_progress_count++;
1382 if (no_progress_count >= 2000
1387 "requested_frame", requested_frame,
1388 "no_progress_count", no_progress_count,
1402 prev_video_pts_seconds = video_pts_seconds;
1410 "largest_frame_processed", largest_frame_processed,
1411 "Working Cache Count", working_cache.
Count());
1420 CheckWorkingFrames(requested_frame);
1436 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1439 if (!frame->has_image_data) {
1444 frame->AddAudioSilence(samples_in_frame);
1450 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1451 if (last_final_video_frame && last_final_video_frame->has_image_data
1452 && last_final_video_frame->number <= requested_frame) {
1453 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
1454 }
else if (last_video_frame && last_video_frame->has_image_data
1455 && last_video_frame->number <= requested_frame) {
1456 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
1460 f->AddAudioSilence(samples_in_frame);
1468int FFmpegReader::GetNextPacket() {
1469 int found_packet = 0;
1470 AVPacket *next_packet;
1471 next_packet =
new AVPacket();
1472 found_packet = av_read_frame(pFormatCtx, next_packet);
1476 RemoveAVPacket(packet);
1479 if (found_packet >= 0) {
1481 packet = next_packet;
1484 if (packet->stream_index == videoStream) {
1486 }
else if (packet->stream_index == audioStream) {
1495 return found_packet;
1499bool FFmpegReader::GetAVFrame() {
1500 int frameFinished = 0;
1501 auto note_hw_decode_failure = [&](
int err,
const char* stage) {
1503 if (!
hw_de_on || !hw_de_supported || force_sw_decode) {
1506 if (err == AVERROR_INVALIDDATA && packet_status.
video_decoded == 0) {
1507 hw_decode_error_count++;
1509 std::string(
"FFmpegReader::GetAVFrame (hardware decode failure candidate during ") + stage +
")",
1510 "error_count", hw_decode_error_count,
1512 if (hw_decode_error_count >= 3) {
1513 hw_decode_failed =
true;
1526 int send_packet_err = 0;
1527 int64_t send_packet_pts = 0;
1528 if ((packet && packet->stream_index == videoStream) || !packet) {
1529 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1531 if (packet && send_packet_err >= 0) {
1532 send_packet_pts = GetPacketPTS();
1533 hold_packet =
false;
1543 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1544 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);
1545 note_hw_decode_failure(send_packet_err,
"send_packet");
1546 if (send_packet_err == AVERROR(EAGAIN)) {
1548 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1550 if (send_packet_err == AVERROR(EINVAL)) {
1551 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);
1553 if (send_packet_err == AVERROR(ENOMEM)) {
1554 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);
1561 int receive_frame_err = 0;
1562 AVFrame *decoded_frame = next_frame;
1563 AVFrame *next_frame2;
1571 next_frame2 = next_frame;
1574 while (receive_frame_err >= 0) {
1575 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1577 if (receive_frame_err != 0) {
1578 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);
1579 note_hw_decode_failure(receive_frame_err,
"receive_frame");
1581 if (receive_frame_err == AVERROR_EOF) {
1583 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1584 avcodec_flush_buffers(pCodecCtx);
1587 if (receive_frame_err == AVERROR(EINVAL)) {
1589 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1590 avcodec_flush_buffers(pCodecCtx);
1592 if (receive_frame_err == AVERROR(EAGAIN)) {
1594 "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);
1596 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1598 "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);
1609 if (next_frame2->format == hw_de_av_pix_fmt) {
1610 if ((err = av_hwframe_transfer_data(next_frame, next_frame2, 0)) < 0) {
1612 "FFmpegReader::GetAVFrame (Failed to transfer data to output frame)",
1615 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_transfer");
1618 if ((err = av_frame_copy_props(next_frame, next_frame2)) < 0) {
1620 "FFmpegReader::GetAVFrame (Failed to copy props to output frame)",
1623 note_hw_decode_failure(AVERROR_INVALIDDATA,
"hwframe_copy_props");
1626 if (next_frame->format == AV_PIX_FMT_NONE) {
1627 next_frame->format = pCodecCtx->sw_pix_fmt;
1629 if (next_frame->width <= 0) {
1630 next_frame->width = next_frame2->width;
1632 if (next_frame->height <= 0) {
1633 next_frame->height = next_frame2->height;
1635 decoded_frame = next_frame;
1638 decoded_frame = next_frame2;
1644 decoded_frame = next_frame2;
1647 if (!decoded_frame->data[0]) {
1649 "FFmpegReader::GetAVFrame (Decoded frame missing image data)",
1650 "format", decoded_frame->format,
1651 "width", decoded_frame->width,
1652 "height", decoded_frame->height);
1653 note_hw_decode_failure(AVERROR_INVALIDDATA,
"decoded_frame_empty");
1660 hw_decode_error_count = 0;
1662 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
1663 hw_decode_succeeded =
true;
1669 AVPixelFormat decoded_pix_fmt = (AVPixelFormat)(decoded_frame->format);
1670 if (decoded_pix_fmt == AV_PIX_FMT_NONE)
1671 decoded_pix_fmt = (AVPixelFormat)(pStream->codecpar->format);
1675 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)decoded_frame->data, decoded_frame->linesize,
1677 pFrame->format = decoded_pix_fmt;
1680 pFrame->color_range = decoded_frame->color_range;
1681 pFrame->colorspace = decoded_frame->colorspace;
1682 pFrame->color_primaries = decoded_frame->color_primaries;
1683 pFrame->color_trc = decoded_frame->color_trc;
1684 pFrame->chroma_location = decoded_frame->chroma_location;
1690 if (decoded_frame->pts != AV_NOPTS_VALUE) {
1693 video_pts = decoded_frame->pts;
1694 }
else if (decoded_frame->pkt_dts != AV_NOPTS_VALUE) {
1696 video_pts = decoded_frame->pkt_dts;
1700 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1706 if (
hw_de_on && hw_de_supported && next_frame2 != next_frame) {
1711 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1717 if (frameFinished) {
1721 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.
width,
1730 return frameFinished;
1733bool FFmpegReader::ReopenWithoutHardwareDecode(int64_t requested_frame) {
1735 if (!hw_decode_failed || force_sw_decode) {
1740 "FFmpegReader::ReopenWithoutHardwareDecode (falling back to software decode)",
1741 "requested_frame", requested_frame,
1742 "video_packets_read", packet_status.
video_read,
1744 "hw_decode_error_count", hw_decode_error_count);
1746 force_sw_decode =
true;
1747 hw_decode_failed =
false;
1748 hw_decode_error_count = 0;
1752 Seek(requested_frame);
1755 (void) requested_frame;
1762 return hw_decode_succeeded;
1769bool FFmpegReader::CheckSeek() {
1772 const int64_t kSeekRetryMax = 5;
1773 const int kSeekStagnantMax = 2;
1777 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1785 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1787 if (max_seeked_frame == last_seek_max_frame) {
1788 seek_stagnant_count++;
1790 last_seek_max_frame = max_seeked_frame;
1791 seek_stagnant_count = 0;
1795 if (max_seeked_frame >= seeking_frame) {
1798 "is_video_seek", is_video_seek,
1799 "max_seeked_frame", max_seeked_frame,
1800 "seeking_frame", seeking_frame,
1801 "seeking_pts", seeking_pts,
1802 "seek_video_frame_found", seek_video_frame_found,
1803 "seek_audio_frame_found", seek_audio_frame_found);
1806 if (seek_count < kSeekRetryMax) {
1807 Seek(seeking_frame - (10 * seek_count * seek_count));
1808 }
else if (seek_stagnant_count >= kSeekStagnantMax) {
1810 Seek(seeking_frame - (10 * kSeekRetryMax * kSeekRetryMax));
1813 Seek(seeking_frame - (10 * seek_count * seek_count));
1818 "is_video_seek", is_video_seek,
1819 "packet->pts", GetPacketPTS(),
1820 "seeking_pts", seeking_pts,
1821 "seeking_frame", seeking_frame,
1822 "seek_video_frame_found", seek_video_frame_found,
1823 "seek_audio_frame_found", seek_audio_frame_found);
1837void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1840 int frame_finished = GetAVFrame();
1843 if (!frame_finished) {
1846 RemoveAVFrame(pFrame);
1852 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1855 if (!seek_video_frame_found && is_seeking)
1856 seek_video_frame_found = current_frame;
1862 working_cache.
Add(CreateFrame(requested_frame));
1868 AVPixelFormat decoded_pix_fmt = (pFrame && pFrame->format != AV_PIX_FMT_NONE)
1869 ?
static_cast<AVPixelFormat
>(pFrame->format)
1871 bool src_full_range = (pFrame && pFrame->color_range == AVCOL_RANGE_JPEG);
1872 AVPixelFormat src_pix_fmt = NormalizeDeprecatedPixFmt(decoded_pix_fmt, src_full_range);
1873 int src_width = (pFrame && pFrame->width > 0) ? pFrame->width :
info.width;
1874 int src_height = (pFrame && pFrame->height > 0) ? pFrame->height :
info.height;
1875 int height = src_height;
1876 int width = src_width;
1880 AVFrame *pFrameRGB = pFrameRGB_cached;
1883 if (pFrameRGB ==
nullptr)
1885 pFrameRGB_cached = pFrameRGB;
1888 uint8_t *buffer =
nullptr;
1909 max_width = std::max(
float(max_width), max_width * max_scale_x);
1910 max_height = std::max(
float(max_height), max_height * max_scale_y);
1916 QSize width_size(max_width * max_scale_x,
1919 max_height * max_scale_y);
1921 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1922 max_width = std::max(max_width, width_size.width());
1923 max_height = std::max(max_height, width_size.height());
1925 max_width = std::max(max_width, height_size.width());
1926 max_height = std::max(max_height, height_size.height());
1933 float preview_ratio = 1.0;
1940 max_width =
info.
width * max_scale_x * preview_ratio;
1941 max_height =
info.
height * max_scale_y * preview_ratio;
1949 int original_height = src_height;
1950 if (max_width != 0 && max_height != 0 && max_width < width && max_height < height) {
1952 float ratio = float(width) / float(height);
1953 int possible_width = round(max_height * ratio);
1954 int possible_height = round(max_width / ratio);
1956 if (possible_width <= max_width) {
1958 width = possible_width;
1959 height = max_height;
1963 height = possible_height;
1968 const int bytes_per_pixel = 4;
1969 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
1972 constexpr size_t ALIGNMENT = 32;
1973 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
1974 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
1979 int scale_mode = SWS_FAST_BILINEAR;
1981 scale_mode = SWS_BICUBIC;
1983 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);
1984 if (!img_convert_ctx)
1986 const int *src_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
1987 const int *dst_coeff = sws_getCoefficients(SWS_CS_DEFAULT);
1988 const int dst_full_range = 1;
1989 sws_setColorspaceDetails(img_convert_ctx, src_coeff, src_full_range ? 1 : 0,
1990 dst_coeff, dst_full_range, 0, 1 << 16, 1 << 16);
1992 if (!pFrame || !pFrame->data[0] || pFrame->linesize[0] <= 0) {
1994 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
1995 hw_decode_failed =
true;
1997 "FFmpegReader::ProcessVideoPacket (Invalid source frame; forcing software fallback)",
1998 "requested_frame", requested_frame,
1999 "current_frame", current_frame,
2000 "src_pix_fmt", src_pix_fmt,
2001 "src_width", src_width,
2002 "src_height", src_height);
2006 RemoveAVFrame(pFrame);
2013 const int scaled_lines = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
2014 original_height, pFrameRGB->data, pFrameRGB->linesize);
2015 if (scaled_lines <= 0) {
2017 if (
hw_de_on && hw_de_supported && !force_sw_decode) {
2018 hw_decode_failed =
true;
2020 "FFmpegReader::ProcessVideoPacket (sws_scale failed; forcing software fallback)",
2021 "requested_frame", requested_frame,
2022 "current_frame", current_frame,
2023 "scaled_lines", scaled_lines,
2024 "src_pix_fmt", src_pix_fmt,
2025 "src_width", src_width,
2026 "src_height", src_height);
2031 RemoveAVFrame(pFrame);
2037 std::shared_ptr<Frame> f = CreateFrame(current_frame);
2040 if (!ffmpeg_has_alpha(src_pix_fmt)) {
2042 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
2045 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
2049 working_cache.
Add(f);
2052 last_video_frame = f;
2058 RemoveAVFrame(pFrame);
2064 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
2068void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
2071 if (packet && packet->pts != AV_NOPTS_VALUE) {
2073 location = GetAudioPTSLocation(packet->pts);
2076 if (!seek_audio_frame_found && is_seeking)
2077 seek_audio_frame_found = location.
frame;
2084 working_cache.
Add(CreateFrame(requested_frame));
2088 "requested_frame", requested_frame,
2089 "target_frame", location.
frame,
2093 int frame_finished = 0;
2097 int packet_samples = 0;
2101 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
2102 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
2106 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
2107 if (receive_frame_err >= 0) {
2110 if (receive_frame_err == AVERROR_EOF) {
2114 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
2116 avcodec_flush_buffers(aCodecCtx);
2118 if (receive_frame_err != 0) {
2123 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
2126 if (frame_finished) {
2132 audio_pts = audio_frame->pts;
2135 location = GetAudioPTSLocation(audio_pts);
2138 int plane_size = -1;
2144 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
2148 packet_samples = audio_frame->nb_samples * nb_channels;
2157 int pts_remaining_samples = packet_samples /
info.
channels;
2160 if (pts_remaining_samples == 0) {
2162 "packet_samples", packet_samples,
2164 "pts_remaining_samples", pts_remaining_samples);
2168 while (pts_remaining_samples) {
2173 int samples = samples_per_frame - previous_packet_location.
sample_start;
2174 if (samples > pts_remaining_samples)
2175 samples = pts_remaining_samples;
2178 pts_remaining_samples -= samples;
2180 if (pts_remaining_samples > 0) {
2182 previous_packet_location.
frame++;
2191 "packet_samples", packet_samples,
2199 audio_converted->nb_samples = audio_frame->nb_samples;
2200 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
2216 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
2225 audio_converted->data,
2226 audio_converted->linesize[0],
2227 audio_converted->nb_samples,
2229 audio_frame->linesize[0],
2230 audio_frame->nb_samples);
2233 int64_t starting_frame_number = -1;
2234 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++) {
2236 starting_frame_number = location.
frame;
2237 int channel_buffer_size = nb_samples;
2238 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
2242 int remaining_samples = channel_buffer_size;
2243 while (remaining_samples > 0) {
2248 int samples = std::fmin(samples_per_frame - start, remaining_samples);
2251 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
2254 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
2258 "frame", starting_frame_number,
2261 "channel", channel_filter,
2262 "samples_per_frame", samples_per_frame);
2265 working_cache.
Add(f);
2268 remaining_samples -= samples;
2271 if (remaining_samples > 0)
2272 channel_buffer += samples;
2275 starting_frame_number++;
2283 av_free(audio_converted->data[0]);
2292 "requested_frame", requested_frame,
2293 "starting_frame", location.
frame,
2294 "end_frame", starting_frame_number - 1,
2295 "audio_pts_seconds", audio_pts_seconds);
2301void FFmpegReader::Seek(int64_t requested_frame) {
2303 if (requested_frame < 1)
2304 requested_frame = 1;
2307 if (requested_frame > largest_frame_processed && packet_status.
end_of_file) {
2314 "requested_frame", requested_frame,
2315 "seek_count", seek_count,
2316 "last_frame", last_frame);
2319 working_cache.
Clear();
2323 video_pts_seconds = NO_PTS_OFFSET;
2325 audio_pts_seconds = NO_PTS_OFFSET;
2326 hold_packet =
false;
2328 current_video_frame = 0;
2329 largest_frame_processed = 0;
2330 last_final_video_frame.reset();
2335 packet_status.
reset(
false);
2341 int buffer_amount = 12;
2342 if (requested_frame - buffer_amount < 20) {
2356 if (seek_count == 1) {
2359 seeking_pts = ConvertFrameToVideoPTS(1);
2361 seek_audio_frame_found = 0;
2362 seek_video_frame_found = 0;
2366 bool seek_worked =
false;
2367 int64_t seek_target = 0;
2371 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
2376 is_video_seek =
true;
2383 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
2388 is_video_seek =
false;
2397 avcodec_flush_buffers(aCodecCtx);
2401 avcodec_flush_buffers(pCodecCtx);
2404 previous_packet_location.
frame = -1;
2409 if (seek_count == 1) {
2411 seeking_pts = seek_target;
2412 seeking_frame = requested_frame;
2414 seek_audio_frame_found = 0;
2415 seek_video_frame_found = 0;
2443int64_t FFmpegReader::GetPacketPTS() {
2445 int64_t current_pts = packet->pts;
2446 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2447 current_pts = packet->dts;
2453 return AV_NOPTS_VALUE;
2458void FFmpegReader::UpdatePTSOffset() {
2459 if (pts_offset_seconds != NO_PTS_OFFSET) {
2463 pts_offset_seconds = 0.0;
2464 double video_pts_offset_seconds = 0.0;
2465 double audio_pts_offset_seconds = 0.0;
2467 bool has_video_pts =
false;
2470 has_video_pts =
true;
2472 bool has_audio_pts =
false;
2475 has_audio_pts =
true;
2479 while (!has_video_pts || !has_audio_pts) {
2481 if (GetNextPacket() < 0)
2486 int64_t pts = GetPacketPTS();
2489 if (!has_video_pts && packet->stream_index == videoStream) {
2495 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2496 has_video_pts =
true;
2499 else if (!has_audio_pts && packet->stream_index == audioStream) {
2505 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2506 has_audio_pts =
true;
2516 pts_offset_seconds = video_pts_offset_seconds;
2518 pts_offset_seconds = audio_pts_offset_seconds;
2519 }
else if (has_video_pts && has_audio_pts) {
2521 pts_offset_seconds = video_pts_offset_seconds;
2526int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2528 int64_t previous_video_frame = current_video_frame;
2530 const double video_timebase_value =
2536 double video_seconds = (double(pts) * video_timebase_value) + pts_offset_seconds;
2539 int64_t frame = round(video_seconds * fps_value) + 1;
2542 if (current_video_frame == 0)
2543 current_video_frame = frame;
2547 if (frame == previous_video_frame) {
2552 current_video_frame++;
2561int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2563 const double video_timebase_value =
2569 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2572 int64_t video_pts = round(seconds / video_timebase_value);
2579int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2581 const double audio_timebase_value =
2587 double seconds = (double(frame_number - 1) / fps_value) + pts_offset_seconds;
2590 int64_t audio_pts = round(seconds / audio_timebase_value);
2597AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2598 const double audio_timebase_value =
2605 double audio_seconds = (double(pts) * audio_timebase_value) + pts_offset_seconds;
2608 double frame = (audio_seconds * fps_value) + 1;
2611 int64_t whole_frame = int64_t(frame);
2614 double sample_start_percentage = frame - double(whole_frame);
2620 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2623 if (whole_frame < 1)
2625 if (sample_start < 0)
2632 if (previous_packet_location.
frame != -1) {
2633 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2634 int64_t orig_frame = location.
frame;
2639 location.
frame = previous_packet_location.
frame;
2642 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);
2651 previous_packet_location = location;
2658std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2660 std::shared_ptr<Frame> output = working_cache.
GetFrame(requested_frame);
2664 output = working_cache.
GetFrame(requested_frame);
2665 if(output)
return output;
2673 working_cache.
Add(output);
2676 if (requested_frame > largest_frame_processed)
2677 largest_frame_processed = requested_frame;
2684bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2687 bool seek_trash =
false;
2688 int64_t max_seeked_frame = seek_audio_frame_found;
2689 if (seek_video_frame_found > max_seeked_frame) {
2690 max_seeked_frame = seek_video_frame_found;
2692 if ((
info.
has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2693 (
info.
has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2701void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2704 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2707 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.
GetFrames();
2708 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2711 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2714 std::shared_ptr<Frame> f = *working_itr;
2717 if (!f || f->number > requested_frame) {
2723 double frame_pts_seconds = (double(f->number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2724 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2727 bool is_video_ready =
false;
2728 bool is_audio_ready =
false;
2729 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2730 if ((frame_pts_seconds <= video_pts_seconds)
2731 || (recent_pts_diff > 1.5)
2735 is_video_ready =
true;
2737 "frame_number", f->number,
2738 "frame_pts_seconds", frame_pts_seconds,
2739 "video_pts_seconds", video_pts_seconds,
2740 "recent_pts_diff", recent_pts_diff);
2746 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2747 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2751 if (!f->has_image_data
2752 && last_final_video_frame
2753 && last_final_video_frame->has_image_data
2754 && last_final_video_frame->number <= f->number) {
2755 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2759 if (!f->has_image_data
2761 && last_video_frame->has_image_data
2762 && last_video_frame->number <= f->number) {
2763 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2767 if (!f->has_image_data) {
2769 "FFmpegReader::CheckWorkingFrames (no previous image found; using black frame)",
2770 "frame_number", f->number);
2771 f->AddColor(
"#000000");
2776 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2777 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2778 || (recent_pts_diff > 1.5)
2783 is_audio_ready =
true;
2785 "frame_number", f->number,
2786 "frame_pts_seconds", frame_pts_seconds,
2787 "audio_pts_seconds", audio_pts_seconds,
2788 "audio_pts_diff", audio_pts_diff,
2789 "recent_pts_diff", recent_pts_diff);
2791 bool is_seek_trash = IsPartialFrame(f->number);
2799 "frame_number", f->number,
2800 "is_video_ready", is_video_ready,
2801 "is_audio_ready", is_audio_ready,
2808 && !packet_status.
end_of_file && !is_seek_trash) {
2813 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2814 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2816 if (!f->has_image_data
2817 && last_final_video_frame
2818 && last_final_video_frame->has_image_data
2819 && last_final_video_frame->number <= f->number) {
2820 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2822 if (!f->has_image_data
2824 && last_video_frame->has_image_data
2825 && last_video_frame->number <= f->number) {
2826 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2833 if (!f->has_image_data && is_video_ready && is_audio_ready) {
2835 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2836 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2838 if (!f->has_image_data
2839 && last_final_video_frame
2840 && last_final_video_frame->has_image_data
2841 && last_final_video_frame->number <= f->number) {
2842 f->AddImage(std::make_shared<QImage>(last_final_video_frame->GetImage()->copy()));
2844 if (!f->has_image_data
2846 && last_video_frame->has_image_data
2847 && last_video_frame->number <= f->number) {
2848 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2854 if (!f->has_image_data) {
2858 if ((!packet_status.
end_of_file && is_video_ready && is_audio_ready) || packet_status.
end_of_file || is_seek_trash) {
2861 "requested_frame", requested_frame,
2862 "f->number", f->number,
2863 "is_seek_trash", is_seek_trash,
2864 "Working Cache Count", working_cache.
Count(),
2868 if (!is_seek_trash) {
2871 if (f->has_image_data) {
2872 last_final_video_frame = f;
2876 working_cache.
Remove(f->number);
2879 last_frame = f->number;
2882 working_cache.
Remove(f->number);
2889 working_frames.clear();
2890 working_frames.shrink_to_fit();
2894void FFmpegReader::CheckFPS() {
2902 int frames_per_second[3] = {0,0,0};
2903 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
2906 int all_frames_detected = 0;
2907 int starting_frames_detected = 0;
2912 if (GetNextPacket() < 0)
2917 if (packet->stream_index == videoStream) {
2920 fps_index = int(video_seconds);
2923 if (fps_index >= 0 && fps_index < max_fps_index) {
2925 starting_frames_detected++;
2926 frames_per_second[fps_index]++;
2930 all_frames_detected++;
2935 float avg_fps = 30.0;
2936 if (starting_frames_detected > 0 && fps_index > 0) {
2937 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
2941 if (avg_fps < 8.0) {
2950 if (all_frames_detected > 0) {
2964void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
2968 av_freep(&remove_frame->data[0]);
2976void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
2981 delete remove_packet;
2996 root[
"type"] =
"FFmpegReader";
2997 root[
"path"] =
path;
2998 switch (duration_strategy) {
3000 root[
"duration_strategy"] =
"VideoPreferred";
3003 root[
"duration_strategy"] =
"AudioPreferred";
3007 root[
"duration_strategy"] =
"LongestStream";
3024 catch (
const std::exception& e) {
3026 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
3037 if (!root[
"path"].isNull())
3038 path = root[
"path"].asString();
3039 if (!root[
"duration_strategy"].isNull()) {
3040 const std::string strategy = root[
"duration_strategy"].asString();
3041 if (strategy ==
"VideoPreferred") {
3043 }
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 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.
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,...
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)