1 /*
2  *  Copyright (c) 2012, The WebRTC project authors. All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions are
6  *  met:
7  *
8  *    * Redistributions of source code must retain the above copyright
9  *      notice, this list of conditions and the following disclaimer.
10  *
11  *    * Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in
13  *      the documentation and/or other materials provided with the
14  *      distribution.
15  *
16  *    * Neither the name of Google nor the names of its contributors may
17  *      be used to endorse or promote products derived from this software
18  *      without specific prior written permission.
19  *
20  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #ifndef WEBRTCGMPVIDEOCODEC_H_
34 #define WEBRTCGMPVIDEOCODEC_H_
35 
36 #include <iostream>
37 #include <queue>
38 #include <string>
39 
40 #include "nsThreadUtils.h"
41 #include "mozilla/Monitor.h"
42 #include "mozilla/Mutex.h"
43 
44 #include "mozIGeckoMediaPluginService.h"
45 #include "MediaConduitInterface.h"
46 #include "AudioConduit.h"
47 #include "VideoConduit.h"
48 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
49 
50 #include "gmp-video-host.h"
51 #include "GMPVideoDecoderProxy.h"
52 #include "GMPVideoEncoderProxy.h"
53 
54 #include "PeerConnectionImpl.h"
55 
56 namespace mozilla {
57 
58 // Class that allows code on the other side of webrtc.org to tell
59 // WebrtcGmpVideoEncoder/Decoder what PC they should send errors to.
60 // This is necessary because webrtc.org gives us no way to plumb the handle
61 // through, nor does it give us any way to inform it of an error that will
62 // make it back to the PC that cares (except for errors encountered
63 // synchronously in functions like InitEncode/Decode, which will not happen
64 // because GMP init is async).
65 // Right now, this is used in MediaPipelineFactory.
66 class WebrtcGmpPCHandleSetter
67 {
68   public:
69     explicit WebrtcGmpPCHandleSetter(const std::string& aPCHandle);
70 
71     ~WebrtcGmpPCHandleSetter();
72 
73     static std::string GetCurrentHandle();
74 
75   private:
76     static std::string sCurrentHandle;
77 };
78 
79 class GmpInitDoneRunnable : public Runnable
80 {
81   public:
GmpInitDoneRunnable(const std::string & aPCHandle)82     explicit GmpInitDoneRunnable(const std::string& aPCHandle) :
83       mResult(WEBRTC_VIDEO_CODEC_OK),
84       mPCHandle(aPCHandle)
85     {
86     }
87 
Run()88     NS_IMETHOD Run() override
89     {
90       if (mResult == WEBRTC_VIDEO_CODEC_OK) {
91         // Might be useful to notify the PeerConnection about successful init
92         // someday.
93         return NS_OK;
94       }
95 
96       PeerConnectionWrapper wrapper(mPCHandle);
97       if (wrapper.impl()) {
98         wrapper.impl()->OnMediaError(mError);
99       }
100       return NS_OK;
101     }
102 
103     void Dispatch(int32_t aResult, const std::string& aError = "")
104     {
105       mResult = aResult;
106       mError = aError;
107       nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
108       if (mainThread) {
109         // For some reason, the compiler on CI is treating |this| as a const
110         // pointer, despite the fact that we're in a non-const function. And,
111         // interestingly enough, correcting this doesn't require a const_cast.
112         mainThread->Dispatch(do_AddRef(static_cast<nsIRunnable*>(this)),
113                              NS_DISPATCH_NORMAL);
114       }
115     }
116 
Result()117     int32_t Result()
118     {
119       return mResult;
120     }
121 
122   private:
123     int32_t mResult;
124     std::string mPCHandle;
125     std::string mError;
126 };
127 
128 class WebrtcGmpVideoEncoder : public GMPVideoEncoderCallbackProxy
129 {
130 public:
131   WebrtcGmpVideoEncoder();
132   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoEncoder);
133 
134   // Implement VideoEncoder interface, sort of.
135   // (We cannot use |Release|, since that's needed for nsRefPtr)
PluginID()136   virtual uint64_t PluginID() const
137   {
138     return mCachedPluginId;
139   }
140 
141   virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
142                              int32_t aNumberOfCores,
143                              uint32_t aMaxPayloadSize);
144 
145   virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
146                          const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
147                          const std::vector<webrtc::VideoFrameType>* aFrameTypes);
148 
149   virtual int32_t RegisterEncodeCompleteCallback(
150     webrtc::EncodedImageCallback* aCallback);
151 
152   virtual int32_t ReleaseGmp();
153 
154   virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
155                                        int aRTT);
156 
157   virtual int32_t SetRates(uint32_t aNewBitRate,
158                            uint32_t aFrameRate);
159 
160   // GMPVideoEncoderCallback virtual functions.
161   virtual void Terminated() override;
162 
163   virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
164                        const nsTArray<uint8_t>& aCodecSpecificInfo) override;
165 
Error(GMPErr aError)166   virtual void Error(GMPErr aError) override {
167   }
168 
169 private:
170   virtual ~WebrtcGmpVideoEncoder();
171 
172   static void InitEncode_g(const RefPtr<WebrtcGmpVideoEncoder>& aThis,
173                            const GMPVideoCodec& aCodecParams,
174                            int32_t aNumberOfCores,
175                            uint32_t aMaxPayloadSize,
176                            const RefPtr<GmpInitDoneRunnable>& aInitDone);
177   int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
178                       const GMPVideoCodec& aCodecParams,
179                       uint32_t aMaxPayloadSize,
180                       std::string* aErrorOut);
181   int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP,
182                       GMPVideoHost* aHost,
183                       std::string* aErrorOut);
184   int32_t InitEncoderForSize(unsigned short aWidth,
185                              unsigned short aHeight,
186                              std::string* aErrorOut);
187   static void ReleaseGmp_g(RefPtr<WebrtcGmpVideoEncoder>& aEncoder);
188   void Close_g();
189 
190   class InitDoneCallback : public GetGMPVideoEncoderCallback
191   {
192   public:
InitDoneCallback(const RefPtr<WebrtcGmpVideoEncoder> & aEncoder,const RefPtr<GmpInitDoneRunnable> & aInitDone,const GMPVideoCodec & aCodecParams,uint32_t aMaxPayloadSize)193     InitDoneCallback(const RefPtr<WebrtcGmpVideoEncoder>& aEncoder,
194                      const RefPtr<GmpInitDoneRunnable>& aInitDone,
195                      const GMPVideoCodec& aCodecParams,
196                      uint32_t aMaxPayloadSize)
197       : mEncoder(aEncoder),
198         mInitDone(aInitDone),
199         mCodecParams(aCodecParams),
200         mMaxPayloadSize(aMaxPayloadSize)
201     {
202     }
203 
Done(GMPVideoEncoderProxy * aGMP,GMPVideoHost * aHost)204     virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
205     {
206       std::string errorOut;
207       int32_t result = mEncoder->GmpInitDone(aGMP,
208                                              aHost,
209                                              mCodecParams,
210                                              mMaxPayloadSize,
211                                              &errorOut);
212 
213       mInitDone->Dispatch(result, errorOut);
214     }
215 
216   private:
217     RefPtr<WebrtcGmpVideoEncoder> mEncoder;
218     RefPtr<GmpInitDoneRunnable> mInitDone;
219     GMPVideoCodec mCodecParams;
220     uint32_t mMaxPayloadSize;
221   };
222 
223   int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
224                    const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
225                    const std::vector<webrtc::VideoFrameType>* aFrameTypes);
226   void RegetEncoderForResolutionChange(
227       uint32_t aWidth,
228       uint32_t aHeight,
229       const RefPtr<GmpInitDoneRunnable>& aInitDone);
230 
231   class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
232   {
233   public:
InitDoneForResolutionChangeCallback(const RefPtr<WebrtcGmpVideoEncoder> & aEncoder,const RefPtr<GmpInitDoneRunnable> & aInitDone,uint32_t aWidth,uint32_t aHeight)234     InitDoneForResolutionChangeCallback(
235         const RefPtr<WebrtcGmpVideoEncoder>& aEncoder,
236         const RefPtr<GmpInitDoneRunnable>& aInitDone,
237         uint32_t aWidth,
238         uint32_t aHeight)
239       : mEncoder(aEncoder),
240         mInitDone(aInitDone),
241         mWidth(aWidth),
242         mHeight(aHeight)
243     {
244     }
245 
Done(GMPVideoEncoderProxy * aGMP,GMPVideoHost * aHost)246     virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
247     {
248       std::string errorOut;
249       int32_t result = mEncoder->GmpInitDone(aGMP, aHost, &errorOut);
250       if (result != WEBRTC_VIDEO_CODEC_OK) {
251         mInitDone->Dispatch(result, errorOut);
252         return;
253       }
254 
255       result = mEncoder->InitEncoderForSize(mWidth, mHeight, &errorOut);
256       mInitDone->Dispatch(result, errorOut);
257     }
258 
259   private:
260     RefPtr<WebrtcGmpVideoEncoder> mEncoder;
261     RefPtr<GmpInitDoneRunnable> mInitDone;
262     uint32_t mWidth;
263     uint32_t mHeight;
264   };
265 
266   static int32_t SetRates_g(RefPtr<WebrtcGmpVideoEncoder> aThis,
267                              uint32_t aNewBitRate,
268                              uint32_t aFrameRate);
269 
270   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
271   nsCOMPtr<nsIThread> mGMPThread;
272   GMPVideoEncoderProxy* mGMP;
273   // Used to handle a race where Release() is called while init is in progress
274   bool mInitting;
275   GMPVideoHost* mHost;
276   GMPVideoCodec mCodecParams;
277   uint32_t mMaxPayloadSize;
278   webrtc::CodecSpecificInfo mCodecSpecificInfo;
279   // Protects mCallback
280   Mutex mCallbackMutex;
281   webrtc::EncodedImageCallback* mCallback;
282   uint64_t mCachedPluginId;
283   std::string mPCHandle;
284 };
285 
286 
287 // Basically a strong ref to a WebrtcGmpVideoEncoder, that also translates
288 // from Release() to WebrtcGmpVideoEncoder::ReleaseGmp(), since we need
289 // WebrtcGmpVideoEncoder::Release() for managing the refcount.
290 // The webrtc.org code gets one of these, so it doesn't unilaterally delete
291 // the "real" encoder.
292 class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder
293 {
294   public:
WebrtcVideoEncoderProxy()295     WebrtcVideoEncoderProxy() :
296       mEncoderImpl(new WebrtcGmpVideoEncoder)
297     {}
298 
~WebrtcVideoEncoderProxy()299     virtual ~WebrtcVideoEncoderProxy()
300     {
301       RegisterEncodeCompleteCallback(nullptr);
302     }
303 
PluginID()304     uint64_t PluginID() const override
305     {
306       return mEncoderImpl->PluginID();
307     }
308 
InitEncode(const webrtc::VideoCodec * aCodecSettings,int32_t aNumberOfCores,size_t aMaxPayloadSize)309     int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
310                        int32_t aNumberOfCores,
311                        size_t aMaxPayloadSize) override
312     {
313       return mEncoderImpl->InitEncode(aCodecSettings,
314                                       aNumberOfCores,
315                                       aMaxPayloadSize);
316     }
317 
Encode(const webrtc::I420VideoFrame & aInputImage,const webrtc::CodecSpecificInfo * aCodecSpecificInfo,const std::vector<webrtc::VideoFrameType> * aFrameTypes)318     int32_t Encode(
319         const webrtc::I420VideoFrame& aInputImage,
320         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
321         const std::vector<webrtc::VideoFrameType>* aFrameTypes) override
322     {
323       return mEncoderImpl->Encode(aInputImage,
324                                   aCodecSpecificInfo,
325                                   aFrameTypes);
326     }
327 
RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback * aCallback)328     int32_t RegisterEncodeCompleteCallback(
329       webrtc::EncodedImageCallback* aCallback) override
330     {
331       return mEncoderImpl->RegisterEncodeCompleteCallback(aCallback);
332     }
333 
Release()334     int32_t Release() override
335     {
336       return mEncoderImpl->ReleaseGmp();
337     }
338 
SetChannelParameters(uint32_t aPacketLoss,int64_t aRTT)339     int32_t SetChannelParameters(uint32_t aPacketLoss,
340                                  int64_t aRTT) override
341     {
342       return mEncoderImpl->SetChannelParameters(aPacketLoss, aRTT);
343     }
344 
SetRates(uint32_t aNewBitRate,uint32_t aFrameRate)345     int32_t SetRates(uint32_t aNewBitRate,
346                      uint32_t aFrameRate) override
347     {
348       return mEncoderImpl->SetRates(aNewBitRate, aFrameRate);
349     }
350 
351   private:
352     RefPtr<WebrtcGmpVideoEncoder> mEncoderImpl;
353 };
354 
355 class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy
356 {
357 public:
358   WebrtcGmpVideoDecoder();
359   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoDecoder);
360 
361   // Implement VideoEncoder interface, sort of.
362   // (We cannot use |Release|, since that's needed for nsRefPtr)
PluginID()363   virtual uint64_t PluginID() const
364   {
365     return mCachedPluginId;
366   }
367 
368   virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
369                              int32_t aNumberOfCores);
370   virtual int32_t Decode(const webrtc::EncodedImage& aInputImage,
371                          bool aMissingFrames,
372                          const webrtc::RTPFragmentationHeader* aFragmentation,
373                          const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
374                          int64_t aRenderTimeMs);
375   virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback);
376 
377   virtual int32_t ReleaseGmp();
378 
379   virtual int32_t Reset();
380 
381   // GMPVideoDecoderCallbackProxy
382   virtual void Terminated() override;
383 
384   virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
385 
ReceivedDecodedReferenceFrame(const uint64_t aPictureId)386   virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override {
387     MOZ_CRASH();
388   }
389 
ReceivedDecodedFrame(const uint64_t aPictureId)390   virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {
391     MOZ_CRASH();
392   }
393 
InputDataExhausted()394   virtual void InputDataExhausted() override {
395   }
396 
DrainComplete()397   virtual void DrainComplete() override {
398   }
399 
ResetComplete()400   virtual void ResetComplete() override {
401   }
402 
Error(GMPErr aError)403   virtual void Error(GMPErr aError) override {
404      mDecoderStatus = aError;
405   }
406 
407 private:
408   virtual ~WebrtcGmpVideoDecoder();
409 
410   static void InitDecode_g(
411       const RefPtr<WebrtcGmpVideoDecoder>& aThis,
412       const webrtc::VideoCodec* aCodecSettings,
413       int32_t aNumberOfCores,
414       const RefPtr<GmpInitDoneRunnable>& aInitDone);
415   int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP,
416                       GMPVideoHost* aHost,
417                       std::string* aErrorOut);
418   static void ReleaseGmp_g(RefPtr<WebrtcGmpVideoDecoder>& aDecoder);
419   void Close_g();
420 
421   class InitDoneCallback : public GetGMPVideoDecoderCallback
422   {
423   public:
InitDoneCallback(const RefPtr<WebrtcGmpVideoDecoder> & aDecoder,const RefPtr<GmpInitDoneRunnable> & aInitDone)424     explicit InitDoneCallback(const RefPtr<WebrtcGmpVideoDecoder>& aDecoder,
425                               const RefPtr<GmpInitDoneRunnable>& aInitDone)
426       : mDecoder(aDecoder),
427         mInitDone(aInitDone)
428     {
429     }
430 
Done(GMPVideoDecoderProxy * aGMP,GMPVideoHost * aHost)431     virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
432     {
433       std::string errorOut;
434       int32_t result = mDecoder->GmpInitDone(aGMP, aHost, &errorOut);
435 
436       mInitDone->Dispatch(result, errorOut);
437     }
438 
439   private:
440     RefPtr<WebrtcGmpVideoDecoder> mDecoder;
441     RefPtr<GmpInitDoneRunnable> mInitDone;
442   };
443 
444   virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
445                            bool aMissingFrames,
446                            const webrtc::RTPFragmentationHeader* aFragmentation,
447                            const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
448                            int64_t aRenderTimeMs);
449 
450   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
451   nsCOMPtr<nsIThread> mGMPThread;
452   GMPVideoDecoderProxy* mGMP; // Addref is held for us
453   // Used to handle a race where Release() is called while init is in progress
454   bool mInitting;
455   GMPVideoHost* mHost;
456   // Protects mCallback
457   Mutex mCallbackMutex;
458   webrtc::DecodedImageCallback* mCallback;
459   Atomic<uint64_t> mCachedPluginId;
460   GMPErr mDecoderStatus;
461   std::string mPCHandle;
462 };
463 
464 // Basically a strong ref to a WebrtcGmpVideoDecoder, that also translates
465 // from Release() to WebrtcGmpVideoDecoder::ReleaseGmp(), since we need
466 // WebrtcGmpVideoDecoder::Release() for managing the refcount.
467 // The webrtc.org code gets one of these, so it doesn't unilaterally delete
468 // the "real" encoder.
469 class WebrtcVideoDecoderProxy : public WebrtcVideoDecoder
470 {
471   public:
WebrtcVideoDecoderProxy()472     WebrtcVideoDecoderProxy() :
473       mDecoderImpl(new WebrtcGmpVideoDecoder)
474     {}
475 
~WebrtcVideoDecoderProxy()476     virtual ~WebrtcVideoDecoderProxy()
477     {
478       RegisterDecodeCompleteCallback(nullptr);
479     }
480 
PluginID()481     uint64_t PluginID() const override
482     {
483       return mDecoderImpl->PluginID();
484     }
485 
InitDecode(const webrtc::VideoCodec * aCodecSettings,int32_t aNumberOfCores)486     int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
487                        int32_t aNumberOfCores) override
488     {
489       return mDecoderImpl->InitDecode(aCodecSettings, aNumberOfCores);
490     }
491 
Decode(const webrtc::EncodedImage & aInputImage,bool aMissingFrames,const webrtc::RTPFragmentationHeader * aFragmentation,const webrtc::CodecSpecificInfo * aCodecSpecificInfo,int64_t aRenderTimeMs)492     int32_t Decode(
493         const webrtc::EncodedImage& aInputImage,
494         bool aMissingFrames,
495         const webrtc::RTPFragmentationHeader* aFragmentation,
496         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
497         int64_t aRenderTimeMs) override
498     {
499       return mDecoderImpl->Decode(aInputImage,
500                                   aMissingFrames,
501                                   aFragmentation,
502                                   aCodecSpecificInfo,
503                                   aRenderTimeMs);
504     }
505 
RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback * aCallback)506     int32_t RegisterDecodeCompleteCallback(
507       webrtc::DecodedImageCallback* aCallback) override
508     {
509       return mDecoderImpl->RegisterDecodeCompleteCallback(aCallback);
510     }
511 
Release()512     int32_t Release() override
513     {
514       return mDecoderImpl->ReleaseGmp();
515     }
516 
Reset()517     int32_t Reset() override
518     {
519       return mDecoderImpl->Reset();
520     }
521 
522   private:
523     RefPtr<WebrtcGmpVideoDecoder> mDecoderImpl;
524 };
525 
526 }
527 
528 #endif
529