browser(firefox): cross thread sync in screencast (#16320)
* nsIScreencastServiceClient is not thread safe refcounted so we make nsScreencastService::Session a thread safe refcounted object and keep it alive while there are inflight frames. Once such frames get handled on the main thread we check if the session has been stopped.
* Removed mCaptureCallbackCs in favor of atomic counter (mClient is not accessed only on the main thread).
* HeadlessWindowCapturer now holds RefPtr to the headless window object to avoid use after free when clearing it as a listener on the widget.
* ScreencastEncoder is not ref counted anymore.
Pretty-diff: 5f5042ff1e
			
			
This commit is contained in:
		
							parent
							
								
									7c4099a011
								
							
						
					
					
						commit
						02aa31048c
					
				|  | @ -1,2 +1,2 @@ | ||||||
| 1342 | 1343 | ||||||
| Changed: yurys@chromium.org Thu Aug  4 18:33:22 PDT 2022 | Changed: yurys@chromium.org Fri Aug  5 15:17:14 PDT 2022 | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ class HeadlessWindowCapturer : public webrtc::VideoCaptureModuleEx { | ||||||
|  private: |  private: | ||||||
|   void NotifyFrameCaptured(const webrtc::VideoFrame& frame); |   void NotifyFrameCaptured(const webrtc::VideoFrame& frame); | ||||||
| 
 | 
 | ||||||
|   mozilla::widget::HeadlessWidget* mWindow = nullptr; |   RefPtr<mozilla::widget::HeadlessWidget> mWindow; | ||||||
|   rtc::RecursiveCriticalSection _callBackCs; |   rtc::RecursiveCriticalSection _callBackCs; | ||||||
|   std::set<rtc::VideoSinkInterface<webrtc::VideoFrame>*> _dataCallBacks; |   std::set<rtc::VideoSinkInterface<webrtc::VideoFrame>*> _dataCallBacks; | ||||||
|   std::set<webrtc::RawFrameCallback*> _rawFrameCallbacks; |   std::set<webrtc::RawFrameCallback*> _rawFrameCallbacks; | ||||||
|  |  | ||||||
|  | @ -299,7 +299,7 @@ ScreencastEncoder::~ScreencastEncoder() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RefPtr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin) | std::unique_ptr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin) | ||||||
| { | { | ||||||
|     vpx_codec_iface_t* codec_interface = vpx_codec_vp8_cx(); |     vpx_codec_iface_t* codec_interface = vpx_codec_vp8_cx(); | ||||||
|     if (!codec_interface) { |     if (!codec_interface) { | ||||||
|  | @ -340,7 +340,7 @@ RefPtr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, cons | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<VPXCodec> vpxCodec(new VPXCodec(std::move(codec), cfg, file)); |     std::unique_ptr<VPXCodec> vpxCodec(new VPXCodec(std::move(codec), cfg, file)); | ||||||
|     // fprintf(stderr, "ScreencastEncoder initialized with: %s\n", vpx_codec_iface_name(codec_interface));
 |     // fprintf(stderr, "ScreencastEncoder initialized with: %s\n", vpx_codec_iface_name(codec_interface));
 | ||||||
|     return new ScreencastEncoder(std::move(vpxCodec), margin); |     return std::make_unique<ScreencastEncoder>(std::move(vpxCodec), margin); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScreencastEncoder::flushLastFrame() | void ScreencastEncoder::flushLastFrame() | ||||||
|  |  | ||||||
|  | @ -19,22 +19,20 @@ class VideoFrame; | ||||||
| namespace mozilla { | namespace mozilla { | ||||||
| 
 | 
 | ||||||
| class ScreencastEncoder { | class ScreencastEncoder { | ||||||
|     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ScreencastEncoder) |  | ||||||
| public: | public: | ||||||
|     static constexpr int fps = 25; |     static constexpr int fps = 25; | ||||||
| 
 | 
 | ||||||
|     static RefPtr<ScreencastEncoder> create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin); |     static std::unique_ptr<ScreencastEncoder> create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin); | ||||||
| 
 | 
 | ||||||
|     class VPXCodec; |     class VPXCodec; | ||||||
|     ScreencastEncoder(std::unique_ptr<VPXCodec>, const gfx::IntMargin& margin); |     ScreencastEncoder(std::unique_ptr<VPXCodec>, const gfx::IntMargin& margin); | ||||||
|  |     ~ScreencastEncoder(); | ||||||
| 
 | 
 | ||||||
|     void encodeFrame(const webrtc::VideoFrame& videoFrame); |     void encodeFrame(const webrtc::VideoFrame& videoFrame); | ||||||
| 
 | 
 | ||||||
|     void finish(std::function<void()>&& callback); |     void finish(std::function<void()>&& callback); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     ~ScreencastEncoder(); |  | ||||||
| 
 |  | ||||||
|     void flushLastFrame(); |     void flushLastFrame(); | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<VPXCodec> m_vpxCodec; |     std::unique_ptr<VPXCodec> m_vpxCodec; | ||||||
|  |  | ||||||
|  | @ -78,11 +78,10 @@ nsresult generateUid(nsString& uid) { | ||||||
| 
 | 
 | ||||||
| class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::VideoFrame>, | class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::VideoFrame>, | ||||||
|                                      public webrtc::RawFrameCallback { |                                      public webrtc::RawFrameCallback { | ||||||
|  public: |  | ||||||
|   Session( |   Session( | ||||||
|     nsIScreencastServiceClient* client, |     nsIScreencastServiceClient* client, | ||||||
|     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, |     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, | ||||||
|     RefPtr<ScreencastEncoder>&& encoder, |     std::unique_ptr<ScreencastEncoder> encoder, | ||||||
|     int width, int height, |     int width, int height, | ||||||
|     int viewportWidth, int viewportHeight, |     int viewportWidth, int viewportHeight, | ||||||
|     gfx::IntMargin margin, |     gfx::IntMargin margin, | ||||||
|  | @ -97,6 +96,20 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       , mViewportHeight(viewportHeight) |       , mViewportHeight(viewportHeight) | ||||||
|       , mMargin(margin) { |       , mMargin(margin) { | ||||||
|   } |   } | ||||||
|  |   ~Session() override = default; | ||||||
|  | 
 | ||||||
|  |  public: | ||||||
|  |   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session) | ||||||
|  |   static RefPtr<Session> Create( | ||||||
|  |     nsIScreencastServiceClient* client, | ||||||
|  |     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, | ||||||
|  |     std::unique_ptr<ScreencastEncoder> encoder, | ||||||
|  |     int width, int height, | ||||||
|  |     int viewportWidth, int viewportHeight, | ||||||
|  |     gfx::IntMargin margin, | ||||||
|  |     uint32_t jpegQuality) { | ||||||
|  |     return do_AddRef(new Session(client, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, jpegQuality)); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   bool Start() { |   bool Start() { | ||||||
|     webrtc::VideoCaptureCapability capability; |     webrtc::VideoCaptureCapability capability; | ||||||
|  | @ -119,6 +132,11 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void Stop() { |   void Stop() { | ||||||
|  |     if (mStopped) { | ||||||
|  |       fprintf(stderr, "Screencast session has already been stopped\n"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     mStopped = true; | ||||||
|     if (mEncoder) |     if (mEncoder) | ||||||
|       mCaptureModule->DeRegisterCaptureDataCallback(this); |       mCaptureModule->DeRegisterCaptureDataCallback(this); | ||||||
|     else |     else | ||||||
|  | @ -128,23 +146,23 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       fprintf(stderr, "StopCapture error %d\n", error); |       fprintf(stderr, "StopCapture error %d\n", error); | ||||||
|     } |     } | ||||||
|     if (mEncoder) { |     if (mEncoder) { | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |       mEncoder->finish([this, protect = RefPtr{this}] { | ||||||
|       mEncoder->finish([client = std::move(mClient)] { |  | ||||||
|         NS_DispatchToMainThread(NS_NewRunnableFunction( |         NS_DispatchToMainThread(NS_NewRunnableFunction( | ||||||
|             "NotifyScreencastStopped", [client = std::move(client)]() -> void { |             "NotifyScreencastStopped", [this, protect = std::move(protect)]() -> void { | ||||||
|               client->ScreencastStopped(); |               mClient->ScreencastStopped(); | ||||||
|             })); |             })); | ||||||
|       }); |       }); | ||||||
|     } else { |     } else { | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |  | ||||||
|       mClient->ScreencastStopped(); |       mClient->ScreencastStopped(); | ||||||
|       mClient = nullptr; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void ScreencastFrameAck() { |   void ScreencastFrameAck() { | ||||||
|     rtc::CritScope lock(&mCaptureCallbackCs); |     if (mFramesInFlight.load() == 0) { | ||||||
|     --mFramesInFlight; |       fprintf(stderr, "ScreencastFrameAck is called while there are no inflight frames\n"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     mFramesInFlight.fetch_sub(1); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // These callbacks end up running on the VideoCapture thread.
 |   // These callbacks end up running on the VideoCapture thread.
 | ||||||
|  | @ -167,15 +185,8 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|     if (mViewportHeight && pageHeight > mViewportHeight) |     if (mViewportHeight && pageHeight > mViewportHeight) | ||||||
|       pageHeight = mViewportHeight; |       pageHeight = mViewportHeight; | ||||||
| 
 | 
 | ||||||
|     { |     if (mFramesInFlight.load() >= kMaxFramesInFlight) | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |       return; | ||||||
|       if (mFramesInFlight >= kMaxFramesInFlight) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       ++mFramesInFlight; |  | ||||||
|       if (!mClient) |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     int screenshotWidth = pageWidth; |     int screenshotWidth = pageWidth; | ||||||
|     int screenshotHeight = pageHeight; |     int screenshotHeight = pageHeight; | ||||||
|  | @ -256,21 +267,23 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nsIScreencastServiceClient* client = mClient.get(); |     mFramesInFlight.fetch_add(1); | ||||||
|     NS_DispatchToMainThread(NS_NewRunnableFunction( |     NS_DispatchToMainThread(NS_NewRunnableFunction( | ||||||
|         "NotifyScreencastFrame", [client, base64, pageWidth, pageHeight]() -> void { |         "NotifyScreencastFrame", [this, protect = RefPtr{this}, base64, pageWidth, pageHeight]() -> void { | ||||||
|  |           if (mStopped) | ||||||
|  |             return; | ||||||
|           NS_ConvertUTF8toUTF16 utf16(base64); |           NS_ConvertUTF8toUTF16 utf16(base64); | ||||||
|           client->ScreencastFrame(utf16, pageWidth, pageHeight); |           mClient->ScreencastFrame(utf16, pageWidth, pageHeight); | ||||||
|         })); |         })); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   RefPtr<nsIScreencastServiceClient> mClient; |   RefPtr<nsIScreencastServiceClient> mClient; | ||||||
|   rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> mCaptureModule; |   rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> mCaptureModule; | ||||||
|   RefPtr<ScreencastEncoder> mEncoder; |   std::unique_ptr<ScreencastEncoder> mEncoder; | ||||||
|   uint32_t mJpegQuality; |   uint32_t mJpegQuality; | ||||||
|   rtc::RecursiveCriticalSection mCaptureCallbackCs; |   bool mStopped = false; | ||||||
|   uint32_t mFramesInFlight = 0; |   std::atomic<uint32_t> mFramesInFlight = 0; | ||||||
|   int mWidth; |   int mWidth; | ||||||
|   int mHeight; |   int mHeight; | ||||||
|   int mViewportWidth; |   int mViewportWidth; | ||||||
|  | @ -322,7 +335,7 @@ nsresult nsScreencastService::StartVideoRecording(nsIScreencastServiceClient* aC | ||||||
|   margin.top += offsetTop; |   margin.top += offsetTop; | ||||||
| 
 | 
 | ||||||
|   nsCString error; |   nsCString error; | ||||||
|   RefPtr<ScreencastEncoder> encoder; |   std::unique_ptr<ScreencastEncoder> encoder; | ||||||
|   if (isVideo) { |   if (isVideo) { | ||||||
|     encoder = ScreencastEncoder::create(error, PromiseFlatCString(aVideoFileName), width, height, margin); |     encoder = ScreencastEncoder::create(error, PromiseFlatCString(aVideoFileName), width, height, margin); | ||||||
|     if (!encoder) { |     if (!encoder) { | ||||||
|  | @ -336,7 +349,7 @@ nsresult nsScreencastService::StartVideoRecording(nsIScreencastServiceClient* aC | ||||||
|   NS_ENSURE_SUCCESS(rv, rv); |   NS_ENSURE_SUCCESS(rv, rv); | ||||||
|   sessionId = uid; |   sessionId = uid; | ||||||
| 
 | 
 | ||||||
|   auto session = std::make_unique<Session>(aClient, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, isVideo ? 0 : quality); |   auto session = Session::Create(aClient, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, isVideo ? 0 : quality); | ||||||
|   if (!session->Start()) |   if (!session->Start()) | ||||||
|     return NS_ERROR_FAILURE; |     return NS_ERROR_FAILURE; | ||||||
|   mIdToSession.emplace(sessionId, std::move(session)); |   mIdToSession.emplace(sessionId, std::move(session)); | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ class nsScreencastService final : public nsIScreencastService { | ||||||
|   ~nsScreencastService(); |   ~nsScreencastService(); | ||||||
| 
 | 
 | ||||||
|   class Session; |   class Session; | ||||||
|   std::map<nsString, std::unique_ptr<Session>> mIdToSession; |   std::map<nsString, RefPtr<Session>> mIdToSession; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| }  // namespace mozilla
 | }  // namespace mozilla
 | ||||||
|  |  | ||||||
|  | @ -1,2 +1,2 @@ | ||||||
| 1343 | 1344 | ||||||
| Changed: yurys@chromium.org Thu Aug  4 18:29:49 PDT 2022 | Changed: yurys@chromium.org Fri Aug  5 15:15:08 PDT 2022 | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ class HeadlessWindowCapturer : public webrtc::VideoCaptureModuleEx { | ||||||
|  private: |  private: | ||||||
|   void NotifyFrameCaptured(const webrtc::VideoFrame& frame); |   void NotifyFrameCaptured(const webrtc::VideoFrame& frame); | ||||||
| 
 | 
 | ||||||
|   mozilla::widget::HeadlessWidget* mWindow = nullptr; |   RefPtr<mozilla::widget::HeadlessWidget> mWindow; | ||||||
|   rtc::RecursiveCriticalSection _callBackCs; |   rtc::RecursiveCriticalSection _callBackCs; | ||||||
|   std::set<rtc::VideoSinkInterface<webrtc::VideoFrame>*> _dataCallBacks; |   std::set<rtc::VideoSinkInterface<webrtc::VideoFrame>*> _dataCallBacks; | ||||||
|   std::set<webrtc::RawFrameCallback*> _rawFrameCallbacks; |   std::set<webrtc::RawFrameCallback*> _rawFrameCallbacks; | ||||||
|  |  | ||||||
|  | @ -299,7 +299,7 @@ ScreencastEncoder::~ScreencastEncoder() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RefPtr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin) | std::unique_ptr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin) | ||||||
| { | { | ||||||
|     vpx_codec_iface_t* codec_interface = vpx_codec_vp8_cx(); |     vpx_codec_iface_t* codec_interface = vpx_codec_vp8_cx(); | ||||||
|     if (!codec_interface) { |     if (!codec_interface) { | ||||||
|  | @ -340,7 +340,7 @@ RefPtr<ScreencastEncoder> ScreencastEncoder::create(nsCString& errorString, cons | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<VPXCodec> vpxCodec(new VPXCodec(std::move(codec), cfg, file)); |     std::unique_ptr<VPXCodec> vpxCodec(new VPXCodec(std::move(codec), cfg, file)); | ||||||
|     // fprintf(stderr, "ScreencastEncoder initialized with: %s\n", vpx_codec_iface_name(codec_interface));
 |     // fprintf(stderr, "ScreencastEncoder initialized with: %s\n", vpx_codec_iface_name(codec_interface));
 | ||||||
|     return new ScreencastEncoder(std::move(vpxCodec), margin); |     return std::make_unique<ScreencastEncoder>(std::move(vpxCodec), margin); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ScreencastEncoder::flushLastFrame() | void ScreencastEncoder::flushLastFrame() | ||||||
|  |  | ||||||
|  | @ -19,22 +19,20 @@ class VideoFrame; | ||||||
| namespace mozilla { | namespace mozilla { | ||||||
| 
 | 
 | ||||||
| class ScreencastEncoder { | class ScreencastEncoder { | ||||||
|     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ScreencastEncoder) |  | ||||||
| public: | public: | ||||||
|     static constexpr int fps = 25; |     static constexpr int fps = 25; | ||||||
| 
 | 
 | ||||||
|     static RefPtr<ScreencastEncoder> create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin); |     static std::unique_ptr<ScreencastEncoder> create(nsCString& errorString, const nsCString& filePath, int width, int height, const gfx::IntMargin& margin); | ||||||
| 
 | 
 | ||||||
|     class VPXCodec; |     class VPXCodec; | ||||||
|     ScreencastEncoder(std::unique_ptr<VPXCodec>, const gfx::IntMargin& margin); |     ScreencastEncoder(std::unique_ptr<VPXCodec>, const gfx::IntMargin& margin); | ||||||
|  |     ~ScreencastEncoder(); | ||||||
| 
 | 
 | ||||||
|     void encodeFrame(const webrtc::VideoFrame& videoFrame); |     void encodeFrame(const webrtc::VideoFrame& videoFrame); | ||||||
| 
 | 
 | ||||||
|     void finish(std::function<void()>&& callback); |     void finish(std::function<void()>&& callback); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     ~ScreencastEncoder(); |  | ||||||
| 
 |  | ||||||
|     void flushLastFrame(); |     void flushLastFrame(); | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<VPXCodec> m_vpxCodec; |     std::unique_ptr<VPXCodec> m_vpxCodec; | ||||||
|  |  | ||||||
|  | @ -78,11 +78,10 @@ nsresult generateUid(nsString& uid) { | ||||||
| 
 | 
 | ||||||
| class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::VideoFrame>, | class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::VideoFrame>, | ||||||
|                                      public webrtc::RawFrameCallback { |                                      public webrtc::RawFrameCallback { | ||||||
|  public: |  | ||||||
|   Session( |   Session( | ||||||
|     nsIScreencastServiceClient* client, |     nsIScreencastServiceClient* client, | ||||||
|     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, |     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, | ||||||
|     RefPtr<ScreencastEncoder>&& encoder, |     std::unique_ptr<ScreencastEncoder> encoder, | ||||||
|     int width, int height, |     int width, int height, | ||||||
|     int viewportWidth, int viewportHeight, |     int viewportWidth, int viewportHeight, | ||||||
|     gfx::IntMargin margin, |     gfx::IntMargin margin, | ||||||
|  | @ -97,6 +96,20 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       , mViewportHeight(viewportHeight) |       , mViewportHeight(viewportHeight) | ||||||
|       , mMargin(margin) { |       , mMargin(margin) { | ||||||
|   } |   } | ||||||
|  |   ~Session() override = default; | ||||||
|  | 
 | ||||||
|  |  public: | ||||||
|  |   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session) | ||||||
|  |   static RefPtr<Session> Create( | ||||||
|  |     nsIScreencastServiceClient* client, | ||||||
|  |     rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>&& capturer, | ||||||
|  |     std::unique_ptr<ScreencastEncoder> encoder, | ||||||
|  |     int width, int height, | ||||||
|  |     int viewportWidth, int viewportHeight, | ||||||
|  |     gfx::IntMargin margin, | ||||||
|  |     uint32_t jpegQuality) { | ||||||
|  |     return do_AddRef(new Session(client, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, jpegQuality)); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   bool Start() { |   bool Start() { | ||||||
|     webrtc::VideoCaptureCapability capability; |     webrtc::VideoCaptureCapability capability; | ||||||
|  | @ -119,6 +132,11 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void Stop() { |   void Stop() { | ||||||
|  |     if (mStopped) { | ||||||
|  |       fprintf(stderr, "Screencast session has already been stopped\n"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     mStopped = true; | ||||||
|     if (mEncoder) |     if (mEncoder) | ||||||
|       mCaptureModule->DeRegisterCaptureDataCallback(this); |       mCaptureModule->DeRegisterCaptureDataCallback(this); | ||||||
|     else |     else | ||||||
|  | @ -128,23 +146,23 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       fprintf(stderr, "StopCapture error %d\n", error); |       fprintf(stderr, "StopCapture error %d\n", error); | ||||||
|     } |     } | ||||||
|     if (mEncoder) { |     if (mEncoder) { | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |       mEncoder->finish([this, protect = RefPtr{this}] { | ||||||
|       mEncoder->finish([client = std::move(mClient)] { |  | ||||||
|         NS_DispatchToMainThread(NS_NewRunnableFunction( |         NS_DispatchToMainThread(NS_NewRunnableFunction( | ||||||
|             "NotifyScreencastStopped", [client = std::move(client)]() -> void { |             "NotifyScreencastStopped", [this, protect = std::move(protect)]() -> void { | ||||||
|               client->ScreencastStopped(); |               mClient->ScreencastStopped(); | ||||||
|             })); |             })); | ||||||
|       }); |       }); | ||||||
|     } else { |     } else { | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |  | ||||||
|       mClient->ScreencastStopped(); |       mClient->ScreencastStopped(); | ||||||
|       mClient = nullptr; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void ScreencastFrameAck() { |   void ScreencastFrameAck() { | ||||||
|     rtc::CritScope lock(&mCaptureCallbackCs); |     if (mFramesInFlight.load() == 0) { | ||||||
|     --mFramesInFlight; |       fprintf(stderr, "ScreencastFrameAck is called while there are no inflight frames\n"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     mFramesInFlight.fetch_sub(1); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // These callbacks end up running on the VideoCapture thread.
 |   // These callbacks end up running on the VideoCapture thread.
 | ||||||
|  | @ -167,15 +185,8 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|     if (mViewportHeight && pageHeight > mViewportHeight) |     if (mViewportHeight && pageHeight > mViewportHeight) | ||||||
|       pageHeight = mViewportHeight; |       pageHeight = mViewportHeight; | ||||||
| 
 | 
 | ||||||
|     { |     if (mFramesInFlight.load() >= kMaxFramesInFlight) | ||||||
|       rtc::CritScope lock(&mCaptureCallbackCs); |       return; | ||||||
|       if (mFramesInFlight >= kMaxFramesInFlight) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       ++mFramesInFlight; |  | ||||||
|       if (!mClient) |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     int screenshotWidth = pageWidth; |     int screenshotWidth = pageWidth; | ||||||
|     int screenshotHeight = pageHeight; |     int screenshotHeight = pageHeight; | ||||||
|  | @ -256,21 +267,23 @@ class nsScreencastService::Session : public rtc::VideoSinkInterface<webrtc::Vide | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nsIScreencastServiceClient* client = mClient.get(); |     mFramesInFlight.fetch_add(1); | ||||||
|     NS_DispatchToMainThread(NS_NewRunnableFunction( |     NS_DispatchToMainThread(NS_NewRunnableFunction( | ||||||
|         "NotifyScreencastFrame", [client, base64, pageWidth, pageHeight]() -> void { |         "NotifyScreencastFrame", [this, protect = RefPtr{this}, base64, pageWidth, pageHeight]() -> void { | ||||||
|  |           if (mStopped) | ||||||
|  |             return; | ||||||
|           NS_ConvertUTF8toUTF16 utf16(base64); |           NS_ConvertUTF8toUTF16 utf16(base64); | ||||||
|           client->ScreencastFrame(utf16, pageWidth, pageHeight); |           mClient->ScreencastFrame(utf16, pageWidth, pageHeight); | ||||||
|         })); |         })); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   RefPtr<nsIScreencastServiceClient> mClient; |   RefPtr<nsIScreencastServiceClient> mClient; | ||||||
|   rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> mCaptureModule; |   rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> mCaptureModule; | ||||||
|   RefPtr<ScreencastEncoder> mEncoder; |   std::unique_ptr<ScreencastEncoder> mEncoder; | ||||||
|   uint32_t mJpegQuality; |   uint32_t mJpegQuality; | ||||||
|   rtc::RecursiveCriticalSection mCaptureCallbackCs; |   bool mStopped = false; | ||||||
|   uint32_t mFramesInFlight = 0; |   std::atomic<uint32_t> mFramesInFlight = 0; | ||||||
|   int mWidth; |   int mWidth; | ||||||
|   int mHeight; |   int mHeight; | ||||||
|   int mViewportWidth; |   int mViewportWidth; | ||||||
|  | @ -322,7 +335,7 @@ nsresult nsScreencastService::StartVideoRecording(nsIScreencastServiceClient* aC | ||||||
|   margin.top += offsetTop; |   margin.top += offsetTop; | ||||||
| 
 | 
 | ||||||
|   nsCString error; |   nsCString error; | ||||||
|   RefPtr<ScreencastEncoder> encoder; |   std::unique_ptr<ScreencastEncoder> encoder; | ||||||
|   if (isVideo) { |   if (isVideo) { | ||||||
|     encoder = ScreencastEncoder::create(error, PromiseFlatCString(aVideoFileName), width, height, margin); |     encoder = ScreencastEncoder::create(error, PromiseFlatCString(aVideoFileName), width, height, margin); | ||||||
|     if (!encoder) { |     if (!encoder) { | ||||||
|  | @ -336,7 +349,7 @@ nsresult nsScreencastService::StartVideoRecording(nsIScreencastServiceClient* aC | ||||||
|   NS_ENSURE_SUCCESS(rv, rv); |   NS_ENSURE_SUCCESS(rv, rv); | ||||||
|   sessionId = uid; |   sessionId = uid; | ||||||
| 
 | 
 | ||||||
|   auto session = std::make_unique<Session>(aClient, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, isVideo ? 0 : quality); |   auto session = Session::Create(aClient, std::move(capturer), std::move(encoder), width, height, viewportWidth, viewportHeight, margin, isVideo ? 0 : quality); | ||||||
|   if (!session->Start()) |   if (!session->Start()) | ||||||
|     return NS_ERROR_FAILURE; |     return NS_ERROR_FAILURE; | ||||||
|   mIdToSession.emplace(sessionId, std::move(session)); |   mIdToSession.emplace(sessionId, std::move(session)); | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ class nsScreencastService final : public nsIScreencastService { | ||||||
|   ~nsScreencastService(); |   ~nsScreencastService(); | ||||||
| 
 | 
 | ||||||
|   class Session; |   class Session; | ||||||
|   std::map<nsString, std::unique_ptr<Session>> mIdToSession; |   std::map<nsString, RefPtr<Session>> mIdToSession; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| }  // namespace mozilla
 | }  // namespace mozilla
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue