1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 #if !defined(PlatformEncoderModule_h_)
8 #  define PlatformEncoderModule_h_
9 
10 #  include "MP4Decoder.h"
11 #  include "MediaData.h"
12 #  include "MediaInfo.h"
13 #  include "MediaResult.h"
14 #  include "mozilla/Attributes.h"
15 #  include "mozilla/Maybe.h"
16 #  include "mozilla/MozPromise.h"
17 #  include "mozilla/RefPtr.h"
18 #  include "mozilla/TaskQueue.h"
19 #  include "mozilla/dom/ImageBitmapBinding.h"
20 #  include "nsISupportsImpl.h"
21 #  include "VPXDecoder.h"
22 
23 namespace mozilla {
24 
25 class MediaDataEncoder;
26 struct CreateEncoderParams;
27 
28 class PlatformEncoderModule {
29  public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformEncoderModule)30   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformEncoderModule)
31 
32   virtual already_AddRefed<MediaDataEncoder> CreateVideoEncoder(
33       const CreateEncoderParams& aParams) const {
34     return nullptr;
35   };
36 
CreateAudioEncoder(const CreateEncoderParams & aParams)37   virtual already_AddRefed<MediaDataEncoder> CreateAudioEncoder(
38       const CreateEncoderParams& aParams) const {
39     return nullptr;
40   };
41 
42   // Indicates if the PlatformDecoderModule supports encoding of aMimeType.
43   virtual bool SupportsMimeType(const nsACString& aMimeType) const = 0;
44 
45  protected:
46   PlatformEncoderModule() = default;
47   virtual ~PlatformEncoderModule() = default;
48 };
49 
50 class MediaDataEncoder {
51  public:
52   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataEncoder)
53 
54   enum class Usage {
55     Realtime,  // For WebRTC
56     Record     // For MediaRecoder
57   };
58 
59   enum class CodecType {
60     _BeginVideo_,
61     H264,
62     VP8,
63     VP9,
64     _EndVideo_,
65     _BeginAudio_ = _EndVideo_,
66     Opus,
67     G722,
68     _EndAudio_,
69     Unknown,
70   };
71 
72   struct H264Specific final {
73     enum class ProfileLevel { BaselineAutoLevel, MainAutoLevel };
74 
75     const ProfileLevel mProfileLevel;
76 
H264Specificfinal77     explicit H264Specific(const ProfileLevel aProfileLevel)
78         : mProfileLevel(aProfileLevel) {}
79   };
80 
81   struct OpusSpecific final {
82     enum class Application { Voip, Audio, RestricedLowDelay };
83 
84     const Application mApplication;
85     const uint8_t mComplexity;  // from 0-10
86 
OpusSpecificfinal87     OpusSpecific(const Application aApplication, const uint8_t aComplexity)
88         : mApplication(aApplication), mComplexity(aComplexity) {
89       MOZ_ASSERT(mComplexity <= 10);
90     }
91   };
92 
93 // From webrtc::VideoCodecVP8. mResilience is a boolean value because while
94 // VP8ResilienceMode has 3 values, kResilientFrames is not supported.
95 #  define VPX_COMMON_SETTINGS         \
96     const Complexity mComplexity;     \
97     const bool mResilience;           \
98     const uint8_t mNumTemporalLayers; \
99     const bool mDenoising;            \
100     const bool mAutoResize;           \
101     const bool mFrameDropping;
102 
103 // See webrtc::VideoEncoder::GetDefaultVp(8|9)Settings().
104 #  define VPX_COMMON_DEFAULTS(resize)                                          \
105     mComplexity(Complexity::Normal), mResilience(true), mNumTemporalLayers(1), \
106         mDenoising(true), mAutoResize(resize), mFrameDropping(0)
107 
108   struct VPXSpecific final {
109     enum class Complexity { Normal, High, Higher, Max };
110     struct VP8 final {
111       VPX_COMMON_SETTINGS
112       // Ignore webrtc::VideoCodecVP8::errorConcealmentOn,
113       // for it's always false in the codebase (except libwebrtc test cases).
114 
VP8final::final115       VP8() : VPX_COMMON_DEFAULTS(false /* auto resize */) {}
VP8final::final116       VP8(const Complexity aComplexity, const bool aResilience,
117           const uint8_t aNumTemporalLayers, const bool aDenoising,
118           const bool aAutoResize, const bool aFrameDropping)
119           : mComplexity(aComplexity),
120             mResilience(aResilience),
121             mNumTemporalLayers(aNumTemporalLayers),
122             mDenoising(aDenoising),
123             mAutoResize(aAutoResize),
124             mFrameDropping(aFrameDropping) {}
125     };
126 
127     struct VP9 final {
128       VPX_COMMON_SETTINGS
129       // From webrtc::VideoCodecVP9.
130       bool mAdaptiveQp;
131       uint8_t mNumSpatialLayers;
132       bool mFlexible;
133 
VP9final::final134       VP9()
135           : VPX_COMMON_DEFAULTS(true /* auto resize */),
136             mAdaptiveQp(true),
137             mNumSpatialLayers(1),
138             mFlexible(false) {}
VP9final::final139       VP9(const Complexity aComplexity, const bool aResilience,
140           const uint8_t aNumTemporalLayers, const bool aDenoising,
141           const bool aAutoResize, const bool aFrameDropping,
142           const bool aAdaptiveQp, const uint8_t aNumSpatialLayers,
143           const bool aFlexible)
144           : mComplexity(aComplexity),
145             mResilience(aResilience),
146             mNumTemporalLayers(aNumTemporalLayers),
147             mDenoising(aDenoising),
148             mAutoResize(aAutoResize),
149             mFrameDropping(aFrameDropping),
150             mAdaptiveQp(aAdaptiveQp),
151             mNumSpatialLayers(aNumSpatialLayers),
152             mFlexible(aFlexible) {}
153     };
154 
155     VPXSpecific() = delete;
156   };
157 
IsVideo(const CodecType aCodec)158   static bool IsVideo(const CodecType aCodec) {
159     return aCodec > CodecType::_BeginVideo_ && aCodec < CodecType::_EndVideo_;
160   }
IsAudio(const CodecType aCodec)161   static bool IsAudio(const CodecType aCodec) {
162     return aCodec > CodecType::_BeginAudio_ && aCodec < CodecType::_EndAudio_;
163   }
164 
165   using PixelFormat = dom::ImageBitmapFormat;
166   // Sample rate for audio, framerate for video, and bitrate for both.
167   using Rate = uint32_t;
168 
169   using InitPromise =
170       MozPromise<TrackInfo::TrackType, MediaResult, /* IsExclusive = */ true>;
171   using EncodedData = nsTArray<RefPtr<MediaRawData>>;
172   using EncodePromise =
173       MozPromise<EncodedData, MediaResult, /* IsExclusive = */ true>;
174 
175   // Initialize the encoder. It should be ready to encode once the returned
176   // promise resolves. The encoder should do any initialization here, rather
177   // than in its constructor or PlatformEncoderModule::Create*Encoder(),
178   // so that if the client needs to shutdown during initialization,
179   // it can call Shutdown() to cancel this operation. Any initialization
180   // that requires blocking the calling thread in this function *must*
181   // be done here so that it can be canceled by calling Shutdown()!
182   virtual RefPtr<InitPromise> Init() = 0;
183 
184   // Inserts a sample into the encoder's encode pipeline. The EncodePromise it
185   // returns will be resolved with already encoded MediaRawData at the moment,
186   // or empty when there is none available yet.
187   virtual RefPtr<EncodePromise> Encode(const MediaData* aSample) = 0;
188 
189   // Causes all complete samples in the pipeline that can be encoded to be
190   // output. It indicates that there is no more input sample to insert.
191   // This function is asynchronous.
192   // The MediaDataEncoder shall resolve the pending EncodePromise with drained
193   // samples. Drain will be called multiple times until the resolved
194   // EncodePromise is empty which indicates that there are no more samples to
195   // drain.
196   virtual RefPtr<EncodePromise> Drain() = 0;
197 
198   // Cancels all init/encode/drain operations, and shuts down the encoder. The
199   // platform encoder should clean up any resources it's using and release
200   // memory etc. The shutdown promise will be resolved once the encoder has
201   // completed shutdown. The client will delete the decoder once the promise is
202   // resolved.
203   // The ShutdownPromise must only ever be resolved.
204   virtual RefPtr<ShutdownPromise> Shutdown() = 0;
205 
SetBitrate(Rate aBitsPerSec)206   virtual RefPtr<GenericPromise> SetBitrate(Rate aBitsPerSec) {
207     return GenericPromise::CreateAndResolve(true, __func__);
208   }
209 
210   // Decoder needs to decide whether or not hardware acceleration is supported
211   // after creating. It doesn't need to call Init() before calling this
212   // function.
IsHardwareAccelerated(nsACString & aFailureReason)213   virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const {
214     return false;
215   }
216 
217   // Return the name of the MediaDataEncoder, only used for encoding.
218   // May be accessed in a non thread-safe fashion.
219   virtual nsCString GetDescriptionName() const = 0;
220 
221   friend class PlatformEncoderModule;
222 
223  protected:
224   template <typename T>
225   struct BaseConfig {
226     const CodecType mCodecType;
227     const Usage mUsage;
228     const Rate mBitsPerSec;
229     Maybe<T> mCodecSpecific;
230 
SetCodecSpecificBaseConfig231     void SetCodecSpecific(const T& aCodecSpecific) {
232       mCodecSpecific.emplace(aCodecSpecific);
233     }
234 
235    protected:
BaseConfigBaseConfig236     BaseConfig(const CodecType aCodecType, const Usage aUsage,
237                const Rate aBitsPerSec)
238         : mCodecType(aCodecType), mUsage(aUsage), mBitsPerSec(aBitsPerSec) {}
239 
240     virtual ~BaseConfig() = default;
241   };
242 
243   template <typename T>
244   struct VideoConfig final : public BaseConfig<T> {
245     const gfx::IntSize mSize;
246     const PixelFormat mSourcePixelFormat;
247     const uint8_t mFramerate;
248     const size_t mKeyframeInterval;
249 
VideoConfigfinal250     VideoConfig(const CodecType aCodecType, const Usage aUsage,
251                 const gfx::IntSize& aSize, const PixelFormat aSourcePixelFormat,
252                 const uint8_t aFramerate, const size_t aKeyframeInterval,
253                 const Rate aBitrate)
254         : BaseConfig<T>(aCodecType, aUsage, aBitrate),
255           mSize(aSize),
256           mSourcePixelFormat(aSourcePixelFormat),
257           mFramerate(aFramerate),
258           mKeyframeInterval(aKeyframeInterval) {}
259   };
260 
261   template <typename T>
262   struct AudioConfig final : public BaseConfig<T> {
263     const uint8_t mNumChannels;
264     const Rate mSampleRate;
265 
AudioConfigfinal266     AudioConfig(const CodecType aCodecType, const Usage aUsage,
267                 const Rate aBitrate, const Rate aSampleRate,
268                 const uint8_t aNumChannels)
269         : BaseConfig<T>(aCodecType, aUsage, aBitrate),
270           mNumChannels(aNumChannels),
271           mSampleRate(aSampleRate) {}
272   };
273 
274   virtual ~MediaDataEncoder() = default;
275 
276  public:
277   using H264Config = VideoConfig<H264Specific>;
278   using VP8Config = VideoConfig<VPXSpecific::VP8>;
279   using VP9Config = VideoConfig<VPXSpecific::VP9>;
280 };
281 
282 struct MOZ_STACK_CLASS CreateEncoderParams final {
283   union CodecSpecific {
284     MediaDataEncoder::H264Specific mH264;
285     MediaDataEncoder::OpusSpecific mOpus;
286     MediaDataEncoder::VPXSpecific::VP8 mVP8;
287     MediaDataEncoder::VPXSpecific::VP9 mVP9;
288 
CodecSpecific(const MediaDataEncoder::H264Specific && aH264)289     explicit CodecSpecific(const MediaDataEncoder::H264Specific&& aH264)
290         : mH264(aH264) {}
CodecSpecific(const MediaDataEncoder::OpusSpecific && aOpus)291     explicit CodecSpecific(const MediaDataEncoder::OpusSpecific&& aOpus)
292         : mOpus(aOpus) {}
CodecSpecific(const MediaDataEncoder::VPXSpecific::VP8 && aVP8)293     explicit CodecSpecific(const MediaDataEncoder::VPXSpecific::VP8&& aVP8)
294         : mVP8(aVP8) {}
CodecSpecific(const MediaDataEncoder::VPXSpecific::VP9 && aVP9)295     explicit CodecSpecific(const MediaDataEncoder::VPXSpecific::VP9&& aVP9)
296         : mVP9(aVP9) {}
297   };
298 
CreateEncoderParamsfinal299   CreateEncoderParams(const TrackInfo& aConfig,
300                       const MediaDataEncoder::Usage aUsage,
301                       const RefPtr<TaskQueue> aTaskQueue,
302                       const MediaDataEncoder::PixelFormat aPixelFormat,
303                       const uint8_t aFramerate, const size_t aKeyframeInterval,
304                       const MediaDataEncoder::Rate aBitrate)
305       : mConfig(aConfig),
306         mUsage(aUsage),
307         mTaskQueue(aTaskQueue),
308         mPixelFormat(aPixelFormat),
309         mFramerate(aFramerate),
310         mKeyframeInterval(aKeyframeInterval),
311         mBitrate(aBitrate) {
312     MOZ_ASSERT(mTaskQueue);
313   }
314 
315   template <typename... Ts>
CreateEncoderParamsfinal316   CreateEncoderParams(const TrackInfo& aConfig,
317                       const MediaDataEncoder::Usage aUsage,
318                       const RefPtr<TaskQueue> aTaskQueue,
319                       const MediaDataEncoder::PixelFormat aPixelFormat,
320                       const uint8_t aFramerate, const size_t aKeyframeInterval,
321                       const MediaDataEncoder::Rate aBitrate,
322                       const Ts&&... aCodecSpecific)
323       : mConfig(aConfig),
324         mUsage(aUsage),
325         mTaskQueue(aTaskQueue),
326         mPixelFormat(aPixelFormat),
327         mFramerate(aFramerate),
328         mKeyframeInterval(aKeyframeInterval),
329         mBitrate(aBitrate) {
330     MOZ_ASSERT(mTaskQueue);
331     SetCodecSpecific(std::forward<const Ts>(aCodecSpecific)...);
332   }
333 
334   template <typename T>
SetCodecSpecificfinal335   void SetCodecSpecific(const T&& aCodecSpecific) {
336     mCodecSpecific.emplace(std::forward<const T>(aCodecSpecific));
337   }
338 
ToH264Configfinal339   const MediaDataEncoder::H264Config ToH264Config() const {
340     const VideoInfo* info = mConfig.GetAsVideoInfo();
341     MOZ_ASSERT(info);
342 
343     auto config = MediaDataEncoder::H264Config(
344         MediaDataEncoder::CodecType::H264, mUsage, info->mImage, mPixelFormat,
345         mFramerate, mKeyframeInterval, mBitrate);
346     if (mCodecSpecific) {
347       config.SetCodecSpecific(mCodecSpecific.ref().mH264);
348     }
349 
350     return config;
351   }
352 
ToVP8Configfinal353   const MediaDataEncoder::VP8Config ToVP8Config() const {
354     const VideoInfo* info = mConfig.GetAsVideoInfo();
355     MOZ_ASSERT(info);
356 
357     auto config = MediaDataEncoder::VP8Config(
358         CodecTypeForMime(info->mMimeType), mUsage, info->mImage, mPixelFormat,
359         mFramerate, mKeyframeInterval, mBitrate);
360     if (mCodecSpecific) {
361       config.SetCodecSpecific(mCodecSpecific.ref().mVP8);
362     }
363     return config;
364   }
365 
ToVP9Configfinal366   const MediaDataEncoder::VP9Config ToVP9Config() const {
367     const VideoInfo* info = mConfig.GetAsVideoInfo();
368     MOZ_ASSERT(info);
369 
370     auto config = MediaDataEncoder::VP9Config(
371         CodecTypeForMime(info->mMimeType), mUsage, info->mImage, mPixelFormat,
372         mFramerate, mKeyframeInterval, mBitrate);
373     if (mCodecSpecific) {
374       config.SetCodecSpecific(mCodecSpecific.ref().mVP9);
375     }
376     return config;
377   }
378 
CodecTypeForMimefinal379   static MediaDataEncoder::CodecType CodecTypeForMime(
380       const nsACString& aMimeType) {
381     if (MP4Decoder::IsH264(aMimeType)) {
382       return MediaDataEncoder::CodecType::H264;
383     } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
384       return MediaDataEncoder::CodecType::VP8;
385     } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9)) {
386       return MediaDataEncoder::CodecType::VP9;
387     } else {
388       MOZ_ASSERT_UNREACHABLE("Unsupported Mimetype");
389       return MediaDataEncoder::CodecType::Unknown;
390     }
391   }
392 
393   const TrackInfo& mConfig;
394   const MediaDataEncoder::Usage mUsage;
395   const RefPtr<TaskQueue> mTaskQueue;
396   const MediaDataEncoder::PixelFormat mPixelFormat;
397   const uint8_t mFramerate;
398   const size_t mKeyframeInterval;
399   const MediaDataEncoder::Rate mBitrate;
400   Maybe<CodecSpecific> mCodecSpecific;
401 
402  private:
403 };
404 
405 }  // namespace mozilla
406 
407 #endif /* PlatformEncoderModule_h_ */
408