summaryrefslogtreecommitdiffstats
path: root/dom/media/utils/PerformanceRecorder.h
blob: 582d56e5e3428cf9662d0cdd415555dc26cb07eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_PerformanceRecorder_h
#define mozilla_PerformanceRecorder_h

#include <type_traits>

#include "mozilla/Attributes.h"
#include "mozilla/BaseProfilerMarkersPrerequisites.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypedEnumBits.h"
#include "nsStringFwd.h"
#include "nsTPriorityQueue.h"
#include "mozilla/ProfilerMarkers.h"

namespace mozilla {
namespace gfx {
enum class YUVColorSpace : uint8_t;
enum class ColorDepth : uint8_t;
enum class ColorRange : uint8_t;
}  // namespace gfx

struct TrackingId {
  enum class Source : uint8_t {
    Unimplemented,
    AudioDestinationNode,
    Camera,
    Canvas,
    ChannelDecoder,
    HLSDecoder,
    MediaCapabilities,
    MediaElementDecoder,
    MediaElementStream,
    MSEDecoder,
    RTCRtpReceiver,
    Screen,
    Tab,
    Window,
    LAST
  };
  enum class TrackAcrossProcesses : uint8_t {
    Yes,
    No,
  };
  TrackingId();
  TrackingId(Source aSource, uint32_t aUniqueInProcId,
             TrackAcrossProcesses aTrack = TrackAcrossProcesses::No);

  nsCString ToString() const;

  Source mSource;
  uint32_t mUniqueInProcId;
  Maybe<uint32_t> mProcId;
};

enum class MediaInfoFlag : uint16_t {
  None = (0 << 0),
  NonKeyFrame = (1 << 0),
  KeyFrame = (1 << 1),
  SoftwareDecoding = (1 << 2),
  HardwareDecoding = (1 << 3),
  VIDEO_AV1 = (1 << 4),
  VIDEO_H264 = (1 << 5),
  VIDEO_VP8 = (1 << 6),
  VIDEO_VP9 = (1 << 7),
  VIDEO_THEORA = (1 << 8),
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MediaInfoFlag)

/**
 * This represents the different stages that a media data will go through
 * within the playback journey.
 *
 *           |---|           |---|                 |------|
 *            Copy Demuxed    Copy Demuxed          Copy Decoded
 *            Data            Data                  Video
 *   |------------- |      |-----------------------------------|
 *     Request Demux         Request Decode
 * |-----------------------------------------------------------|
 *   Request Data
 *
 * RequestData : Record the time where MediaDecoderStateMachine(MDSM) starts
 * asking for a decoded data to MDSM receives a decoded data.
 *
 * RequestDemux : Record the time where MediaFormatReader(MFR) starts asking
 * a demuxed sample to MFR received a demuxed sample. This stage is a sub-
 * stage of RequestData.
 *
 * CopyDemuxedData : On some situations, we will need to copy the demuxed
 * data, which is still not decoded yet so its size is still small. This
 * records the time which we spend on copying data. This stage could happen
 * multiple times, either being a sub-stage of RequestDemux (in MSE case),
 * or being a sub-stage of RequestDecode (when sending data via IPC).
 *
 * RequestDecode : Record the time where MFR starts asking decoder to return
 * a decoded data to MFR receives a decoded data. As the decoder might be
 * remote, this stage might include the time spending on IPC trips. This
 * stage is a sub-stage of RequestData.
 *
 * CopyDecodedVideo : If we can't reuse same decoder texture to the
 * compositor, then we have to copy video data to to another sharable
 * texture. This records the time which we spend on copying data. This stage
 * is a sub- stage of RequestDecode.
 */
enum class MediaStage : uint8_t {
  Invalid,
  RequestData,
  RequestDemux,
  CopyDemuxedData,
  RequestDecode,
  CopyDecodedVideo,
};

class PlaybackStage {
 public:
  explicit PlaybackStage(MediaStage aStage, int32_t aHeight = 0,
                         MediaInfoFlag aFlag = MediaInfoFlag::None)
      : mStage(aStage), mHeight(aHeight), mFlag(aFlag) {
    MOZ_ASSERT(aStage != MediaStage::Invalid);
  }

  ProfilerString8View Name() const;
  const MarkerCategory& Category() const {
    return baseprofiler::category::MEDIA_PLAYBACK;
  }

  MediaStage mStage;
  int32_t mHeight;
  MediaInfoFlag mFlag;

