summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/PlatformEncoderModule.h
blob: 1dd21330db3ab7b5607ac2d5415454888f390f18 (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
408
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */

#if !defined(PlatformEncoderModule_h_)
#  define PlatformEncoderModule_h_

#  include "MP4Decoder.h"
#  include "MediaData.h"
#  include "MediaInfo.h"
#  include "MediaResult.h"
#  include "mozilla/Attributes.h"
#  include "mozilla/Maybe.h"
#  include "mozilla/MozPromise.h"
#  include "mozilla/RefPtr.h"
#  include "mozilla/TaskQueue.h"
#  include "mozilla/dom/ImageBitmapBinding.h"
#  include "nsISupportsImpl.h"
#  include "VPXDecoder.h"

namespace mozilla {

class MediaDataEncoder;
struct CreateEncoderParams;

class PlatformEncoderModule {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformEncoderModule)

  virtual already_AddRefed<MediaDataEncoder> CreateVideoEncoder(
      const CreateEncoderParams& aParams,
      const bool aHardwareNotAllowed) const {
    return nullptr;
  };

  virtual already_AddRefed<MediaDataEncoder> CreateAudioEncoder(
      const CreateEncoderParams& aParams) const {
    return nullptr;
  };

  // Indicates if the PlatformDecoderModule supports encoding of aMimeType.
  virtual bool SupportsMimeType(const nsACString& aMimeType) const = 0;

 protected:
  PlatformEncoderModule() = default;
  virtual ~PlatformEncoderModule() = default;
};

class MediaDataEncoder {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataEncoder)

  enum class Usage {
    Realtime,  // For WebRTC
    Record     // For MediaRecoder
  };

  enum class CodecType {
    _BeginVideo_,
    H264,
    VP8,
    VP9,
    _EndVideo_,
    _BeginAudio_ = _EndVideo_,
    Opus,
    G722,
    _EndAudio_,
    Unknown,
  };

  struct H264Specific final {
    enum class ProfileLevel { BaselineAutoLevel, MainAutoLevel };

    const ProfileLevel mProfileLevel;

    explicit H264Specific(const ProfileLevel aProfileLevel)
        : mProfileLevel(aProfileLevel) {}
  };

  struct OpusSpecific final {
    enum class Application { Voip, Audio, RestricedLowDelay };

    const Application mApplication;
    const uint8_t mComplexity;  // from 0-10

    OpusSpecific(const Application aApplication, const uint8_t aComplexity)
        : mApplication(aApplication), mComplexity(aComplexity) {
      MOZ_ASSERT(mComplexity <= 10);
    }
  };

// From webrtc::VideoCodecVP8. mResilience is a boolean value because while
// VP8ResilienceMode has 3 values, kResilientFrames is not supported.
#  define VPX_COMMON_SETTINGS         \
    const Complexity mComplexity;     \
    const bool mResilience;           \
    const uint8_t mNumTemporalLayers; \
    const bool mDenoising;            \
    const bool mAutoResize;           \
    const bool mFrameDropping;

// See webrtc::VideoEncoder::GetDefaultVp(8|9)Settings().
#  define VPX_COMMON_DEFAULTS(resize)                                          \
    mComplexity(Complexity::Normal), mResilience(true), mNumTemporalLayers(1), \
        mDenoising(true), mAutoResize(resize), mFrameDropping(0)

  struct VPXSpecific final {
    enum class Complexity { Normal, High, Higher, Max };
    struct VP8 final {
      VPX_COMMON_SETTINGS
      // Ignore webrtc::VideoCodecVP8::errorConcealmentOn,
      // for it's always false in the codebase (except libwebrtc test cases).

      VP8() : VPX_COMMON_DEFAULTS(false /* auto resize */) {}
      VP8(const Complexity aComplexity, const bool aResilience,
          const uint8_t aNumTemporalLayers, const bool aDenoising,
          const bool aAutoResize, const bool aFrameDropping)
          : mComplexity(aComplexity),
            mResilience(aResilience),
            mNumTemporalLayers(aNumTemporalLayers),
            mDenoising(aDenoising),
            mAutoResize(aAutoResize),
            mFrameDropping(aFrameDropping) {}
    };

    struct VP9 final {
      VPX_COMMON_SETTINGS
      // From webrtc::VideoCodecVP9.
      bool mAdaptiveQp;
      uint8_t mNumSpatialLayers;
      bool mFlexible;

      VP9()
          : VPX_COMMON_DEFAULTS(true /* auto resize */),
            mAdaptiveQp(true),
            mNumSpatialLayers(1),
            mFlexible(false) {}
      VP9(const Complexity aComplexity, const bool aResilience,
          const uint8_t aNumTemporalLayers, const bool aDenoising,
          const bool aAutoResize, const bool aFrameDropping,
          const bool aAdaptiveQp, const uint8_t aNumSpatialLayers,
          const bool aFlexible)
          : mComplexity(aComplexity),
            mResilience(aResilience),
            mNumTemporalLayers(aNumTemporalLayers),
            mDenoising(aDenoising),
            mAutoResize(aAutoResize),
            mFrameDropping(aFrameDropping),
            mAdaptiveQp(aAdaptiveQp),
            mNumSpatialLayers(aNumSpatialLayers),
            mFlexible(aFlexible) {}
    };

    VPXSpecific() = delete;
  };

  static bool IsVideo(const CodecType aCodec) {
    return aCodec > CodecType::_BeginVideo_ && aCodec < CodecType::_EndVideo_;
  }
  static bool IsAudio(const CodecType aCodec) {
    return aCodec > CodecType::_BeginAudio_ && aCodec < CodecType::_EndAudio_;
  }

  using PixelFormat = dom::ImageBitmapFormat;
  // Sample rate for audio, framerate for video, and bitrate for both.
  using Rate = uint32_t;

  using InitPromise =
      MozPromise<TrackInfo::TrackType, MediaResult, /* IsExclusive = */ true>;
  using EncodedData = nsTArray<RefPtr<MediaRawData>>;
  using EncodePromise =
      MozPromise<EncodedData, MediaResult, /* IsExclusive = */ true>;

  // Initialize the encoder. It should be ready to encode once the returned
  // promise resolves. The encoder should do any initialization here, rather
  // than in its constructor or PlatformEncoderModule::Create*Encoder(),
  // so that if the client needs to shutdown during initialization,
  // it can call Shutdown() to cancel this operation. Any initialization
  // that requires blocking the calling thread in this function *must*
  // be done here so that it can be canceled by calling Shutdown()!
  virtual RefPtr<InitPromise> Init() = 0;

  // Inserts a sample into the encoder's encode pipeline. The EncodePromise it
  // returns will be resolved with already encoded MediaRawData at the moment,
  // or empty when there is none available yet.
  virtual RefPtr<EncodePromise> Encode(const MediaData* aSample) = 0;

  // Causes all complete samples in the pipeline that can be encoded to be
  // output. It indicates that there is no more input sample to insert.
  // This function is asynchronous.
  // The MediaDataEncoder shall resolve the pending EncodePromise with drained
  // samples. Drain will be called multiple times until the resolved
  // EncodePromise is empty which indicates that there are no more samples to
  // drain.
  virtual RefPtr<EncodePromise> Drain() = 0;

  // Cancels all init/encode/drain operations, and shuts down the encoder. The
  // platform encoder should clean up any resources it's using and release
  // memory etc. The shutdown promise will be resolved once the encoder has
  // completed shutdown. The client will delete the decoder once the promise is
  // resolved.
  // The ShutdownPromise must only ever be resolved.
  virtual RefPtr<ShutdownPromise> Shutdown() = 0;

  virtual RefPtr<GenericPromise> SetBitrate(Rate aBitsPerSec) {
    return GenericPromise::CreateAndResolve(true, __func__);
  }

  // Decoder needs to decide whether or not hardware acceleration is supported
  // after creating. It doesn't need to call Init() before calling this
  // function.
  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const {
    return false;
  }

  // Return the name of the MediaDataEncoder, only used for encoding.
  // May be accessed in a non thread-safe fashion.
  virtual nsCString GetDescriptionName() const = 0;

  friend class PlatformEncoderModule;

 protected:
  template <typename T>
  struct BaseConfig {
    const CodecType mCodecType;
    const Usage mUsage;
    const Rate mBitsPerSec;
    Maybe<T> mCodecSpecific;

    void SetCodecSpecific(const T& aCodecSpecific) {
      mCodecSpecific.emplace(aCodecSpecific);
    }

   protected:
    BaseConfig(const CodecType aCodecType, const Usage aUsage,
               const Rate aBitsPerSec)
        : mCodecType(aCodecType), mUsage(aUsage), mBitsPerSec(aBitsPerSec) {}

    virtual ~BaseConfig() = default;
  };

  template <typename T>
  struct VideoConfig final : public BaseConfig<T> {
    const gfx::IntSize mSize;
    const PixelFormat mSourcePixelFormat;
    const uint8_t mFramerate;
    const size_t mKeyframeInterval;

    VideoConfig(const CodecType aCodecType, const Usage aUsage,
                const gfx::IntSize& aSize, const PixelFormat aSourcePixelFormat,
                const uint8_t aFramerate, const size_t aKeyframeInterval,
                const Rate aBitrate)
        : BaseConfig<T>(aCodecType, aUsage, aBitrate),
          mSize(aSize),
          mSourcePixelFormat(aSourcePixelFormat),
          mFramerate(aFramerate),
          mKeyframeInterval(aKeyframeInterval) {}
  };

  template <typename T>
  struct AudioConfig final : public BaseConfig<T> {
    const uint8_t mNumChannels;
    const Rate mSampleRate;

    AudioConfig(const CodecType aCodecType, const Usage aUsage,
                const Rate aBitrate, const Rate aSampleRate,
                const uint8_t aNumChannels)
        : BaseConfig<T>(aCodecType, aUsage, aBitrate),
          mNumChannels(aNumChannels),
          mSampleRate(aSampleRate) {}
  };

  virtual ~MediaDataEncoder() = default;

 public:
  using H264Config = VideoConfig<H264Specific>;
  using VP8Config = VideoConfig<VPXSpecific::VP8>;
  using VP9Config = VideoConfig<VPXSpecific::VP9>;
};

