Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mediasource/TrackBuffersManager.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef MOZILLA_TRACKBUFFERSMANAGER_H_
8
#define MOZILLA_TRACKBUFFERSMANAGER_H_
9
10
#include "mozilla/Atomics.h"
11
#include "mozilla/Maybe.h"
12
#include "mozilla/Mutex.h"
13
#include "mozilla/NotNull.h"
14
#include "mozilla/TaskQueue.h"
15
16
#include "MediaContainerType.h"
17
#include "MediaData.h"
18
#include "MediaDataDemuxer.h"
19
#include "MediaResult.h"
20
#include "MediaSourceDecoder.h"
21
#include "SourceBufferTask.h"
22
#include "TimeUnits.h"
23
#include "nsAutoPtr.h"
24
#include "nsTArray.h"
25
26
namespace mozilla {
27
28
class AbstractThread;
29
class ContainerParser;
30
class MediaByteBuffer;
31
class MediaRawData;
32
class MediaSourceDemuxer;
33
class SourceBufferResource;
34
35
class SourceBufferTaskQueue
36
{
37
public:
38
0
  SourceBufferTaskQueue() { }
39
40
  ~SourceBufferTaskQueue()
41
0
  {
42
0
    MOZ_ASSERT(mQueue.IsEmpty(), "All tasks must have been processed");
43
0
  }
44
45
  void Push(SourceBufferTask* aTask)
46
0
  {
47
0
    mQueue.AppendElement(aTask);
48
0
  }
49
50
  already_AddRefed<SourceBufferTask> Pop()
51
0
  {
52
0
    if (!mQueue.Length()) {
53
0
      return nullptr;
54
0
    }
55
0
    RefPtr<SourceBufferTask> task = std::move(mQueue[0]);
56
0
    mQueue.RemoveElementAt(0);
57
0
    return task.forget();
58
0
  }
59
60
  nsTArray<RefPtr<SourceBufferTask>>::size_type Length() const
61
0
  {
62
0
    return mQueue.Length();
63
0
  }
64
private:
65
  nsTArray<RefPtr<SourceBufferTask>> mQueue;
66
};
67
68
DDLoggedTypeDeclName(TrackBuffersManager);
69
70
class TrackBuffersManager : public DecoderDoctorLifeLogger<TrackBuffersManager>
71
{
72
public:
73
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffersManager);
74
75
  enum class EvictDataResult : int8_t
76
  {
77
    NO_DATA_EVICTED,
78
    CANT_EVICT,
79
    BUFFER_FULL,
80
  };
81
82
  typedef TrackInfo::TrackType TrackType;
83
  typedef MediaData::Type MediaType;
84
  typedef nsTArray<RefPtr<MediaRawData>> TrackBuffer;
85
  typedef SourceBufferTask::AppendPromise AppendPromise;
86
  typedef SourceBufferTask::RangeRemovalPromise RangeRemovalPromise;
87
88
  // Interface for SourceBuffer
89
  TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
90
                      const MediaContainerType& aType);
91
92
  // Queue a task to add data to the end of the input buffer and run the MSE
93
  // Buffer Append Algorithm
94
  // 3.5.5 Buffer Append Algorithm.
95
  // http://w3c.github.io/media-source/index.html#sourcebuffer-buffer-append
96
  RefPtr<AppendPromise> AppendData(already_AddRefed<MediaByteBuffer> aData,
97
                                   const SourceBufferAttributes& aAttributes);
98
99
  // Queue a task to abort any pending AppendData.
100
  // Does nothing at this stage.
101
  void AbortAppendData();
102
103
  // Queue a task to run MSE Reset Parser State Algorithm.
104
  // 3.5.2 Reset Parser State
105
  void ResetParserState(SourceBufferAttributes& aAttributes);
106
107
  // Queue a task to run the MSE range removal algorithm.
108
  // http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
109
  RefPtr<RangeRemovalPromise> RangeRemoval(media::TimeUnit aStart,
110
                                             media::TimeUnit aEnd);
