/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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 https://mozilla.org/MPL/2.0/. */ #include #include "CamerasParent.h" #include "VideoEngine.h" #include "api/video/i420_buffer.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "mozilla/Preferences.h" #include "mozilla/SyncRunnable.h" #include "mozilla/ipc/BackgroundParent.h" #include "video_engine/video_capture_factory.h" using testing::_; using testing::Eq; using testing::Property; using testing::Test; namespace mozilla::camera { static void WaitForBackgroundThread() { nsCOMPtr backgroundThread = ipc::BackgroundParent::GetBackgroundThread(); MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( backgroundThread, NS_NewRunnableFunction("TestAggregateCapturer::TearDown", [] {}))); } class MockCamerasParent : public CamerasParent { public: static already_AddRefed Create() { nsCOMPtr backgroundThread = ipc::BackgroundParent::GetBackgroundThread(); RefPtr parent; MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( backgroundThread, NS_NewRunnableFunction("TestAggregateCapturer::SetUp", [&] { parent = new MockCamerasParent(); }))); return parent.forget(); } MOCK_METHOD(int, DeliverFrameOverIPC, (CaptureEngine, int, const Span&, const TrackingId&, (Variant&&), const VideoFrameProperties&), (override)); }; struct TestAggregateCapturer : public Test { static constexpr uint64_t kWindowId = 1; const CaptureEngine mCapEngine = CameraEngine; const CaptureDeviceType mDeviceType = ([&] { switch (mCapEngine) { case InvalidEngine: case MaxEngine: case CameraEngine: return CaptureDeviceType::Camera; case ScreenEngine: return CaptureDeviceType::Screen; case WinEngine: return CaptureDeviceType::Window; case BrowserEngine: return CaptureDeviceType::Browser; } return CaptureDeviceType::Camera; })(); RefPtr mEngine = VideoEngine::Create(mDeviceType, MakeRefPtr()); RefPtr mParent; std::unique_ptr mAggregator; void SetUp() override { Preferences::SetBool("media.getusermedia.camera.fake.force", true); nsTArray capabilities; mParent = MockCamerasParent::Create(); constexpr size_t capacity = 32; char deviceName[capacity], uniqueId[capacity]; auto info = mEngine->GetOrCreateVideoCaptureDeviceInfo(); info->GetDeviceName(0, deviceName, capacity, uniqueId, capacity); for (int i = 0; i < info->NumberOfCapabilities(uniqueId); ++i) { webrtc::VideoCaptureCapability cap; if (info->GetCapability(uniqueId, i, cap) == 0) { capabilities.AppendElement(std::move(cap)); } } mAggregator = AggregateCapturer::Create(GetCurrentSerialEventTarget(), mCapEngine, mEngine, nsCString(uniqueId, capacity), kWindowId, std::move(capabilities), mParent); } void TearDown() override { Preferences::ClearUser("media.getusermedia.camera.fake.force"); mAggregator->RemoveStreamsFor(mParent); mAggregator = nullptr; mParent = nullptr; mEngine = nullptr; // Resetting mParent bounces the delete to the background thread. Do it here // too, to stay in sync. WaitForBackgroundThread(); // Process video capture thread messages from the CamerasParent dtor. NS_ProcessPendingEvents(nullptr); } }; TEST_F(TestAggregateCapturer, EmptyLifeCycle) { // Checks that lifecycle is OK with simple Create()/RemoveStreamsFor(). } TEST_F(TestAggregateCapturer, TwoStreamsLifeCycle) { // Checks that lifecycle is OK with simple // Create()+AddStream()/RemoveStreamsFor(). mAggregator->AddStream(mParent, mEngine->GenerateId(), kWindowId); } TEST_F(TestAggregateCapturer, FrameDelivery) { constexpr int width = 240, height = 160; constexpr int64_t time = 123; EXPECT_CALL(*mParent, DeliverFrameOverIPC( CameraEngine, mAggregator->mCaptureId, _, _, _, Property(&VideoFrameProperties::renderTimeMs, Eq(time)))); auto buffer = webrtc::I420Buffer::Create(width, height); webrtc::I420Buffer::SetBlack(buffer.get()); auto frame = webrtc::VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_ms(time) .build(); mAggregator->OnFrame(frame); WaitForBackgroundThread(); } } // namespace mozilla::camera