// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Containers/Array.h" #include "Templates/SharedPointer.h" #include "MediaSampleSink.h" /** * Collection of media sample sinks. * * @param SampleType The type of media samples that the sinks process. */ template> class TMediaSampleSinks { public: /** * Add the given media sample sink to the collection. * * @param SampleSink The sink to add. * @see Num, Remove */ void Add(const TSharedRef& SampleSink, UMediaPlayer* Player = nullptr) { if (Sinks.Find(SampleSink) != INDEX_NONE) { return; } Sinks.Add(SampleSink); FMediaSampleSinkEventData Data; Data.Attached = { Player }; SampleSink->ReceiveEvent(EMediaSampleSinkEvent::Attached, Data); } /** * Enqueue the given media samples to the registered sinks. * * This method will also remove expired sinks that haven't been removed yet. * * @param Sample The media sample to enqueue. * @param MaxQueueDepth The maximum depth of the sink queues before overflow. * @return true if the sample was enqueued to all sinks, false if one or more sinks overflowed. * @see Flush */ bool Enqueue(const TSharedRef& Sample) { bool Overflowed = false; for (int32 SinkIndex = Sinks.Num() - 1; SinkIndex >= 0; --SinkIndex) { TSharedPtr Sink = Sinks[SinkIndex].Pin(); if (Sink.IsValid()) { if (!Sink->CanAcceptSamples(1)) { Overflowed = true; } else { Sink->Enqueue(Sample); FMediaSampleSinkEventData Data; Sink->ReceiveEvent(EMediaSampleSinkEvent::SampleDataUpdate, Data); } } else { Sinks.RemoveAt(SinkIndex); } } return !Overflowed; } /** * Flush all registered sinks. * * This method will also remove expired sinks that haven't been removed yet. * * @see Enqueue */ void Flush(UMediaPlayer* MediaPlayer) { for (int32 SinkIndex = Sinks.Num() - 1; SinkIndex >= 0; --SinkIndex) { TSharedPtr Sink = Sinks[SinkIndex].Pin(); if (Sink.IsValid()) { Sink->RequestFlush(); FMediaSampleSinkEventData Data; Data.FlushWasRequested = { MediaPlayer }; Sink->ReceiveEvent(EMediaSampleSinkEvent::FlushWasRequested, Data); } else { Sinks.RemoveAt(SinkIndex); } } } /** * Get the number of sinks in this collection. * * @return Number of sinks. * @see Add, Remove */ int32 Num() const { return Sinks.Num(); } /** * Check if any sinks are inserted * * @return True if empty, false otherwise */ bool IsEmpty() const { return Sinks.IsEmpty(); } /** * Remove the given media sample sink from the collection. * * @param SampleSink The sink to remove. * @see Add, Num */ void Remove(const TSharedRef& SampleSink, UMediaPlayer* Player = nullptr) { if (Sinks.RemoveSingle(SampleSink)) { FMediaSampleSinkEventData Data; Data.Detached = { Player }; SampleSink->ReceiveEvent(EMediaSampleSinkEvent::Detached, Data); } } /** * Remove any invalid sinks */ void Cleanup() { for (int32 SinkIndex = Sinks.Num() - 1; SinkIndex >= 0; --SinkIndex) { if (!Sinks[SinkIndex].IsValid()) { Sinks.RemoveAt(SinkIndex); } } } /** * Receive event and broadcast to sinks */ void ReceiveEvent(EMediaSampleSinkEvent Event, const FMediaSampleSinkEventData& Data) { for (int32 SinkIndex = Sinks.Num() - 1; SinkIndex >= 0; --SinkIndex) { TSharedPtr Sink = Sinks[SinkIndex].Pin(); if (Sink.IsValid()) { Sink->ReceiveEvent(Event, Data); } else { Sinks.RemoveAt(SinkIndex); } } } protected: /** The collection of registered sinks. */ TArray> Sinks; }; typedef TMediaSampleSinks FMediaVideoSampleSinks; class FMediaAudioSampleSinks : public TMediaSampleSinks { public: /** * Get primary audio sink and cleanup any invalid sinks */ TSharedPtr GetPrimaryAudioSink() { Cleanup(); if (Sinks.Num() == 0) { return TSharedPtr(); } return Sinks[0].Pin(); } }; typedef TMediaSampleSinks FMediaOverlaySampleSinks; typedef TMediaSampleSinks FMediaBinarySampleSinks;