Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/imagecapture/CaptureTask.cpp
Line
Count
Source (jump to first uncovered line)
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
class CaptureTask::MediaStreamEventListener : public MediaStreamTrackListener
19
{
20
public:
21
  explicit MediaStreamEventListener(CaptureTask* aCaptureTask)
22
0
    : mCaptureTask(aCaptureTask) {};
23
24
  // MediaStreamListener methods.
25
  void NotifyEnded() override
26
0
  {
27
0
    if(!mCaptureTask->mImageGrabbedOrTrackEnd) {
28
0
      mCaptureTask->PostTrackEndEvent();
29
0
    }
30
0
  }
31
32
private:
33
  CaptureTask* mCaptureTask;
34
};
35
36
CaptureTask::CaptureTask(dom::ImageCapture* aImageCapture)
37
  : mImageCapture(aImageCapture)
38
  , mEventListener(new MediaStreamEventListener(this))
39
  , mImageGrabbedOrTrackEnd(false)
40
  , mPrincipalChanged(false)
41
0
{
42
0
}
43
44
nsresult
45
CaptureTask::TaskComplete(already_AddRefed<dom::Blob> aBlob, nsresult aRv)
46
0
{
47
0
  MOZ_ASSERT(NS_IsMainThread());
48
0
49
0
  DetachTrack();
50
0
51
0
  nsresult rv;
52
0
  RefPtr<dom::Blob> blob(aBlob);
53
0
54
0
  // 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
0
  }
58
0
59
0
  if (mPrincipalChanged) {
60
0
    aRv = NS_ERROR_DOM_SECURITY_ERR;
61
0
    IC_LOG("MediaStream principal should not change during TakePhoto().");
62
0
  }
63
0
64
0
  if (NS_SUCCEEDED(aRv)) {
65
0
    rv = mImageCapture->PostBlobEvent(blob);
66
0
  } else {
67
0
    rv = mImageCapture->PostErrorEvent(dom::ImageCaptureError::PHOTO_ERROR, aRv);
68
0
  }
69
0
70
0
  // Ensure ImageCapture dereference on main thread here because the TakePhoto()
71
0
  // sequences stopped here.
72
0
  mImageCapture = nullptr;
73
0
74
0
  return rv;
75
0
}
76
77
void
78
CaptureTask::AttachTrack()
79
0
{
80
0
  MOZ_ASSERT(NS_IsMainThread());
81
0
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
CaptureTask::DetachTrack()
90
0
{
91
0
  MOZ_ASSERT(NS_IsMainThread());
92
0
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
CaptureTask::PrincipalChanged(dom::MediaStreamTrack* aMediaStreamTrack)
101
0
{
102
0
  MOZ_ASSERT(NS_IsMainThread());
103
0
  mPrincipalChanged = true;
104
0
}
105
106
void
107
CaptureTask::SetCurrentFrames(const VideoSegment& aSegment)
108
0
{
109
0
  if (mImageGrabbedOrTrackEnd) {
110
0
    return;
111
0
  }
112
0
113
0
  // Callback for encoding complete, it calls on main thread.
114
0
  class EncodeComplete : public dom::EncodeCompleteCallback
115
0
  {
116
0
  public:
117
0
    explicit EncodeComplete(CaptureTask* aTask) : mTask(aTask) {}
118
0
119
0
    nsresult ReceiveBlob(already_AddRefed<dom::Blob> aBlob) override
120
0
    {
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
0
    }
126
0
127
0
  protected:
128
0
    RefPtr<CaptureTask> mTask;
129
0
  };
130
0
131
0
  for (VideoSegment::ConstChunkIterator iter(aSegment);
132
0
       !iter.IsEnded(); iter.Next()) {
133
0
    VideoChunk chunk = *iter;
134
0
135
0
    // 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
0
        // Create a black image.
141
0
        image = VideoFrame::CreateBlackImage(chunk.mFrame.GetIntrinsicSize());
142
0
      } else {
143
0
        image = chunk.mFrame.GetImage();
144
0
      }
145
0
      if (!image) {
146
0
        MOZ_ASSERT(image);
147
0
        continue;
148
0
      }
149
0
      mImageGrabbedOrTrackEnd = true;
150
0
151
0
      // Encode image.
152
0
      nsresult rv;
153
0
      nsAutoString type(NS_LITERAL_STRING("image/jpeg"));
154
0
      nsAutoString options;
155
0
      rv = dom::ImageEncoder::ExtractDataFromLayersImageAsync(
156
0
                                type,
157
0
                                options,
158
0
                                false,
159
0
                                image,
160
0
                                false,
161
0
                                new EncodeComplete(this));
162
0
      if (NS_FAILED(rv)) {
163
0
        PostTrackEndEvent();
164
0
      }
165
0
      return;
166
0
    }
167
0
  }
168
0
}
169
170
void
171
CaptureTask::PostTrackEndEvent()
172
0
{
173
0
  mImageGrabbedOrTrackEnd = true;
174
0
175
0
  // Got track end or finish event, stop the task.
176
0
  class TrackEndRunnable : public Runnable
177
0
  {
178
0
  public:
179
0
    explicit TrackEndRunnable(CaptureTask* aTask)
180
0
      : mozilla::Runnable("TrackEndRunnable")
181
0
      , mTask(aTask)
182
0
    {
183
0
    }
184
0
185
0
    NS_IMETHOD Run() override
186
0
    {
187
0
      mTask->TaskComplete(nullptr, NS_ERROR_FAILURE);
188
0
      mTask = nullptr;
189
0
      return NS_OK;
190
0
    }
191
0
192
0
  protected:
193
0
    RefPtr<CaptureTask> mTask;
194
0
  };
195
0
196
0
  IC_LOG("Got MediaStream track removed or finished event.");
197
0
  nsCOMPtr<nsIRunnable> event = new TrackEndRunnable(this);
198
0
  SystemGroup::Dispatch(TaskCategory::Other, event.forget());
199
0
}
200
201
} // namespace mozilla