FakeVideoSource.cpp (5753B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FakeVideoSource.h" 8 9 #include "ImageContainer.h" 10 #include "mozilla/SyncRunnable.h" 11 #include "mozilla/gfx/Tools.h" 12 13 #ifdef MOZ_WEBRTC 14 # include "common/YuvStamper.h" 15 # include "prtime.h" 16 #endif 17 18 using namespace mozilla::gfx; 19 20 namespace mozilla { 21 22 FakeVideoSource::FakeVideoSource(nsISerialEventTarget* aTarget) 23 : mTarget(aTarget) {} 24 25 FakeVideoSource::~FakeVideoSource() = default; 26 27 int32_t FakeVideoSource::StartCapture(int32_t aWidth, int32_t aHeight, 28 const TimeDuration& aFrameInterval) { 29 MutexAutoLock lock(mMutex); 30 31 mTimer = NS_NewTimer(mTarget.GetEventTarget()); 32 if (!mTimer) { 33 return -1; 34 } 35 36 MOZ_ALWAYS_SUCCEEDS(mTarget.Dispatch(NS_NewRunnableFunction( 37 "FakeVideoSource::StartCapture", 38 [self = RefPtr(this), this, aWidth, aHeight] { 39 mTarget.AssertOnCurrentThread(); 40 if (!mImageContainer) { 41 mImageContainer = MakeAndAddRef<layers::ImageContainer>( 42 layers::ImageUsageType::Webrtc, 43 layers::ImageContainer::ASYNCHRONOUS); 44 } 45 mWidth = aWidth; 46 mHeight = aHeight; 47 }))); 48 49 // Start timer for subsequent frames 50 mTimer->InitHighResolutionWithNamedFuncCallback( 51 [](nsITimer* aTimer, void* aClosure) { 52 RefPtr<FakeVideoSource> capturer = 53 static_cast<FakeVideoSource*>(aClosure); 54 capturer->mTarget.AssertOnCurrentThread(); 55 capturer->GenerateImage(); 56 }, 57 this, aFrameInterval, nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP, 58 "FakeVideoSource::GenerateFrame"_ns); 59 60 return 0; 61 } 62 63 int32_t FakeVideoSource::StopCapture() { 64 MutexAutoLock lock(mMutex); 65 66 if (!mTimer) { 67 return 0; 68 } 69 70 mTimer->Cancel(); 71 mTimer = nullptr; 72 73 MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( 74 mTarget.GetEventTarget(), 75 NS_NewRunnableFunction( 76 "FakeVideoSource::StopCapture", [self = RefPtr(this), this] { 77 mTarget.AssertOnCurrentThread(); 78 if (!mImageContainer) { 79 mImageContainer = MakeAndAddRef<layers::ImageContainer>( 80 layers::ImageUsageType::Webrtc, 81 layers::ImageContainer::ASYNCHRONOUS); 82 } 83 }))); 84 85 return 0; 86 } 87 88 bool FakeVideoSource::CaptureStarted() { 89 MutexAutoLock lock(mMutex); 90 return mTimer; 91 } 92 93 void FakeVideoSource::SetTrackingId(uint32_t aTrackingIdProcId) { 94 MOZ_ALWAYS_SUCCEEDS(mTarget.Dispatch(NS_NewRunnableFunction( 95 "FakeVideoSource:::SetTrackingId", 96 [self = RefPtr(this), this, aTrackingIdProcId] { 97 mTarget.AssertOnCurrentThread(); 98 if (NS_WARN_IF(mTrackingId.isSome())) { 99 // This capture instance must be shared across multiple camera 100 // requests. For now ignore other requests than the first. 101 return; 102 } 103 mTrackingId.emplace(TrackingId::Source::Camera, aTrackingIdProcId); 104 }))); 105 } 106 107 static bool AllocateSolidColorFrame(layers::PlanarYCbCrData& aData, int aWidth, 108 int aHeight, int aY, int aCb, int aCr) { 109 // Allocate a single frame with a solid color 110 int yStride = GetAlignedStride<2>(aWidth, 1); 111 int yLen = yStride * aHeight; 112 int cbcrStride = yStride / 2; 113 int cbLen = cbcrStride * GetAlignedStride<2>(aHeight, 1) / 2; 114 int crLen = cbLen; 115 uint8_t* frame = (uint8_t*)malloc(yLen + cbLen + crLen); 116 if (!frame) { 117 return false; 118 } 119 memset(frame, aY, yLen); 120 memset(frame + yLen, aCb, cbLen); 121 memset(frame + yLen + cbLen, aCr, crLen); 122 123 aData.mYChannel = frame; 124 aData.mYStride = yStride; 125 aData.mCbCrStride = cbcrStride; 126 aData.mCbChannel = frame + yLen; 127 aData.mCrChannel = aData.mCbChannel + cbLen; 128 aData.mPictureRect = IntRect(0, 0, aWidth, aHeight); 129 aData.mStereoMode = StereoMode::MONO; 130 aData.mYUVColorSpace = gfx::YUVColorSpace::BT601; 131 aData.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 132 return true; 133 } 134 135 static void ReleaseFrame(layers::PlanarYCbCrData& aData) { 136 free(aData.mYChannel); 137 } 138 139 void FakeVideoSource::GenerateImage() { 140 mTarget.AssertOnCurrentThread(); 141 142 const TimeStamp now = TimeStamp::Now(); 143 144 if (mTrackingId) { 145 mCaptureRecorder.Start(0, "FakeVideoSource"_ns, *mTrackingId, mWidth, 146 mHeight, CaptureStage::ImageType::I420); 147 } 148 149 // Update the target color 150 if (mCr <= 16) { 151 if (mCb < 240) { 152 mCb++; 153 } else { 154 mCr++; 155 } 156 } else if (mCb >= 240) { 157 if (mCr < 240) { 158 mCr++; 159 } else { 160 mCb--; 161 } 162 } else if (mCr >= 240) { 163 if (mCb > 16) { 164 mCb--; 165 } else { 166 mCr--; 167 } 168 } else { 169 mCr--; 170 } 171 172 // Allocate a single solid color image 173 RefPtr<layers::PlanarYCbCrImage> ycbcr_image = 174 mImageContainer->CreatePlanarYCbCrImage(); 175 layers::PlanarYCbCrData data; 176 if (NS_WARN_IF( 177 !AllocateSolidColorFrame(data, mWidth, mHeight, 0x80, mCb, mCr))) { 178 return; 179 } 180 181 #ifdef MOZ_WEBRTC 182 uint64_t timestamp = PR_Now(); 183 YuvStamper::Encode(mWidth, mHeight, mWidth, data.mYChannel, 184 reinterpret_cast<unsigned char*>(×tamp), 185 sizeof(timestamp), 0, 0); 186 #endif 187 188 bool setData = NS_SUCCEEDED(ycbcr_image->CopyData(data)); 189 MOZ_ASSERT(setData); 190 191 // SetData copies data, so we can free the frame 192 ReleaseFrame(data); 193 194 if (!setData) { 195 return; 196 } 197 198 mGeneratedImageEvent.Notify(ycbcr_image, now); 199 mCaptureRecorder.Record(0); 200 } 201 202 } // namespace mozilla