// ------------------------------------------------ // Copyright Joe Marshall 2024- All Rights Reserved // ------------------------------------------------ // // An implementation of IMediaSamples for the new // v2 video timing in Unreal media player. // // With new timing, whilst in theory IMediaSamples // is a queue, in use only the most recent sample // is ever read. // // Given we know that, this class dumps any // previous samples immediately, which allows // the underlying hardware image to be released as // soon as possible, which makes video decoding // work more efficiently. // ------------------------------------------------ #pragma once #include "AndroidVulkanTextureSample.h" #include "IMediaSamples.h" #include "UnrealLogging.h" // for v2 video timing, the sample queue just // holds a single image class FVideoMediaSampleHolder : public IMediaSamples { public: void AddVideo(TSharedRef &InSample) { if (VideoSample.IsValid()) { // force sample texture clear because // we're not going to display it VideoSample->Clear(); UE_LOGFMT(LogDirectVideo, VeryVerbose, "Clearing videoframe {0}", VideoSample.GetSharedReferenceCount()); } VideoSample = InSample; UE_LOGFMT(LogDirectVideo, VeryVerbose, "CSet video frame {0}", VideoSample.IsValid()); } virtual bool FetchVideo( TRange TimeRange, TSharedPtr &OutSample) override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "BFetch video frame {0}", VideoSample.IsValid()); if (VideoSample.IsValid()) { OutSample = VideoSample; VideoSample.Reset(); return true; } return false; // override in child classes, if supported } virtual bool FetchVideo( TRange TimeRange, TSharedPtr &OutSample) override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "AFetch video frame {0}", VideoSample.IsValid()); if (VideoSample.IsValid()) { OutSample = VideoSample; VideoSample.Reset(); return true; } return false; // override in child classes, if supported } virtual EFetchBestSampleResult FetchBestVideoSampleForTimeRange( const TRange &TimeRange, TSharedPtr &OutSample, bool bReverse) { UE_LOGFMT(LogDirectVideo, VeryVerbose, "Best sample {0}", VideoSample.IsValid()); if (VideoSample.IsValid()) { OutSample = VideoSample; VideoSample.Reset(); return EFetchBestSampleResult::Ok; } else { return EFetchBestSampleResult::NoSample; } } virtual void FlushSamples() override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "Flush samples {0}", VideoSample.IsValid()); // override in child classes, if supported VideoSample.Reset(); FlushCount += 1; } virtual uint32 GetFlushCount() { return FlushCount; } virtual bool PeekVideoSampleTime(FMediaTimeStamp &TimeStamp) override { if (VideoSample.IsValid()) { TimeStamp = VideoSample->GetTime(); UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp {0}", TimeStamp.Time.GetTotalSeconds()); return true; } else { // UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp returning zero"); // TimeStamp = // FMediaTimeStamp(FTimespan(0LL),FMediaTimeStamp::MakeSequenceIndex(0, 0)); // return true; UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp returning false"); return false; } } virtual int32 NumVideoSamples() const override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "Query num samples {0}", VideoSample.IsValid()); if (VideoSample.IsValid()) { return 1; } else { return 0; } } bool DiscardVideoSamples(const TRange &TimeRange, bool bReverse) override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "Discard video samples {0}", VideoSample.IsValid()); return false; } virtual uint32 PurgeOutdatedVideoSamples(const FMediaTimeStamp &ReferenceTime, bool bReversed, FTimespan MaxAge) override { UE_LOGFMT(LogDirectVideo, VeryVerbose, "PurgeOutdatedVideoSamples {0} {1}", VideoSample.IsValid(), ReferenceTime.Time.GetTicks() * 100); return 0; }; TSharedPtr VideoSample; uint32 FlushCount = 0; };