UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
MediaSampleQueue.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Queue.h"
7#include "IMediaSamples.h"
9#include "Math/Interval.h"
10#include "Misc/App.h"
11#include "Misc/ScopeLock.h"
12#include "Misc/Timespan.h"
14
15#include "MediaSampleSink.h"
16#include "MediaSampleSource.h"
17
18#include "IMediaTimeSource.h"
19#include "IMediaAudioSample.h"
20#include "IMediaTextureSample.h"
21#include "IMediaBinarySample.h"
22#include "IMediaOverlaySample.h"
23
24
31
35template<typename SampleType, typename SinkType=TMediaSampleSink<SampleType>>
37 : public SinkType
38 , public TMediaSampleSource<SampleType>
39{
40public:
41
45
47 virtual ~TMediaSampleQueue() { }
48
49public:
50
61 int32 Num() const
62 {
63 return Samples.Num();
64 }
65
75 {
78 {
80 }
81 return n;
82 }
83
84public:
85
86 //~ TMediaSampleSource interface (to be called only from consumer thread)
87
89 {
91
92 if (Samples.Num() == 0)
93 {
94 return false; // empty queue
95 }
96
98
99 if (!Sample.IsValid())
100 {
101 return false; // pending flush
102 }
103
104 Samples.RemoveAt(0);
105
106 OutSample = Sample;
107
108 return true;
109 }
110
112 {
114
115 if (Samples.Num() == 0)
116 {
117 return false; // empty queue
118 }
119
121
122 if (!Sample.IsValid())
123 {
124 return false; // pending flush
125 }
126
127 OutSample = Sample;
128
129 return true;
130 }
131
133 {
135 for(int32 i=0,iMax=Samples.Num(); i<iMax; ++i)
136 {
137 if (Samples[i].IsValid())
138 {
139 OutSampleTimeRanges.Emplace(TRange<FMediaTimeStamp>(Samples[i]->GetTime(), Samples[i]->GetTime() + Samples[i]->GetDuration()));
140 }
141 }
142 }
143
144 virtual bool Pop() override
145 {
147
148 if (Samples.Num() == 0)
149 {
150 return false; // empty queue
151 }
152
153 if (!Samples[0].IsValid())
154 {
155 return false; // pending flush
156 }
157
158 Samples.RemoveAt(0);
159
160 return true;
161 }
162
163 bool Discard(const TRange<FMediaTimeStamp>& TimeRange, bool bReverse)
164 {
165 // Code below assumes a fully specified range, no open bounds!
166 check(TimeRange.HasLowerBound() && TimeRange.HasUpperBound());
167
169
172
173 // Found anything?
174 if (FirstPossibleIndex >= 0)
175 {
177
178 // Remove all samples indicated...
182 return true;
183 }
184 return false;
185 }
186
188 {
189 // Notes:
190 // - Reverse playback still works with increasing indices in the queue. PTS values will be going down in it, rather than up,
191 // but the order of indices is still identical.
192 // - The code below must be able to deal with time ranges that span loop points (different secondary sequence indices)
193
194 // Code below assumes a fully specified range, no open bounds!
195 check(TimeRange.HasLowerBound() && TimeRange.HasUpperBound());
196
197 OutSample.Reset();
198
200
203
204 // Found anything?
205 if (FirstPossibleIndex >= 0)
206 {
208 {
209 //
210 // Return the latest sample with the most "coverage" for the given time range that can be fetched
211 // (this naturally depends on how many samples or available and hence timing - the result is not consistent between instances or repeat runs)
212 //
214 {
215 // More then one sample. Find the one that fits the bill, best...
216 // (we look for the one with the largest overlap & newest time)
219 for (int32 Idx = FirstPossibleIndex; Idx <= LastPossibleIndex; ++Idx)
220 {
222
223 // Check once more if this sample is actually overlapping as we may get non-monotonically increasing data...
224 TRange<FMediaTimeStamp> SampleTimeRange = TRange<FMediaTimeStamp>(Sample->GetTime(), Sample->GetTime() + Sample->GetDuration());
225
226 if (TimeRange.Overlaps(SampleTimeRange))
227 {
228 // Ok. This one is real, see if it is a better fit than the last one...
230
231 FMediaTimeStamp SampleDuration(SampleInRangeRange.Size<FMediaTimeStamp>());
232 if (SampleDuration >= BestDuration)
233 {
234 BestDuration = SampleDuration;
235 BestIndex = Idx;
236 }
237 }
238 }
239
241
242 // Found the best. Return it & delete all candidate samples up and including it from the queue
247 }
248 else
249 {
250 // Single sample found: we just take it!
251 OutSample = Samples[FirstPossibleIndex]; //-V781 PVS-Studio triggers incorrectly here: Variable checked after being used (likely a template code issue, but harmless)
253 }
254 }
255 else
256 {
257 //
258 // Return the first sample with maximum possible coverage given the time range or nothing if the sample is not yet in the queue
259 // (this yields reproducible results between instances as far as the selection of frames is concerned if the passed in ranges are identical in each run / instance)
260 //
261
262 for (int32 Idx = FirstPossibleIndex; Idx <= LastPossibleIndex; ++Idx)
263 {
265
266 TRange<FMediaTimeStamp> SampleTimeRange = TRange<FMediaTimeStamp>(Sample->GetTime(), Sample->GetTime() + Sample->GetDuration());
267
268 check(TimeRange.Overlaps(SampleTimeRange));
270
271 FMediaTimeStamp SampleDuration(SampleInRangeRange.Size<FMediaTimeStamp>());
272
273 // Do we either have full coverage or no "better" sample could be after this one and still in range?
274 if (!bReverse ? (SampleDuration.Time >= Sample->GetDuration() || (TimeRange.GetUpperBoundValue() - (Sample->GetTime() + Sample->GetDuration())) < SampleDuration)
275 : (SampleDuration.Time >= Sample->GetDuration() || (Sample->GetTime() - TimeRange.GetLowerBoundValue()) < SampleDuration))
276 {
277 // Yes, so we return this one and remove all candidates and this one from the queue
278 OutSample = Sample;
282 break;
283 }
284 }
285 }
286 }
287
288 // Any frames considered outdated?
289 if (NumOldSamplesAtBegin != 0)
290 {
291 // Cleanup samples that are now considered outdated...
294 }
295
296 // Return true if we got a sample...
298 }
299
300
302 {
304
305 int32 Num = Samples.Num();
306 if (Num > 0)
307 {
308 // All samples at or beyond the reference time are good to stay
309 int32 Idx;
310 if (!bReversed)
311 {
312 for (Idx = Num - 1; Idx >= 0; --Idx)
313 {
314 if (Samples[Idx]->GetTime() < ReferenceTime)
315 {
316 break;
317 }
318 }
319 }
320 else
321 {
322 for (Idx = Num - 1; Idx >= 0; --Idx)
323 {
324 if (Samples[Idx]->GetTime() > ReferenceTime)
325 {
326 break;
327 }
328 }
329 }
330 // Accumulate durations of samples from the reference time backwards to judge what to purge as "too old"
332 for (; Idx >= 0; --Idx)
333 {
334 auto Duration = Samples[Idx]->GetDuration();
335 Age += Duration;
336 if (Age > MaxAge)
337 {
338 // All earlier samples, including the current one are "too old"
339 Samples.RemoveAt(0, Idx + 1);
340 NumDroppedSamples += (uint32)(Idx + 1);
341 return Idx + 1;
342 }
343 }
344 }
345 return 0;
346 }
347
349 {
351 while(Samples.Num() && Samples[0]->GetTime().GetSequenceIndex() < InUntilIndex)
352 {
353 Samples.RemoveAt(0);
355 }
356 }
357
358public:
359
360 //~ TMediaSampleSink interface (to be called only from producer thread)
361
362 virtual bool Enqueue(const TSharedRef<SampleType, ESPMode::ThreadSafe>& Sample) override
363 {
365
367 {
368 return false;
369 }
370
371 Samples.Push(Sample);
372 return true;
373 }
374
375 virtual void RequestFlush() override
376 {
378 Samples.Empty();
379 ++FlushCount;
380 }
381
382 virtual uint32 GetFlushCount() const
383 {
385 return FlushCount;
386 }
387
388 virtual bool CanAcceptSamples(int32 NumSamples) const override
389 {
390 return (MaxSamplesInQueue < 0) || ((Samples.Num() + NumSamples) <= MaxSamplesInQueue);
391 }
392
393protected:
395 {
399
400 int32 Num = Samples.Num();
401 if (Num > 0)
402 {
403 for (int32 Idx = 0; Idx < Num; ++Idx)
404 {
406 TRange<FMediaTimeStamp> SampleTimeRange = TRange<FMediaTimeStamp>(Sample->GetTime(), Sample->GetTime() + Sample->GetDuration());
407
408 if (TimeRange.Overlaps(SampleTimeRange))
409 {
410 // Sample is at least partially inside the requested range, recall the range of samples we find...
411 if (FirstPossibleIndex < 0)
412 {
413 FirstPossibleIndex = Idx;
414 }
415 LastPossibleIndex = Idx;
416 }
417 else
418 {
419 if (!bReverse ? (SampleTimeRange.GetLowerBoundValue() >= TimeRange.GetUpperBoundValue()) :
420 (SampleTimeRange.GetUpperBoundValue() <= TimeRange.GetLowerBoundValue()))
421 {
422 // Sample is entirely past requested time range, we can stop
423 // (we assume monotonically increasing time stamps here)
424 break;
425 }
426
427 // If the incoming data it not monotonically increasing we might get here after we already found the first overlapping sample
428 // -> we do not count further non-overlapping, older samples into this range
429 if (FirstPossibleIndex < 0)
430 {
431 // Sample is before time range, we will delete is later, no reason to keep it
433 }
434 else
435 {
436 // If we find an older non-overlapping sample after an overlapping one, we move the last possible index on to ensure these samples die ASAP
437 LastPossibleIndex = Idx;
438 }
439 }
440 }
441 }
442
443 }
444
450};
451
452
454class FMediaAudioSampleQueue : public TMediaSampleQueue<class IMediaAudioSample, class FMediaAudioSampleSink>
455{
456public:
460
466
475
477 {
479 return AudioTime;
480 }
481
482 void InvalidateAudioTime() override
483 {
485 AudioTime.Invalidate();
486 }
487
494private:
495 FMediaTimeStampSample AudioTime;
496};
497
500
503
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
TMediaSampleQueue< class IMediaTextureSample > FMediaTextureSampleQueue
Definition MediaSampleQueue.h:505
TMediaSampleQueue< class IMediaOverlaySample > FMediaOverlaySampleQueue
Definition MediaSampleQueue.h:502
EMediaSampleQueueFetchResult
Definition MediaSampleQueue.h:26
TMediaSampleQueue< class IMediaBinarySample > FMediaBinarySampleQueue
Definition MediaSampleQueue.h:499
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition MediaSampleQueue.h:455
void SetAudioTime(const FMediaTimeStampSample &InAudioTime)
Definition MediaSampleQueue.h:461
virtual void RequestFlush() override
Definition MediaSampleQueue.h:488
void InvalidateAudioTime() override
Definition MediaSampleQueue.h:482
FMediaTimeStampSample GetAudioTime() const override
Definition MediaSampleQueue.h:476
void SetAudioTimeIfEqualFlushCount(const FMediaTimeStampSample &InAudioTime, uint32 InFlushCount)
Definition MediaSampleQueue.h:467
FMediaAudioSampleQueue(uint32 MaxSamplesInQueue=-1)
Definition MediaSampleQueue.h:457
Definition MediaSampleSink.h:149
Definition IMediaTimeSource.h:197
void Invalidate()
Definition IMediaTimeSource.h:202
Definition IMediaTimeSource.h:28
FTimespan Time
Definition IMediaTimeSource.h:190
Definition ScopeLock.h:141
Definition IMediaAudioSample.h:41
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void RemoveAt(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2083
UE_NODEBUG UE_FORCEINLINE_HINT void Push(ElementType &&Item)
Definition Array.h:1224
UE_REWRITE bool IsEmpty() const
Definition Array.h:1133
void Empty(SizeType Slack=0)
Definition Array.h:2273
Definition MediaSampleQueue.h:39
virtual void GetSampleTimes(TArray< TRange< FMediaTimeStamp > > &OutSampleTimeRanges) override
Definition MediaSampleQueue.h:132
FCriticalSection CriticalSection
Definition MediaSampleQueue.h:445
uint32 NumDroppedSamples
Definition MediaSampleQueue.h:449
virtual void RequestFlush() override
Definition MediaSampleQueue.h:375
TMediaSampleQueue(int32 InMaxSamplesInQueue=-1)
Definition MediaSampleQueue.h:42
virtual bool CanAcceptSamples(int32 NumSamples) const override
Definition MediaSampleQueue.h:388
virtual bool Peek(TSharedPtr< SampleType, ESPMode::ThreadSafe > &OutSample) override
Definition MediaSampleQueue.h:111
uint32 FlushCount
Definition MediaSampleQueue.h:448
int32 Num() const
Definition MediaSampleQueue.h:61
void PurgeUntilSequenceIndex(int32 InUntilIndex)
Definition MediaSampleQueue.h:348
bool Discard(const TRange< FMediaTimeStamp > &TimeRange, bool bReverse)
Definition MediaSampleQueue.h:163
void FindRangeInQueue(const TRange< FMediaTimeStamp > &TimeRange, bool bReverse, int32 &FirstPossibleIndex, int32 &LastPossibleIndex, int32 &NumOldSamplesAtBegin)
Definition MediaSampleQueue.h:394
uint32 GetNumDroppedSamples(bool bInClearToZero)
Definition MediaSampleQueue.h:74
EMediaSampleQueueFetchResult FetchBestSampleForTimeRange(const TRange< FMediaTimeStamp > &TimeRange, TSharedPtr< SampleType, ESPMode::ThreadSafe > &OutSample, bool bReverse, bool bConsistentResult)
Definition MediaSampleQueue.h:187
TArray< TSharedPtr< SampleType, ESPMode::ThreadSafe > > Samples
Definition MediaSampleQueue.h:446
virtual bool Pop() override
Definition MediaSampleQueue.h:144
virtual ~TMediaSampleQueue()
Definition MediaSampleQueue.h:47
int32 MaxSamplesInQueue
Definition MediaSampleQueue.h:447
virtual bool Enqueue(const TSharedRef< SampleType, ESPMode::ThreadSafe > &Sample) override
Definition MediaSampleQueue.h:362
virtual bool Dequeue(TSharedPtr< SampleType, ESPMode::ThreadSafe > &OutSample) override
Definition MediaSampleQueue.h:88
virtual uint32 GetFlushCount() const
Definition MediaSampleQueue.h:382
uint32 PurgeOutdatedSamples(const FMediaTimeStamp &ReferenceTime, bool bReversed, FTimespan MaxAge)
Definition MediaSampleQueue.h:301
Definition MediaSampleSource.h:20
Definition Range.h:50
bool HasLowerBound() const
Definition Range.h:320
bool Overlaps(const TRange &Other) const
Definition Range.h:382
bool HasUpperBound() const
Definition Range.h:331
ElementValueOrConstRef GetLowerBoundValue() const
Definition Range.h:263
ElementValueOrConstRef GetUpperBoundValue() const
Definition Range.h:309
Definition SharedPointer.h:692
Definition SharedPointer.h:153
Definition Timespan.h:76
static FTimespan Zero()
Definition Timespan.h:747