111
112
  // Schedule data eviction if necessary as the next call to AppendData will
113
  // add aSize bytes.
114
  // Eviction is done in two steps, first remove data up to aPlaybackTime
115
  // and if still more space is needed remove from the end.
116
  EvictDataResult EvictData(const media::TimeUnit& aPlaybackTime, int64_t aSize);
117
118
  // Queue a task to run ChangeType
119
  void ChangeType(const MediaContainerType& aType);
120
121
  // Returns the buffered range currently managed.
122
  // This may be called on any thread.
123
  // Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
124
  media::TimeIntervals Buffered() const;
125
  media::TimeUnit HighestStartTime() const;
126
  media::TimeUnit HighestEndTime() const;
127
128
  // Return the size of the data managed by this SourceBufferContentManager.
129
  int64_t GetSize() const;
130
131
  // Indicate that the MediaSource parent object got into "ended" state.
132
  void Ended();
133
134
  // The parent SourceBuffer is about to be destroyed.
135
  void Detach();
136
137
  int64_t EvictionThreshold() const;
138
139
  // Interface for MediaSourceDemuxer
140
  MediaInfo GetMetadata() const;
141
  const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack) const;
142
  const media::TimeIntervals& Buffered(TrackInfo::TrackType) const;
143
  const media::TimeUnit& HighestStartTime(TrackInfo::TrackType) const;
144
  media::TimeIntervals SafeBuffered(TrackInfo::TrackType) const;
145
  bool IsEnded() const
146
0
  {
147
0
    return mEnded;
148
0
  }
149
  uint32_t Evictable(TrackInfo::TrackType aTrack) const;
150
  media::TimeUnit Seek(TrackInfo::TrackType aTrack,
151
                       const media::TimeUnit& aTime,
152
                       const media::TimeUnit& aFuzz);
153
  uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
154
                                       const media::TimeUnit& aTimeThreadshold,
155
                                       const media::TimeUnit& aFuzz,
156
                                       bool& aFound);
157
158
  already_AddRefed<MediaRawData> GetSample(TrackInfo::TrackType aTrack,
159
                                           const media::TimeUnit& aFuzz,
160
                                           MediaResult& aResult);
161
  int32_t FindCurrentPosition(TrackInfo::TrackType aTrack,
162
                              const media::TimeUnit& aFuzz) const;
163
164
  // Will set the next GetSample index if needed. This information is determined
165
  // through the value of mNextSampleTimecode. Return false if the index
166
  // couldn't be determined or if there's nothing more that could be demuxed.
167
  // This occurs if either the track buffer doesn't contain the required
168
  // timecode or is empty.
169
  nsresult SetNextGetSampleIndexIfNeeded(TrackInfo::TrackType aTrack,
170
                                         const media::TimeUnit& aFuzz);
171
172
  media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,
173
                                           const media::TimeUnit& aFuzz);
174
175
  void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes) const;
176
177
private:
178
  typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> CodedFrameProcessingPromise;
179
180
  // for MediaSourceDemuxer::GetMozDebugReaderData
181
  friend class MediaSourceDemuxer;
182
  ~TrackBuffersManager();
183
  // All following functions run on the taskqueue.
184
  RefPtr<AppendPromise> DoAppendData(already_AddRefed<MediaByteBuffer> aData,
185
                                     const SourceBufferAttributes& aAttributes);
186
  void ScheduleSegmentParserLoop();
187
  void SegmentParserLoop();
188
  void InitializationSegmentReceived();
189
  void ShutdownDemuxers();
190
  void CreateDemuxerforMIMEType();
191
  void ResetDemuxingState();
192
  void NeedMoreData();
193
  void RejectAppend(const MediaResult& aRejectValue, const char* aName);
194
  // Will return a promise that will be resolved once all frames of the current
195
  // media segment have been processed.
196
  RefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
197
  void CompleteCodedFrameProcessing();
198
  // Called by ResetParserState.
199
  void CompleteResetParserState();
200
  RefPtr<RangeRemovalPromise>