struct MOZ_STACK_CLASS CreateEncoderParams final {
  union CodecSpecific {
    MediaDataEncoder::H264Specific mH264;
    MediaDataEncoder::OpusSpecific mOpus;
    MediaDataEncoder::VPXSpecific::VP8 mVP8;
    MediaDataEncoder::VPXSpecific::VP9 mVP9;

    explicit CodecSpecific(const MediaDataEncoder::H264Specific&& aH264)
        : mH264(aH264) {}
    explicit CodecSpecific(const MediaDataEncoder::OpusSpecific&& aOpus)
        : mOpus(aOpus) {}
    explicit CodecSpecific(const MediaDataEncoder::VPXSpecific::VP8&& aVP8)
        : mVP8(aVP8) {}
    explicit CodecSpecific(const MediaDataEncoder::VPXSpecific::VP9&& aVP9)
        : mVP9(aVP9) {}
  };

  CreateEncoderParams(const TrackInfo& aConfig,
                      const MediaDataEncoder::Usage aUsage,
                      const RefPtr<TaskQueue> aTaskQueue,
                      const MediaDataEncoder::PixelFormat aPixelFormat,
                      const uint8_t aFramerate, const size_t aKeyframeInterval,
                      const MediaDataEncoder::Rate aBitrate)
      : mConfig(aConfig),
        mUsage(aUsage),
        mTaskQueue(aTaskQueue),
        mPixelFormat(aPixelFormat),
        mFramerate(aFramerate),
        mKeyframeInterval(aKeyframeInterval),
        mBitrate(aBitrate) {
    MOZ_ASSERT(mTaskQueue);
  }

