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/. */
7 #include "CaptureTask.h"
8 #include "mozilla/dom/ImageCapture.h"
9 #include "mozilla/dom/ImageCaptureError.h"
10 #include "mozilla/dom/ImageEncoder.h"
11 #include "mozilla/dom/MediaStreamTrack.h"
12 #include "mozilla/dom/VideoStreamTrack.h"
14 #include "nsThreadUtils.h"
18 class CaptureTask::MediaStreamEventListener
: public MediaStreamTrackListener
{
20 explicit MediaStreamEventListener(CaptureTask
* aCaptureTask
)
21 : mCaptureTask(aCaptureTask
){};
23 // MediaStreamTrackListener methods.
24 void NotifyEnded() override
{
25 if (!mCaptureTask
->mImageGrabbedOrTrackEnd
) {
26 mCaptureTask
->PostTrackEndEvent();
31 CaptureTask
* mCaptureTask
;
34 CaptureTask::CaptureTask(dom::ImageCapture
* aImageCapture
)
35 : mImageCapture(aImageCapture
),
36 mEventListener(new MediaStreamEventListener(this)),
37 mImageGrabbedOrTrackEnd(false),
38 mPrincipalChanged(false) {}
40 nsresult
CaptureTask::TaskComplete(already_AddRefed
<dom::Blob
> aBlob
,
42 MOZ_ASSERT(NS_IsMainThread());
47 RefPtr
<dom::Blob
> blob(aBlob
);
49 // We have to set the parent because the blob has been generated with a valid
52 blob
= dom::Blob::Create(mImageCapture
->GetParentObject(), blob
->Impl());
55 if (mPrincipalChanged
) {
56 aRv
= NS_ERROR_DOM_SECURITY_ERR
;
57 IC_LOG("MediaStream principal should not change during TakePhoto().");
60 if (NS_SUCCEEDED(aRv
)) {
61 rv
= mImageCapture
->PostBlobEvent(blob
);
64 mImageCapture
->PostErrorEvent(dom::ImageCaptureError::PHOTO_ERROR
, aRv
);
67 // Ensure ImageCapture dereference on main thread here because the TakePhoto()
68 // sequences stopped here.
69 mImageCapture
= nullptr;
74 void CaptureTask::AttachTrack() {
75 MOZ_ASSERT(NS_IsMainThread());
77 dom::MediaStreamTrack
* track
= mImageCapture
->GetVideoStreamTrack();
78 track
->AddPrincipalChangeObserver(this);
79 track
->AddListener(mEventListener
.get());
80 track
->AddDirectListener(this);
83 void CaptureTask::DetachTrack() {
84 MOZ_ASSERT(NS_IsMainThread());
86 dom::MediaStreamTrack
* track
= mImageCapture
->GetVideoStreamTrack();
87 track
->RemovePrincipalChangeObserver(this);
88 track
->RemoveListener(mEventListener
.get());
89 track
->RemoveDirectListener(this);
92 void CaptureTask::PrincipalChanged(dom::MediaStreamTrack
* aMediaStreamTrack
) {
93 MOZ_ASSERT(NS_IsMainThread());
94 mPrincipalChanged
= true;
97 void CaptureTask::SetCurrentFrames(const VideoSegment
& aSegment
) {
98 if (mImageGrabbedOrTrackEnd
) {
102 // Callback for encoding complete, it calls on main thread.
103 class EncodeComplete
: public dom::EncodeCompleteCallback
{
105 explicit EncodeComplete(CaptureTask
* aTask
) : mTask(aTask
) {}
107 nsresult
ReceiveBlob(already_AddRefed
<dom::Blob
> aBlob
) override
{
108 RefPtr
<dom::Blob
> blob(aBlob
);
109 mTask
->TaskComplete(blob
.forget(), NS_OK
);
115 RefPtr
<CaptureTask
> mTask
;
118 for (VideoSegment::ConstChunkIterator
iter(aSegment
); !iter
.IsEnded();
120 VideoChunk chunk
= *iter
;
122 // Extract the first valid video frame.
124 if (!chunk
.IsNull()) {
125 RefPtr
<layers::Image
> image
;
126 if (chunk
.mFrame
.GetForceBlack()) {
127 // Create a black image.
128 image
= VideoFrame::CreateBlackImage(chunk
.mFrame
.GetIntrinsicSize());
130 image
= chunk
.mFrame
.GetImage();
136 mImageGrabbedOrTrackEnd
= true;
140 nsAutoString
type(NS_LITERAL_STRING("image/jpeg"));
141 nsAutoString options
;
142 rv
= dom::ImageEncoder::ExtractDataFromLayersImageAsync(
143 type
, options
, false, image
, false, new EncodeComplete(this));
152 void CaptureTask::PostTrackEndEvent() {
153 mImageGrabbedOrTrackEnd
= true;
155 // Got track end or finish event, stop the task.
156 class TrackEndRunnable
: public Runnable
{
158 explicit TrackEndRunnable(CaptureTask
* aTask
)
159 : mozilla::Runnable("TrackEndRunnable"), mTask(aTask
) {}
161 NS_IMETHOD
Run() override
{
162 mTask
->TaskComplete(nullptr, NS_ERROR_FAILURE
);
168 RefPtr
<CaptureTask
> mTask
;
171 IC_LOG("Got MediaStream track removed or finished event.");
172 nsCOMPtr
<nsIRunnable
> event
= new TrackEndRunnable(this);
173 SystemGroup::Dispatch(TaskCategory::Other
, event
.forget());
176 } // namespace mozilla