 private:
  mutable Maybe<nsCString> mName;
};

class CaptureStage {
 public:
  enum class ImageType : uint8_t {
    Unknown,
    I420,
    YUY2,
    YV12,
    UYVY,
    NV12,
    NV21,
    MJPEG,
  };

  CaptureStage(nsCString aSource, TrackingId aTrackingId, int32_t aWidth,
               int32_t aHeight, ImageType aImageType)
      : mSource(std::move(aSource)),
        mTrackingId(std::move(aTrackingId)),
        mWidth(aWidth),
        mHeight(aHeight),
        mImageType(aImageType) {}

  ProfilerString8View Name() const;
  const MarkerCategory& Category() const {
    return baseprofiler::category::MEDIA_RT;
  }

  nsCString mSource;
  TrackingId mTrackingId;
  int32_t mWidth;
  int32_t mHeight;
  ImageType mImageType;

 private:
  mutable Maybe<nsCString> mName;
};

class CopyVideoStage {
 public:
  CopyVideoStage(nsCString aSource, TrackingId aTrackingId, int32_t aWidth,
                 int32_t aHeight)
      : mSource(std::move(aSource)),
        mTrackingId(std::move(aTrackingId)),
        mWidth(aWidth),
        mHeight(aHeight) {}

  ProfilerString8View Name() const;
  const MarkerCategory& Category() const {
    return baseprofiler::category::MEDIA_RT;
  }

  // The name of the source that performs this stage.
  nsCString mSource;
  // A unique id identifying the source of the video frame this stage is
  // performed for.
  TrackingId mTrackingId;
  int32_t mWidth;
  int32_t mHeight;

 private:
  mutable Maybe<nsCString> mName;
};

class DecodeStage {
 public:
  enum ImageFormat : uint8_t {
    YUV420P,
    YUV422P,
    YUV444P,
    NV12,
    YV12,
    NV21,
    P010,
    P016,
    RGBA32,
    RGB24,
    GBRP,
    ANDROID_SURFACE,
  };

  DecodeStage(nsCString aSource, TrackingId aTrackingId, MediaInfoFlag aFlag)
      : mSource(std::move(aSource)),
        mTrackingId(std::move(aTrackingId)),
        mFlag(aFlag) {}
  ProfilerString8View Name() const;
  const MarkerCategory& Category() const {
    return baseprofiler::category::MEDIA_PLAYBACK;
  }

  void SetResolution(int aWidth, int aHeight) {
    mWidth = Some(aWidth);
    mHeight = Some(aHeight);
  }
  void SetImageFormat(ImageFormat aFormat) { mImageFormat = Some(aFormat); }
  void SetYUVColorSpace(gfx::YUVColorSpace aColorSpace) {
    mYUVColorSpace = Some(aColorSpace);
  }
  void SetColorRange(gfx::ColorRange aColorRange) {
    mColorRange = Some(aColorRange);
  }
  void SetColorDepth(gfx::ColorDepth aColorDepth) {
    mColorDepth = Some(aColorDepth);
  }

  // The name of the source that performs this stage.
  nsCString mSource;
  // A unique id identifying the source of the video frame this stage is
  // performed for.
  TrackingId mTrackingId;
  MediaInfoFlag mFlag;
  Maybe<int> mWidth;
  Maybe<int> mHeight;
  Maybe<ImageFormat> mImageFormat;
  Maybe<gfx::YUVColorSpace> mYUVColorSpace;
  Maybe<gfx::ColorRange> mColorRange;
  Maybe<gfx::ColorDepth> mColorDepth;
  mutable Maybe<nsCString> mName;
};

class PerformanceRecorderBase {
 public:
  static bool IsMeasurementEnabled();
  static TimeStamp GetCurrentTimeForMeasurement();

  // Return the resolution range for the given height. Eg. V:1080<h<=1440.
  static const char* FindMediaResolution(int32_t aHeight);

 protected:
  // We would enable the measurement on testing.
  static inline bool sEnableMeasurementForTesting = false;
};

template <typename StageType>
class PerformanceRecorderImpl : public PerformanceRecorderBase {
 public:
  ~PerformanceRecorderImpl() = default;

  PerformanceRecorderImpl(PerformanceRecorderImpl&& aRhs) noexcept
      : mStages(std::move(aRhs.mStages)) {}
  PerformanceRecorderImpl& operator=(PerformanceRecorderImpl&&) = delete;
  PerformanceRecorderImpl(const PerformanceRecorderImpl&) = delete;
  PerformanceRecorderImpl& operator=(const PerformanceRecorderImpl&) = delete;

