Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/video_capture_manager.h" | 5 #include "content/browser/renderer_host/media/video_capture_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
| 13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 14 #include "base/location.h" | 14 #include "base/location.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/single_thread_task_runner.h" | 17 #include "base/single_thread_task_runner.h" |
| 18 #include "base/stl_util.h" | 18 #include "base/stl_util.h" |
| 19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
| 20 #include "base/task_runner_util.h" | 20 #include "base/task_runner_util.h" |
| 21 #include "base/threading/sequenced_worker_pool.h" | 21 #include "base/threading/sequenced_worker_pool.h" |
| 22 #include "base/threading/thread_task_runner_handle.h" | 22 #include "base/threading/thread_task_runner_handle.h" |
| 23 #include "build/build_config.h" | 23 #include "build/build_config.h" |
| 24 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" | 24 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" |
| 25 #include "content/browser/media/capture/web_contents_video_capture_device.h" | 25 #include "content/browser/media/capture/web_contents_video_capture_device.h" |
| 26 #include "content/browser/media/media_internals.h" | 26 #include "content/browser/media/media_internals.h" |
| 27 #include "content/browser/renderer_host/media/video_capture_controller.h" | 27 #include "content/browser/renderer_host/media/video_capture_controller.h" |
| 28 #include "content/browser/renderer_host/media/video_capture_controller_event_han dler.h" | 28 #include "content/browser/renderer_host/media/video_capture_controller_event_han dler.h" |
| 29 #include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h" | |
| 30 #include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread. h" | |
| 29 #include "content/public/browser/browser_thread.h" | 31 #include "content/public/browser/browser_thread.h" |
| 30 #include "content/public/browser/desktop_media_id.h" | 32 #include "content/public/browser/desktop_media_id.h" |
| 31 #include "content/public/common/media_stream_request.h" | 33 #include "content/public/common/media_stream_request.h" |
| 32 #include "media/base/bind_to_current_loop.h" | 34 #include "media/base/bind_to_current_loop.h" |
| 33 #include "media/base/media_switches.h" | 35 #include "media/base/media_switches.h" |
| 36 #include "media/capture/video/video_capture_buffer_pool_impl.h" | |
| 37 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h" | |
| 34 #include "media/capture/video/video_capture_device.h" | 38 #include "media/capture/video/video_capture_device.h" |
| 39 #include "media/capture/video/video_capture_device_client.h" | |
| 35 #include "media/capture/video/video_capture_device_factory.h" | 40 #include "media/capture/video/video_capture_device_factory.h" |
| 36 | 41 |
| 37 #if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID) | 42 #if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID) |
| 38 #include "content/browser/media/capture/desktop_capture_device.h" | 43 #include "content/browser/media/capture/desktop_capture_device.h" |
| 39 #if defined(USE_AURA) | 44 #if defined(USE_AURA) |
| 40 #include "content/browser/media/capture/desktop_capture_device_aura.h" | 45 #include "content/browser/media/capture/desktop_capture_device_aura.h" |
| 41 #endif | 46 #endif |
| 42 #endif | 47 #endif |
| 43 | 48 |
| 44 #if defined(ENABLE_SCREEN_CAPTURE) && defined(OS_ANDROID) | 49 #if defined(ENABLE_SCREEN_CAPTURE) && defined(OS_ANDROID) |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 106 formats->erase(last, formats->end()); | 111 formats->erase(last, formats->end()); |
| 107 // Mark all formats as I420, since this is what the renderer side will get | 112 // Mark all formats as I420, since this is what the renderer side will get |
| 108 // anyhow: the actual pixel format is decided at the device level. | 113 // anyhow: the actual pixel format is decided at the device level. |
| 109 // Don't do this for Y16 format as it is handled separatelly. | 114 // Don't do this for Y16 format as it is handled separatelly. |
| 110 for (auto& format : *formats) { | 115 for (auto& format : *formats) { |
| 111 if (format.pixel_format != media::PIXEL_FORMAT_Y16) | 116 if (format.pixel_format != media::PIXEL_FORMAT_Y16) |
| 112 format.pixel_format = media::PIXEL_FORMAT_I420; | 117 format.pixel_format = media::PIXEL_FORMAT_I420; |
| 113 } | 118 } |
| 114 } | 119 } |
| 115 | 120 |
| 116 // The maximum number of buffers in the capture pipeline. See | 121 // The maximum number of video frame buffers in-flight at any one time. This |
| 117 // VideoCaptureController ctor comments for more details. | 122 // value should be based on the logical capacity of the capture pipeline, and |
| 123 // not on hardware performance. For example, tab capture requires more buffers | |
| 124 // than webcam capture because the pipeline is longer (it includes read-backs | |
| 125 // pending in the GPU pipeline). | |
| 118 const int kMaxNumberOfBuffers = 3; | 126 const int kMaxNumberOfBuffers = 3; |
| 119 // TODO(miu): The value for tab capture should be determined programmatically. | 127 // TODO(miu): The value for tab capture should be determined programmatically. |
| 120 // http://crbug.com/460318 | 128 // http://crbug.com/460318 |
| 121 const int kMaxNumberOfBuffersForTabCapture = 10; | 129 const int kMaxNumberOfBuffersForTabCapture = 10; |
| 122 | 130 |
| 123 // Used for logging capture events. | 131 // Used for logging capture events. |
| 124 // Elements in this enum should not be deleted or rearranged; the only | 132 // Elements in this enum should not be deleted or rearranged; the only |
| 125 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT. | 133 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT. |
| 126 enum VideoCaptureEvent { | 134 enum VideoCaptureEvent { |
| 127 VIDEO_CAPTURE_START_CAPTURE = 0, | 135 VIDEO_CAPTURE_START_CAPTURE = 0, |
| 128 VIDEO_CAPTURE_STOP_CAPTURE_OK = 1, | 136 VIDEO_CAPTURE_STOP_CAPTURE_OK = 1, |
| 129 VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR = 2, | 137 VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR = 2, |
| 130 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE = 3, | 138 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE = 3, |
| 131 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB = 4, | 139 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB = 4, |
| 132 NUM_VIDEO_CAPTURE_EVENT | 140 NUM_VIDEO_CAPTURE_EVENT |
| 133 }; | 141 }; |
| 134 | 142 |
| 135 void LogVideoCaptureEvent(VideoCaptureEvent event) { | 143 void LogVideoCaptureEvent(VideoCaptureEvent event) { |
| 136 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event", | 144 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event", |
| 137 event, | 145 event, |
| 138 NUM_VIDEO_CAPTURE_EVENT); | 146 NUM_VIDEO_CAPTURE_EVENT); |
| 139 } | 147 } |
| 140 | 148 |
| 141 // Counter used for identifying a DeviceRequest to start a capture device. | 149 // Counter used for identifying a DeviceRequest to start a capture device. |
| 142 static int g_device_start_id = 0; | 150 static int g_device_start_id = 0; |
| 143 | 151 |
| 144 const media::VideoCaptureSessionId kFakeSessionId = -1; | 152 const media::VideoCaptureSessionId kFakeSessionId = -1; |
| 145 | 153 |
| 154 std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder( | |
| 155 const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) { | |
| 156 return base::MakeUnique<content::VideoCaptureGpuJpegDecoder>(decode_done_cb); | |
| 157 } | |
| 158 | |
| 146 } // namespace | 159 } // namespace |
| 147 | 160 |
| 148 namespace content { | 161 namespace content { |
| 149 | 162 |
| 150 // Instances of this struct go through 3 different phases during their lifetime. | 163 // Instances of this struct go through several different phases during their |
| 164 // lifetime. | |
| 151 // Phase 1: When first created (in GetOrCreateDeviceEntry()), this consists of | 165 // Phase 1: When first created (in GetOrCreateDeviceEntry()), this consists of |
| 152 // only a controller. Clients can already connect to the controller, but there | 166 // only the |video_capture_controller|. Clients can already connect to the |
| 153 // is no device present. | 167 // controller, but there is no |buffer_pool| or |video_capture_device| present. |
| 154 // Phase 2: When a request to "start" the entry comes in (via | 168 // Phase 2: When a request to "start" the entry comes in (via |
| 155 // HandleQueuedStartRequest()), a VideoCaptureDevice::Client is created | 169 // HandleQueuedStartRequest()), |buffer_pool| is created and creation of |
| 156 // via video_capture_controller()->NewDeviceClient() and is used to schedule the | 170 // |video_capture_device| is scheduled to run asynchronously on the Device |
| 157 // creation and start of a VideoCaptureDevice on the Device Thread. | 171 // Thread. |
| 158 // Phase 3: As soon as the creation of the VideoCaptureDevice is complete, this | 172 // Phase 3: As soon as the creation of the VideoCaptureDevice is complete, this |
| 159 // newly created VideoCaptureDevice instance is connected to the | 173 // newly created VideoCaptureDevice instance is connected to the |
| 160 // VideoCaptureController via SetConsumerFeedbackObserver(). | 174 // VideoCaptureController via SetConsumerFeedbackObserver(). Furthermore, the |
| 161 class VideoCaptureManager::DeviceEntry { | 175 // |buffer_pool| is connected to the |video_capture_controller| as a |
| 176 // FrameBufferPool via SetFrameBufferPool(). | |
| 177 // Phase 4: This phase can only be reached on Android. When the application goes | |
| 178 // to the background, the |video_capture_device| is asynchronously stopped and | |
| 179 // released on the Device Thread. The existing |buffer_pool| is kept alive, and | |
| 180 // all clients of |video_capture_controller| stay connected. When the | |
| 181 // application is resumed, we transition to Phase 2, except that the existing | |
| 182 // |buffer_pool| get reused instead of creating a new one. | |
|
mcasas
2016/12/22 03:07:43
s/get/gets/
| |
| 183 struct VideoCaptureManager::DeviceEntry { | |
| 162 public: | 184 public: |
| 163 DeviceEntry(MediaStreamType stream_type, | 185 DeviceEntry(MediaStreamType stream_type, |
| 164 const std::string& id, | 186 const std::string& id, |
| 165 std::unique_ptr<VideoCaptureController> controller, | |
| 166 const media::VideoCaptureParams& params); | 187 const media::VideoCaptureParams& params); |
| 167 ~DeviceEntry(); | 188 ~DeviceEntry(); |
| 189 std::unique_ptr<media::VideoCaptureDevice::Client> CreateDeviceClient(); | |
| 190 std::unique_ptr<media::FrameBufferPool> CreateFrameBufferPool(); | |
| 168 | 191 |
| 169 const int serial_id; | 192 const int serial_id; |
| 170 const MediaStreamType stream_type; | 193 const MediaStreamType stream_type; |
| 171 const std::string id; | 194 const std::string id; |
| 172 const media::VideoCaptureParams parameters; | 195 const media::VideoCaptureParams parameters; |
| 173 | 196 VideoCaptureController video_capture_controller; |
| 174 VideoCaptureController* video_capture_controller() const; | 197 scoped_refptr<media::VideoCaptureBufferPool> buffer_pool; |
| 175 media::VideoCaptureDevice* video_capture_device() const; | 198 std::unique_ptr<media::VideoCaptureDevice> video_capture_device; |
| 176 | |
| 177 void SetVideoCaptureDevice(std::unique_ptr<VideoCaptureDevice> device); | |
| 178 std::unique_ptr<VideoCaptureDevice> ReleaseVideoCaptureDevice(); | |
| 179 | |
| 180 private: | |
| 181 const std::unique_ptr<VideoCaptureController> video_capture_controller_; | |
| 182 | |
| 183 std::unique_ptr<VideoCaptureDevice> video_capture_device_; | |
| 184 | |
| 185 base::ThreadChecker thread_checker_; | |
| 186 }; | 199 }; |
| 187 | 200 |
| 188 // Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported | 201 // Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported |
| 189 // video formats. | 202 // video formats. |
| 190 struct VideoCaptureManager::DeviceInfo { | 203 struct VideoCaptureManager::DeviceInfo { |
| 191 DeviceInfo(); | 204 DeviceInfo(); |
| 192 DeviceInfo(media::VideoCaptureDeviceDescriptor descriptor); | 205 DeviceInfo(media::VideoCaptureDeviceDescriptor descriptor); |
| 193 DeviceInfo(const DeviceInfo& other); | 206 DeviceInfo(const DeviceInfo& other); |
| 194 ~DeviceInfo(); | 207 ~DeviceInfo(); |
| 195 DeviceInfo& operator=(const DeviceInfo& other); | 208 DeviceInfo& operator=(const DeviceInfo& other); |
| 196 | 209 |
| 197 media::VideoCaptureDeviceDescriptor descriptor; | 210 media::VideoCaptureDeviceDescriptor descriptor; |
| 198 media::VideoCaptureFormats supported_formats; | 211 media::VideoCaptureFormats supported_formats; |
| 199 }; | 212 }; |
| 200 | 213 |
| 214 class BufferPoolFrameBufferPool : public media::FrameBufferPool { | |
| 215 public: | |
| 216 explicit BufferPoolFrameBufferPool( | |
| 217 scoped_refptr<media::VideoCaptureBufferPool> buffer_pool) | |
| 218 : buffer_pool_(std::move(buffer_pool)) {} | |
| 219 | |
| 220 void SetBufferHold(int buffer_id) override { | |
| 221 buffer_pool_->HoldForConsumers(buffer_id, 1); | |
| 222 } | |
| 223 | |
| 224 void ReleaseBufferHold(int buffer_id) override { | |
| 225 buffer_pool_->RelinquishConsumerHold(buffer_id, 1); | |
| 226 } | |
| 227 | |
| 228 mojo::ScopedSharedBufferHandle GetHandleForTransit(int buffer_id) override { | |
| 229 return buffer_pool_->GetHandleForTransit(buffer_id); | |
| 230 } | |
| 231 | |
| 232 private: | |
| 233 scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_; | |
| 234 }; | |
| 235 | |
| 201 // Class used for queuing request for starting a device. | 236 // Class used for queuing request for starting a device. |
| 202 class VideoCaptureManager::CaptureDeviceStartRequest { | 237 class VideoCaptureManager::CaptureDeviceStartRequest { |
| 203 public: | 238 public: |
| 204 CaptureDeviceStartRequest(int serial_id, | 239 CaptureDeviceStartRequest(int serial_id, |
| 205 media::VideoCaptureSessionId session_id, | 240 media::VideoCaptureSessionId session_id, |
| 206 const media::VideoCaptureParams& params); | 241 const media::VideoCaptureParams& params); |
| 207 int serial_id() const { return serial_id_; } | 242 int serial_id() const { return serial_id_; } |
| 208 media::VideoCaptureSessionId session_id() const { return session_id_; } | 243 media::VideoCaptureSessionId session_id() const { return session_id_; } |
| 209 media::VideoCaptureParams params() const { return params_; } | 244 media::VideoCaptureParams params() const { return params_; } |
| 210 | 245 |
| 211 // Set to true if the device should be stopped before it has successfully | 246 // Set to true if the device should be stopped before it has successfully |
| 212 // been started. | 247 // been started. |
| 213 bool abort_start() const { return abort_start_; } | 248 bool abort_start() const { return abort_start_; } |
| 214 void set_abort_start() { abort_start_ = true; } | 249 void set_abort_start() { abort_start_ = true; } |
| 215 | 250 |
| 216 private: | 251 private: |
| 217 const int serial_id_; | 252 const int serial_id_; |
| 218 const media::VideoCaptureSessionId session_id_; | 253 const media::VideoCaptureSessionId session_id_; |
| 219 const media::VideoCaptureParams params_; | 254 const media::VideoCaptureParams params_; |
| 220 // Set to true if the device should be stopped before it has successfully | 255 // Set to true if the device should be stopped before it has successfully |
| 221 // been started. | 256 // been started. |
| 222 bool abort_start_; | 257 bool abort_start_; |
| 223 }; | 258 }; |
| 224 | 259 |
| 225 VideoCaptureManager::DeviceEntry::DeviceEntry( | 260 VideoCaptureManager::DeviceEntry::DeviceEntry( |
| 226 MediaStreamType stream_type, | 261 MediaStreamType stream_type, |
| 227 const std::string& id, | 262 const std::string& id, |
| 228 std::unique_ptr<VideoCaptureController> controller, | |
| 229 const media::VideoCaptureParams& params) | 263 const media::VideoCaptureParams& params) |
| 230 : serial_id(g_device_start_id++), | 264 : serial_id(g_device_start_id++), |
| 231 stream_type(stream_type), | 265 stream_type(stream_type), |
| 232 id(id), | 266 id(id), |
| 233 parameters(params), | 267 parameters(params) {} |
| 234 video_capture_controller_(std::move(controller)) {} | |
| 235 | 268 |
| 236 VideoCaptureManager::DeviceEntry::~DeviceEntry() { | 269 VideoCaptureManager::DeviceEntry::~DeviceEntry() { |
| 237 DCHECK(thread_checker_.CalledOnValidThread()); | 270 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 238 // DCHECK that this DeviceEntry does not still own a | 271 // DCHECK that this DeviceEntry does not still own a |
| 239 // media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on | 272 // media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on |
| 240 // the device thread. | 273 // the device thread. |
| 241 DCHECK(video_capture_device_ == nullptr); | 274 DCHECK(video_capture_device == nullptr); |
| 242 } | 275 } |
| 243 | 276 |
| 244 void VideoCaptureManager::DeviceEntry::SetVideoCaptureDevice( | 277 std::unique_ptr<media::VideoCaptureDevice::Client> |
| 245 std::unique_ptr<VideoCaptureDevice> device) { | 278 VideoCaptureManager::DeviceEntry::CreateDeviceClient() { |
| 246 DCHECK(thread_checker_.CalledOnValidThread()); | 279 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 247 video_capture_device_.swap(device); | 280 |
| 281 const int max_buffers = stream_type == MEDIA_TAB_VIDEO_CAPTURE | |
| 282 ? kMaxNumberOfBuffersForTabCapture | |
| 283 : kMaxNumberOfBuffers; | |
| 284 if (!buffer_pool) { | |
| 285 buffer_pool = new media::VideoCaptureBufferPoolImpl( | |
|
mcasas
2016/12/22 03:07:43
If this is only a special use case for Android, us
| |
| 286 base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(), | |
| 287 max_buffers); | |
| 288 } | |
| 289 | |
| 290 return base::MakeUnique<media::VideoCaptureDeviceClient>( | |
| 291 base::MakeUnique<VideoFrameReceiverOnIOThread>( | |
| 292 video_capture_controller.GetWeakPtrForIOThread()), | |
| 293 buffer_pool, | |
| 294 base::Bind( | |
| 295 &CreateGpuJpegDecoder, | |
| 296 base::Bind(&media::VideoFrameReceiver::OnIncomingCapturedVideoFrame, | |
| 297 video_capture_controller.GetWeakPtrForIOThread()))); | |
| 248 } | 298 } |
| 249 | 299 |
| 250 std::unique_ptr<media::VideoCaptureDevice> | 300 std::unique_ptr<media::FrameBufferPool> |
| 251 VideoCaptureManager::DeviceEntry::ReleaseVideoCaptureDevice() { | 301 VideoCaptureManager::DeviceEntry::CreateFrameBufferPool() { |
| 252 DCHECK(thread_checker_.CalledOnValidThread()); | 302 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 253 return std::move(video_capture_device_); | 303 DCHECK(buffer_pool); |
| 254 } | 304 return base::MakeUnique<BufferPoolFrameBufferPool>(buffer_pool); |
| 255 | |
| 256 VideoCaptureController* | |
| 257 VideoCaptureManager::DeviceEntry::video_capture_controller() const { | |
| 258 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 259 return video_capture_controller_.get(); | |
| 260 } | |
| 261 | |
| 262 media::VideoCaptureDevice* | |
| 263 VideoCaptureManager::DeviceEntry::video_capture_device() const { | |
| 264 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 265 return video_capture_device_.get(); | |
| 266 } | 305 } |
| 267 | 306 |
| 268 VideoCaptureManager::DeviceInfo::DeviceInfo() = default; | 307 VideoCaptureManager::DeviceInfo::DeviceInfo() = default; |
| 269 | 308 |
| 270 VideoCaptureManager::DeviceInfo::DeviceInfo( | 309 VideoCaptureManager::DeviceInfo::DeviceInfo( |
| 271 media::VideoCaptureDeviceDescriptor descriptor) | 310 media::VideoCaptureDeviceDescriptor descriptor) |
| 272 : descriptor(descriptor) {} | 311 : descriptor(descriptor) {} |
| 273 | 312 |
| 274 VideoCaptureManager::DeviceInfo::DeviceInfo( | 313 VideoCaptureManager::DeviceInfo::DeviceInfo( |
| 275 const VideoCaptureManager::DeviceInfo& other) = default; | 314 const VideoCaptureManager::DeviceInfo& other) = default; |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 377 if (session_it == sessions_.end()) { | 416 if (session_it == sessions_.end()) { |
| 378 NOTREACHED(); | 417 NOTREACHED(); |
| 379 return; | 418 return; |
| 380 } | 419 } |
| 381 | 420 |
| 382 DeviceEntry* const existing_device = | 421 DeviceEntry* const existing_device = |
| 383 GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id); | 422 GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id); |
| 384 if (existing_device) { | 423 if (existing_device) { |
| 385 // Remove any client that is still using the session. This is safe to call | 424 // Remove any client that is still using the session. This is safe to call |
| 386 // even if there are no clients using the session. | 425 // even if there are no clients using the session. |
| 387 existing_device->video_capture_controller() | 426 existing_device->video_capture_controller.StopSession(capture_session_id); |
| 388 ->StopSession(capture_session_id); | |
| 389 | 427 |
| 390 // StopSession() may have removed the last client, so we might need to | 428 // StopSession() may have removed the last client, so we might need to |
| 391 // close the device. | 429 // close the device. |
| 392 DestroyDeviceEntryIfNoClients(existing_device); | 430 DestroyDeviceEntryIfNoClients(existing_device); |
| 393 } | 431 } |
| 394 | 432 |
| 395 // Notify listeners asynchronously, and forget the session. | 433 // Notify listeners asynchronously, and forget the session. |
| 396 base::ThreadTaskRunnerHandle::Get()->PostTask( | 434 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 397 FROM_HERE, base::Bind(&VideoCaptureManager::OnClosed, this, | 435 FROM_HERE, base::Bind(&VideoCaptureManager::OnClosed, this, |
| 398 session_it->second.type, capture_session_id)); | 436 session_it->second.type, capture_session_id)); |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 426 if (request->serial_id() == entry->serial_id) { | 464 if (request->serial_id() == entry->serial_id) { |
| 427 request->set_abort_start(); | 465 request->set_abort_start(); |
| 428 DVLOG(3) << "DoStopDevice, aborting start request for device " | 466 DVLOG(3) << "DoStopDevice, aborting start request for device " |
| 429 << entry->id << " serial_id = " << entry->serial_id; | 467 << entry->id << " serial_id = " << entry->serial_id; |
| 430 return; | 468 return; |
| 431 } | 469 } |
| 432 } | 470 } |
| 433 | 471 |
| 434 DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id | 472 DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id |
| 435 << " serial_id = " << entry->serial_id << "."; | 473 << " serial_id = " << entry->serial_id << "."; |
| 436 entry->video_capture_controller()->OnLog( | 474 entry->video_capture_controller.OnLog( |
| 437 base::StringPrintf("Stopping device: id: %s", entry->id.c_str())); | 475 base::StringPrintf("Stopping device: id: %s", entry->id.c_str())); |
| 438 entry->video_capture_controller()->SetConsumerFeedbackObserver(nullptr); | 476 entry->video_capture_controller.SetConsumerFeedbackObserver(nullptr); |
| 477 entry->video_capture_controller.SetFrameBufferPool(nullptr); | |
| 439 | 478 |
| 440 // |entry->video_capture_device| can be null if creating the device has | 479 // |entry->video_capture_device| can be null if creating the device has |
| 441 // failed. | 480 // failed. |
| 442 if (entry->video_capture_device()) { | 481 if (entry->video_capture_device) { |
| 443 device_task_runner_->PostTask( | 482 device_task_runner_->PostTask( |
| 444 FROM_HERE, | 483 FROM_HERE, |
| 445 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, | 484 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| 446 base::Passed(entry->ReleaseVideoCaptureDevice()))); | 485 base::Passed(&entry->video_capture_device))); |
| 447 } | 486 } |
| 448 } | 487 } |
| 449 | 488 |
| 450 void VideoCaptureManager::HandleQueuedStartRequest() { | 489 void VideoCaptureManager::HandleQueuedStartRequest() { |
| 451 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 490 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 452 // Remove all start requests that have been aborted. | 491 // Remove all start requests that have been aborted. |
| 453 while (device_start_queue_.begin() != device_start_queue_.end() && | 492 while (device_start_queue_.begin() != device_start_queue_.end() && |
| 454 device_start_queue_.begin()->abort_start()) { | 493 device_start_queue_.begin()->abort_start()) { |
| 455 device_start_queue_.pop_front(); | 494 device_start_queue_.pop_front(); |
| 456 } | 495 } |
| 457 DeviceStartQueue::iterator request = device_start_queue_.begin(); | 496 DeviceStartQueue::iterator request = device_start_queue_.begin(); |
| 458 if (request == device_start_queue_.end()) | 497 if (request == device_start_queue_.end()) |
| 459 return; | 498 return; |
| 460 | 499 |
| 461 const int serial_id = request->serial_id(); | 500 const int serial_id = request->serial_id(); |
| 462 DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id); | 501 DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id); |
| 463 DCHECK(entry); | 502 DCHECK(entry); |
| 464 | 503 |
| 465 DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = " | 504 DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = " |
| 466 << entry->id << " start id = " << entry->serial_id; | 505 << entry->id << " start id = " << entry->serial_id; |
| 467 | 506 |
| 507 std::unique_ptr<media::VideoCaptureDevice::Client> device_client = | |
| 508 entry->CreateDeviceClient(); | |
| 509 std::unique_ptr<media::FrameBufferPool> frame_buffer_pool = | |
| 510 entry->CreateFrameBufferPool(); | |
| 511 | |
| 468 base::Callback<std::unique_ptr<VideoCaptureDevice>(void)> | 512 base::Callback<std::unique_ptr<VideoCaptureDevice>(void)> |
| 469 start_capture_function; | 513 start_capture_function; |
| 470 | 514 |
| 471 switch (entry->stream_type) { | 515 switch (entry->stream_type) { |
| 472 case MEDIA_DEVICE_VIDEO_CAPTURE: { | 516 case MEDIA_DEVICE_VIDEO_CAPTURE: { |
| 473 // We look up the device id from the renderer in our local enumeration | 517 // We look up the device id from the renderer in our local enumeration |
| 474 // since the renderer does not have all the information that might be | 518 // since the renderer does not have all the information that might be |
| 475 // held in the browser-side VideoCaptureDevice::Name structure. | 519 // held in the browser-side VideoCaptureDevice::Name structure. |
| 476 const DeviceInfo* found = GetDeviceInfoById(entry->id); | 520 const DeviceInfo* found = GetDeviceInfoById(entry->id); |
| 477 if (found) { | 521 if (found) { |
| 478 entry->video_capture_controller()->OnLog( | 522 entry->video_capture_controller.OnLog( |
| 479 base::StringPrintf("Starting device: id: %s, name: %s, api: %s", | 523 base::StringPrintf("Starting device: id: %s, name: %s, api: %s", |
| 480 found->descriptor.device_id.c_str(), | 524 found->descriptor.device_id.c_str(), |
| 481 found->descriptor.GetNameAndModel().c_str(), | 525 found->descriptor.GetNameAndModel().c_str(), |
| 482 found->descriptor.GetCaptureApiTypeString())); | 526 found->descriptor.GetCaptureApiTypeString())); |
| 483 | 527 |
| 484 start_capture_function = base::Bind( | 528 start_capture_function = |
| 485 &VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread, this, | 529 base::Bind(&VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread, |
| 486 found->descriptor, request->params(), | 530 this, found->descriptor, request->params(), |
| 487 base::Passed(entry->video_capture_controller()->NewDeviceClient())); | 531 base::Passed(std::move(device_client))); |
| 488 } else { | 532 } else { |
| 489 // Errors from DoStartDeviceCaptureOnDeviceThread go via | 533 // Errors from DoStartDeviceCaptureOnDeviceThread go via |
| 490 // VideoCaptureDeviceClient::OnError, which needs some thread | 534 // VideoCaptureDeviceClient::OnError, which needs some thread |
| 491 // dancing to get errors processed on the IO thread. But since | 535 // dancing to get errors processed on the IO thread. But since |
| 492 // we're on that thread, we call VideoCaptureController | 536 // we're on that thread, we call VideoCaptureController |
| 493 // methods directly. | 537 // methods directly. |
| 494 const std::string log_message = base::StringPrintf( | 538 const std::string log_message = base::StringPrintf( |
| 495 "Error on %s:%d: device %s unknown. Maybe recently disconnected?", | 539 "Error on %s:%d: device %s unknown. Maybe recently disconnected?", |
| 496 __FILE__, __LINE__, entry->id.c_str()); | 540 __FILE__, __LINE__, entry->id.c_str()); |
| 497 DLOG(ERROR) << log_message; | 541 DLOG(ERROR) << log_message; |
| 498 entry->video_capture_controller()->OnLog(log_message); | 542 entry->video_capture_controller.OnLog(log_message); |
| 499 entry->video_capture_controller()->OnError(); | 543 entry->video_capture_controller.OnError(); |
| 500 // Drop the failed start request. | 544 // Drop the failed start request. |
| 501 device_start_queue_.pop_front(); | 545 device_start_queue_.pop_front(); |
| 502 | 546 |
| 503 return; | 547 return; |
| 504 } | 548 } |
| 505 break; | 549 break; |
| 506 } | 550 } |
| 507 case MEDIA_TAB_VIDEO_CAPTURE: | 551 case MEDIA_TAB_VIDEO_CAPTURE: |
| 508 start_capture_function = base::Bind( | 552 start_capture_function = base::Bind( |
| 509 &VideoCaptureManager::DoStartTabCaptureOnDeviceThread, this, | 553 &VideoCaptureManager::DoStartTabCaptureOnDeviceThread, this, |
| 510 entry->id, request->params(), | 554 entry->id, request->params(), base::Passed(std::move(device_client))); |
| 511 base::Passed(entry->video_capture_controller()->NewDeviceClient())); | |
| 512 break; | 555 break; |
| 513 | 556 |
| 514 case MEDIA_DESKTOP_VIDEO_CAPTURE: | 557 case MEDIA_DESKTOP_VIDEO_CAPTURE: |
| 515 start_capture_function = base::Bind( | 558 start_capture_function = base::Bind( |
| 516 &VideoCaptureManager::DoStartDesktopCaptureOnDeviceThread, this, | 559 &VideoCaptureManager::DoStartDesktopCaptureOnDeviceThread, this, |
| 517 entry->id, request->params(), | 560 entry->id, request->params(), base::Passed(std::move(device_client))); |
| 518 base::Passed(entry->video_capture_controller()->NewDeviceClient())); | |
| 519 break; | 561 break; |
| 520 | 562 |
| 521 default: { | 563 default: { |
| 522 NOTIMPLEMENTED(); | 564 NOTIMPLEMENTED(); |
| 523 return; | 565 return; |
| 524 } | 566 } |
| 525 } | 567 } |
| 526 base::PostTaskAndReplyWithResult( | 568 base::PostTaskAndReplyWithResult( |
| 527 device_task_runner_.get(), FROM_HERE, start_capture_function, | 569 device_task_runner_.get(), FROM_HERE, start_capture_function, |
| 528 base::Bind(&VideoCaptureManager::OnDeviceStarted, this, | 570 base::Bind(&VideoCaptureManager::OnDeviceStarted, this, |
| 529 request->serial_id())); | 571 request->serial_id(), base::Passed(&frame_buffer_pool))); |
| 530 } | 572 } |
| 531 | 573 |
| 532 void VideoCaptureManager::OnDeviceStarted( | 574 void VideoCaptureManager::OnDeviceStarted( |
| 533 int serial_id, | 575 int serial_id, |
| 576 std::unique_ptr<media::FrameBufferPool> frame_buffer_pool, | |
| 534 std::unique_ptr<VideoCaptureDevice> device) { | 577 std::unique_ptr<VideoCaptureDevice> device) { |
| 535 DVLOG(3) << __func__; | 578 DVLOG(3) << __func__; |
| 536 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 579 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 537 DCHECK_EQ(serial_id, device_start_queue_.begin()->serial_id()); | 580 DCHECK_EQ(serial_id, device_start_queue_.begin()->serial_id()); |
| 538 // |device| can be null if creation failed in | 581 // |device| can be null if creation failed in |
| 539 // DoStartDeviceCaptureOnDeviceThread. | 582 // DoStartDeviceCaptureOnDeviceThread. |
| 540 if (device_start_queue_.front().abort_start()) { | 583 if (device_start_queue_.front().abort_start()) { |
| 541 // The device is no longer wanted. Stop the device again. | 584 // The device is no longer wanted. Stop the device again. |
| 542 DVLOG(3) << "OnDeviceStarted but start request have been aborted."; | 585 DVLOG(3) << "OnDeviceStarted but start request have been aborted."; |
| 543 media::VideoCaptureDevice* device_ptr = device.get(); | 586 media::VideoCaptureDevice* device_ptr = device.get(); |
| 544 base::Closure closure = | 587 base::Closure closure = |
| 545 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, | 588 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
| 546 base::Passed(&device)); | 589 base::Passed(&device)); |
| 547 if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) { | 590 if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) { |
| 548 // PostTask failed. The device must be stopped anyway. | 591 // PostTask failed. The device must be stopped anyway. |
| 549 device_ptr->StopAndDeAllocate(); | 592 device_ptr->StopAndDeAllocate(); |
| 550 } | 593 } |
| 551 } else { | 594 } else { |
| 552 DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id); | 595 DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id); |
| 553 DCHECK(entry); | 596 DCHECK(entry); |
| 554 DCHECK(!entry->video_capture_device()); | 597 DCHECK(!entry->video_capture_device); |
| 555 // Passing raw pointer |device.get()| to the controller is safe, | 598 if (device) { |
| 556 // because we transfer ownership of it to |entry|. We are calling | 599 entry->video_capture_controller.SetFrameBufferPool( |
| 557 // SetConsumerFeedbackObserver(nullptr) before releasing | 600 std::move(frame_buffer_pool)); |
| 558 // |entry->video_capture_device_| on the |device_task_runner_|. | 601 // Passing raw pointer |device.get()| to the controller is safe, |
| 559 entry->video_capture_controller()->SetConsumerFeedbackObserver( | 602 // because we transfer ownership of it to |entry|. We are calling |
| 560 base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>( | 603 // SetConsumerFeedbackObserver(nullptr) before releasing |
| 561 device.get(), device_task_runner_)); | 604 // |entry->video_capture_device_| on the |device_task_runner_|. |
| 562 entry->SetVideoCaptureDevice(std::move(device)); | 605 entry->video_capture_controller.SetConsumerFeedbackObserver( |
| 606 base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>( | |
| 607 device.get(), device_task_runner_)); | |
| 608 } | |
| 609 entry->video_capture_device = std::move(device); | |
| 563 | 610 |
| 564 if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) { | 611 if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) { |
| 565 const media::VideoCaptureSessionId session_id = | 612 const media::VideoCaptureSessionId session_id = |
| 566 device_start_queue_.front().session_id(); | 613 device_start_queue_.front().session_id(); |
| 567 DCHECK(session_id != kFakeSessionId); | 614 DCHECK(session_id != kFakeSessionId); |
| 568 MaybePostDesktopCaptureWindowId(session_id); | 615 MaybePostDesktopCaptureWindowId(session_id); |
| 569 } | 616 } |
| 570 | 617 |
| 571 auto it = photo_request_queue_.begin(); | 618 auto it = photo_request_queue_.begin(); |
| 572 while (it != photo_request_queue_.end()) { | 619 while (it != photo_request_queue_.end()) { |
| 573 auto request = it++; | 620 auto request = it++; |
| 574 DeviceEntry* maybe_entry = GetDeviceEntryBySessionId(request->first); | 621 DeviceEntry* maybe_entry = GetDeviceEntryBySessionId(request->first); |
| 575 if (maybe_entry && maybe_entry->video_capture_device()) { | 622 if (maybe_entry && maybe_entry->video_capture_device) { |
| 576 request->second.Run(maybe_entry->video_capture_device()); | 623 request->second.Run(maybe_entry->video_capture_device.get()); |
| 577 photo_request_queue_.erase(request); | 624 photo_request_queue_.erase(request); |
| 578 } | 625 } |
| 579 } | 626 } |
| 580 } | 627 } |
| 581 | 628 |
| 582 device_start_queue_.pop_front(); | 629 device_start_queue_.pop_front(); |
| 583 HandleQueuedStartRequest(); | 630 HandleQueuedStartRequest(); |
| 584 } | 631 } |
| 585 | 632 |
| 586 std::unique_ptr<media::VideoCaptureDevice> | 633 std::unique_ptr<media::VideoCaptureDevice> |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 679 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 726 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 680 DVLOG(1) << __func__ << ", session_id = " << session_id << ", request: " | 727 DVLOG(1) << __func__ << ", session_id = " << session_id << ", request: " |
| 681 << media::VideoCaptureFormat::ToString(params.requested_format); | 728 << media::VideoCaptureFormat::ToString(params.requested_format); |
| 682 | 729 |
| 683 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id, params); | 730 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id, params); |
| 684 if (!entry) { | 731 if (!entry) { |
| 685 done_cb.Run(base::WeakPtr<VideoCaptureController>()); | 732 done_cb.Run(base::WeakPtr<VideoCaptureController>()); |
| 686 return; | 733 return; |
| 687 } | 734 } |
| 688 | 735 |
| 689 DCHECK(entry->video_capture_controller()); | |
| 690 | |
| 691 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE); | 736 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE); |
| 692 | 737 |
| 693 // First client starts the device. | 738 // First client starts the device. |
| 694 if (!entry->video_capture_controller()->HasActiveClient() && | 739 if (!entry->video_capture_controller.HasActiveClient() && |
| 695 !entry->video_capture_controller()->HasPausedClient()) { | 740 !entry->video_capture_controller.HasPausedClient()) { |
| 696 DVLOG(1) << "VideoCaptureManager starting device (type = " | 741 DVLOG(1) << "VideoCaptureManager starting device (type = " |
| 697 << entry->stream_type << ", id = " << entry->id << ")"; | 742 << entry->stream_type << ", id = " << entry->id << ")"; |
| 698 QueueStartDevice(session_id, entry, params); | 743 QueueStartDevice(session_id, entry, params); |
| 699 } | 744 } |
| 700 // Run the callback first, as AddClient() may trigger OnFrameInfo(). | 745 // Run the callback first, as AddClient() may trigger OnFrameInfo(). |
| 701 done_cb.Run(entry->video_capture_controller()->GetWeakPtrForIOThread()); | 746 done_cb.Run(entry->video_capture_controller.GetWeakPtrForIOThread()); |
| 702 entry->video_capture_controller()->AddClient( | 747 entry->video_capture_controller.AddClient(client_id, client_handler, |
| 703 client_id, client_handler, session_id, params); | 748 session_id, params); |
| 704 } | 749 } |
| 705 | 750 |
| 706 void VideoCaptureManager::StopCaptureForClient( | 751 void VideoCaptureManager::StopCaptureForClient( |
| 707 VideoCaptureController* controller, | 752 VideoCaptureController* controller, |
| 708 VideoCaptureControllerID client_id, | 753 VideoCaptureControllerID client_id, |
| 709 VideoCaptureControllerEventHandler* client_handler, | 754 VideoCaptureControllerEventHandler* client_handler, |
| 710 bool aborted_due_to_error) { | 755 bool aborted_due_to_error) { |
| 711 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 756 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 712 DCHECK(controller); | 757 DCHECK(controller); |
| 713 DCHECK(client_handler); | 758 DCHECK(client_handler); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 758 DCHECK(controller); | 803 DCHECK(controller); |
| 759 DCHECK(client_handler); | 804 DCHECK(client_handler); |
| 760 DeviceEntry* entry = GetDeviceEntryByController(controller); | 805 DeviceEntry* entry = GetDeviceEntryByController(controller); |
| 761 if (!entry) | 806 if (!entry) |
| 762 NOTREACHED() << "Got Null entry while pausing capture"; | 807 NOTREACHED() << "Got Null entry while pausing capture"; |
| 763 | 808 |
| 764 const bool had_active_client = controller->HasActiveClient(); | 809 const bool had_active_client = controller->HasActiveClient(); |
| 765 controller->PauseClient(client_id, client_handler); | 810 controller->PauseClient(client_id, client_handler); |
| 766 if (!had_active_client || controller->HasActiveClient()) | 811 if (!had_active_client || controller->HasActiveClient()) |
| 767 return; | 812 return; |
| 768 if (media::VideoCaptureDevice* device = entry->video_capture_device()) { | 813 if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) { |
| 769 device_task_runner_->PostTask( | 814 device_task_runner_->PostTask( |
| 770 FROM_HERE, | 815 FROM_HERE, |
| 771 base::Bind(&VideoCaptureDevice::MaybeSuspend, | 816 base::Bind(&VideoCaptureDevice::MaybeSuspend, |
| 772 // Unretained is safe to use here because |device| would be | 817 // Unretained is safe to use here because |device| would be |
| 773 // null if it was scheduled for shutdown and destruction, and | 818 // null if it was scheduled for shutdown and destruction, and |
| 774 // because this task is guaranteed to run before the task | 819 // because this task is guaranteed to run before the task |
| 775 // that destroys the |device|. | 820 // that destroys the |device|. |
| 776 base::Unretained(device))); | 821 base::Unretained(device))); |
| 777 } | 822 } |
| 778 } | 823 } |
| 779 | 824 |
| 780 void VideoCaptureManager::ResumeCaptureForClient( | 825 void VideoCaptureManager::ResumeCaptureForClient( |
| 781 media::VideoCaptureSessionId session_id, | 826 media::VideoCaptureSessionId session_id, |
| 782 const media::VideoCaptureParams& params, | 827 const media::VideoCaptureParams& params, |
| 783 VideoCaptureController* controller, | 828 VideoCaptureController* controller, |
| 784 VideoCaptureControllerID client_id, | 829 VideoCaptureControllerID client_id, |
| 785 VideoCaptureControllerEventHandler* client_handler) { | 830 VideoCaptureControllerEventHandler* client_handler) { |
| 786 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 831 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 787 DCHECK(controller); | 832 DCHECK(controller); |
| 788 DCHECK(client_handler); | 833 DCHECK(client_handler); |
| 789 | 834 |
| 790 DeviceEntry* entry = GetDeviceEntryByController(controller); | 835 DeviceEntry* entry = GetDeviceEntryByController(controller); |
| 791 if (!entry) | 836 if (!entry) |
| 792 NOTREACHED() << "Got Null entry while resuming capture"; | 837 NOTREACHED() << "Got Null entry while resuming capture"; |
| 793 | 838 |
| 794 const bool had_active_client = controller->HasActiveClient(); | 839 const bool had_active_client = controller->HasActiveClient(); |
| 795 controller->ResumeClient(client_id, client_handler); | 840 controller->ResumeClient(client_id, client_handler); |
| 796 if (had_active_client || !controller->HasActiveClient()) | 841 if (had_active_client || !controller->HasActiveClient()) |
| 797 return; | 842 return; |
| 798 if (media::VideoCaptureDevice* device = entry->video_capture_device()) { | 843 if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) { |
| 799 device_task_runner_->PostTask( | 844 device_task_runner_->PostTask( |
| 800 FROM_HERE, | 845 FROM_HERE, |
| 801 base::Bind(&VideoCaptureDevice::Resume, | 846 base::Bind(&VideoCaptureDevice::Resume, |
| 802 // Unretained is safe to use here because |device| would be | 847 // Unretained is safe to use here because |device| would be |
| 803 // null if it was scheduled for shutdown and destruction, and | 848 // null if it was scheduled for shutdown and destruction, and |
| 804 // because this task is guaranteed to run before the task | 849 // because this task is guaranteed to run before the task |
| 805 // that destroys the |device|. | 850 // that destroys the |device|. |
| 806 base::Unretained(device))); | 851 base::Unretained(device))); |
| 807 } | 852 } |
| 808 } | 853 } |
| 809 | 854 |
| 810 void VideoCaptureManager::RequestRefreshFrameForClient( | 855 void VideoCaptureManager::RequestRefreshFrameForClient( |
| 811 VideoCaptureController* controller) { | 856 VideoCaptureController* controller) { |
| 812 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 857 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 813 | 858 |
| 814 if (DeviceEntry* entry = GetDeviceEntryByController(controller)) { | 859 if (DeviceEntry* entry = GetDeviceEntryByController(controller)) { |
| 815 if (media::VideoCaptureDevice* device = entry->video_capture_device()) { | 860 if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) { |
| 816 device_task_runner_->PostTask( | 861 device_task_runner_->PostTask( |
| 817 FROM_HERE, | 862 FROM_HERE, |
| 818 base::Bind(&VideoCaptureDevice::RequestRefreshFrame, | 863 base::Bind(&VideoCaptureDevice::RequestRefreshFrame, |
| 819 // Unretained is safe to use here because |device| would be | 864 // Unretained is safe to use here because |device| would be |
| 820 // null if it was scheduled for shutdown and destruction, | 865 // null if it was scheduled for shutdown and destruction, |
| 821 // and because this task is guaranteed to run before the | 866 // and because this task is guaranteed to run before the |
| 822 // task that destroys the |device|. | 867 // task that destroys the |device|. |
| 823 base::Unretained(device))); | 868 base::Unretained(device))); |
| 824 } | 869 } |
| 825 } | 870 } |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 853 if (it == sessions_.end()) | 898 if (it == sessions_.end()) |
| 854 return false; | 899 return false; |
| 855 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name; | 900 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name; |
| 856 | 901 |
| 857 // Return the currently in-use format(s) of the device, if it's started. | 902 // Return the currently in-use format(s) of the device, if it's started. |
| 858 DeviceEntry* device_in_use = | 903 DeviceEntry* device_in_use = |
| 859 GetDeviceEntryByTypeAndId(it->second.type, it->second.id); | 904 GetDeviceEntryByTypeAndId(it->second.type, it->second.id); |
| 860 if (device_in_use) { | 905 if (device_in_use) { |
| 861 // Currently only one format-in-use is supported at the VCC level. | 906 // Currently only one format-in-use is supported at the VCC level. |
| 862 formats_in_use->push_back( | 907 formats_in_use->push_back( |
| 863 device_in_use->video_capture_controller()->GetVideoCaptureFormat()); | 908 device_in_use->video_capture_controller.GetVideoCaptureFormat()); |
| 864 } | 909 } |
| 865 return true; | 910 return true; |
| 866 } | 911 } |
| 867 | 912 |
| 868 void VideoCaptureManager::SetDesktopCaptureWindowId( | 913 void VideoCaptureManager::SetDesktopCaptureWindowId( |
| 869 media::VideoCaptureSessionId session_id, | 914 media::VideoCaptureSessionId session_id, |
| 870 gfx::NativeViewId window_id) { | 915 gfx::NativeViewId window_id) { |
| 871 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 916 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 872 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id; | 917 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id; |
| 873 | 918 |
| 874 notification_window_ids_[session_id] = window_id; | 919 notification_window_ids_[session_id] = window_id; |
| 875 MaybePostDesktopCaptureWindowId(session_id); | 920 MaybePostDesktopCaptureWindowId(session_id); |
| 876 } | 921 } |
| 877 | 922 |
| 878 void VideoCaptureManager::MaybePostDesktopCaptureWindowId( | 923 void VideoCaptureManager::MaybePostDesktopCaptureWindowId( |
| 879 media::VideoCaptureSessionId session_id) { | 924 media::VideoCaptureSessionId session_id) { |
| 880 SessionMap::iterator session_it = sessions_.find(session_id); | 925 SessionMap::iterator session_it = sessions_.find(session_id); |
| 881 if (session_it == sessions_.end()) | 926 if (session_it == sessions_.end()) |
| 882 return; | 927 return; |
| 883 | 928 |
| 884 DeviceEntry* const existing_device = | 929 DeviceEntry* const existing_device = |
| 885 GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id); | 930 GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id); |
| 886 if (!existing_device) { | 931 if (!existing_device) { |
| 887 DVLOG(2) << "Failed to find an existing screen capture device."; | 932 DVLOG(2) << "Failed to find an existing screen capture device."; |
| 888 return; | 933 return; |
| 889 } | 934 } |
| 890 | 935 |
| 891 if (!existing_device->video_capture_device()) { | 936 if (!existing_device->video_capture_device) { |
| 892 DVLOG(2) << "Screen capture device not yet started."; | 937 DVLOG(2) << "Screen capture device not yet started."; |
| 893 return; | 938 return; |
| 894 } | 939 } |
| 895 | 940 |
| 896 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type); | 941 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type); |
| 897 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id); | 942 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id); |
| 898 if (id.is_null()) | 943 if (id.is_null()) |
| 899 return; | 944 return; |
| 900 | 945 |
| 901 auto window_id_it = notification_window_ids_.find(session_id); | 946 auto window_id_it = notification_window_ids_.find(session_id); |
| 902 if (window_id_it == notification_window_ids_.end()) { | 947 if (window_id_it == notification_window_ids_.end()) { |
| 903 DVLOG(2) << "Notification window id not set for screen capture."; | 948 DVLOG(2) << "Notification window id not set for screen capture."; |
| 904 return; | 949 return; |
| 905 } | 950 } |
| 906 | 951 |
| 907 // Post |existing_device->video_capture_device| to the VideoCaptureDevice to | 952 // Post |existing_device->video_capture_device| to the VideoCaptureDevice to |
| 908 // the device_task_runner_. This is safe since the device is destroyed on the | 953 // the device_task_runner_. This is safe since the device is destroyed on the |
| 909 // device_task_runner_. | 954 // device_task_runner_. |
| 910 device_task_runner_->PostTask( | 955 device_task_runner_->PostTask( |
| 911 FROM_HERE, | 956 FROM_HERE, |
| 912 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread, | 957 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread, |
| 913 this, | 958 this, existing_device->video_capture_device.get(), |
| 914 existing_device->video_capture_device(), | |
| 915 window_id_it->second)); | 959 window_id_it->second)); |
| 916 | 960 |
| 917 notification_window_ids_.erase(window_id_it); | 961 notification_window_ids_.erase(window_id_it); |
| 918 } | 962 } |
| 919 | 963 |
| 920 void VideoCaptureManager::GetPhotoCapabilities( | 964 void VideoCaptureManager::GetPhotoCapabilities( |
| 921 int session_id, | 965 int session_id, |
| 922 VideoCaptureDevice::GetPhotoCapabilitiesCallback callback) { | 966 VideoCaptureDevice::GetPhotoCapabilitiesCallback callback) { |
| 923 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 967 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 924 | 968 |
| 925 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); | 969 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); |
| 926 if (!entry) | 970 if (!entry) |
| 927 return; | 971 return; |
| 928 VideoCaptureDevice* device = entry->video_capture_device(); | 972 VideoCaptureDevice* device = entry->video_capture_device.get(); |
| 929 if (device) { | 973 if (device) { |
| 930 VideoCaptureManager::DoGetPhotoCapabilities(std::move(callback), device); | 974 VideoCaptureManager::DoGetPhotoCapabilities(std::move(callback), device); |
| 931 return; | 975 return; |
| 932 } | 976 } |
| 933 // |entry| is known but |device| is nullptr, queue up a request for later. | 977 // |entry| is known but |device| is nullptr, queue up a request for later. |
| 934 photo_request_queue_.emplace_back( | 978 photo_request_queue_.emplace_back( |
| 935 session_id, base::Bind(&VideoCaptureManager::DoGetPhotoCapabilities, this, | 979 session_id, base::Bind(&VideoCaptureManager::DoGetPhotoCapabilities, this, |
| 936 base::Passed(&callback))); | 980 base::Passed(&callback))); |
| 937 } | 981 } |
| 938 | 982 |
| 939 void VideoCaptureManager::SetPhotoOptions( | 983 void VideoCaptureManager::SetPhotoOptions( |
| 940 int session_id, | 984 int session_id, |
| 941 media::mojom::PhotoSettingsPtr settings, | 985 media::mojom::PhotoSettingsPtr settings, |
| 942 VideoCaptureDevice::SetPhotoOptionsCallback callback) { | 986 VideoCaptureDevice::SetPhotoOptionsCallback callback) { |
| 943 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 987 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 944 | 988 |
| 945 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); | 989 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); |
| 946 if (!entry) | 990 if (!entry) |
| 947 return; | 991 return; |
| 948 VideoCaptureDevice* device = entry->video_capture_device(); | 992 VideoCaptureDevice* device = entry->video_capture_device.get(); |
| 949 if (device) { | 993 if (device) { |
| 950 VideoCaptureManager::DoSetPhotoOptions(std::move(callback), | 994 VideoCaptureManager::DoSetPhotoOptions(std::move(callback), |
| 951 std::move(settings), device); | 995 std::move(settings), device); |
| 952 return; | 996 return; |
| 953 } | 997 } |
| 954 // |entry| is known but |device| is nullptr, queue up a request for later. | 998 // |entry| is known but |device| is nullptr, queue up a request for later. |
| 955 photo_request_queue_.emplace_back( | 999 photo_request_queue_.emplace_back( |
| 956 session_id, base::Bind(&VideoCaptureManager::DoSetPhotoOptions, this, | 1000 session_id, base::Bind(&VideoCaptureManager::DoSetPhotoOptions, this, |
| 957 base::Passed(&callback), base::Passed(&settings))); | 1001 base::Passed(&callback), base::Passed(&settings))); |
| 958 } | 1002 } |
| 959 | 1003 |
| 960 void VideoCaptureManager::TakePhoto( | 1004 void VideoCaptureManager::TakePhoto( |
| 961 int session_id, | 1005 int session_id, |
| 962 VideoCaptureDevice::TakePhotoCallback callback) { | 1006 VideoCaptureDevice::TakePhotoCallback callback) { |
| 963 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1007 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 964 | 1008 |
| 965 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); | 1009 const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id); |
| 966 if (!entry) | 1010 if (!entry) |
| 967 return; | 1011 return; |
| 968 VideoCaptureDevice* device = entry->video_capture_device(); | 1012 VideoCaptureDevice* device = entry->video_capture_device.get(); |
| 969 if (device) { | 1013 if (device) { |
| 970 VideoCaptureManager::DoTakePhoto(std::move(callback), device); | 1014 VideoCaptureManager::DoTakePhoto(std::move(callback), device); |
| 971 return; | 1015 return; |
| 972 } | 1016 } |
| 973 // |entry| is known but |device| is nullptr, queue up a request for later. | 1017 // |entry| is known but |device| is nullptr, queue up a request for later. |
| 974 photo_request_queue_.emplace_back( | 1018 photo_request_queue_.emplace_back( |
| 975 session_id, base::Bind(&VideoCaptureManager::DoTakePhoto, this, | 1019 session_id, base::Bind(&VideoCaptureManager::DoTakePhoto, this, |
| 976 base::Passed(&callback))); | 1020 base::Passed(&callback))); |
| 977 } | 1021 } |
| 978 | 1022 |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1066 ConsolidateCaptureFormats(&device_info.supported_formats); | 1110 ConsolidateCaptureFormats(&device_info.supported_formats); |
| 1067 new_devices_info_cache.push_back(device_info); | 1111 new_devices_info_cache.push_back(device_info); |
| 1068 } | 1112 } |
| 1069 | 1113 |
| 1070 on_devices_enumerated_callback.Run(new_devices_info_cache); | 1114 on_devices_enumerated_callback.Run(new_devices_info_cache); |
| 1071 } | 1115 } |
| 1072 | 1116 |
| 1073 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { | 1117 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { |
| 1074 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1118 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 1075 // Removal of the last client stops the device. | 1119 // Removal of the last client stops the device. |
| 1076 if (!entry->video_capture_controller()->HasActiveClient() && | 1120 if (!entry->video_capture_controller.HasActiveClient() && |
| 1077 !entry->video_capture_controller()->HasPausedClient()) { | 1121 !entry->video_capture_controller.HasPausedClient()) { |
| 1078 DVLOG(1) << "VideoCaptureManager stopping device (type = " | 1122 DVLOG(1) << "VideoCaptureManager stopping device (type = " |
| 1079 << entry->stream_type << ", id = " << entry->id << ")"; | 1123 << entry->stream_type << ", id = " << entry->id << ")"; |
| 1080 | 1124 |
| 1081 // The DeviceEntry is removed from |devices_| immediately. The controller is | 1125 // The DeviceEntry is removed from |devices_| immediately. The controller is |
| 1082 // deleted immediately, and the device is freed asynchronously. After this | 1126 // deleted immediately, and the device is freed asynchronously. After this |
| 1083 // point, subsequent requests to open this same device ID will create a new | 1127 // point, subsequent requests to open this same device ID will create a new |
| 1084 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. | 1128 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. |
| 1085 DoStopDevice(entry); | 1129 DoStopDevice(entry); |
| 1086 // TODO(mcasas): use a helper function https://crbug.com/624854. | 1130 // TODO(mcasas): use a helper function https://crbug.com/624854. |
| 1087 DeviceEntries::iterator device_it = | 1131 DeviceEntries::iterator device_it = |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1117 return nullptr; | 1161 return nullptr; |
| 1118 } | 1162 } |
| 1119 | 1163 |
| 1120 VideoCaptureManager::DeviceEntry* | 1164 VideoCaptureManager::DeviceEntry* |
| 1121 VideoCaptureManager::GetDeviceEntryByController( | 1165 VideoCaptureManager::GetDeviceEntryByController( |
| 1122 const VideoCaptureController* controller) const { | 1166 const VideoCaptureController* controller) const { |
| 1123 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1167 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 1124 | 1168 |
| 1125 // Look up |controller| in |devices_|. | 1169 // Look up |controller| in |devices_|. |
| 1126 for (const std::unique_ptr<DeviceEntry>& device : devices_) { | 1170 for (const std::unique_ptr<DeviceEntry>& device : devices_) { |
| 1127 if (device->video_capture_controller() == controller) | 1171 if (&device->video_capture_controller == controller) |
| 1128 return device.get(); | 1172 return device.get(); |
| 1129 } | 1173 } |
| 1130 return nullptr; | 1174 return nullptr; |
| 1131 } | 1175 } |
| 1132 | 1176 |
| 1133 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetDeviceEntryBySerialId( | 1177 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetDeviceEntryBySerialId( |
| 1134 int serial_id) const { | 1178 int serial_id) const { |
| 1135 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1179 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 1136 | 1180 |
| 1137 for (const std::unique_ptr<DeviceEntry>& device : devices_) { | 1181 for (const std::unique_ptr<DeviceEntry>& device : devices_) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1162 | 1206 |
| 1163 // Check if another session has already opened this device. If so, just | 1207 // Check if another session has already opened this device. If so, just |
| 1164 // use that opened device. | 1208 // use that opened device. |
| 1165 DeviceEntry* const existing_device = | 1209 DeviceEntry* const existing_device = |
| 1166 GetDeviceEntryByTypeAndId(device_info.type, device_info.id); | 1210 GetDeviceEntryByTypeAndId(device_info.type, device_info.id); |
| 1167 if (existing_device) { | 1211 if (existing_device) { |
| 1168 DCHECK_EQ(device_info.type, existing_device->stream_type); | 1212 DCHECK_EQ(device_info.type, existing_device->stream_type); |
| 1169 return existing_device; | 1213 return existing_device; |
| 1170 } | 1214 } |
| 1171 | 1215 |
| 1172 const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ? | 1216 devices_.emplace_back( |
| 1173 kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers; | 1217 new DeviceEntry(device_info.type, device_info.id, params)); |
| 1174 std::unique_ptr<VideoCaptureController> video_capture_controller( | 1218 return devices_.back().get(); |
| 1175 new VideoCaptureController(max_buffers)); | |
| 1176 DeviceEntry* new_device = | |
| 1177 new DeviceEntry(device_info.type, device_info.id, | |
| 1178 std::move(video_capture_controller), params); | |
| 1179 devices_.emplace_back(new_device); | |
| 1180 return new_device; | |
| 1181 } | 1219 } |
| 1182 | 1220 |
| 1183 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread( | 1221 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread( |
| 1184 media::VideoCaptureDevice* device, | 1222 media::VideoCaptureDevice* device, |
| 1185 gfx::NativeViewId window_id) { | 1223 gfx::NativeViewId window_id) { |
| 1186 DCHECK(IsOnDeviceThread()); | 1224 DCHECK(IsOnDeviceThread()); |
| 1187 #if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID) | 1225 #if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID) |
| 1188 DesktopCaptureDevice* desktop_device = | 1226 DesktopCaptureDevice* desktop_device = |
| 1189 static_cast<DesktopCaptureDevice*>(device); | 1227 static_cast<DesktopCaptureDevice*>(device); |
| 1190 desktop_device->SetNotificationWindowId(window_id); | 1228 desktop_device->SetNotificationWindowId(window_id); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1256 } | 1294 } |
| 1257 } | 1295 } |
| 1258 | 1296 |
| 1259 void VideoCaptureManager::ResumeDevices() { | 1297 void VideoCaptureManager::ResumeDevices() { |
| 1260 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1298 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 1261 | 1299 |
| 1262 for (auto& entry : devices_) { | 1300 for (auto& entry : devices_) { |
| 1263 // Do not resume Content Video Capture devices, e.g. Tab or Screen capture. | 1301 // Do not resume Content Video Capture devices, e.g. Tab or Screen capture. |
| 1264 // Do not try to restart already running devices. | 1302 // Do not try to restart already running devices. |
| 1265 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE || | 1303 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE || |
| 1266 entry->video_capture_device()) | 1304 entry->video_capture_device) |
| 1267 continue; | 1305 continue; |
| 1268 | 1306 |
| 1269 // Check if the device is already in the start queue. | 1307 // Check if the device is already in the start queue. |
| 1270 bool device_in_queue = false; | 1308 bool device_in_queue = false; |
| 1271 for (auto& request : device_start_queue_) { | 1309 for (auto& request : device_start_queue_) { |
| 1272 if (request.serial_id() == entry->serial_id) { | 1310 if (request.serial_id() == entry->serial_id) { |
| 1273 device_in_queue = true; | 1311 device_in_queue = true; |
| 1274 break; | 1312 break; |
| 1275 } | 1313 } |
| 1276 } | 1314 } |
| 1277 | 1315 |
| 1278 if (!device_in_queue) { | 1316 if (!device_in_queue) { |
| 1279 // Session ID is only valid for Screen capture. So we can fake it to | 1317 // Session ID is only valid for Screen capture. So we can fake it to |
| 1280 // resume video capture devices here. | 1318 // resume video capture devices here. |
| 1281 QueueStartDevice(kFakeSessionId, entry.get(), entry->parameters); | 1319 QueueStartDevice(kFakeSessionId, entry.get(), entry->parameters); |
| 1282 } | 1320 } |
| 1283 } | 1321 } |
| 1284 } | 1322 } |
| 1285 #endif // defined(OS_ANDROID) | 1323 #endif // defined(OS_ANDROID) |
| 1286 | 1324 |
| 1287 } // namespace content | 1325 } // namespace content |
| OLD | NEW |