1// Copyright (c) 2012 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// Implementation notes about interactions with VideoCaptureImpl.
6//
7// How is VideoCaptureImpl used:
8//
9// VideoCaptureImpl is an IO thread object while VideoCaptureImplManager
10// lives only on the render thread. It is only possible to access an
11// object of VideoCaptureImpl via a task on the IO thread.
12//
13// How is VideoCaptureImpl deleted:
14//
15// A task is posted to the IO thread to delete a VideoCaptureImpl.
16// Immediately after that the pointer to it is dropped. This means no
17// access to this VideoCaptureImpl object is possible on the render
18// thread. Also note that VideoCaptureImpl does not post task to itself.
19//
20// The use of Unretained:
21//
22// We make sure deletion is the last task on the IO thread for a
23// VideoCaptureImpl object. This allows the use of Unretained() binding.
24
25#include "content/renderer/media/video_capture_impl_manager.h"
26
27#include "base/bind.h"
28#include "base/bind_helpers.h"
29#include "content/child/child_process.h"
30#include "content/renderer/media/video_capture_impl.h"
31#include "content/renderer/media/video_capture_message_filter.h"
32#include "media/base/bind_to_current_loop.h"
33
34namespace content {
35
36VideoCaptureImplManager::VideoCaptureImplManager()
37    : next_client_id_(0),
38      filter_(new VideoCaptureMessageFilter()),
39      weak_factory_(this) {
40}
41
42VideoCaptureImplManager::~VideoCaptureImplManager() {
43  DCHECK(thread_checker_.CalledOnValidThread());
44  if (devices_.empty())
45    return;
46  // Forcibly release all video capture resources.
47  for (VideoCaptureDeviceMap::iterator it = devices_.begin();
48       it != devices_.end(); ++it) {
49    VideoCaptureImpl* impl = it->second.second;
50    ChildProcess::current()->io_message_loop_proxy()->PostTask(
51        FROM_HERE,
52        base::Bind(&VideoCaptureImpl::DeInit,
53                   base::Unretained(impl)));
54    ChildProcess::current()->io_message_loop_proxy()->PostTask(
55        FROM_HERE,
56        base::Bind(&base::DeletePointer<VideoCaptureImpl>,
57                   base::Unretained(impl)));
58  }
59  devices_.clear();
60}
61
62base::Closure VideoCaptureImplManager::UseDevice(
63    media::VideoCaptureSessionId id) {
64  DCHECK(thread_checker_.CalledOnValidThread());
65
66  VideoCaptureImpl* impl = NULL;
67  VideoCaptureDeviceMap::iterator it = devices_.find(id);
68  if (it == devices_.end()) {
69    impl = CreateVideoCaptureImplForTesting(id, filter_.get());
70    if (!impl)
71      impl = new VideoCaptureImpl(id, filter_.get());
72    devices_[id] = std::make_pair(1, impl);
73    ChildProcess::current()->io_message_loop_proxy()->PostTask(
74        FROM_HERE,
75        base::Bind(&VideoCaptureImpl::Init,
76                   base::Unretained(impl)));
77  } else {
78    ++it->second.first;
79  }
80  return base::Bind(&VideoCaptureImplManager::UnrefDevice,
81                    weak_factory_.GetWeakPtr(), id);
82}
83
84base::Closure VideoCaptureImplManager::StartCapture(
85    media::VideoCaptureSessionId id,
86    const media::VideoCaptureParams& params,
87    const VideoCaptureStateUpdateCB& state_update_cb,
88    const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
89  DCHECK(thread_checker_.CalledOnValidThread());
90  VideoCaptureDeviceMap::iterator it = devices_.find(id);
91  DCHECK(it != devices_.end());
92  VideoCaptureImpl* impl = it->second.second;
93
94  // This ID is used to identify a client of VideoCaptureImpl.
95  const int client_id = ++next_client_id_;
96
97  ChildProcess::current()->io_message_loop_proxy()->PostTask(
98      FROM_HERE,
99      base::Bind(&VideoCaptureImpl::StartCapture,
100                 base::Unretained(impl),
101                 client_id,
102                 params,
103                 state_update_cb,
104                 deliver_frame_cb));
105  return base::Bind(&VideoCaptureImplManager::StopCapture,
106                    weak_factory_.GetWeakPtr(),
107                    client_id, id);
108}
109
110void VideoCaptureImplManager::GetDeviceSupportedFormats(
111    media::VideoCaptureSessionId id,
112    const VideoCaptureDeviceFormatsCB& callback) {
113  DCHECK(thread_checker_.CalledOnValidThread());
114  VideoCaptureDeviceMap::iterator it = devices_.find(id);
115  DCHECK(it != devices_.end());
116  VideoCaptureImpl* impl = it->second.second;
117  ChildProcess::current()->io_message_loop_proxy()->PostTask(
118      FROM_HERE,
119      base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats,
120                 base::Unretained(impl), callback));
121}
122
123void VideoCaptureImplManager::GetDeviceFormatsInUse(
124    media::VideoCaptureSessionId id,
125    const VideoCaptureDeviceFormatsCB& callback) {
126  DCHECK(thread_checker_.CalledOnValidThread());
127  VideoCaptureDeviceMap::iterator it = devices_.find(id);
128  DCHECK(it != devices_.end());
129  VideoCaptureImpl* impl = it->second.second;
130  ChildProcess::current()->io_message_loop_proxy()->PostTask(
131      FROM_HERE,
132      base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse,
133                 base::Unretained(impl), callback));
134}
135
136VideoCaptureImpl*
137VideoCaptureImplManager::CreateVideoCaptureImplForTesting(
138    media::VideoCaptureSessionId id,
139    VideoCaptureMessageFilter* filter) const {
140  return NULL;
141}
142
143void VideoCaptureImplManager::StopCapture(
144    int client_id, media::VideoCaptureSessionId id) {
145  DCHECK(thread_checker_.CalledOnValidThread());
146  VideoCaptureDeviceMap::iterator it = devices_.find(id);
147  DCHECK(it != devices_.end());
148  VideoCaptureImpl* impl = it->second.second;
149  ChildProcess::current()->io_message_loop_proxy()->PostTask(
150      FROM_HERE,
151      base::Bind(&VideoCaptureImpl::StopCapture,
152                 base::Unretained(impl), client_id));
153}
154
155void VideoCaptureImplManager::UnrefDevice(
156    media::VideoCaptureSessionId id) {
157  DCHECK(thread_checker_.CalledOnValidThread());
158  VideoCaptureDeviceMap::iterator it = devices_.find(id);
159  DCHECK(it != devices_.end());
160  VideoCaptureImpl* impl = it->second.second;
161
162  // Unref and destroy on the IO thread if there's no more client.
163  DCHECK(it->second.first);
164  --it->second.first;
165  if (!it->second.first) {
166    devices_.erase(id);
167    ChildProcess::current()->io_message_loop_proxy()->PostTask(
168        FROM_HERE,
169        base::Bind(&VideoCaptureImpl::DeInit,
170                   base::Unretained(impl)));
171    ChildProcess::current()->io_message_loop_proxy()->PostTask(
172        FROM_HERE,
173        base::Bind(&base::DeletePointer<VideoCaptureImpl>,
174                   base::Unretained(impl)));
175  }
176}
177
178void VideoCaptureImplManager::SuspendDevices(bool suspend) {
179  DCHECK(thread_checker_.CalledOnValidThread());
180  for (VideoCaptureDeviceMap::iterator it = devices_.begin();
181       it != devices_.end(); ++it) {
182    VideoCaptureImpl* impl = it->second.second;
183    ChildProcess::current()->io_message_loop_proxy()->PostTask(
184        FROM_HERE,
185        base::Bind(&VideoCaptureImpl::SuspendCapture,
186                   base::Unretained(impl), suspend));
187  }
188}
189
190}  // namespace content
191