From d338e0b650e53482933aa8ee0167ab3a538349a5 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 17 Dec 2019 11:18:36 +0900 Subject: [PATCH 1/2] Fix crashes and failures on decoding some types of album art images --- include/FFmpegReader.h | 3 +++ src/FFmpegReader.cpp | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index ec082965..76d3243a 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -195,6 +195,9 @@ namespace openshot { /// Get the PTS for the current video packet int64_t GetVideoPTS(); + /// Check if there's an album art + bool HasAlbumArt(); + /// Remove partial frames due to seek bool IsPartialFrame(int64_t requested_frame); diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index c8ce141f..21b007cb 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -435,6 +435,14 @@ void FFmpegReader::Open() { } #endif // HAVE_HW_ACCEL + // Disable per-frame threading for album arts + // Using FF_THREAD_FRAME adds one frame decoding delay per thread, + // but there's only one frame in this case. + if (HasAlbumArt()) + { + pCodecCtx->thread_type &= ~FF_THREAD_FRAME; + } + // Open video codec if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0) throw InvalidCodec("A video codec was found, but could not be opened.", path); @@ -641,6 +649,11 @@ void FFmpegReader::Close() { } } +bool FFmpegReader::HasAlbumArt() { + return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream] + && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC); +} + void FFmpegReader::UpdateAudioInfo() { // Set values of FileInfo struct info.has_audio = true; @@ -1779,8 +1792,8 @@ void FFmpegReader::Seek(int64_t requested_frame) { bool seek_worked = false; int64_t seek_target = 0; - // Seek video stream (if any) - if (!seek_worked && info.has_video) { + // Seek video stream (if any), except album arts + if (!seek_worked && info.has_video && !HasAlbumArt()) { seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount); if (av_seek_frame(pFormatCtx, info.video_stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0) { fprintf(stderr, "%s: error while seeking video stream\n", pFormatCtx->AV_FILENAME); @@ -2089,14 +2102,12 @@ bool FFmpegReader::CheckMissingFrame(int64_t requested_frame) { // Special MP3 Handling (ignore more than 1 video frame) if (info.has_audio and info.has_video) { - AVCodecID aCodecId = AV_FIND_DECODER_CODEC_ID(aStream); - AVCodecID vCodecId = AV_FIND_DECODER_CODEC_ID(pStream); // If MP3 with single video frame, handle this special case by copying the previously // decoded image to the new frame. Otherwise, it will spend a huge amount of // CPU time looking for missing images for all the audio-only frames. if (checked_frames[requested_frame] > 8 && !missing_video_frames.count(requested_frame) && !processing_audio_frames.count(requested_frame) && processed_audio_frames.count(requested_frame) && - last_frame && last_video_frame && last_video_frame->has_image_data && aCodecId == AV_CODEC_ID_MP3 && (vCodecId == AV_CODEC_ID_MJPEGB || vCodecId == AV_CODEC_ID_MJPEG)) { + last_video_frame && last_video_frame->has_image_data && HasAlbumArt()) { missing_video_frames.insert(std::pair(requested_frame, last_video_frame->number)); missing_video_frames_source.insert(std::pair(last_video_frame->number, requested_frame)); missing_frames.Add(last_video_frame); From c0db2609e8eaafdfce465011be32d7b47030923f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 20 Oct 2020 11:01:42 +0900 Subject: [PATCH 2/2] Add a comment explaining FFmpegReader::HasAlbumArt()'s behavior --- src/FFmpegReader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index f8af2ba1..eb9debfa 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -637,6 +637,9 @@ void FFmpegReader::Close() { } bool FFmpegReader::HasAlbumArt() { + // Check if the video stream we use is an attached picture + // This won't return true if the file has a cover image as a secondary stream + // like an MKV file with an attached image file return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream] && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC); }