Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
Currently when WebRTC isn't visible (app is in background or tab is n…
Browse files Browse the repository at this point in the history
…ot visible) in chrome Android, we stop sending captured video frame, but still hold the camera, this makes camera can not be used by other apps.

This patch releases the camera on pause, and allocate it again when resume, since SuspendDevices() is only called in Android, this is Android only behavior.

BUG=417659
TBR=tommi,tsepez

Review URL: https://codereview.chromium.org/615043006

Cr-Commit-Position: refs/heads/master@{#297933}

Review URL: https://codereview.chromium.org/627033004

Cr-Commit-Position: refs/branch-heads/2171@{#60}
Cr-Branched-From: 267aeeb-refs/heads/master@{#297060}
  • Loading branch information
Tao Bai committed Oct 6, 2014
1 parent 0d61547 commit 67934b0
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 8 deletions.
37 changes: 34 additions & 3 deletions content/browser/renderer_host/media/video_capture_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ struct VideoCaptureController::ControllerClient {
render_process_handle(render_process),
session_id(session_id),
parameters(params),
session_closed(false) {}
session_closed(false),
paused(false) {}

~ControllerClient() {}

Expand Down Expand Up @@ -135,6 +136,10 @@ struct VideoCaptureController::ControllerClient {
// implicitly), we could avoid tracking this state here in the Controller, and
// simplify the code in both places.
bool session_closed;

// Indicates whether the client is paused, if true, VideoCaptureController
// stops updating its buffer.
bool paused;
};

// Receives events from the VideoCaptureDevice and posts them to a
Expand Down Expand Up @@ -269,6 +274,22 @@ int VideoCaptureController::RemoveClient(
return session_id;
}

void VideoCaptureController::PauseOrResumeClient(
const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
bool pause) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureController::PauseOrResumeClient, id "
<< id.device_id << ", " << pause;

ControllerClient* client = FindClient(id, event_handler, controller_clients_);
if (!client)
return;

DCHECK(client->paused != pause);
client->paused = pause;
}

void VideoCaptureController::StopSession(int session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
Expand Down Expand Up @@ -573,7 +594,7 @@ void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
for (ControllerClients::iterator client_it = controller_clients_.begin();
client_it != controller_clients_.end(); ++client_it) {
ControllerClient* client = *client_it;
if (client->session_closed)
if (client->session_closed || client->paused)
continue;

if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
Expand Down Expand Up @@ -684,9 +705,19 @@ VideoCaptureController::FindClient(
return NULL;
}

int VideoCaptureController::GetClientCount() {
int VideoCaptureController::GetClientCount() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return controller_clients_.size();
}

int VideoCaptureController::GetActiveClientCount() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int active_client_count = 0;
for (ControllerClient* client : controller_clients_) {
if (!client->paused)
++active_client_count;
}
return active_client_count;
}

} // namespace content
10 changes: 9 additions & 1 deletion content/browser/renderer_host/media/video_capture_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ class CONTENT_EXPORT VideoCaptureController {
int RemoveClient(const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler);

int GetClientCount();
// Pause or resume the video capture for specified client.
void PauseOrResumeClient(const VideoCaptureControllerID& id,
VideoCaptureControllerEventHandler* event_handler,
bool pause);

int GetClientCount() const;

// Return the number of clients that aren't paused.
int GetActiveClientCount() const;

// API called directly by VideoCaptureManager in case the device is
// prematurely closed.
Expand Down
31 changes: 29 additions & 2 deletions content/browser/renderer_host/media/video_capture_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ bool VideoCaptureHost::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(VideoCaptureHost, message)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Start, OnStartCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Pause, OnPauseCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Resume, OnResumeCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Stop, OnStopCapture)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_BufferReady, OnReceiveEmptyBuffer)
IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_GetDeviceSupportedFormats,
Expand Down Expand Up @@ -295,8 +296,34 @@ void VideoCaptureHost::OnStopCapture(int device_id) {
void VideoCaptureHost::OnPauseCapture(int device_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::OnPauseCapture, device_id " << device_id;
// Not used.
Send(new VideoCaptureMsg_StateChanged(device_id, VIDEO_CAPTURE_STATE_ERROR));

VideoCaptureControllerID controller_id(device_id);
EntryMap::iterator it = entries_.find(controller_id);
if (it == entries_.end())
return;

if (it->second) {
media_stream_manager_->video_capture_manager()->PauseCaptureForClient(
it->second.get(), controller_id, this);
}
}

void VideoCaptureHost::OnResumeCapture(
int device_id,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureHost::OnResumeCapture, device_id " << device_id;

VideoCaptureControllerID controller_id(device_id);
EntryMap::iterator it = entries_.find(controller_id);
if (it == entries_.end())
return;

if (it->second) {
media_stream_manager_->video_capture_manager()->ResumeCaptureForClient(
session_id, params, it->second.get(), controller_id, this);
}
}

void VideoCaptureHost::OnReceiveEmptyBuffer(int device_id,
Expand Down
4 changes: 4 additions & 0 deletions content/browser/renderer_host/media/video_capture_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ class CONTENT_EXPORT VideoCaptureHost
// IPC message: Pause capture on device referenced by |device_id|.
void OnPauseCapture(int device_id);

void OnResumeCapture(int device_id,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params);

// IPC message: Receive an empty buffer from renderer. Send it to device
// referenced by |device_id|.
void OnReceiveEmptyBuffer(int device_id, int buffer_id, uint32 sync_point);
Expand Down
67 changes: 66 additions & 1 deletion content/browser/renderer_host/media/video_capture_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ void VideoCaptureManager::StartCaptureForClient(
LogVideoCaptureEvent(VIDEO_CAPTURE_EVENT_START_CAPTURE);

// First client starts the device.
if (entry->video_capture_controller->GetClientCount() == 0) {
if (entry->video_capture_controller->GetActiveClientCount() == 0) {
DVLOG(1) << "VideoCaptureManager starting device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";

Expand Down Expand Up @@ -367,6 +367,71 @@ void VideoCaptureManager::StopCaptureForClient(
DestroyDeviceEntryIfNoClients(entry);
}

void VideoCaptureManager::PauseCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
DeviceEntry* entry = GetDeviceEntryForController(controller);
if (!entry) {
NOTREACHED();
return;
}

// We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
// system.
if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
return;

controller->PauseOrResumeClient(client_id, client_handler, true);
if (controller->GetActiveClientCount() != 0)
return;

// There is no more client, release the camera.
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Unretained(entry)));
}

void VideoCaptureManager::ResumeCaptureForClient(
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params,
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);

DeviceEntry* entry = GetDeviceEntryForController(controller);
if (!entry) {
NOTREACHED();
return;
}

// We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
return;

controller->PauseOrResumeClient(client_id, client_handler, false);
if (controller->GetActiveClientCount() != 1)
return;

// This is first active client, allocate the camera.
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoCaptureManager::DoStartDeviceOnDeviceThread,
this,
session_id,
entry,
params,
base::Passed(entry->video_capture_controller->NewDeviceClient())));
}

bool VideoCaptureManager::GetDeviceSupportedFormats(
media::VideoCaptureSessionId capture_session_id,
media::VideoCaptureFormats* supported_formats) {
Expand Down
22 changes: 22 additions & 0 deletions content/browser/renderer_host/media/video_capture_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,28 @@ class CONTENT_EXPORT VideoCaptureManager : public MediaStreamProvider {
VideoCaptureControllerEventHandler* client_handler,
bool aborted_due_to_error);

// Called by VideoCaptureHost to pause to update video buffer specified by
// |client_id| and |client_handler|. If all clients of |controller| are
// paused, the corresponding device will be closed.
void PauseCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler);

// Called by VideoCaptureHost to resume to update video buffer specified by
// |client_id| and |client_handler|. The |session_id| and |params| should be
// same as those used in StartCaptureForClient().
// If this is first active client of |controller|, device will be allocated
// and it will take a little time to resume.
// Allocating device could failed if other app holds the camera, the error
// will be notified through VideoCaptureControllerEventHandler::OnError().
void ResumeCaptureForClient(
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params,
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler);

// Retrieves all capture supported formats for a particular device. Returns
// false if the |capture_session_id| is not found. The supported formats are
// cached during device(s) enumeration, and depending on the underlying
Expand Down
7 changes: 7 additions & 0 deletions content/common/media/video_capture_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ IPC_MESSAGE_CONTROL3(VideoCaptureHostMsg_Start,
IPC_MESSAGE_CONTROL1(VideoCaptureHostMsg_Pause,
int /* device_id */)

// Resume the video capture specified by |device_id|, |session_id| and
// |params|.
IPC_MESSAGE_CONTROL3(VideoCaptureHostMsg_Resume,
int, /* device_id */
media::VideoCaptureSessionId, /* session_id */
media::VideoCaptureParams /* params */)

// Close the video capture specified by |device_id|.
IPC_MESSAGE_CONTROL1(VideoCaptureHostMsg_Stop,
int /* device_id */)
Expand Down
5 changes: 4 additions & 1 deletion content/renderer/media/video_capture_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ void VideoCaptureImpl::DeInit() {

void VideoCaptureImpl::SuspendCapture(bool suspend) {
DCHECK(thread_checker_.CalledOnValidThread());
suspended_ = suspend;
Send(suspend ?
static_cast<IPC::Message*>(new VideoCaptureHostMsg_Pause(device_id_)) :
static_cast<IPC::Message*>(
new VideoCaptureHostMsg_Resume(device_id_, session_id_, params_)));
}

void VideoCaptureImpl::StartCapture(
Expand Down

0 comments on commit 67934b0

Please sign in to comment.