| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  | /* 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/. */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "HeadlessWindowCapturer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "api/video/i420_buffer.h"
 | 
					
						
							|  |  |  | #include "HeadlessWidget.h"
 | 
					
						
							|  |  |  | #include "libyuv.h"
 | 
					
						
							|  |  |  | #include "mozilla/EndianUtils.h"
 | 
					
						
							|  |  |  | #include "mozilla/gfx/DataSurfaceHelpers.h"
 | 
					
						
							| 
									
										
										
										
											2022-01-18 04:16:04 -07:00
										 |  |  | #include "rtc_base/ref_counted_object.h"
 | 
					
						
							|  |  |  | #include "rtc_base/time_utils.h"
 | 
					
						
							|  |  |  | #include "api/scoped_refptr.h"
 | 
					
						
							| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | using namespace mozilla::widget; | 
					
						
							|  |  |  | using namespace webrtc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace mozilla { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 19:11:42 -07:00
										 |  |  | rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> HeadlessWindowCapturer::Create(HeadlessWidget* headlessWindow) { | 
					
						
							| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  |   return new rtc::RefCountedObject<HeadlessWindowCapturer>(headlessWindow); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HeadlessWindowCapturer::HeadlessWindowCapturer(mozilla::widget::HeadlessWidget* window) | 
					
						
							|  |  |  |     : mWindow(window) { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | HeadlessWindowCapturer::~HeadlessWindowCapturer() { | 
					
						
							|  |  |  |   StopCapture(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HeadlessWindowCapturer::RegisterCaptureDataCallback(rtc::VideoSinkInterface<webrtc::VideoFrame>* dataCallback) { | 
					
						
							|  |  |  |   rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |   _dataCallBacks.insert(dataCallback); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void HeadlessWindowCapturer::DeRegisterCaptureDataCallback(rtc::VideoSinkInterface<webrtc::VideoFrame>* dataCallback) { | 
					
						
							|  |  |  |   rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |   auto it = _dataCallBacks.find(dataCallback); | 
					
						
							|  |  |  |   if (it != _dataCallBacks.end()) { | 
					
						
							|  |  |  |     _dataCallBacks.erase(it); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 19:11:42 -07:00
										 |  |  | void HeadlessWindowCapturer::RegisterRawFrameCallback(webrtc::RawFrameCallback* rawFrameCallback) { | 
					
						
							|  |  |  |   rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |   _rawFrameCallbacks.insert(rawFrameCallback); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HeadlessWindowCapturer::DeRegisterRawFrameCallback(webrtc::RawFrameCallback* rawFrameCallback) { | 
					
						
							|  |  |  |   rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |   auto it = _rawFrameCallbacks.find(rawFrameCallback); | 
					
						
							|  |  |  |   if (it != _rawFrameCallbacks.end()) { | 
					
						
							|  |  |  |     _rawFrameCallbacks.erase(it); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  | void HeadlessWindowCapturer::NotifyFrameCaptured(const webrtc::VideoFrame& frame) { | 
					
						
							|  |  |  |   rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |   for (auto dataCallBack : _dataCallBacks) | 
					
						
							|  |  |  |     dataCallBack->OnFrame(frame); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int32_t HeadlessWindowCapturer::StopCaptureIfAllClientsClose() { | 
					
						
							|  |  |  |   if (_dataCallBacks.empty()) { | 
					
						
							|  |  |  |     return StopCapture(); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int32_t HeadlessWindowCapturer::StartCapture(const VideoCaptureCapability& capability) { | 
					
						
							|  |  |  |   mWindow->SetSnapshotListener([this] (RefPtr<gfx::DataSourceSurface>&& dataSurface){ | 
					
						
							|  |  |  |     if (!NS_IsInCompositorThread()) { | 
					
						
							|  |  |  |       fprintf(stderr, "SnapshotListener is called not on the Compositor thread!\n"); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dataSurface->GetFormat() != gfx::SurfaceFormat::B8G8R8A8) { | 
					
						
							| 
									
										
										
										
											2021-05-06 19:11:42 -07:00
										 |  |  |       fprintf(stderr, "Unexpected snapshot surface format: %hhd\n", dataSurface->GetFormat()); | 
					
						
							| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 19:11:42 -07:00
										 |  |  |     webrtc::VideoCaptureCapability frameInfo; | 
					
						
							|  |  |  |     frameInfo.width = dataSurface->GetSize().width; | 
					
						
							|  |  |  |     frameInfo.height = dataSurface->GetSize().height; | 
					
						
							|  |  |  | #if MOZ_LITTLE_ENDIAN()
 | 
					
						
							|  |  |  |     frameInfo.videoType = VideoType::kARGB; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     frameInfo.videoType = VideoType::kBGRA; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 18:05:26 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |       rtc::CritScope lock2(&_callBackCs); | 
					
						
							|  |  |  |       for (auto rawFrameCallback : _rawFrameCallbacks) { | 
					
						
							|  |  |  |         rawFrameCallback->OnRawFrame(dataSurface->GetData(), dataSurface->Stride(), frameInfo); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!_dataCallBacks.size()) | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2021-05-06 19:11:42 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-14 12:25:41 -07:00
										 |  |  |     int width = dataSurface->GetSize().width; | 
					
						
							|  |  |  |     int height = dataSurface->GetSize().height; | 
					
						
							|  |  |  |     rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(width, height); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     gfx::DataSourceSurface::ScopedMap map(dataSurface.get(), gfx::DataSourceSurface::MapType::READ); | 
					
						
							|  |  |  |     if (!map.IsMapped()) { | 
					
						
							|  |  |  |       fprintf(stderr, "Failed to map snapshot bytes!\n"); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if MOZ_LITTLE_ENDIAN()
 | 
					
						
							|  |  |  |     const int conversionResult = libyuv::ARGBToI420( | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     const int conversionResult = libyuv::BGRAToI420( | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         map.GetData(), map.GetStride(), | 
					
						
							|  |  |  |         buffer->MutableDataY(), buffer->StrideY(), | 
					
						
							|  |  |  |         buffer->MutableDataU(), buffer->StrideU(), | 
					
						
							|  |  |  |         buffer->MutableDataV(), buffer->StrideV(), | 
					
						
							|  |  |  |         width, height); | 
					
						
							|  |  |  |     if (conversionResult != 0) { | 
					
						
							|  |  |  |       fprintf(stderr, "Failed to convert capture frame to I420: %d\n", conversionResult); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VideoFrame captureFrame(buffer, 0, rtc::TimeMillis(), kVideoRotation_0); | 
					
						
							|  |  |  |     NotifyFrameCaptured(captureFrame); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int32_t HeadlessWindowCapturer::StopCapture() { | 
					
						
							|  |  |  |   if (!CaptureStarted()) | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   mWindow->SetSnapshotListener(nullptr); | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool HeadlessWindowCapturer::CaptureStarted() { | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace mozilla
 |