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