201
    CodedFrameRemovalWithPromise(media::TimeInterval aInterval);
202
  bool CodedFrameRemoval(media::TimeInterval aInterval);
203
  void SetAppendState(SourceBufferAttributes::AppendState aAppendState);
204
205
  bool HasVideo() const
206
0
  {
207
0
    return mVideoTracks.mNumTracks > 0;
208
0
  }
209
  bool HasAudio() const
210
0
  {
211
0
    return mAudioTracks.mNumTracks > 0;
212
0
  }
213
214
  // The input buffer as per http://w3c.github.io/media-source/index.html#sourcebuffer-input-buffer
215
  RefPtr<MediaByteBuffer> mInputBuffer;
216
  // Buffer full flag as per https://w3c.github.io/media-source/#sourcebuffer-buffer-full-flag.
217
  // Accessed on both the main thread and the task queue.
218
  Atomic<bool> mBufferFull;
219
  bool mFirstInitializationSegmentReceived;
220
  bool mChangeTypeReceived;
221
  // Set to true once a new segment is started.
222
  bool mNewMediaSegmentStarted;
223
  bool mActiveTrack;
224
  MediaContainerType mType;
225
226
  // ContainerParser objects and methods.
227
  // Those are used to parse the incoming input buffer.
228
229
  // Recreate the ContainerParser and if aReuseInitData is true then
230
  // feed it with the previous init segment found.
231
  void RecreateParser(bool aReuseInitData);
232
  nsAutoPtr<ContainerParser> mParser;
233
234
  // Demuxer objects and methods.
235
  void AppendDataToCurrentInputBuffer(MediaByteBuffer* aData);
236
  RefPtr<MediaByteBuffer> mInitData;
237
  // Temporary input buffer to handle partial media segment header.
238
  // We store the current input buffer content into it should we need to
239
  // reinitialize the demuxer once we have some samples and a discontinuity is
240
  // detected.
241
  RefPtr<MediaByteBuffer> mPendingInputBuffer;
242
  RefPtr<SourceBufferResource> mCurrentInputBuffer;
243
  RefPtr<MediaDataDemuxer> mInputDemuxer;
244
  // Length already processed in current media segment.
245
  uint64_t mProcessedInput;
246
  Maybe<media::TimeUnit> mLastParsedEndTime;
247
248
  void OnDemuxerInitDone(const MediaResult& aResult);
249
  void OnDemuxerInitFailed(const MediaResult& aFailure);
250
  void OnDemuxerResetDone(const MediaResult& aResult);
251
  MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
252
253
  void OnDemuxFailed(TrackType aTrack, const MediaResult& aError);
254
  void DoDemuxVideo();
255
  void OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
256
  void OnVideoDemuxFailed(const MediaResult& aError)
257
0
  {
258
0
    mVideoTracks.mDemuxRequest.Complete();
259
0
    OnDemuxFailed(TrackType::kVideoTrack, aError);
260
0
  }
261
  void DoDemuxAudio();
262
  void OnAudioDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
263
  void OnAudioDemuxFailed(const MediaResult& aError)
264
0
  {
265
0
    mAudioTracks.mDemuxRequest.Complete();
266
0
    OnDemuxFailed(TrackType::kAudioTrack, aError);
267
0
  }
268
269
  // Dispatches an "encrypted" event is any sample in array has initData
270
  // present.
271
  void MaybeDispatchEncryptedEvent(
272
    const nsTArray<RefPtr<MediaRawData>>& aSamples);
273
274
  void DoEvictData(const media::TimeUnit& aPlaybackTime, int64_t aSizeToEvict);
275
276
  struct TrackData
