1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/pepper/pepper_audio_input_host.h"
6
7#include "base/logging.h"
8#include "build/build_config.h"
9#include "content/renderer/pepper/pepper_media_device_manager.h"
10#include "content/renderer/pepper/pepper_platform_audio_input.h"
11#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
12#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
13#include "content/renderer/render_view_impl.h"
14#include "ipc/ipc_message.h"
15#include "media/audio/shared_memory_util.h"
16#include "ppapi/c/pp_errors.h"
17#include "ppapi/host/dispatch_host_message.h"
18#include "ppapi/host/ppapi_host.h"
19#include "ppapi/proxy/ppapi_messages.h"
20#include "ppapi/proxy/serialized_structs.h"
21#include "third_party/WebKit/public/web/WebDocument.h"
22#include "third_party/WebKit/public/web/WebElement.h"
23#include "third_party/WebKit/public/web/WebPluginContainer.h"
24
25namespace content {
26
27namespace {
28
29base::PlatformFile ConvertSyncSocketHandle(const base::SyncSocket& socket) {
30  return socket.handle();
31}
32
33base::PlatformFile ConvertSharedMemoryHandle(
34    const base::SharedMemory& shared_memory) {
35#if defined(OS_POSIX)
36  return shared_memory.handle().fd;
37#elif defined(OS_WIN)
38  return shared_memory.handle();
39#else
40#error "Platform not supported."
41#endif
42}
43
44}  // namespace
45
46PepperAudioInputHost::PepperAudioInputHost(
47    RendererPpapiHostImpl* host,
48    PP_Instance instance,
49    PP_Resource resource)
50    : ResourceHost(host->GetPpapiHost(), instance, resource),
51      renderer_ppapi_host_(host),
52      audio_input_(NULL),
53      enumeration_helper_(
54          this,
55          PepperMediaDeviceManager::GetForRenderView(
56              host->GetRenderViewForInstance(pp_instance())),
57          PP_DEVICETYPE_DEV_AUDIOCAPTURE) {
58}
59
60PepperAudioInputHost::~PepperAudioInputHost() {
61  Close();
62}
63
64int32_t PepperAudioInputHost::OnResourceMessageReceived(
65    const IPC::Message& msg,
66    ppapi::host::HostMessageContext* context) {
67  int32_t result = PP_ERROR_FAILED;
68  if (enumeration_helper_.HandleResourceMessage(msg, context, &result))
69    return result;
70
71  IPC_BEGIN_MESSAGE_MAP(PepperAudioInputHost, msg)
72    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioInput_Open, OnOpen)
73    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioInput_StartOrStop,
74                                      OnStartOrStop);
75    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioInput_Close,
76                                        OnClose);
77  IPC_END_MESSAGE_MAP()
78  return PP_ERROR_FAILED;
79}
80
81void PepperAudioInputHost::StreamCreated(
82    base::SharedMemoryHandle shared_memory_handle,
83    size_t shared_memory_size,
84    base::SyncSocket::Handle socket) {
85  OnOpenComplete(PP_OK, shared_memory_handle, shared_memory_size, socket);
86}
87
88void PepperAudioInputHost::StreamCreationFailed() {
89  OnOpenComplete(PP_ERROR_FAILED, base::SharedMemory::NULLHandle(), 0,
90                 base::SyncSocket::kInvalidHandle);
91}
92
93int32_t PepperAudioInputHost::OnOpen(
94    ppapi::host::HostMessageContext* context,
95    const std::string& device_id,
96    PP_AudioSampleRate sample_rate,
97    uint32_t sample_frame_count) {
98  if (open_context_)
99    return PP_ERROR_INPROGRESS;
100  if (audio_input_)
101    return PP_ERROR_FAILED;
102
103  PepperPluginInstanceImpl* instance =
104      renderer_ppapi_host_->GetPluginInstanceImpl(pp_instance());
105  if (!instance)
106    return PP_ERROR_FAILED;
107
108  // When it is done, we'll get called back on StreamCreated() or
109  // StreamCreationFailed().
110  RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
111      renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
112
113  audio_input_ = PepperPlatformAudioInput::Create(
114      render_view->AsWeakPtr(), device_id,
115      instance->container()->element().document().url(),
116      static_cast<int>(sample_rate),
117      static_cast<int>(sample_frame_count), this);
118  if (audio_input_) {
119    open_context_.reset(new ppapi::host::ReplyMessageContext(
120        context->MakeReplyMessageContext()));
121    return PP_OK_COMPLETIONPENDING;
122  } else {
123    return PP_ERROR_FAILED;
124  }
125}
126
127int32_t PepperAudioInputHost::OnStartOrStop(
128    ppapi::host::HostMessageContext* /* context */,
129    bool capture) {
130  if (!audio_input_)
131    return PP_ERROR_FAILED;
132  if (capture)
133    audio_input_->StartCapture();
134  else
135    audio_input_->StopCapture();
136  return PP_OK;
137}
138
139int32_t PepperAudioInputHost::OnClose(
140    ppapi::host::HostMessageContext* /* context */) {
141  Close();
142  return PP_OK;
143}
144
145void PepperAudioInputHost::OnOpenComplete(
146    int32_t result,
147    base::SharedMemoryHandle shared_memory_handle,
148    size_t shared_memory_size,
149    base::SyncSocket::Handle socket_handle) {
150  // Make sure the handles are cleaned up.
151  base::SyncSocket scoped_socket(socket_handle);
152  base::SharedMemory scoped_shared_memory(shared_memory_handle, false);
153
154  if (!open_context_) {
155    NOTREACHED();
156    return;
157  }
158
159  ppapi::proxy::SerializedHandle serialized_socket_handle(
160      ppapi::proxy::SerializedHandle::SOCKET);
161  ppapi::proxy::SerializedHandle serialized_shared_memory_handle(
162      ppapi::proxy::SerializedHandle::SHARED_MEMORY);
163
164  if (result == PP_OK) {
165    IPC::PlatformFileForTransit temp_socket =
166        IPC::InvalidPlatformFileForTransit();
167    base::SharedMemoryHandle temp_shmem = base::SharedMemory::NULLHandle();
168    result = GetRemoteHandles(
169        scoped_socket, scoped_shared_memory, &temp_socket, &temp_shmem);
170
171    serialized_socket_handle.set_socket(temp_socket);
172    // Note that we must call TotalSharedMemorySizeInBytes() because extra space
173    // in shared memory is allocated for book-keeping, so the actual size of the
174    // shared memory buffer is larger than |shared_memory_size|. When sending to
175    // NaCl, NaClIPCAdapter expects this size to match the size of the full
176    // shared memory buffer.
177    serialized_shared_memory_handle.set_shmem(
178        temp_shmem, media::TotalSharedMemorySizeInBytes(shared_memory_size));
179  }
180
181  // Send all the values, even on error. This simplifies some of our cleanup
182  // code since the handles will be in the other process and could be
183  // inconvenient to clean up. Our IPC code will automatically handle this for
184  // us, as long as the remote side always closes the handles it receives, even
185  // in the failure case.
186  open_context_->params.set_result(result);
187  open_context_->params.AppendHandle(serialized_socket_handle);
188  open_context_->params.AppendHandle(serialized_shared_memory_handle);
189
190  host()->SendReply(*open_context_, PpapiPluginMsg_AudioInput_OpenReply());
191  open_context_.reset();
192}
193
194int32_t PepperAudioInputHost::GetRemoteHandles(
195    const base::SyncSocket& socket,
196    const base::SharedMemory& shared_memory,
197    IPC::PlatformFileForTransit* remote_socket_handle,
198    base::SharedMemoryHandle* remote_shared_memory_handle) {
199  *remote_socket_handle = renderer_ppapi_host_->ShareHandleWithRemote(
200      ConvertSyncSocketHandle(socket), false);
201  if (*remote_socket_handle == IPC::InvalidPlatformFileForTransit())
202    return PP_ERROR_FAILED;
203
204  *remote_shared_memory_handle = renderer_ppapi_host_->ShareHandleWithRemote(
205      ConvertSharedMemoryHandle(shared_memory), false);
206  if (*remote_shared_memory_handle == IPC::InvalidPlatformFileForTransit())
207    return PP_ERROR_FAILED;
208
209  return PP_OK;
210}
211
212void PepperAudioInputHost::Close() {
213  if (!audio_input_)
214    return;
215
216  audio_input_->ShutDown();
217  audio_input_ = NULL;
218
219  if (open_context_) {
220    open_context_->params.set_result(PP_ERROR_ABORTED);
221    host()->SendReply(*open_context_, PpapiPluginMsg_AudioInput_OpenReply());
222    open_context_.reset();
223  }
224}
225
226}  // namespace content
227
228