Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(611)

Unified Diff: content/browser/renderer_host/media/media_stream_manager.cc

Issue 13989003: Replace MediaStreamUIController with MediaStreamUIProxy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/media/media_stream_manager.cc
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 1d632623fdee51e985724d0c91a45f72bb8f3662..4be7d3107114e3c35e30dbfae280fcbd4619f641 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -14,7 +14,7 @@
#include "base/threading/thread.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/media_stream_requester.h"
-#include "content/browser/renderer_host/media/media_stream_ui_controller.h"
+#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include "content/browser/renderer_host/media/web_contents_capture_util.h"
#include "content/public/browser/browser_thread.h"
@@ -54,37 +54,19 @@ static std::string RandomLabel() {
}
// Helper to verify if a media stream type is part of options or not.
-static bool Requested(const StreamOptions& options,
+static bool Requested(const MediaStreamRequest& request,
MediaStreamType stream_type) {
- return (options.audio_type == stream_type ||
- options.video_type == stream_type);
+ return (request.audio_type == stream_type ||
+ request.video_type == stream_type);
}
// TODO(xians): Merge DeviceRequest with MediaStreamRequest.
class MediaStreamManager::DeviceRequest {
public:
- DeviceRequest()
- : requester(NULL),
- type(MEDIA_GENERATE_STREAM),
- render_process_id(-1),
- render_view_id(-1),
- state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) {
- }
-
DeviceRequest(MediaStreamRequester* requester,
- const StreamOptions& request_options,
- MediaStreamRequestType request_type,
- int render_process_id,
- int render_view_id,
- const GURL& request_security_origin,
- const std::string& requested_device_id)
+ const MediaStreamRequest& request)
: requester(requester),
- options(request_options),
- type(request_type),
- render_process_id(render_process_id),
- render_view_id(render_view_id),
- security_origin(request_security_origin),
- requested_device_id(requested_device_id),
+ request(request),
state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) {
}
@@ -94,8 +76,8 @@ class MediaStreamManager::DeviceRequest {
void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
state_[stream_type] = new_state;
- if (options.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
- options.audio_type != MEDIA_TAB_AUDIO_CAPTURE) {
+ if (request.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
+ request.audio_type != MEDIA_TAB_AUDIO_CAPTURE) {
return;
}
@@ -109,12 +91,11 @@ class MediaStreamManager::DeviceRequest {
// used internally within the content module.
std::string device_id =
WebContentsCaptureUtil::StripWebContentsDeviceScheme(
- requested_device_id);
+ request.requested_device_id);
media_observer->OnMediaRequestStateChanged(
- render_process_id, render_view_id,
- MediaStreamDevice(
- stream_type, device_id, device_id), new_state);
+ request.render_process_id, request.render_view_id,
+ MediaStreamDevice(stream_type, device_id, device_id), new_state);
}
MediaRequestState state(MediaStreamType stream_type) const {
@@ -122,18 +103,16 @@ class MediaStreamManager::DeviceRequest {
}
MediaStreamRequester* const requester; // Can be NULL.
- const StreamOptions options;
- const MediaStreamRequestType type;
- const int render_process_id;
- const int render_view_id;
- const GURL security_origin;
- const std::string requested_device_id;
+ MediaStreamRequest request;
+
StreamDeviceInfoArray devices;
// Callback to the requester which audio/video devices have been selected.
// It can be null if the requester has no interest to know the result.
// Currently it is only used by |DEVICE_ACCESS| type.
- MediaRequestResponseCallback callback;
+ MediaStreamManager::MediaRequestResponseCallback callback;
+
+ scoped_ptr<MediaStreamUIProxy> ui_proxy;
private:
std::vector<MediaRequestState> state_;
@@ -147,11 +126,11 @@ MediaStreamManager::EnumerationCache::~EnumerationCache() {
}
MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
- : ui_controller_(new MediaStreamUIController(this)),
- audio_manager_(audio_manager),
+ : audio_manager_(audio_manager),
monitoring_started_(false),
io_loop_(NULL),
- screen_capture_active_(false) {
+ screen_capture_active_(false),
+ use_fake_ui_(false) {
DCHECK(audio_manager_);
memset(active_enumeration_ref_count_, 0,
sizeof(active_enumeration_ref_count_));
@@ -194,13 +173,11 @@ std::string MediaStreamManager::MakeMediaAccessRequest(
const MediaRequestResponseCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Create a new request based on options.
- DeviceRequest* request = new DeviceRequest(NULL,
- options,
- MEDIA_DEVICE_ACCESS,
- render_process_id,
- render_view_id,
- security_origin,
- std::string());
+ MediaStreamRequest stream_request(
+ render_process_id, render_view_id, security_origin,
+ MEDIA_DEVICE_ACCESS, std::string(),
+ options.audio_type, options.video_type);
+ DeviceRequest* request = new DeviceRequest(NULL, stream_request);
const std::string& label = AddRequest(request);
request->callback = callback;
@@ -267,13 +244,11 @@ std::string MediaStreamManager::GenerateStream(
}
// Create a new request based on options.
- DeviceRequest* request = new DeviceRequest(requester,
- options,
- MEDIA_GENERATE_STREAM,
- target_render_process_id,
- target_render_view_id,
- security_origin,
- requested_device_id);
+ MediaStreamRequest stream_request(
+ target_render_process_id, target_render_view_id, security_origin,
+ MEDIA_GENERATE_STREAM, requested_device_id,
+ options.audio_type, options.video_type);
+ DeviceRequest* request = new DeviceRequest(requester, stream_request);
const std::string& label = AddRequest(request);
HandleRequest(label);
return label;
@@ -284,9 +259,6 @@ void MediaStreamManager::CancelRequest(const std::string& label) {
DeviceRequests::iterator it = requests_.find(label);
if (it != requests_.end()) {
- // The request isn't complete, notify the UI immediately.
- ui_controller_->CancelUIRequest(label);
-
if (!RequestDone(*it->second)) {
// TODO(xians): update the |state| to STATE_DONE to trigger a state
// changed notification to UI before deleting the request?
@@ -320,7 +292,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) {
// Find the request and close all open devices for the request.
DeviceRequests::iterator it = requests_.find(label);
if (it != requests_.end()) {
- if (it->second->type == MEDIA_ENUMERATE_DEVICES) {
+ if (it->second->request.request_type == MEDIA_ENUMERATE_DEVICES) {
StopEnumerateDevices(label);
return;
}
@@ -332,7 +304,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) {
device_it != request->devices.end(); ++device_it) {
GetDeviceManager(device_it->device.type)->Close(device_it->session_id);
}
- if (request->type == MEDIA_GENERATE_STREAM &&
+ if (request->request.request_type == MEDIA_GENERATE_STREAM &&
RequestDone(*request)) {
// Notify observers that this device is being closed.
for (int i = MEDIA_NO_SERVICE + 1; i != NUM_MEDIA_TYPES; ++i) {
@@ -342,12 +314,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) {
MEDIA_REQUEST_STATE_CLOSING);
}
}
- NotifyUIDevicesClosed(label);
}
-
- // If request isn't complete, notify the UI on the cancellation. And it
- // is also safe to call CancelUIRequest if the request has been done.
- ui_controller_->CancelUIRequest(label);
}
}
@@ -384,13 +351,11 @@ std::string MediaStreamManager::EnumerateDevices(
return std::string();
}
- DeviceRequest* request = new DeviceRequest(requester,
- options,
- MEDIA_ENUMERATE_DEVICES,
- render_process_id,
- render_view_id,
- security_origin,
- std::string());
+ MediaStreamRequest stream_request(
+ render_process_id, render_view_id, security_origin,
+ MEDIA_ENUMERATE_DEVICES, std::string(),
+ options.audio_type, options.video_type);
+ DeviceRequest* request = new DeviceRequest(requester, stream_request);
const std::string& label = AddRequest(request);
if (cache->valid) {
@@ -415,7 +380,7 @@ void MediaStreamManager::StopEnumerateDevices(const std::string& label) {
DeviceRequests::iterator it = requests_.find(label);
if (it != requests_.end()) {
- DCHECK_EQ(it->second->type, MEDIA_ENUMERATE_DEVICES);
+ DCHECK_EQ(it->second->request.request_type, MEDIA_ENUMERATE_DEVICES);
// Delete the DeviceRequest.
scoped_ptr<DeviceRequest> request(it->second);
RemoveRequest(it);
@@ -444,29 +409,17 @@ std::string MediaStreamManager::OpenDevice(
return std::string();
}
- DeviceRequest* request = new DeviceRequest(requester,
- options,
- MEDIA_OPEN_DEVICE,
- render_process_id,
- render_view_id,
- security_origin,
- device_id);
+ MediaStreamRequest stream_request(
+ render_process_id, render_view_id, security_origin,
+ MEDIA_OPEN_DEVICE, device_id,
+ options.audio_type, options.video_type);
+ DeviceRequest* request = new DeviceRequest(requester, stream_request);
const std::string& label = AddRequest(request);
StartEnumeration(request);
return label;
}
-void MediaStreamManager::NotifyUIDevicesOpened(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- ui_controller_->NotifyUIIndicatorDevicesOpened(label);
-}
-
-void MediaStreamManager::NotifyUIDevicesClosed(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- ui_controller_->NotifyUIIndicatorDevicesClosed(label);
-}
-
void MediaStreamManager::SendCachedDeviceList(
EnumerationCache* cache,
const std::string& label) {
@@ -523,7 +476,7 @@ void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
// Start enumeration for devices of all requested device types.
for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
- if (Requested(request->options, stream_type)) {
+ if (Requested(request->request, stream_type)) {
request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
if (active_enumeration_ref_count_[stream_type] == 0) {
@@ -549,35 +502,38 @@ std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
}
void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it) {
- if (it->second->options.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) {
+ if (it->second->request.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) {
DCHECK(screen_capture_active_);
screen_capture_active_ = false;
}
- NotifyUIDevicesClosed(it->first);
-
requests_.erase(it);
}
void MediaStreamManager::PostRequestToUI(const std::string& label) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DeviceRequest* request = requests_[label];
- // Get user confirmation to use capture devices.
- ui_controller_->MakeUIRequest(label,
- request->render_process_id,
- request->render_view_id,
- request->options,
- request->security_origin,
- request->type,
- request->requested_device_id);
+
+ if (use_fake_ui_) {
+ if (!fake_ui_)
+ fake_ui_.reset(new FakeMediaStreamUIProxy());
+ request->ui_proxy = fake_ui_.Pass();
+ } else {
+ request->ui_proxy.reset(new MediaStreamUIProxy());
+ }
+
+ request->ui_proxy->RequestAccess(
+ request->request,
+ base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
+ base::Unretained(this), label));
}
void MediaStreamManager::HandleRequest(const std::string& label) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DeviceRequest* request = requests_[label];
- const MediaStreamType audio_type = request->options.audio_type;
- const MediaStreamType video_type = request->options.video_type;
+ const MediaStreamType audio_type = request->request.audio_type;
+ const MediaStreamType video_type = request->request.video_type;
bool is_web_contents_capture =
audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
@@ -679,7 +635,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type,
return;
}
- switch (request->type) {
+ switch (request->request.request_type) {
case MEDIA_OPEN_DEVICE:
request->requester->DeviceOpened(label, devices->front());
break;
@@ -709,7 +665,9 @@ void MediaStreamManager::Opened(MediaStreamType stream_type,
}
request->requester->StreamGenerated(label, audio_devices, video_devices);
- NotifyUIDevicesOpened(label);
+ request->ui_proxy->OnStarted(
+ base::Bind(&MediaStreamManager::StopStreamFromUI,
+ base::Unretained(this), label));
break;
}
default:
@@ -751,10 +709,9 @@ void MediaStreamManager::DevicesEnumerated(
std::list<std::string> label_list;
for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
++it) {
- if (it->second->state(stream_type) ==
- MEDIA_REQUEST_STATE_REQUESTED &&
- Requested(it->second->options, stream_type)) {
- if (it->second->type != MEDIA_ENUMERATE_DEVICES)
+ if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
+ Requested(it->second->request, stream_type)) {
+ if (it->second->request.request_type != MEDIA_ENUMERATE_DEVICES)
it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
label_list.push_back(it->first);
}
@@ -762,15 +719,15 @@ void MediaStreamManager::DevicesEnumerated(
for (std::list<std::string>::iterator it = label_list.begin();
it != label_list.end(); ++it) {
DeviceRequest* request = requests_[*it];
- switch (request->type) {
+ switch (request->request.request_type) {
case MEDIA_ENUMERATE_DEVICES:
if (need_update_clients && request->requester)
request->requester->DevicesEnumerated(*it, devices);
break;
default:
- if (request->state(request->options.audio_type) ==
+ if (request->state(request->request.audio_type) ==
MEDIA_REQUEST_STATE_REQUESTED ||
- request->state(request->options.video_type) ==
+ request->state(request->request.video_type) ==
MEDIA_REQUEST_STATE_REQUESTED) {
// We are doing enumeration for other type of media, wait until it is
// all done before posting the request to UI because UI needs
@@ -842,28 +799,37 @@ void MediaStreamManager::Error(MediaStreamType stream_type,
}
}
-void MediaStreamManager::DevicesAccepted(const std::string& label,
- const StreamDeviceInfoArray& devices) {
+void MediaStreamManager::HandleAccessRequestResponse(
+ const std::string& label,
+ const MediaStreamDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(!devices.empty());
+
DeviceRequests::iterator request_it = requests_.find(label);
if (request_it == requests_.end()) {
return;
}
- if (request_it->second->type == MEDIA_DEVICE_ACCESS) {
+ // Handle the case when the request was denied.
+ if (devices.empty()) {
+ // Notify the users about the request result.
scoped_ptr<DeviceRequest> request(request_it->second);
- if (!request->callback.is_null()) {
- // Map the devices to MediaStreamDevices.
- MediaStreamDevices selected_devices;
- for (StreamDeviceInfoArray::const_iterator it = devices.begin();
- it != devices.end(); ++it) {
- selected_devices.push_back(it->device);
- }
+ if (request->requester)
+ request->requester->StreamGenerationFailed(label);
- request->callback.Run(label, selected_devices);
+ if (request->request.request_type == MEDIA_DEVICE_ACCESS &&
+ !request->callback.is_null()) {
+ request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
}
+ RemoveRequest(request_it);
+ return;
+ }
+
+ if (request_it->second->request.request_type == MEDIA_DEVICE_ACCESS) {
+ scoped_ptr<DeviceRequest> request(request_it->second);
+ if (!request->callback.is_null())
+ request->callback.Run(devices, request->ui_proxy.Pass());
+
// Delete the request since it is done.
RemoveRequest(request_it);
return;
@@ -871,16 +837,18 @@ void MediaStreamManager::DevicesAccepted(const std::string& label,
// Process all newly-accepted devices for this request.
DeviceRequest* request = request_it->second;
- bool found_audio = false, found_video = false;
- for (StreamDeviceInfoArray::const_iterator device_it = devices.begin();
+ bool found_audio = false;
+ bool found_video = false;
+ for (MediaStreamDevices::const_iterator device_it = devices.begin();
device_it != devices.end(); ++device_it) {
- StreamDeviceInfo device_info = *device_it; // Make a copy.
+ StreamDeviceInfo device_info;
+ device_info.device = *device_it;
// TODO(justinlin): Nicer way to do this?
// Re-append the device's id since we lost it when posting request to UI.
if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
- device_info.device.id = request->requested_device_id;
+ device_info.device.id = request->request.requested_device_id;
// Initialize the sample_rate and channel_layout here since for audio
// mirroring, we don't go through EnumerateDevices where these are usually
@@ -902,48 +870,26 @@ void MediaStreamManager::DevicesAccepted(const std::string& label,
// Set in_use to false to be able to track if this device has been
// opened. in_use might be true if the device type can be used in more
// than one session.
- DCHECK_EQ(request->state(device_it->device.type),
- MEDIA_REQUEST_STATE_PENDING_APPROVAL);
device_info.in_use = false;
device_info.session_id =
GetDeviceManager(device_info.device.type)->Open(device_info);
- request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_OPENING);
+ request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
request->devices.push_back(device_info);
- if (device_info.device.type == request->options.audio_type) {
+ if (device_info.device.type == request->request.audio_type) {
found_audio = true;
- } else if (device_info.device.type == request->options.video_type) {
+ } else if (device_info.device.type == request->request.video_type) {
found_video = true;
}
}
// Check whether we've received all stream types requested.
- if (!found_audio && IsAudioMediaType(request->options.audio_type))
- request->SetState(request->options.audio_type, MEDIA_REQUEST_STATE_ERROR);
-
- if (!found_video && IsVideoMediaType(request->options.video_type))
- request->SetState(request->options.video_type, MEDIA_REQUEST_STATE_ERROR);
-}
-
-void MediaStreamManager::SettingsError(const std::string& label) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- // Erase this request and report an error.
- DeviceRequests::iterator it = requests_.find(label);
- if (it == requests_.end())
- return;
-
- // Notify the users about the request result.
- scoped_ptr<DeviceRequest> request(it->second);
- if (request->requester)
- request->requester->StreamGenerationFailed(label);
+ if (!found_audio && IsAudioMediaType(request->request.audio_type))
+ request->SetState(request->request.audio_type, MEDIA_REQUEST_STATE_ERROR);
- if (request->type == MEDIA_DEVICE_ACCESS &&
- !request->callback.is_null()) {
- request->callback.Run(label, MediaStreamDevices());
- }
-
- RemoveRequest(it);
+ if (!found_video && IsVideoMediaType(request->request.video_type))
+ request->SetState(request->request.video_type, MEDIA_REQUEST_STATE_ERROR);
}
void MediaStreamManager::StopStreamFromUI(const std::string& label) {
@@ -960,38 +906,17 @@ void MediaStreamManager::StopStreamFromUI(const std::string& label) {
StopGeneratedStream(label);
}
-void MediaStreamManager::GetAvailableDevices(MediaStreamDevices* devices) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(audio_enumeration_cache_.valid || video_enumeration_cache_.valid);
- DCHECK(devices->empty());
- if (audio_enumeration_cache_.valid) {
- for (StreamDeviceInfoArray::const_iterator it =
- audio_enumeration_cache_.devices.begin();
- it != audio_enumeration_cache_.devices.end();
- ++it) {
- devices->push_back(it->device);
- }
- }
-
- if (video_enumeration_cache_.valid) {
- for (StreamDeviceInfoArray::const_iterator it =
- video_enumeration_cache_.devices.begin();
- it != video_enumeration_cache_.devices.end();
- ++it) {
- devices->push_back(it->device);
- }
- }
-}
-
void MediaStreamManager::UseFakeDevice() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
video_capture_manager()->UseFakeDevice();
audio_input_device_manager()->UseFakeDevice();
- UseFakeUI(scoped_ptr<MediaStreamUI>());
+ UseFakeUI(scoped_ptr<MediaStreamUIProxy>());
}
-void MediaStreamManager::UseFakeUI(scoped_ptr<MediaStreamUI> fake_ui) {
- ui_controller_->UseFakeUI(fake_ui.Pass());
+void MediaStreamManager::UseFakeUI(scoped_ptr<MediaStreamUIProxy> fake_ui) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ use_fake_ui_ = true;
+ fake_ui_ = fake_ui.Pass();
}
void MediaStreamManager::WillDestroyCurrentMessageLoop() {
@@ -1008,7 +933,6 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() {
audio_input_device_manager_ = NULL;
video_capture_manager_ = NULL;
io_loop_ = NULL;
- ui_controller_.reset();
}
void MediaStreamManager::NotifyDevicesChanged(
@@ -1039,23 +963,23 @@ void MediaStreamManager::NotifyDevicesChanged(
bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- const bool requested_audio = IsAudioMediaType(request.options.audio_type);
- const bool requested_video = IsVideoMediaType(request.options.video_type);
+ const bool requested_audio = IsAudioMediaType(request.request.audio_type);
+ const bool requested_video = IsVideoMediaType(request.request.video_type);
const bool audio_done =
!requested_audio ||
- request.state(request.options.audio_type) ==
+ request.state(request.request.audio_type) ==
MEDIA_REQUEST_STATE_DONE ||
- request.state(request.options.audio_type) ==
+ request.state(request.request.audio_type) ==
MEDIA_REQUEST_STATE_ERROR;
if (!audio_done)
return false;
const bool video_done =
!requested_video ||
- request.state(request.options.video_type) ==
+ request.state(request.request.video_type) ==
MEDIA_REQUEST_STATE_DONE ||
- request.state(request.options.video_type) ==
+ request.state(request.request.video_type) ==
MEDIA_REQUEST_STATE_ERROR;
if (!video_done)
return false;

Powered by Google App Engine
This is Rietveld 408576698