  template <typename... Ts>
  CreateEncoderParams(const TrackInfo& aConfig,
                      const MediaDataEncoder::Usage aUsage,
                      const RefPtr<TaskQueue> aTaskQueue,
                      const MediaDataEncoder::PixelFormat aPixelFormat,
                      const uint8_t aFramerate, const size_t aKeyframeInterval,
                      const MediaDataEncoder::Rate aBitrate,
                      const Ts&&... aCodecSpecific)
      : mConfig(aConfig),
        mUsage(aUsage),
        mTaskQueue(aTaskQueue),
        mPixelFormat(aPixelFormat),
        mFramerate(aFramerate),
        mKeyframeInterval(aKeyframeInterval),
        mBitrate(aBitrate) {
    MOZ_ASSERT(mTaskQueue);
    SetCodecSpecific(std::forward<const Ts>(aCodecSpecific)...);
  }

  template <typename T>
  void SetCodecSpecific(const T&& aCodecSpecific) {
    mCodecSpecific.emplace(std::forward<const T>(aCodecSpecific));
  }

  const MediaDataEncoder::H264Config ToH264Config() const {
    const VideoInfo* info = mConfig.GetAsVideoInfo();
    MOZ_ASSERT(info);

    auto config = MediaDataEncoder::H264Config(
        MediaDataEncoder::CodecType::H264, mUsage, info->mImage, mPixelFormat,
        mFramerate, mKeyframeInterval, mBitrate);
    if (mCodecSpecific) {
      config.SetCodecSpecific(mCodecSpecific.ref().mH264);
    }

    return config;
  }