277
  {
278
    TrackData()
279
      : mNumTracks(0)
280
      , mNeedRandomAccessPoint(true)
281
      , mSizeBuffer(0)
282
0
    {}
283
    uint32_t mNumTracks;
284
    // Definition of variables:
285
    // https://w3c.github.io/media-source/#track-buffers
286
    // Last decode timestamp variable that stores the decode timestamp of the
287
    // last coded frame appended in the current coded frame group.
288
    // The variable is initially unset to indicate that no coded frames have
289
    // been appended yet.
290
    Maybe<media::TimeUnit> mLastDecodeTimestamp;
291
    // Last frame duration variable that stores the coded frame duration of the
292
    // last coded frame appended in the current coded frame group.
293
    // The variable is initially unset to indicate that no coded frames have
294
    // been appended yet.
295
    Maybe<media::TimeUnit> mLastFrameDuration;
296
    // Highest end timestamp variable that stores the highest coded frame end
297
    // timestamp across all coded frames in the current coded frame group that
298
    // were appended to this track buffer.
299
    // The variable is initially unset to indicate that no coded frames have
300
    // been appended yet.
301
    Maybe<media::TimeUnit> mHighestEndTimestamp;
302
    // Highest presentation timestamp in track buffer.
303
    // Protected by global monitor, except when reading on the task queue as it
304
    // is only written there.
305
    media::TimeUnit mHighestStartTimestamp;
306
    // Longest frame duration seen since last random access point.
307
    // Only ever accessed when mLastDecodeTimestamp and mLastFrameDuration are
308
    // set.
309
    media::TimeUnit mLongestFrameDuration;
310
    // Need random access point flag variable that keeps track of whether the
311
    // track buffer is waiting for a random access point coded frame.
312
    // The variable is initially set to true to indicate that random access
313
    // point coded frame is needed before anything can be added to the track
314
    // buffer.
315
    bool mNeedRandomAccessPoint;
316
    RefPtr<MediaTrackDemuxer> mDemuxer;
317
    MozPromiseRequestHolder<MediaTrackDemuxer::SamplesPromise> mDemuxRequest;
318
    // Highest end timestamp of the last media segment demuxed.
319
    media::TimeUnit mLastParsedEndTime;
320
321
    // If set, position where the next contiguous frame will be inserted.
322
    // If a discontinuity is detected, it will be unset and recalculated upon
323
    // the next insertion.
324
    Maybe<uint32_t> mNextInsertionIndex;
325
    // Samples just demuxed, but not yet parsed.
326
    TrackBuffer mQueuedSamples;
327
    const TrackBuffer& GetTrackBuffer() const
328
0
    {
329
0
      MOZ_RELEASE_ASSERT(mBuffers.Length(),
330
0
                         "TrackBuffer must have been created");
331
0
      return mBuffers.LastElement();
332
0
    }
333
    TrackBuffer& GetTrackBuffer()
334
0
    {
335
0
      MOZ_RELEASE_ASSERT(mBuffers.Length(),
336
0
                         "TrackBuffer must have been created");
337
0
      return mBuffers.LastElement();
338
0
    }
339
    // We only manage a single track of each type at this time.
340
    nsTArray<TrackBuffer> mBuffers;
341
    // Track buffer ranges variable that represents the presentation time ranges
342
    // occupied by the coded frames currently stored in the track buffer.
343
    media::TimeIntervals mBufferedRanges;
344
    // Sanitized mBufferedRanges with a fuzz of half a sample's duration applied
345
    // This buffered ranges is the basis of what is exposed to the JS.
346
    media::TimeIntervals mSanitizedBufferedRanges;
347
    // Byte size of all samples contained in this track buffer.
348
    uint32_t mSizeBuffer;
349
    // TrackInfo of the first metadata received.
350
    RefPtr<TrackInfoSharedPtr> mInfo;
351
    // TrackInfo of the last metadata parsed (updated with each init segment.
352
    RefPtr<TrackInfoSharedPtr> mLastInfo;
353
354
    // If set, position of the next sample to be retrieved by GetSample().
355
    // If the position is equal to the TrackBuffer's length, it indicates that
356
    // we've reached EOS.
357
    Maybe<uint32_t> mNextGetSampleIndex;
358
    // Approximation of the next sample's decode timestamp.
359
    media::TimeUnit mNextSampleTimecode;
360
    // Approximation of the next sample's presentation timestamp.
361
    media::TimeUnit mNextSampleTime;
362
363
    struct EvictionIndex
364
    {
365
0
      EvictionIndex() { Reset(); }
366
      void Reset()
367
0
      {
368
0
        mEvictable = 0;
369
0
        mLastIndex = 0;
370
0
      }
371
      uint32_t mEvictable;
372
      uint32_t mLastIndex;
373
    };
374
    // Size of data that can be safely evicted during the next eviction
375
    // cycle.
376
    // We consider as evictable all frames up to the last keyframe prior to
377
    // mNextGetSampleIndex. If mNextGetSampleIndex isn't set, then we assume
378
    // that we can't yet evict data.
379
    // Protected by global monitor, except when reading on the task queue as it
380
    // is only written there.
381
    EvictionIndex mEvictionIndex;
382
383
    void ResetAppendState()
384
0
    {
385
0
      mLastDecodeTimestamp.reset();
386
0
      mLastFrameDuration.reset();
387
0
      mHighestEndTimestamp.reset();
388
0
      mNeedRandomAccessPoint = true;
389
0
      mNextInsertionIndex.reset();
390
0
    }
391
392
    void Reset()
393
0
    {
394
0
      ResetAppendState();
395
0
      mEvictionIndex.Reset();
396
0
      for (auto& buffer : mBuffers) {
397
0
        buffer.Clear();
398
0
      }
399
0
      mSizeBuffer = 0;
400
0
      mNextGetSampleIndex.reset();
401
0
      mBufferedRanges.Clear();
402
0
      mSanitizedBufferedRanges.Clear();
403
0
    }
404
405
    void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes) const;
