Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / media / capture / content_video_capture_device_core.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/media/capture/content_video_capture_device_core.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/callback_forward.h"
10 #include "base/callback_helpers.h"
11 #include "base/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/metrics/histogram.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/synchronization/lock.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_checker.h"
23 #include "base/time/time.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "media/base/bind_to_current_loop.h"
26 #include "media/base/video_frame.h"
27 #include "media/base/video_util.h"
28 #include "media/video/capture/video_capture_types.h"
29 #include "ui/gfx/rect.h"
30
31 namespace content {
32
33 namespace {
34
35 void DeleteCaptureMachineOnUIThread(
36     scoped_ptr<VideoCaptureMachine> capture_machine) {
37   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
38
39   capture_machine.reset();
40 }
41
42 }  // namespace
43
44 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
45     scoped_ptr<media::VideoCaptureDevice::Client> client,
46     scoped_ptr<VideoCaptureOracle> oracle,
47     const media::VideoCaptureParams& params)
48     : client_(client.Pass()),
49       oracle_(oracle.Pass()),
50       params_(params),
51       capture_size_updated_(false) {
52   switch (params_.requested_format.pixel_format) {
53     case media::PIXEL_FORMAT_I420:
54       video_frame_format_ = media::VideoFrame::I420;
55       break;
56     case media::PIXEL_FORMAT_TEXTURE:
57       video_frame_format_ = media::VideoFrame::NATIVE_TEXTURE;
58       break;
59     default:
60       LOG(FATAL) << "Unexpected pixel_format "
61                  << params_.requested_format.pixel_format;
62   }
63 }
64
65 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
66
67 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
68     VideoCaptureOracle::Event event,
69     const gfx::Rect& damage_rect,
70     base::TimeTicks event_time,
71     scoped_refptr<media::VideoFrame>* storage,
72     CaptureFrameCallback* callback) {
73   base::AutoLock guard(lock_);
74
75   if (!client_)
76     return false;  // Capture is stopped.
77
78   // Always round up the coded size to multiple of 16 pixels.
79   // See http://crbug.com/402151.
80   const gfx::Size visible_size = params_.requested_format.frame_size;
81   const gfx::Size coded_size((visible_size.width() + 15) & ~15,
82                              (visible_size.height() + 15) & ~15);
83
84   scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
85       client_->ReserveOutputBuffer(video_frame_format_, coded_size);
86   const bool should_capture =
87       oracle_->ObserveEventAndDecideCapture(event, damage_rect, event_time);
88   const bool content_is_dirty =
89       (event == VideoCaptureOracle::kCompositorUpdate ||
90        event == VideoCaptureOracle::kSoftwarePaint);
91   const char* event_name =
92       (event == VideoCaptureOracle::kTimerPoll ? "poll" :
93        (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
94        "paint"));
95
96   // Consider the various reasons not to initiate a capture.
97   if (should_capture && !output_buffer.get()) {
98     TRACE_EVENT_INSTANT1("mirroring",
99                          "PipelineLimited",
100                          TRACE_EVENT_SCOPE_THREAD,
101                          "trigger",
102                          event_name);
103     return false;
104   } else if (!should_capture && output_buffer.get()) {
105     if (content_is_dirty) {
106       // This is a normal and acceptable way to drop a frame. We've hit our
107       // capture rate limit: for example, the content is animating at 60fps but
108       // we're capturing at 30fps.
109       TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
110                            TRACE_EVENT_SCOPE_THREAD,
111                            "trigger", event_name);
112     }
113     return false;
114   } else if (!should_capture && !output_buffer.get()) {
115     // We decided not to capture, but we wouldn't have been able to if we wanted
116     // to because no output buffer was available.
117     TRACE_EVENT_INSTANT1("mirroring", "NearlyPipelineLimited",
118                          TRACE_EVENT_SCOPE_THREAD,
119                          "trigger", event_name);
120     return false;
121   }
122   int frame_number = oracle_->RecordCapture();
123   TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
124                            "frame_number", frame_number,
125                            "trigger", event_name);
126   // NATIVE_TEXTURE frames wrap a texture mailbox, which we don't have at the
127   // moment.  We do not construct those frames.
128   if (video_frame_format_ != media::VideoFrame::NATIVE_TEXTURE) {
129     *storage = media::VideoFrame::WrapExternalPackedMemory(
130         video_frame_format_,
131         coded_size,
132         gfx::Rect(visible_size),
133         visible_size,
134         static_cast<uint8*>(output_buffer->data()),
135         output_buffer->size(),
136         base::SharedMemory::NULLHandle(),
137         base::TimeDelta(),
138         base::Closure());
139   }
140   *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
141                          this,
142                          frame_number,
143                          output_buffer);
144   return true;
145 }
146
147 gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const {
148   base::AutoLock guard(lock_);
149   return params_.requested_format.frame_size;
150 }
151
152 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
153   base::AutoLock guard(lock_);
154
155   // If this is the first call to UpdateCaptureSize(), or the receiver supports
156   // variable resolution, then determine the capture size by treating the
157   // requested width and height as maxima.
158   if (!capture_size_updated_ || params_.resolution_change_policy ==
159       media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT) {
160     // The capture resolution should not exceed the source frame size.
161     // In other words it should downscale the image but not upscale it.
162     if (source_size.width() > params_.requested_format.frame_size.width() ||
163         source_size.height() > params_.requested_format.frame_size.height()) {
164       gfx::Rect capture_rect = media::ComputeLetterboxRegion(
165           gfx::Rect(params_.requested_format.frame_size), source_size);
166       params_.requested_format.frame_size.SetSize(
167           MakeEven(capture_rect.width()), MakeEven(capture_rect.height()));
168     } else {
169       params_.requested_format.frame_size.SetSize(
170           MakeEven(source_size.width()), MakeEven(source_size.height()));
171     }
172     capture_size_updated_ = true;
173   }
174 }
175
176 void ThreadSafeCaptureOracle::Stop() {
177   base::AutoLock guard(lock_);
178   client_.reset();
179 }
180
181 void ThreadSafeCaptureOracle::ReportError(const std::string& reason) {
182   base::AutoLock guard(lock_);
183   if (client_)
184     client_->OnError(reason);
185 }
186
187 void ThreadSafeCaptureOracle::DidCaptureFrame(
188     int frame_number,
189     const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
190     const scoped_refptr<media::VideoFrame>& frame,
191     base::TimeTicks timestamp,
192     bool success) {
193   base::AutoLock guard(lock_);
194   TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
195                          "success", success,
196                          "timestamp", timestamp.ToInternalValue());
197
198   if (!client_)
199     return;  // Capture is stopped.
200
201   if (success) {
202     if (oracle_->CompleteCapture(frame_number, &timestamp)) {
203       media::VideoCaptureFormat format = params_.requested_format;
204       format.frame_size = frame->coded_size();
205       client_->OnIncomingCapturedVideoFrame(buffer, format, frame, timestamp);
206     }
207   }
208 }
209
210 void ContentVideoCaptureDeviceCore::AllocateAndStart(
211     const media::VideoCaptureParams& params,
212     scoped_ptr<media::VideoCaptureDevice::Client> client) {
213   DCHECK(thread_checker_.CalledOnValidThread());
214
215   if (state_ != kIdle) {
216     DVLOG(1) << "Allocate() invoked when not in state Idle.";
217     return;
218   }
219
220   if (params.requested_format.frame_rate <= 0) {
221     std::string error_msg("Invalid frame_rate: ");
222     error_msg += base::DoubleToString(params.requested_format.frame_rate);
223     DVLOG(1) << error_msg;
224     client->OnError(error_msg);
225     return;
226   }
227
228   if (params.requested_format.pixel_format != media::PIXEL_FORMAT_I420 &&
229       params.requested_format.pixel_format != media::PIXEL_FORMAT_TEXTURE) {
230     std::string error_msg = base::StringPrintf(
231         "unsupported format: %d", params.requested_format.pixel_format);
232     DVLOG(1) << error_msg;
233     client->OnError(error_msg);
234     return;
235   }
236
237    if (params.requested_format.frame_size.width() < kMinFrameWidth ||
238        params.requested_format.frame_size.height() < kMinFrameHeight) {
239      std::string error_msg =
240          "invalid frame size: " + params.requested_format.frame_size.ToString();
241      DVLOG(1) << error_msg;
242      client->OnError(error_msg);
243      return;
244    }
245
246   media::VideoCaptureParams new_params = params;
247   // Frame dimensions must each be an even integer since the client wants (or
248   // will convert to) YUV420.
249   new_params.requested_format.frame_size.SetSize(
250       MakeEven(params.requested_format.frame_size.width()),
251       MakeEven(params.requested_format.frame_size.height()));
252
253   base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
254       1000000.0 / params.requested_format.frame_rate + 0.5);
255
256   scoped_ptr<VideoCaptureOracle> oracle(
257       new VideoCaptureOracle(capture_period,
258                              kAcceleratedSubscriberIsSupported));
259   oracle_proxy_ =
260       new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), new_params);
261
262   // Starts the capture machine asynchronously.
263   BrowserThread::PostTaskAndReplyWithResult(
264       BrowserThread::UI,
265       FROM_HERE,
266       base::Bind(&VideoCaptureMachine::Start,
267                  base::Unretained(capture_machine_.get()),
268                  oracle_proxy_,
269                  new_params),
270       base::Bind(&ContentVideoCaptureDeviceCore::CaptureStarted, AsWeakPtr()));
271
272   TransitionStateTo(kCapturing);
273 }
274
275 void ContentVideoCaptureDeviceCore::StopAndDeAllocate() {
276   DCHECK(thread_checker_.CalledOnValidThread());
277
278   if (state_ != kCapturing)
279     return;
280
281   oracle_proxy_->Stop();
282   oracle_proxy_ = NULL;
283
284   TransitionStateTo(kIdle);
285
286   // Stops the capture machine asynchronously.
287   BrowserThread::PostTask(
288       BrowserThread::UI, FROM_HERE, base::Bind(
289           &VideoCaptureMachine::Stop,
290           base::Unretained(capture_machine_.get()),
291           base::Bind(&base::DoNothing)));
292 }
293
294 void ContentVideoCaptureDeviceCore::CaptureStarted(bool success) {
295   DCHECK(thread_checker_.CalledOnValidThread());
296   if (!success) {
297     std::string reason("Failed to start capture machine.");
298     DVLOG(1) << reason;
299     Error(reason);
300   }
301 }
302
303 ContentVideoCaptureDeviceCore::ContentVideoCaptureDeviceCore(
304     scoped_ptr<VideoCaptureMachine> capture_machine)
305     : state_(kIdle),
306       capture_machine_(capture_machine.Pass()) {
307   DCHECK(capture_machine_.get());
308 }
309
310 ContentVideoCaptureDeviceCore::~ContentVideoCaptureDeviceCore() {
311   DCHECK(thread_checker_.CalledOnValidThread());
312   DCHECK_NE(state_, kCapturing);
313   // If capture_machine is not NULL, then we need to return to the UI thread to
314   // safely stop the capture machine.
315   if (capture_machine_) {
316     VideoCaptureMachine* capture_machine_ptr = capture_machine_.get();
317     BrowserThread::PostTask(
318         BrowserThread::UI, FROM_HERE,
319         base::Bind(&VideoCaptureMachine::Stop,
320                    base::Unretained(capture_machine_ptr),
321                    base::Bind(&DeleteCaptureMachineOnUIThread,
322                               base::Passed(&capture_machine_))));
323   }
324   DVLOG(1) << "ContentVideoCaptureDeviceCore@" << this << " destroying.";
325 }
326
327 void ContentVideoCaptureDeviceCore::TransitionStateTo(State next_state) {
328   DCHECK(thread_checker_.CalledOnValidThread());
329
330 #ifndef NDEBUG
331   static const char* kStateNames[] = {
332     "Idle", "Allocated", "Capturing", "Error"
333   };
334   DVLOG(1) << "State change: " << kStateNames[state_]
335            << " --> " << kStateNames[next_state];
336 #endif
337
338   state_ = next_state;
339 }
340
341 void ContentVideoCaptureDeviceCore::Error(const std::string& reason) {
342   DCHECK(thread_checker_.CalledOnValidThread());
343
344   if (state_ == kIdle)
345     return;
346
347   if (oracle_proxy_.get())
348     oracle_proxy_->ReportError(reason);
349
350   StopAndDeAllocate();
351   TransitionStateTo(kError);
352 }
353
354 }  // namespace content