  const MediaDataEncoder::VP8Config ToVP8Config() const {
    const VideoInfo* info = mConfig.GetAsVideoInfo();
    MOZ_ASSERT(info);

    auto config = MediaDataEncoder::VP8Config(
        CodecTypeForMime(info->mMimeType), mUsage, info->mImage, mPixelFormat,
        mFramerate, mKeyframeInterval, mBitrate);
    if (mCodecSpecific) {
      config.SetCodecSpecific(mCodecSpecific.ref().mVP8);
    }
    return config;
  }

  const MediaDataEncoder::VP9Config ToVP9Config() const {
    const VideoInfo* info = mConfig.GetAsVideoInfo();
    MOZ_ASSERT(info);

    auto config = MediaDataEncoder::VP9Config(
        CodecTypeForMime(info->mMimeType), mUsage, info->mImage, mPixelFormat,
        mFramerate, mKeyframeInterval, mBitrate);
    if (mCodecSpecific) {
      config.SetCodecSpecific(mCodecSpecific.ref().mVP9);
    }
    return config;
  }

  static MediaDataEncoder::CodecType CodecTypeForMime(
      const nsACString& aMimeType) {
    if (MP4Decoder::IsH264(aMimeType)) {
      return MediaDataEncoder::CodecType::H264;
    } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
      return MediaDataEncoder::CodecType::VP8;
    } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9)) {
      return MediaDataEncoder::CodecType::VP9;
    } else {
      MOZ_ASSERT_UNREACHABLE("Unsupported Mimetype");
      return MediaDataEncoder::CodecType::Unknown;
    }
  }

  const TrackInfo& mConfig;
  const MediaDataEncoder::Usage mUsage;
  const RefPtr<TaskQueue> mTaskQueue;
  const MediaDataEncoder::PixelFormat mPixelFormat;
  const uint8_t mFramerate;
  const size_t mKeyframeInterval;
  const MediaDataEncoder::Rate mBitrate;
  Maybe<CodecSpecific> mCodecSpecific;

 private:
};

}  // namespace mozilla

#endif /* PlatformEncoderModule_h_ */