406
  };
407
408
  void CheckSequenceDiscontinuity(const media::TimeUnit& aPresentationTime);
409
  void ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData);
410
  media::TimeInterval PresentationInterval(const TrackBuffer& aSamples) const;
411
  bool CheckNextInsertionIndex(TrackData& aTrackData,
412
                               const media::TimeUnit& aSampleTime);
413
  void InsertFrames(TrackBuffer& aSamples,
414
                    const media::TimeIntervals& aIntervals,
415
                    TrackData& aTrackData);
416
  void UpdateHighestTimestamp(TrackData& aTrackData,
417
                              const media::TimeUnit& aHighestTime);
418
  // Remove all frames and their dependencies contained in aIntervals.
419
  // Return the index at which frames were first removed or 0 if no frames
420
  // removed.
421
  uint32_t RemoveFrames(const media::TimeIntervals& aIntervals,
422
                        TrackData& aTrackData,
423
                        uint32_t aStartIndex);
424
  // Recalculate track's evictable amount.
425
  void ResetEvictionIndex(TrackData& aTrackData);
426
  void UpdateEvictionIndex(TrackData& aTrackData, uint32_t aCurrentIndex);
427
  // Find index of sample. Return a negative value if not found.
428
  uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
429
                           const media::TimeInterval& aInterval);
430
  const MediaRawData* GetSample(TrackInfo::TrackType aTrack,
431
                                uint32_t aIndex,
432
                                const media::TimeUnit& aExpectedDts,
433
                                const media::TimeUnit& aExpectedPts,
434
                                const media::TimeUnit& aFuzz);
435
  void UpdateBufferedRanges();
436
  void RejectProcessing(const MediaResult& aRejectValue, const char* aName);
437
  void ResolveProcessing(bool aResolveValue, const char* aName);
438
  MozPromiseRequestHolder<CodedFrameProcessingPromise> mProcessingRequest;
439
  MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
440
441
  // Trackbuffers definition.
442
  nsTArray<const TrackData*> GetTracksList() const;
443
  nsTArray<TrackData*> GetTracksList();
444
  TrackData& GetTracksData(TrackType aTrack)
445
  {
446
    switch(aTrack) {
447
      case TrackType::kVideoTrack:
448
        return mVideoTracks;
449
      case TrackType::kAudioTrack:
450
      default:
451
        return mAudioTracks;
452
    }
453
  }
