Commit 97dd4333 authored by piman@chromium.org's avatar piman@chromium.org

Video Capture Pepper API

The API is very simple at this point but works end-to-end.

BUG=None
TEST=VideoCapture sample (in a later CL)


Review URL: http://codereview.chromium.org/7553003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95719 0039d316-1c4b-4281-b951-d872f2087c98
parent a82aa851
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "content/renderer/gpu/renderer_gl_context.h" #include "content/renderer/gpu/renderer_gl_context.h"
#include "content/renderer/gpu/webgraphicscontext3d_command_buffer_impl.h" #include "content/renderer/gpu/webgraphicscontext3d_command_buffer_impl.h"
#include "content/renderer/media/audio_message_filter.h" #include "content/renderer/media/audio_message_filter.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/p2p/p2p_transport_impl.h" #include "content/renderer/p2p/p2p_transport_impl.h"
#include "content/renderer/pepper_platform_context_3d_impl.h" #include "content/renderer/pepper_platform_context_3d_impl.h"
#include "content/renderer/pepper_platform_video_decoder_impl.h" #include "content/renderer/pepper_platform_video_decoder_impl.h"
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
#include "content/renderer/render_widget_fullscreen_pepper.h" #include "content/renderer/render_widget_fullscreen_pepper.h"
#include "content/renderer/webplugin_delegate_proxy.h" #include "content/renderer/webplugin_delegate_proxy.h"
#include "ipc/ipc_channel_handle.h" #include "ipc/ipc_channel_handle.h"
#include "media/video/capture/video_capture_proxy.h"
#include "ppapi/c/dev/pp_video_dev.h" #include "ppapi/c/dev/pp_video_dev.h"
#include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_flash.h" #include "ppapi/c/private/ppb_flash.h"
...@@ -73,8 +75,6 @@ using WebKit::WebView; ...@@ -73,8 +75,6 @@ using WebKit::WebView;
namespace { namespace {
const int32 kDefaultCommandBufferSize = 1024 * 1024;
int32_t PlatformFileToInt(base::PlatformFile handle) { int32_t PlatformFileToInt(base::PlatformFile handle) {
#if defined(OS_WIN) #if defined(OS_WIN)
return static_cast<int32_t>(reinterpret_cast<intptr_t>(handle)); return static_cast<int32_t>(reinterpret_cast<intptr_t>(handle));
...@@ -376,6 +376,64 @@ class QuotaCallbackTranslator : public QuotaDispatcher::Callback { ...@@ -376,6 +376,64 @@ class QuotaCallbackTranslator : public QuotaDispatcher::Callback {
scoped_ptr<PluginCallback> callback_; scoped_ptr<PluginCallback> callback_;
}; };
class PlatformVideoCaptureImpl
: public webkit::ppapi::PluginDelegate::PlatformVideoCapture {
public:
PlatformVideoCaptureImpl(media::VideoCapture::EventHandler* handler)
: handler_proxy_(new media::VideoCaptureHandlerProxy(
handler, base::MessageLoopProxy::CreateForCurrentThread())) {
VideoCaptureImplManager* manager =
RenderThread::current()->video_capture_impl_manager();
// 1 means the "default" video capture device.
// TODO(piman): Add a way to enumerate devices and pass them through the
// API.
video_capture_ = manager->AddDevice(1, handler_proxy_.get());
}
// Overrides from media::VideoCapture::EventHandler
virtual ~PlatformVideoCaptureImpl() {
VideoCaptureImplManager* manager =
RenderThread::current()->video_capture_impl_manager();
manager->RemoveDevice(1, handler_proxy_.get());
}
virtual void StartCapture(
EventHandler* handler,
const VideoCaptureCapability& capability) OVERRIDE {
DCHECK(handler == handler_proxy_->proxied());
video_capture_->StartCapture(handler_proxy_.get(), capability);
}
virtual void StopCapture(EventHandler* handler) OVERRIDE {
DCHECK(handler == handler_proxy_->proxied());
video_capture_->StopCapture(handler_proxy_.get());
}
virtual void FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) OVERRIDE {
video_capture_->FeedBuffer(buffer);
}
virtual bool CaptureStarted() OVERRIDE {
return handler_proxy_->state().started;
}
virtual int CaptureWidth() OVERRIDE {
return handler_proxy_->state().width;
}
virtual int CaptureHeight() OVERRIDE {
return handler_proxy_->state().height;
}
virtual int CaptureFrameRate() OVERRIDE {
return handler_proxy_->state().frame_rate;
}
private:
scoped_ptr<media::VideoCaptureHandlerProxy> handler_proxy_;
media::VideoCapture* video_capture_;
};
} // namespace } // namespace
bool DispatcherWrapper::Init( bool DispatcherWrapper::Init(
...@@ -854,6 +912,12 @@ webkit::ppapi::PluginDelegate::PlatformContext3D* ...@@ -854,6 +912,12 @@ webkit::ppapi::PluginDelegate::PlatformContext3D*
#endif #endif
} }
webkit::ppapi::PluginDelegate::PlatformVideoCapture*
PepperPluginDelegateImpl::CreateVideoCapture(
media::VideoCapture::EventHandler* handler) {
return new PlatformVideoCaptureImpl(handler);
}
webkit::ppapi::PluginDelegate::PlatformVideoDecoder* webkit::ppapi::PluginDelegate::PlatformVideoDecoder*
PepperPluginDelegateImpl::CreateVideoDecoder( PepperPluginDelegateImpl::CreateVideoDecoder(
media::VideoDecodeAccelerator::Client* client, media::VideoDecodeAccelerator::Client* client,
......
...@@ -178,6 +178,8 @@ class PepperPluginDelegateImpl ...@@ -178,6 +178,8 @@ class PepperPluginDelegateImpl
PlatformAudio::Client* client); PlatformAudio::Client* client);
virtual PlatformImage2D* CreateImage2D(int width, int height); virtual PlatformImage2D* CreateImage2D(int width, int height);
virtual PlatformContext3D* CreateContext3D(); virtual PlatformContext3D* CreateContext3D();
virtual PlatformVideoCapture* CreateVideoCapture(
media::VideoCapture::EventHandler* handler) OVERRIDE;
virtual PlatformVideoDecoder* CreateVideoDecoder( virtual PlatformVideoDecoder* CreateVideoDecoder(
media::VideoDecodeAccelerator::Client* client, media::VideoDecodeAccelerator::Client* client,
int32 command_buffer_route_id); int32 command_buffer_route_id);
......
/* Copyright (c) 2011 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PPAPI_C_DEV_PP_VIDEO_CAPTURE_DEV_H_
#define PPAPI_C_DEV_PP_VIDEO_CAPTURE_DEV_H_
#include "ppapi/c/pp_stdint.h"
/**
* PP_VideoCaptureDeviceInfo_Dev is a structure that represent a video capture
* configuration, such as resolution and frame rate.
*/
struct PP_VideoCaptureDeviceInfo_Dev {
uint32_t width;
uint32_t height;
uint32_t frames_per_second;
};
PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_VideoCaptureDeviceInfo_Dev, 12);
/**
* PP_VideoCaptureStatus_Dev is an enumeration that defines the various possible
* states of a VideoCapture.
*/
typedef enum {
/**
* Initial state, capture is stopped.
*/
PP_VIDEO_CAPTURE_STATUS_STOPPED,
/**
* StartCapture has been called, but capture hasn't started yet.
*/
PP_VIDEO_CAPTURE_STATUS_STARTING,
/**
* Capture is started.
*/
PP_VIDEO_CAPTURE_STATUS_STARTED,
/**
* Capture has been started, but is paused because no buffer is available.
*/
PP_VIDEO_CAPTURE_STATUS_PAUSED,
/**
* StopCapture has been called, but capture hasn't stopped yet.
*/
PP_VIDEO_CAPTURE_STATUS_STOPPING
} PP_VideoCaptureStatus_Dev;
PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_VideoCaptureStatus_Dev, 4);
#endif /* PPAPI_C_DEV_PP_VIDEO_CAPTURE_DEV_H_ */
/* Copyright (c) 2011 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PPAPI_C_DEV_PPB_VIDEO_CAPTURE_DEV_H_
#define PPAPI_C_DEV_PPB_VIDEO_CAPTURE_DEV_H_
#include "ppapi/c/dev/pp_video_capture_dev.h"
#include "ppapi/c/pp_bool.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_stdint.h"
#define PPB_VIDEO_CAPTURE_DEV_INTERFACE_0_1 "PPB_VideoCapture(Dev);0.1"
#define PPB_VIDEO_CAPTURE_DEV_INTERFACE PPB_VIDEO_CAPTURE_DEV_INTERFACE_0_1
/**
* Video capture interface. It goes hand-in-hand with PPP_VideoCapture_Dev.
*
* Theory of operation:
* 1- Create a VideoCapture resource using Create.
* 2- Start the capture using StartCapture. You pass in the requested info
* (resolution, frame rate), as well as suggest a number of buffers you will
* need.
* 3- Receive the OnDeviceInfo callback, in PPP_VideoCapture_Dev, which will
* give you the actual capture info (the requested one is not guaranteed), as
* well as an array of buffers allocated by the browser.
* 4- On every frame captured by the browser, OnBufferReady (in
* PPP_VideoCapture_Dev) is called with the index of the buffer from the array
* containing the new frame. The buffer is now "owned" by the plugin, and the
* browser won't reuse it until ReuseBuffer is called.
* 5- When the plugin is done with the buffer, call ReuseBuffer
* 6- Stop the capture using StopCapture.
*
* The browser may change the resolution based on the constraints of the system,
* in which case OnDeviceInfo will be called again, with new buffers.
*
* The buffers contain the pixel data for a frame. The format is planar YUV
* 4:2:0, one byte per pixel, tightly packed (width x height Y values, then
* width/2 x height/2 U values, then width/2 x height/2 V values).
*/
struct PPB_VideoCapture_Dev {
/**
* Creates a new VideoCapture.
*/
PP_Resource (*Create)(PP_Instance instance);
/**
* Returns PP_TRUE if the given resource is a VideoCapture.
*/
PP_Bool (*IsVideoCapture)(PP_Resource video_capture);
/**
* Starts the capture. |requested_info| is a pointer to a structure containing
* the requested resolution and frame rate. |buffer_count| is the number of
* buffers requested by the plugin. Note: it is only used as advisory, the
* browser may allocate more of fewer based on available resources.
* How many buffers depends on usage. At least 2 to make sure latency doesn't
* cause lost frames. If the plugin expects to hold on to more than one buffer
* at a time (e.g. to do multi-frame processing, like video encoding), it
* should request that many more.
*
* Returns PP_ERROR_FAILED if called when the capture was already started, or
* PP_OK on success.
*/
int32_t (*StartCapture)(
PP_Resource video_capture,
const struct PP_VideoCaptureDeviceInfo_Dev* requested_info,
uint32_t buffer_count);
/**
* Allows the browser to reuse a buffer that was previously sent by
* PPP_VideoCapture_Dev.OnBufferReady. |buffer| is the index of the buffer in
* the array returned by PPP_VideoCapture_Dev.OnDeviceInfo.
*
* Returns PP_ERROR_BADARGUMENT if buffer is out of range (greater than the
* number of buffers returned by PPP_VideoCapture_Dev.OnDeviceInfo), or if it
* is not currently owned by the plugin. Returns PP_OK otherwise.
*/
int32_t (*ReuseBuffer)(PP_Resource video_capture, uint32_t buffer);
/**
* Stops the capture.
*
* Returns PP_ERROR_FAILED if the capture wasn't already started, or PP_OK on
* success.
*/
int32_t (*StopCapture)(PP_Resource video_capture);
};
#endif /* PPAPI_C_DEV_PPB_VIDEO_CAPTURE_DEV_H_ */
/* Copyright (c) 2011 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef PPAPI_C_DEV_PPP_VIDEO_CAPTURE_DEV_H_
#define PPAPI_C_DEV_PPP_VIDEO_CAPTURE_DEV_H_
#include "ppapi/c/dev/pp_video_capture_dev.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/c/pp_stdint.h"
#define PPP_VIDEO_CAPTURE_DEV_INTERFACE_0_1 "PPP_VideoCapture(Dev);0.1"
#define PPP_VIDEO_CAPTURE_DEV_INTERFACE PPP_VIDEO_CAPTURE_DEV_INTERFACE_0_1
/**
* Video Capture client interface. See |PPB_VideoCapture_Dev| for general theory
* of operation.
*/
struct PPP_VideoCapture_Dev {
/**
* Signals the capture device information, such as resolution and frame rate,
* and the array of buffers that the browser will use to send pixel data.
*
* |info| is a pointer to the PP_VideoCaptureDeviceInfo_Dev structure
* containing resolution and frame rate.
* |buffer_count| is the number of buffers, and |buffers| is the array of
* PPB_Buffer_Dev buffers.
*
* Note: the buffers are passed without an extra reference. The plugin is
* expected to add its own references to the buffers.
*/
void (*OnDeviceInfo)(PP_Instance instance,
PP_Resource video_capture,
const struct PP_VideoCaptureDeviceInfo_Dev* info,
uint32_t buffer_count,
const PP_Resource* buffers);
/**
* Signals status changes on the VideoCapture. |status| is a
* one of the values from PP_VideoCaptureStatus_Dev;
*/
void (*OnStatus)(PP_Instance instance,
PP_Resource video_capture,
uint32_t status);
/**
* Signals an error from the video capture system.
*
* Errors that can be generated:
* - PP_ERROR_NOMEMORY: not enough memory was available to allocate buffers.
* - PP_ERROR_FAILED: video capture could not start.
*/
void (*OnError)(PP_Instance instance,
PP_Resource video_capture,
uint32_t error_code);
/**
* Signals that a buffer is available for consumption by the plugin.
*
* |buffer| is the index of the buffer, in the array returned by OnDeviceInfo.
*/
void (*OnBufferReady)(PP_Instance,
PP_Resource video_capture,
uint32_t buffer);
};
#endif /* PPAPI_C_DEV_PPP_VIDEO_CAPTURE_DEV_H_ */
...@@ -137,6 +137,8 @@ ...@@ -137,6 +137,8 @@
'thunk/ppb_url_request_info_thunk.cc', 'thunk/ppb_url_request_info_thunk.cc',
'thunk/ppb_url_response_info_api.h', 'thunk/ppb_url_response_info_api.h',
'thunk/ppb_url_response_info_thunk.cc', 'thunk/ppb_url_response_info_thunk.cc',
'thunk/ppb_video_capture_api.h',
'thunk/ppb_video_capture_thunk.cc',
'thunk/ppb_video_decoder_api.h', 'thunk/ppb_video_decoder_api.h',
'thunk/ppb_video_decoder_thunk.cc', 'thunk/ppb_video_decoder_thunk.cc',
'thunk/ppb_video_layer_api.h', 'thunk/ppb_video_layer_api.h',
......
...@@ -51,6 +51,7 @@ enum InterfaceID { ...@@ -51,6 +51,7 @@ enum InterfaceID {
INTERFACE_ID_PPB_URL_UTIL, INTERFACE_ID_PPB_URL_UTIL,
INTERFACE_ID_PPB_VAR, INTERFACE_ID_PPB_VAR,
INTERFACE_ID_PPB_VAR_DEPRECATED, INTERFACE_ID_PPB_VAR_DEPRECATED,
INTERFACE_ID_PPB_VIDEO_CAPTURE_DEV,
INTERFACE_ID_PPB_VIDEO_DECODER_DEV, INTERFACE_ID_PPB_VIDEO_DECODER_DEV,
INTERFACE_ID_PPP_CLASS, INTERFACE_ID_PPP_CLASS,
...@@ -59,6 +60,7 @@ enum InterfaceID { ...@@ -59,6 +60,7 @@ enum InterfaceID {
INTERFACE_ID_PPP_INSTANCE, INTERFACE_ID_PPP_INSTANCE,
INTERFACE_ID_PPP_INSTANCE_PRIVATE, INTERFACE_ID_PPP_INSTANCE_PRIVATE,
INTERFACE_ID_PPP_MESSAGING, INTERFACE_ID_PPP_MESSAGING,
INTERFACE_ID_PPP_VIDEO_CAPTURE_DEV,
INTERFACE_ID_PPP_VIDEO_DECODER_DEV, INTERFACE_ID_PPP_VIDEO_DECODER_DEV,
INTERFACE_ID_RESOURCE_CREATION, INTERFACE_ID_RESOURCE_CREATION,
......
...@@ -291,6 +291,11 @@ PP_Resource ResourceCreationProxy::CreateURLRequestInfo(PP_Instance instance) { ...@@ -291,6 +291,11 @@ PP_Resource ResourceCreationProxy::CreateURLRequestInfo(PP_Instance instance) {
return PPB_URLRequestInfo_Proxy::CreateProxyResource(instance); return PPB_URLRequestInfo_Proxy::CreateProxyResource(instance);
} }
PP_Resource ResourceCreationProxy::CreateVideoCapture(PP_Instance instance) {
NOTIMPLEMENTED();
return 0;
}
PP_Resource ResourceCreationProxy::CreateVideoDecoder( PP_Resource ResourceCreationProxy::CreateVideoDecoder(
PP_Instance instance, PP_Instance instance,
PP_Resource context3d_id, PP_Resource context3d_id,
......
...@@ -109,6 +109,7 @@ class ResourceCreationProxy : public ::ppapi::FunctionGroupBase, ...@@ -109,6 +109,7 @@ class ResourceCreationProxy : public ::ppapi::FunctionGroupBase,
const char* proto) OVERRIDE; const char* proto) OVERRIDE;
virtual PP_Resource CreateURLLoader(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateURLLoader(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateVideoCapture(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateVideoDecoder( virtual PP_Resource CreateVideoDecoder(
PP_Instance instance, PP_Instance instance,
PP_Resource context3d_id, PP_Resource context3d_id,
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
F(PPB_URLLoader_API) \ F(PPB_URLLoader_API) \
F(PPB_URLRequestInfo_API) \ F(PPB_URLRequestInfo_API) \
F(PPB_URLResponseInfo_API) \ F(PPB_URLResponseInfo_API) \
F(PPB_VideoCapture_API) \
F(PPB_VideoDecoder_API) \ F(PPB_VideoDecoder_API) \
F(PPB_VideoLayer_API) \ F(PPB_VideoLayer_API) \
F(PPB_Widget_API) F(PPB_Widget_API)
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_THUNK_VIDEO_CAPTURE_API_H_
#define PPAPI_THUNK_VIDEO_CAPTURE_API_H_
#include "ppapi/c/dev/ppb_video_capture_dev.h"
namespace ppapi {
namespace thunk {
class PPB_VideoCapture_API {
public:
virtual ~PPB_VideoCapture_API() {}
virtual int32_t StartCapture(
const PP_VideoCaptureDeviceInfo_Dev& requested_info,
uint32_t buffer_count) = 0;
virtual int32_t ReuseBuffer(uint32_t buffer) = 0;
virtual int32_t StopCapture() = 0;
};
} // namespace thunk
} // namespace ppapi
#endif // PPAPI_THUNK_VIDEO_CAPTURE_API_H_
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ppapi/c/pp_errors.h"
#include "ppapi/thunk/common.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/thunk.h"
#include "ppapi/thunk/ppb_video_capture_api.h"
#include "ppapi/thunk/resource_creation_api.h"
namespace ppapi {
namespace thunk {
namespace {
typedef EnterResource<PPB_VideoCapture_API> EnterVideoCapture;
PP_Resource Create(PP_Instance instance) {
EnterFunction<ResourceCreationAPI> enter(instance, true);
if (enter.failed())
return 0;
return enter.functions()->CreateVideoCapture(instance);
}
PP_Bool IsVideoCapture(PP_Resource resource) {
EnterVideoCapture enter(resource, false);
return PP_FromBool(enter.succeeded());
}
int32_t StartCapture(PP_Resource video_capture,
const PP_VideoCaptureDeviceInfo_Dev* requested_info,
uint32_t buffer_count) {
EnterVideoCapture enter(video_capture, true);
if (enter.failed())
return PP_ERROR_BADRESOURCE;
return enter.object()->StartCapture(*requested_info, buffer_count);
}
int32_t ReuseBuffer(PP_Resource video_capture,
uint32_t buffer) {
EnterVideoCapture enter(video_capture, true);
if (enter.failed())
return PP_ERROR_BADRESOURCE;
return enter.object()->ReuseBuffer(buffer);
}
int32_t StopCapture(PP_Resource video_capture) {
EnterVideoCapture enter(video_capture, true);
if (enter.failed())
return PP_ERROR_BADRESOURCE;
return enter.object()->StopCapture();
}
const PPB_VideoCapture_Dev g_ppb_videocapture_thunk = {
&Create,
&IsVideoCapture,
&StartCapture,
&ReuseBuffer,
&StopCapture
};
} // namespace
const PPB_VideoCapture_Dev* GetPPB_VideoCapture_Thunk() {
return &g_ppb_videocapture_thunk;
}
} // namespace thunk
} // namespace ppapi
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
struct PP_Flash_Menu; struct PP_Flash_Menu;
struct PP_FontDescription_Dev; struct PP_FontDescription_Dev;
struct PP_VideoCaptureDeviceInfo_Dev;
struct PP_Size; struct PP_Size;
namespace ppapi { namespace ppapi {
...@@ -109,6 +110,7 @@ class ResourceCreationAPI { ...@@ -109,6 +110,7 @@ class ResourceCreationAPI {
const char* proto) = 0; const char* proto) = 0;
virtual PP_Resource CreateURLLoader(PP_Instance instance) = 0; virtual PP_Resource CreateURLLoader(PP_Instance instance) = 0;
virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) = 0; virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) = 0;
virtual PP_Resource CreateVideoCapture(PP_Instance instance) = 0;
virtual PP_Resource CreateVideoDecoder( virtual PP_Resource CreateVideoDecoder(
PP_Instance instance, PP_Instance instance,
PP_Resource context3d_id, PP_Resource context3d_id,
......
...@@ -47,6 +47,7 @@ struct PPB_URLLoader; ...@@ -47,6 +47,7 @@ struct PPB_URLLoader;
struct PPB_URLLoaderTrusted; struct PPB_URLLoaderTrusted;
struct PPB_URLRequestInfo; struct PPB_URLRequestInfo;
struct PPB_URLResponseInfo; struct PPB_URLResponseInfo;
struct PPB_VideoCapture_Dev;
struct PPB_VideoDecoder_Dev; struct PPB_VideoDecoder_Dev;
struct PPB_VideoLayer_Dev; struct PPB_VideoLayer_Dev;
struct PPB_WheelInputEvent; struct PPB_WheelInputEvent;
...@@ -101,6 +102,7 @@ const PPB_URLLoader* GetPPB_URLLoader_Thunk(); ...@@ -101,6 +102,7 @@ const PPB_URLLoader* GetPPB_URLLoader_Thunk();
const PPB_URLLoaderTrusted* GetPPB_URLLoaderTrusted_Thunk(); const PPB_URLLoaderTrusted* GetPPB_URLLoaderTrusted_Thunk();
const PPB_URLRequestInfo* GetPPB_URLRequestInfo_Thunk(); const PPB_URLRequestInfo* GetPPB_URLRequestInfo_Thunk();
const PPB_URLResponseInfo* GetPPB_URLResponseInfo_Thunk(); const PPB_URLResponseInfo* GetPPB_URLResponseInfo_Thunk();
const PPB_VideoCapture_Dev* GetPPB_VideoCapture_Thunk();
const PPB_VideoDecoder_Dev* GetPPB_VideoDecoder_Thunk(); const PPB_VideoDecoder_Dev* GetPPB_VideoDecoder_Thunk();
const PPB_VideoLayer_Dev* GetPPB_VideoLayer_Thunk(); const PPB_VideoLayer_Dev* GetPPB_VideoLayer_Thunk();
const PPB_WheelInputEvent* GetPPB_WheelInputEvent_Thunk(); const PPB_WheelInputEvent* GetPPB_WheelInputEvent_Thunk();
......
...@@ -300,6 +300,8 @@ ...@@ -300,6 +300,8 @@
'../plugins/ppapi/ppb_url_util_impl.h', '../plugins/ppapi/ppb_url_util_impl.h',
'../plugins/ppapi/ppb_var_impl.cc', '../plugins/ppapi/ppb_var_impl.cc',
'../plugins/ppapi/ppb_var_impl.h', '../plugins/ppapi/ppb_var_impl.h',
'../plugins/ppapi/ppb_video_capture_impl.cc',
'../plugins/ppapi/ppb_video_capture_impl.h',
'../plugins/ppapi/ppb_video_decoder_impl.cc', '../plugins/ppapi/ppb_video_decoder_impl.cc',
'../plugins/ppapi/ppb_video_decoder_impl.h', '../plugins/ppapi/ppb_video_decoder_impl.h',
'../plugins/ppapi/ppb_video_layer_impl.cc', '../plugins/ppapi/ppb_video_layer_impl.cc',
......
...@@ -50,6 +50,12 @@ MockPluginDelegate::CreateVideoDecoder( ...@@ -50,6 +50,12 @@ MockPluginDelegate::CreateVideoDecoder(
return NULL; return NULL;
} }
MockPluginDelegate::PlatformVideoCapture*
MockPluginDelegate::CreateVideoCapture(
media::VideoCapture::EventHandler* handler){
return NULL;
}
MockPluginDelegate::PlatformAudio* MockPluginDelegate::CreateAudio( MockPluginDelegate::PlatformAudio* MockPluginDelegate::CreateAudio(
uint32_t sample_rate, uint32_t sample_rate,
uint32_t sample_count, uint32_t sample_count,
......
...@@ -25,6 +25,8 @@ class MockPluginDelegate : public PluginDelegate { ...@@ -25,6 +25,8 @@ class MockPluginDelegate : public PluginDelegate {
virtual PlatformVideoDecoder* CreateVideoDecoder( virtual PlatformVideoDecoder* CreateVideoDecoder(
media::VideoDecodeAccelerator::Client* client, media::VideoDecodeAccelerator::Client* client,
int32 command_buffer_route_id); int32 command_buffer_route_id);
virtual PlatformVideoCapture* CreateVideoCapture(
media::VideoCapture::EventHandler* handler);
virtual PlatformAudio* CreateAudio(uint32_t sample_rate, virtual PlatformAudio* CreateAudio(uint32_t sample_rate,
uint32_t sample_count, uint32_t sample_count,
PlatformAudio::Client* client); PlatformAudio::Client* client);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/sync_socket.h" #include "base/sync_socket.h"
#include "base/time.h" #include "base/time.h"
#include "googleurl/src/gurl.h" #include "googleurl/src/gurl.h"
#include "media/video/capture/video_capture.h"
#include "media/video/video_decode_accelerator.h" #include "media/video/video_decode_accelerator.h"
#include "ppapi/c/dev/pp_video_dev.h" #include "ppapi/c/dev/pp_video_dev.h"
#include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_completion_callback.h"
...@@ -220,6 +221,11 @@ class PluginDelegate { ...@@ -220,6 +221,11 @@ class PluginDelegate {
virtual ~PlatformVideoDecoder() {} virtual ~PlatformVideoDecoder() {}
}; };
class PlatformVideoCapture : public media::VideoCapture {
public:
virtual ~PlatformVideoCapture() {}
};
// Provides access to the ppapi broker. // Provides access to the ppapi broker.
class PpapiBroker { class PpapiBroker {
public: public:
...@@ -261,6 +267,10 @@ class PluginDelegate { ...@@ -261,6 +267,10 @@ class PluginDelegate {
// The caller will own the pointer returned from this. // The caller will own the pointer returned from this.
virtual PlatformContext3D* CreateContext3D() = 0; virtual PlatformContext3D* CreateContext3D() = 0;
// The caller will own the pointer returned from this.
virtual PlatformVideoCapture* CreateVideoCapture(
media::VideoCapture::EventHandler* handler) = 0;
// The caller will own the pointer returned from this. // The caller will own the pointer returned from this.
virtual PlatformVideoDecoder* CreateVideoDecoder( virtual PlatformVideoDecoder* CreateVideoDecoder(
media::VideoDecodeAccelerator::Client* client, media::VideoDecodeAccelerator::Client* client,
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "ppapi/c/dev/ppb_transport_dev.h" #include "ppapi/c/dev/ppb_transport_dev.h"
#include "ppapi/c/dev/ppb_url_util_dev.h" #include "ppapi/c/dev/ppb_url_util_dev.h"
#include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/dev/ppb_var_deprecated.h"
#include "ppapi/c/dev/ppb_video_capture_dev.h"
#include "ppapi/c/dev/ppb_video_decoder_dev.h" #include "ppapi/c/dev/ppb_video_decoder_dev.h"
#include "ppapi/c/dev/ppb_video_layer_dev.h" #include "ppapi/c/dev/ppb_video_layer_dev.h"
#include "ppapi/c/dev/ppb_widget_dev.h" #include "ppapi/c/dev/ppb_widget_dev.h"
...@@ -102,6 +103,7 @@ ...@@ -102,6 +103,7 @@
#include "webkit/plugins/ppapi/ppb_uma_private_impl.h" #include "webkit/plugins/ppapi/ppb_uma_private_impl.h"
#include "webkit/plugins/ppapi/ppb_url_util_impl.h" #include "webkit/plugins/ppapi/ppb_url_util_impl.h"
#include "webkit/plugins/ppapi/ppb_var_impl.h" #include "webkit/plugins/ppapi/ppb_var_impl.h"
#include "webkit/plugins/ppapi/ppb_video_capture_impl.h"
#include "webkit/plugins/ppapi/ppb_video_decoder_impl.h" #include "webkit/plugins/ppapi/ppb_video_decoder_impl.h"
#include "webkit/plugins/ppapi/ppb_video_layer_impl.h" #include "webkit/plugins/ppapi/ppb_video_layer_impl.h"
#include "webkit/plugins/ppapi/resource_tracker.h" #include "webkit/plugins/ppapi/resource_tracker.h"
...@@ -322,6 +324,8 @@ const void* GetInterface(const char* name) { ...@@ -322,6 +324,8 @@ const void* GetInterface(const char* name) {
return PPB_Var_Impl::GetVarInterface(); return PPB_Var_Impl::GetVarInterface();
if (strcmp(name, PPB_VIDEODECODER_DEV_INTERFACE) == 0) if (strcmp(name, PPB_VIDEODECODER_DEV_INTERFACE) == 0)
return ::ppapi::thunk::GetPPB_VideoDecoder_Thunk(); return ::ppapi::thunk::GetPPB_VideoDecoder_Thunk();
if (strcmp(name, PPB_VIDEO_CAPTURE_DEV_INTERFACE) == 0)
return ::ppapi::thunk::GetPPB_VideoCapture_Thunk();
if (strcmp(name, PPB_VIDEOLAYER_DEV_INTERFACE) == 0) if (strcmp(name, PPB_VIDEOLAYER_DEV_INTERFACE) == 0)
return ::ppapi::thunk::GetPPB_VideoLayer_Thunk(); return ::ppapi::thunk::GetPPB_VideoLayer_Thunk();
if (strcmp(name, PPB_WHEEL_INPUT_EVENT_INTERFACE_1_0) == 0) if (strcmp(name, PPB_WHEEL_INPUT_EVENT_INTERFACE_1_0) == 0)
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "webkit/plugins/ppapi/ppb_video_capture_impl.h"
#include <algorithm>
#include <string>
#include "base/logging.h"
#include "ppapi/c/dev/pp_video_capture_dev.h"
#include "ppapi/c/dev/ppb_video_capture_dev.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/thunk/enter.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/ppb_buffer_impl.h"
#include "webkit/plugins/ppapi/resource_tracker.h"
using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_Buffer_API;
using ppapi::thunk::PPB_VideoCapture_API;
namespace webkit {
namespace ppapi {
PPB_VideoCapture_Impl::PPB_VideoCapture_Impl(PluginInstance* instance)
: Resource(instance),
buffer_count_hint_(0),
ppp_videocapture_(NULL),
status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) {
}
PPB_VideoCapture_Impl::~PPB_VideoCapture_Impl() {
if (platform_video_capture_.get())
StopCapture();
}
bool PPB_VideoCapture_Impl::Init() {
ppp_videocapture_ =
static_cast<const PPP_VideoCapture_Dev*>(instance()->module()->
GetPluginInterface(PPP_VIDEO_CAPTURE_DEV_INTERFACE));
if (!ppp_videocapture_)
return false;
platform_video_capture_.reset(
instance()->delegate()->CreateVideoCapture(this));
return !!platform_video_capture_.get();
}
PPB_VideoCapture_API* PPB_VideoCapture_Impl::AsPPB_VideoCapture_API() {
return this;
}
int32_t PPB_VideoCapture_Impl::StartCapture(
const PP_VideoCaptureDeviceInfo_Dev& requested_info,
uint32_t buffer_count) {
switch (status_) {
case PP_VIDEO_CAPTURE_STATUS_STARTING:
case PP_VIDEO_CAPTURE_STATUS_STARTED:
case PP_VIDEO_CAPTURE_STATUS_PAUSED:
default:
return PP_ERROR_FAILED;
case PP_VIDEO_CAPTURE_STATUS_STOPPED:
case PP_VIDEO_CAPTURE_STATUS_STOPPING:
break;
}
DCHECK(buffers_.empty());
buffer_count_hint_ = std::min(buffer_count, 1U);
media::VideoCapture::VideoCaptureCapability capability = {
requested_info.width,
requested_info.height,
requested_info.frames_per_second,
0, // ignored.
media::VideoFrame::I420,
false, // ignored
false // resolution_fixed
};
status_ = PP_VIDEO_CAPTURE_STATUS_STARTING;
platform_video_capture_->StartCapture(this, capability);
return PP_OK;
}
int32_t PPB_VideoCapture_Impl::ReuseBuffer(uint32_t buffer) {
if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
return PP_ERROR_BADARGUMENT;
buffers_[buffer].in_use = false;
return PP_OK;
}
int32_t PPB_VideoCapture_Impl::StopCapture() {
switch (status_) {
case PP_VIDEO_CAPTURE_STATUS_STOPPED:
case PP_VIDEO_CAPTURE_STATUS_STOPPING:
default:
return PP_ERROR_FAILED;
case PP_VIDEO_CAPTURE_STATUS_STARTING:
case PP_VIDEO_CAPTURE_STATUS_STARTED:
case PP_VIDEO_CAPTURE_STATUS_PAUSED:
break;
}
ReleaseBuffers();
status_ = PP_VIDEO_CAPTURE_STATUS_STOPPING;
platform_video_capture_->StopCapture(this);
return PP_OK;
}
void PPB_VideoCapture_Impl::OnStarted(media::VideoCapture* capture) {
switch (status_) {
case PP_VIDEO_CAPTURE_STATUS_STARTING:
case PP_VIDEO_CAPTURE_STATUS_PAUSED:
break;
case PP_VIDEO_CAPTURE_STATUS_STOPPED:
case PP_VIDEO_CAPTURE_STATUS_STOPPING:
case PP_VIDEO_CAPTURE_STATUS_STARTED:
default:
return;
}
status_ = PP_VIDEO_CAPTURE_STATUS_STARTED;
SendStatus();
}
void PPB_VideoCapture_Impl::OnStopped(media::VideoCapture* capture) {
switch (status_) {
case PP_VIDEO_CAPTURE_STATUS_STOPPING:
break;
case PP_VIDEO_CAPTURE_STATUS_STARTING:
case PP_VIDEO_CAPTURE_STATUS_PAUSED:
case PP_VIDEO_CAPTURE_STATUS_STOPPED:
case PP_VIDEO_CAPTURE_STATUS_STARTED:
default:
return;
}
status_ = PP_VIDEO_CAPTURE_STATUS_STOPPED;
SendStatus();
}
void PPB_VideoCapture_Impl::OnPaused(media::VideoCapture* capture) {
switch (status_) {
case PP_VIDEO_CAPTURE_STATUS_STARTING:
case PP_VIDEO_CAPTURE_STATUS_STARTED:
break;
case PP_VIDEO_CAPTURE_STATUS_STOPPED:
case PP_VIDEO_CAPTURE_STATUS_STOPPING:
case PP_VIDEO_CAPTURE_STATUS_PAUSED:
default:
return;
}
status_ = PP_VIDEO_CAPTURE_STATUS_PAUSED;
SendStatus();
}
void PPB_VideoCapture_Impl::OnError(media::VideoCapture* capture,
int error_code) {
// Today, the media layer only sends "1" as an error.
DCHECK(error_code == 1);
// It either comes because some error was detected while starting (e.g. 2
// conflicting "master" resolution), or because the browser failed to start
// the capture.
status_ = PP_VIDEO_CAPTURE_STATUS_STOPPED;
ppp_videocapture_->OnError(instance()->pp_instance(),
ScopedResourceId(this).id,
PP_ERROR_FAILED);
}
void PPB_VideoCapture_Impl::OnBufferReady(
media::VideoCapture* capture,
scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) {
DCHECK(buffer.get());
for (uint32_t i = 0; i < buffers_.size(); ++i) {
if (!buffers_[i].in_use) {
// TODO(piman): it looks like stride isn't actually used/filled.
DCHECK(buffer->stride == 0);
size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()),
buffer->buffer_size);
memcpy(buffers_[i].data, buffer->memory_pointer, size);
platform_video_capture_->FeedBuffer(buffer);
ppp_videocapture_->OnBufferReady(instance()->pp_instance(),
ScopedResourceId(this).id,
i);
return;
}
}
// TODO(piman): signal dropped buffers ?
platform_video_capture_->FeedBuffer(buffer);
}
void PPB_VideoCapture_Impl::OnDeviceInfoReceived(
media::VideoCapture* capture,
const media::VideoCaptureParams& device_info) {
PP_VideoCaptureDeviceInfo_Dev info = {
device_info.width,
device_info.height,
device_info.frame_per_second
};
ReleaseBuffers();
// Allocate buffers. We keep a reference to them, that is released in
// ReleaseBuffers.
// YUV 4:2:0
int uv_width = info.width / 2;
int uv_height = info.height / 2;
size_t size = info.width * info.height + 2 * uv_width * uv_height;
scoped_array<PP_Resource> resources(new PP_Resource[buffer_count_hint_]);
buffers_.reserve(buffer_count_hint_);
for (size_t i = 0; i < buffer_count_hint_; ++i) {
resources[i] = PPB_Buffer_Impl::Create(instance(), size);
if (!resources[i])
break;
EnterResourceNoLock<PPB_Buffer_API> enter(resources[i], true);
DCHECK(enter.succeeded());
BufferInfo info;
info.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
info.data = info.buffer->Map();
if (!info.data) {
ResourceTracker::Get()->UnrefResource(resources[i]);
break;
}
buffers_.push_back(info);
}
if (buffers_.empty()) {
// We couldn't allocate/map buffers at all. Send an error and stop the
// capture.
ppp_videocapture_->OnError(instance()->pp_instance(),
ScopedResourceId(this).id,
PP_ERROR_NOMEMORY);
status_ = PP_VIDEO_CAPTURE_STATUS_STOPPING;
platform_video_capture_->StopCapture(this);
return;
}
ppp_videocapture_->OnDeviceInfo(instance()->pp_instance(),
ScopedResourceId(this).id,
&info,
buffers_.size(),
resources.get());
}
void PPB_VideoCapture_Impl::ReleaseBuffers() {
ResourceTracker *tracker = ResourceTracker::Get();
for (size_t i = 0; i < buffers_.size(); ++i) {
buffers_[i].buffer->Unmap();
tracker->UnrefResource(buffers_[i].buffer->GetReferenceNoAddRef());
}
buffers_.clear();
}
void PPB_VideoCapture_Impl::SendStatus() {
ppp_videocapture_->OnStatus(instance()->pp_instance(),
ScopedResourceId(this).id,
status_);
}
PPB_VideoCapture_Impl::BufferInfo::BufferInfo()
: in_use(false),
data(NULL),
buffer() {
}
PPB_VideoCapture_Impl::BufferInfo::~BufferInfo() {
}
} // namespace ppapi
} // namespace webkit
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WEBKIT_PLUGINS_PPAPI_PPB_VIDEO_CAPTURE_IMPL_H_
#define WEBKIT_PLUGINS_PPAPI_PPB_VIDEO_CAPTURE_IMPL_H_
#include <vector>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_callback_factory.h"
#include "base/memory/scoped_ptr.h"
#include "media/video/capture/video_capture.h"
#include "ppapi/c/dev/ppp_video_capture_dev.h"
#include "ppapi/thunk/ppb_video_capture_api.h"
#include "webkit/plugins/ppapi/plugin_delegate.h"
#include "webkit/plugins/ppapi/ppb_buffer_impl.h"
#include "webkit/plugins/ppapi/resource.h"
struct PP_VideoCaptureDeviceInfo_Dev;
struct PPB_VideoCapture_Dev;
namespace webkit {
namespace ppapi {
class PluginInstance;
class PPB_VideoCapture_Impl : public Resource,
public ::ppapi::thunk::PPB_VideoCapture_API,
public media::VideoCapture::EventHandler {
public:
explicit PPB_VideoCapture_Impl(PluginInstance* instance);
virtual ~PPB_VideoCapture_Impl();
bool Init();
// ResourceObjectBase overrides.
virtual PPB_VideoCapture_API* AsPPB_VideoCapture_API() OVERRIDE;
// PPB_VideoCapture implementation.
virtual int32_t StartCapture(
const PP_VideoCaptureDeviceInfo_Dev& requested_info,
uint32_t buffer_count) OVERRIDE;
virtual int32_t ReuseBuffer(uint32_t buffer) OVERRIDE;
virtual int32_t StopCapture() OVERRIDE;
// media::VideoCapture::EventHandler implementation.
virtual void OnStarted(media::VideoCapture* capture) OVERRIDE;
virtual void OnStopped(media::VideoCapture* capture) OVERRIDE;
virtual void OnPaused(media::VideoCapture* capture) OVERRIDE;
virtual void OnError(media::VideoCapture* capture, int error_code) OVERRIDE;
virtual void OnBufferReady(
media::VideoCapture* capture,
scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) OVERRIDE;
virtual void OnDeviceInfoReceived(
media::VideoCapture* capture,
const media::VideoCaptureParams& device_info) OVERRIDE;
private:
void ReleaseBuffers();
void SendStatus();
scoped_ptr<PluginDelegate::PlatformVideoCapture> platform_video_capture_;
size_t buffer_count_hint_;
struct BufferInfo {
BufferInfo();
~BufferInfo();
bool in_use;
void* data;
scoped_refptr<PPB_Buffer_Impl> buffer;
};
std::vector<BufferInfo> buffers_;
const PPP_VideoCapture_Dev* ppp_videocapture_;
PP_VideoCaptureStatus_Dev status_;
DISALLOW_COPY_AND_ASSIGN(PPB_VideoCapture_Impl);
};
} // namespace ppapi
} // namespace webkit
#endif // WEBKIT_PLUGINS_PPAPI_PPB_VIDEO_DECODER_IMPL_H_
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "webkit/plugins/ppapi/ppb_transport_impl.h" #include "webkit/plugins/ppapi/ppb_transport_impl.h"
#include "webkit/plugins/ppapi/ppb_url_loader_impl.h" #include "webkit/plugins/ppapi/ppb_url_loader_impl.h"
#include "webkit/plugins/ppapi/ppb_url_request_info_impl.h" #include "webkit/plugins/ppapi/ppb_url_request_info_impl.h"
#include "webkit/plugins/ppapi/ppb_video_capture_impl.h"
#include "webkit/plugins/ppapi/ppb_video_decoder_impl.h" #include "webkit/plugins/ppapi/ppb_video_decoder_impl.h"
#include "webkit/plugins/ppapi/ppb_video_layer_impl.h" #include "webkit/plugins/ppapi/ppb_video_layer_impl.h"
...@@ -284,6 +285,14 @@ PP_Resource ResourceCreationImpl::CreateURLRequestInfo(PP_Instance instance) { ...@@ -284,6 +285,14 @@ PP_Resource ResourceCreationImpl::CreateURLRequestInfo(PP_Instance instance) {
return ReturnResource(new PPB_URLRequestInfo_Impl(instance_)); return ReturnResource(new PPB_URLRequestInfo_Impl(instance_));
} }
PP_Resource ResourceCreationImpl::CreateVideoCapture(PP_Instance instance) {
scoped_refptr<PPB_VideoCapture_Impl> video_capture =
new PPB_VideoCapture_Impl(instance_);
if (!video_capture->Init())
return 0;
return ReturnResource(video_capture);
}
PP_Resource ResourceCreationImpl::CreateVideoDecoder( PP_Resource ResourceCreationImpl::CreateVideoDecoder(
PP_Instance instance, PP_Instance instance,
PP_Resource context3d_id, PP_Resource context3d_id,
......
...@@ -100,6 +100,7 @@ class ResourceCreationImpl : public ::ppapi::FunctionGroupBase, ...@@ -100,6 +100,7 @@ class ResourceCreationImpl : public ::ppapi::FunctionGroupBase,
const char* proto) OVERRIDE; const char* proto) OVERRIDE;
virtual PP_Resource CreateURLLoader(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateURLLoader(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) OVERRIDE; virtual PP_Resource CreateURLRequestInfo(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateVideoCapture(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateVideoDecoder( virtual PP_Resource CreateVideoDecoder(
PP_Instance instance, PP_Instance instance,
PP_Resource context3d_id, PP_Resource context3d_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment