tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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*>(&timestamp),
    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