454
  const TrackData& GetTracksData(TrackType aTrack) const
455
  {
456
    switch(aTrack) {
457
      case TrackType::kVideoTrack:
458
        return mVideoTracks;
459
      case TrackType::kAudioTrack:
460
      default:
461
        return mAudioTracks;
462
    }
463
  }
464
  TrackData mVideoTracks;
465
  TrackData mAudioTracks;
466
467
  // TaskQueue methods and objects.
468
  RefPtr<TaskQueue> GetTaskQueueSafe() const
469
0
  {
470
0
    MutexAutoLock mut(mMutex);
471
0
    return mTaskQueue;
472
0
  }
473
  NotNull<AbstractThread*> TaskQueueFromTaskQueue() const
474
0
  {
475
#ifdef DEBUG
476
    RefPtr<TaskQueue> taskQueue = GetTaskQueueSafe();
477
    MOZ_ASSERT(taskQueue && taskQueue->IsCurrentThreadIn());
478
#endif
479
    return WrapNotNull(mTaskQueue.get());
480
0
  }
481
  bool OnTaskQueue() const
482
  {
483
    auto taskQueue = TaskQueueFromTaskQueue();
484
    return taskQueue->IsCurrentThreadIn();
485
  }
486
  void ResetTaskQueue()
487
0
  {
488
0
    MutexAutoLock mut(mMutex);
489
0
    mTaskQueue = nullptr;
490
0
  }
491
492
  // SourceBuffer Queues and running context.
493
  SourceBufferTaskQueue mQueue;
494
  void QueueTask(SourceBufferTask* aTask);
495
  void ProcessTasks();
496
  // Set if the TrackBuffersManager is currently processing a task.
497
  // At this stage, this task is always a AppendBufferTask.
498
  RefPtr<SourceBufferTask> mCurrentTask;
499
  // Current SourceBuffer state for ongoing task.
500
  // Its content is returned to the SourceBuffer once the AppendBufferTask has
501
  // completed.
502
  UniquePtr<SourceBufferAttributes> mSourceBufferAttributes;
503
  // The current sourcebuffer append window. It's content is equivalent to
504
  // mSourceBufferAttributes.mAppendWindowStart/End
505
  media::TimeInterval mAppendWindow;
506
507
  // Strong references to external objects.
508
  nsMainThreadPtrHandle<MediaSourceDecoder> mParentDecoder;
509
510
  const RefPtr<AbstractThread> mAbstractMainThread;
511
512
  // Return public highest end time across all aTracks.
513
  // Monitor must be held.
514
  media::TimeUnit HighestEndTime(nsTArray<const media::TimeIntervals*>& aTracks) const;
515
516
  // Set to true if mediasource state changed to ended.
517
  Atomic<bool> mEnded;
518
519
  // Global size of this source buffer content.
520
  Atomic<int64_t> mSizeSourceBuffer;
521
  const int64_t mVideoEvictionThreshold;
522
  const int64_t mAudioEvictionThreshold;
523
  enum class EvictionState
524
  {
525
    NO_EVICTION_NEEDED,
526
    EVICTION_NEEDED,
527
    EVICTION_COMPLETED,
528
  };
529
  Atomic<EvictionState> mEvictionState;
530
531
  // Monitor to protect following objects accessed across multiple threads.
532
  mutable Mutex mMutex;
533
  // mTaskQueue is only ever written after construction on the task queue.
534
  // As such, it can be accessed while on task queue without the need for the
535
  // mutex.
536
  RefPtr<TaskQueue> mTaskQueue;
537
  // Stable audio and video track time ranges.
538
  media::TimeIntervals mVideoBufferedRanges;
539
  media::TimeIntervals mAudioBufferedRanges;
540
  // MediaInfo of the first init segment read.
541
  MediaInfo mInfo;
542
};
543
544
} // namespace mozilla
545
546
#endif /* MOZILLA_TRACKBUFFERSMANAGER_H_ */