tor-browser

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

CaptureTask.cpp (5730B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CaptureTask.h"
      8 
      9 #include "VideoSegment.h"
     10 #include "gfxUtils.h"
     11 #include "mozilla/SchedulerGroup.h"
     12 #include "mozilla/dom/BlobImpl.h"
     13 #include "mozilla/dom/ImageCapture.h"
     14 #include "mozilla/dom/ImageCaptureError.h"
     15 #include "mozilla/dom/ImageEncoder.h"
     16 #include "mozilla/dom/MediaStreamTrack.h"
     17 #include "mozilla/dom/VideoStreamTrack.h"
     18 #include "nsThreadUtils.h"
     19 
     20 namespace mozilla {
     21 
     22 class CaptureTask::MediaTrackEventListener : public MediaTrackListener {
     23 public:
     24  explicit MediaTrackEventListener(CaptureTask* aCaptureTask)
     25      : mCaptureTask(aCaptureTask) {};
     26 
     27  // MediaTrackListener methods.
     28  void NotifyEnded(MediaTrackGraph* aGraph) override {
     29    mCaptureTask->PostTrackEndEvent();
     30  }
     31 
     32 private:
     33  CaptureTask* mCaptureTask;
     34 };
     35 
     36 CaptureTask::CaptureTask(dom::ImageCapture* aImageCapture)
     37    : mImageCapture(aImageCapture),
     38      mEventListener(new MediaTrackEventListener(this)),
     39      mImageGrabbedOrTrackEnd(false),
     40      mPrincipalChanged(false) {}
     41 
     42 nsresult CaptureTask::TaskComplete(already_AddRefed<dom::BlobImpl> aBlobImpl,
     43                                   nsresult aRv) {
     44  MOZ_ASSERT(NS_IsMainThread());
     45 
     46  DetachTrack();
     47 
     48  nsresult rv;
     49  RefPtr<dom::BlobImpl> blobImpl(aBlobImpl);
     50 
     51  // We have to set the parent because the blob has been generated with a valid
     52  // one.
     53  RefPtr<dom::Blob> blob;
     54  if (blobImpl) {
     55    blob = dom::Blob::Create(mImageCapture->GetOwnerGlobal(), blobImpl);
     56    if (NS_WARN_IF(!blob)) {
     57      return NS_ERROR_FAILURE;
     58    }
     59  }
     60 
     61  if (mPrincipalChanged) {
     62    aRv = NS_ERROR_DOM_SECURITY_ERR;
     63    IC_LOG("MediaStream principal should not change during TakePhoto().");
     64  }
     65 
     66  if (NS_SUCCEEDED(aRv)) {
     67    rv = mImageCapture->PostBlobEvent(blob);
     68  } else {
     69    rv =
     70        mImageCapture->PostErrorEvent(dom::ImageCaptureError::PHOTO_ERROR, aRv);
     71  }
     72 
     73  // Ensure ImageCapture dereference on main thread here because the TakePhoto()
     74  // sequences stopped here.
     75  mImageCapture = nullptr;
     76 
     77  return rv;
     78 }
     79 
     80 void CaptureTask::AttachTrack() {
     81  MOZ_ASSERT(NS_IsMainThread());
     82 
     83  dom::MediaStreamTrack* track = mImageCapture->GetVideoStreamTrack();
     84  track->AddPrincipalChangeObserver(this);
     85  track->AddListener(mEventListener.get());
     86  track->AddDirectListener(this);
     87 }
     88 
     89 void CaptureTask::DetachTrack() {
     90  MOZ_ASSERT(NS_IsMainThread());
     91 
     92  dom::MediaStreamTrack* track = mImageCapture->GetVideoStreamTrack();
     93  track->RemovePrincipalChangeObserver(this);
     94  track->RemoveListener(mEventListener.get());
     95  track->RemoveDirectListener(this);
     96 }
     97 
     98 void CaptureTask::PrincipalChanged(dom::MediaStreamTrack* aMediaStreamTrack) {
     99  MOZ_ASSERT(NS_IsMainThread());
    100  mPrincipalChanged = true;
    101 }
    102 
    103 void CaptureTask::NotifyRealtimeTrackData(MediaTrackGraph* aGraph,
    104                                          TrackTime aTrackOffset,
    105                                          const MediaSegment& aMedia) {
    106  MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
    107  const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
    108 
    109  // Callback for encoding complete, it calls on main thread.
    110  class EncodeComplete : public dom::EncodeCompleteCallback {
    111   public:
    112    explicit EncodeComplete(CaptureTask* aTask) : mTask(aTask) {}
    113 
    114    bool CanBeDeletedOnAnyThread() override {
    115      // EncodeComplete is used from the main thread only.
    116      return false;
    117    }
    118 
    119    nsresult ReceiveBlobImpl(
    120        already_AddRefed<dom::BlobImpl> aBlobImpl) override {
    121      MOZ_ASSERT(NS_IsMainThread());
    122      RefPtr<dom::BlobImpl> blobImpl(aBlobImpl);
    123      mTask->TaskComplete(blobImpl.forget(), NS_OK);
    124      mTask = nullptr;
    125      return NS_OK;
    126    }
    127 
    128   protected:
    129    RefPtr<CaptureTask> mTask;
    130  };
    131 
    132  for (VideoSegment::ConstChunkIterator iter(video); !iter.IsEnded();
    133       iter.Next()) {
    134    VideoChunk chunk = *iter;
    135 
    136    // Extract the first valid video frame.
    137    VideoFrame frame;
    138    if (chunk.IsNull()) {
    139      continue;
    140    }
    141 
    142    RefPtr<layers::Image> image;
    143    if (chunk.mFrame.GetForceBlack()) {
    144      // Create a black image.
    145      image = VideoFrame::CreateBlackImage(chunk.mFrame.GetIntrinsicSize());
    146    } else {
    147      image = chunk.mFrame.GetImage();
    148    }
    149    if (!image) {
    150      MOZ_ASSERT(image);
    151      continue;
    152    }
    153 
    154    bool wasGrabbed = mImageGrabbedOrTrackEnd.exchange(true);
    155    if (wasGrabbed) {
    156      return;
    157    }
    158 
    159    // Encode image.
    160    nsresult rv;
    161    nsAutoString type(u"image/jpeg"_ns);
    162    nsAutoString options;
    163    rv = dom::ImageEncoder::ExtractDataFromLayersImageAsync(
    164        type, options, false, image, CanvasUtils::ImageExtraction::Unrestricted,
    165        VoidCString(), new EncodeComplete(this));
    166    if (NS_FAILED(rv)) {
    167      PostTrackEndEvent();
    168    }
    169  }
    170 }
    171 
    172 void CaptureTask::PostTrackEndEvent() {
    173  bool wasGrabbed = mImageGrabbedOrTrackEnd.exchange(true);
    174  if (wasGrabbed) {
    175    return;
    176  }
    177 
    178  // Got track end or finish event, stop the task.
    179  class TrackEndRunnable : public Runnable {
    180   public:
    181    explicit TrackEndRunnable(CaptureTask* aTask)
    182        : mozilla::Runnable("TrackEndRunnable"), mTask(aTask) {}
    183 
    184    NS_IMETHOD Run() override {
    185      mTask->TaskComplete(nullptr, NS_ERROR_FAILURE);
    186      mTask = nullptr;
    187      return NS_OK;
    188    }
    189 
    190   protected:
    191    RefPtr<CaptureTask> mTask;
    192  };
    193 
    194  IC_LOG("Got MediaTrack track removed or finished event.");
    195  nsCOMPtr<nsIRunnable> event = new TrackEndRunnable(this);
    196  SchedulerGroup::Dispatch(event.forget());
    197 }
    198 
    199 }  // namespace mozilla