 protected:
  PerformanceRecorderImpl() = default;

  // Stores the stage with the current time as its start time, associated with
  // aId.
  template <typename... Args>
  void Start(int64_t aId, Args... aArgs) {
    if (IsMeasurementEnabled()) {
      MutexAutoLock lock(mMutex);
      mStages.Push(std::make_tuple(aId, GetCurrentTimeForMeasurement(),
                                   StageType(std::move(aArgs)...)));
    }
  }

  // Return the passed time since creation of the aId stage in microseconds if
  // it has not yet been recorded. Other stages with lower ids will be
  // discarded. Otherwise, return 0.
  template <typename F>
  float Record(int64_t aId, F&& aStageMutator) {
    Maybe<Entry> entry;
    {
      MutexAutoLock lock(mMutex);
      while (!mStages.IsEmpty() && std::get<0>(mStages.Top()) < aId) {
        mStages.Pop();
      }
      if (mStages.IsEmpty()) {
        return 0.0;
      }
      if (std::get<0>(mStages.Top()) != aId) {
        return 0.0;
      }
      entry = Some(mStages.Pop());
    }
    const auto& startTime = std::get<1>(*entry);
    auto& stage = std::get<2>(*entry);
    MOZ_ASSERT(std::get<0>(*entry) == aId);
    double elapsedTimeUs = 0.0;
    if (!startTime.IsNull() && IsMeasurementEnabled()) {
      const auto now = TimeStamp::Now();
      elapsedTimeUs = (now - startTime).ToMicroseconds();
      MOZ_ASSERT(elapsedTimeUs >= 0, "Elapsed time can't be less than 0!");
      aStageMutator(stage);
      AUTO_PROFILER_STATS(PROFILER_MARKER_UNTYPED);
      ::profiler_add_marker(
          stage.Name(), stage.Category(),
          MarkerOptions(MarkerTiming::Interval(startTime, now)));
    }
    return static_cast<float>(elapsedTimeUs);
  }
  float Record(int64_t aId) {
    return Record(aId, [](auto&) {});
  }

 protected:
  using Entry = std::tuple<int64_t, TimeStamp, StageType>;

  struct IdComparator {
    bool LessThan(const Entry& aTupleA, const Entry& aTupleB) {
      return std::get<0>(aTupleA) < std::get<0>(aTupleB);
    }
  };

  Mutex mMutex{"PerformanceRecorder::mMutex"};
  nsTPriorityQueue<Entry, IdComparator> mStages MOZ_GUARDED_BY(mMutex);
};

/**
 * This class is used to record the time spent on different stages in the media
 * pipeline. `Record()` needs to be called explicitly to record a profiler
 * marker registering the time passed since creation. A stage may be mutated in
 * `Record()` in case data has become available since the recorder started.
 *
 * This variant is intended to be created on the stack when a stage starts, then
 * recorded with `Record()` when the stage is finished.
 */
template <typename StageType>
class PerformanceRecorder : public PerformanceRecorderImpl<StageType> {
  using Super = PerformanceRecorderImpl<StageType>;

 public:
  template <typename... Args>
  explicit PerformanceRecorder(Args... aArgs) {
    Start(std::move(aArgs)...);
  };

 private:
  template <typename... Args>
  void Start(Args... aArgs) {
    Super::Start(0, std::move(aArgs)...);
  }

 public:
  template <typename F>
  float Record(F&& aStageMutator) {
    return Super::Record(0, std::forward<F>(aStageMutator));
  }
  float Record() { return Super::Record(0); }
};

/**
 * This class is used to record the time spent on different stages in the media
 * pipeline. `Start()` and `Record()` needs to be called explicitly to record a
 * profiler marker registering the time passed since creation. A stage may be
 * mutated in `Record()` in case data has become available since the recorder
 * started.
 *
 * This variant is intended to be kept as a member in a class and supports async
 * stages. The async stages may overlap each other. To distinguish different
 * stages from each other, an int64_t is used as identifier. This is often a
 * timestamp in microseconds, see TimeUnit::ToMicroseconds.
 */
template <typename StageType>
class PerformanceRecorderMulti : public PerformanceRecorderImpl<StageType> {
  using Super = PerformanceRecorderImpl<StageType>;

 public:
  PerformanceRecorderMulti() = default;

  using Super::Record;
  using Super::Start;
};

}  // namespace mozilla

#endif  // mozilla_PerformanceRecorder_h