1// Copyright (c) 2013 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_video_source_host.h"
6
7#include "base/bind.h"
8#include "base/safe_numerics.h"
9#include "content/public/renderer/renderer_ppapi_host.h"
10#include "content/renderer/pepper/ppb_image_data_impl.h"
11#include "content/renderer/render_thread_impl.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/host/dispatch_host_message.h"
14#include "ppapi/host/ppapi_host.h"
15#include "ppapi/proxy/ppapi_messages.h"
16#include "ppapi/proxy/ppb_image_data_proxy.h"
17#include "ppapi/shared_impl/scoped_pp_resource.h"
18#include "ppapi/thunk/enter.h"
19#include "ppapi/thunk/ppb_image_data_api.h"
20#include "third_party/libjingle/source/talk/media/base/videocommon.h"
21#include "third_party/libjingle/source/talk/media/base/videoframe.h"
22#include "third_party/skia/include/core/SkBitmap.h"
23
24using ppapi::host::HostMessageContext;
25using ppapi::host::ReplyMessageContext;
26
27namespace content {
28
29PepperVideoSourceHost::FrameReceiver::FrameReceiver(
30    const base::WeakPtr<PepperVideoSourceHost>& host)
31    : host_(host),
32      main_message_loop_proxy_(base::MessageLoopProxy::current()) {
33}
34
35PepperVideoSourceHost::FrameReceiver::~FrameReceiver() {
36}
37
38bool PepperVideoSourceHost::FrameReceiver::GotFrame(
39    cricket::VideoFrame* frame) {
40  // It's not safe to access the host from this thread, so post a task to our
41  // main thread to transfer the new frame.
42  main_message_loop_proxy_->PostTask(
43      FROM_HERE,
44      base::Bind(&FrameReceiver::OnGotFrame,
45                 this,
46                 base::Passed(scoped_ptr<cricket::VideoFrame>(frame))));
47
48  return true;
49}
50
51void PepperVideoSourceHost::FrameReceiver::OnGotFrame(
52    scoped_ptr<cricket::VideoFrame> frame) {
53  if (host_.get()) {
54    // Take ownership of the new frame, and possibly delete any unsent one.
55    host_->last_frame_.swap(frame);
56
57    if (host_->get_frame_pending_)
58      host_->SendGetFrameReply();
59  }
60}
61
62PepperVideoSourceHost::PepperVideoSourceHost(
63    RendererPpapiHost* host,
64    PP_Instance instance,
65    PP_Resource resource)
66    : ResourceHost(host->GetPpapiHost(), instance, resource),
67      renderer_ppapi_host_(host),
68      source_handler_(new VideoSourceHandler(NULL)),
69      get_frame_pending_(false),
70      weak_factory_(this) {
71  frame_receiver_ = new FrameReceiver(weak_factory_.GetWeakPtr());
72}
73
74PepperVideoSourceHost::~PepperVideoSourceHost() {
75  Close();
76}
77
78int32_t PepperVideoSourceHost::OnResourceMessageReceived(
79    const IPC::Message& msg,
80    HostMessageContext* context) {
81  IPC_BEGIN_MESSAGE_MAP(PepperVideoSourceHost, msg)
82    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoSource_Open,
83                                      OnHostMsgOpen)
84    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_GetFrame,
85                                        OnHostMsgGetFrame)
86    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_Close,
87                                        OnHostMsgClose)
88  IPC_END_MESSAGE_MAP()
89  return PP_ERROR_FAILED;
90}
91
92int32_t PepperVideoSourceHost::OnHostMsgOpen(HostMessageContext* context,
93                                             const std::string& stream_url) {
94  GURL gurl(stream_url);
95  if (!gurl.is_valid())
96    return PP_ERROR_BADARGUMENT;
97
98  if (!source_handler_->Open(gurl.spec(), frame_receiver_.get()))
99    return PP_ERROR_BADARGUMENT;
100
101  stream_url_ = gurl.spec();
102
103  ReplyMessageContext reply_context = context->MakeReplyMessageContext();
104  reply_context.params.set_result(PP_OK);
105  host()->SendReply(reply_context, PpapiPluginMsg_VideoSource_OpenReply());
106  return PP_OK_COMPLETIONPENDING;
107}
108
109int32_t PepperVideoSourceHost::OnHostMsgGetFrame(HostMessageContext* context) {
110  if (!source_handler_.get())
111    return PP_ERROR_FAILED;
112  if (get_frame_pending_)
113    return PP_ERROR_INPROGRESS;
114
115  reply_context_ = context->MakeReplyMessageContext();
116  get_frame_pending_ = true;
117
118  // If a frame is ready, try to convert it and send the reply.
119  if (last_frame_.get())
120    SendGetFrameReply();
121
122  return PP_OK_COMPLETIONPENDING;
123}
124
125int32_t PepperVideoSourceHost::OnHostMsgClose(HostMessageContext* context) {
126  Close();
127  return PP_OK;
128}
129
130void PepperVideoSourceHost::SendGetFrameReply() {
131  DCHECK(get_frame_pending_);
132  get_frame_pending_ = false;
133
134  DCHECK(last_frame_.get());
135  scoped_ptr<cricket::VideoFrame> frame(last_frame_.release());
136
137  int32_t width = base::checked_numeric_cast<int32_t>(frame->GetWidth());
138  int32_t height = base::checked_numeric_cast<int32_t>(frame->GetHeight());
139  PP_ImageDataDesc image_desc;
140  IPC::PlatformFileForTransit image_handle;
141  uint32_t byte_count;
142  ppapi::ScopedPPResource resource(
143      ppapi::ScopedPPResource::PassRef(),
144      ppapi::proxy::PPB_ImageData_Proxy::CreateImageData(
145          pp_instance(),
146          ppapi::PPB_ImageData_Shared::SIMPLE,
147          PP_IMAGEDATAFORMAT_BGRA_PREMUL,
148          PP_MakeSize(width, height),
149          false /* init_to_zero */,
150          &image_desc, &image_handle, &byte_count));
151  if (!resource.get()) {
152    SendGetFrameErrorReply(PP_ERROR_FAILED);
153    return;
154  }
155
156  ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_ImageData_API>
157      enter_resource(resource, false);
158  if (enter_resource.failed()) {
159    SendGetFrameErrorReply(PP_ERROR_FAILED);
160    return;
161  }
162
163  PPB_ImageData_Impl* image_data =
164      static_cast<PPB_ImageData_Impl*>(enter_resource.object());
165  ImageDataAutoMapper mapper(image_data);
166  if (!mapper.is_valid()) {
167    SendGetFrameErrorReply(PP_ERROR_FAILED);
168    return;
169  }
170
171  const SkBitmap* bitmap = image_data->GetMappedBitmap();
172  if (!bitmap) {
173    SendGetFrameErrorReply(PP_ERROR_FAILED);
174    return;
175  }
176  uint8_t* bitmap_pixels = static_cast<uint8_t*>(bitmap->getPixels());
177  if (!bitmap_pixels) {
178    SendGetFrameErrorReply(PP_ERROR_FAILED);
179    return;
180  }
181
182  size_t bitmap_size = bitmap->getSize();
183  frame->ConvertToRgbBuffer(cricket::FOURCC_BGRA,
184                            bitmap_pixels,
185                            bitmap_size,
186                            bitmap->rowBytes());
187
188  ppapi::HostResource host_resource;
189  host_resource.SetHostResource(pp_instance(), resource.get());
190
191  // Convert a video timestamp (int64, in nanoseconds) to a time delta (int64,
192  // microseconds) and then to a PP_TimeTicks (a double, in seconds). All times
193  // are relative to the Unix Epoch.
194  base::TimeDelta time_delta = base::TimeDelta::FromMicroseconds(
195      frame->GetTimeStamp() / base::Time::kNanosecondsPerMicrosecond);
196  PP_TimeTicks timestamp = time_delta.InSecondsF();
197
198  ppapi::proxy::SerializedHandle serialized_handle;
199  serialized_handle.set_shmem(image_handle, byte_count);
200  reply_context_.params.AppendHandle(serialized_handle);
201
202  host()->SendReply(reply_context_,
203                    PpapiPluginMsg_VideoSource_GetFrameReply(host_resource,
204                                                             image_desc,
205                                                             timestamp));
206
207  reply_context_ = ppapi::host::ReplyMessageContext();
208
209  // Keep a reference once we know this method succeeds.
210  resource.Release();
211}
212
213void PepperVideoSourceHost::SendGetFrameErrorReply(int32_t error) {
214  reply_context_.params.set_result(error);
215  host()->SendReply(
216      reply_context_,
217      PpapiPluginMsg_VideoSource_GetFrameReply(ppapi::HostResource(),
218                                               PP_ImageDataDesc(),
219                                               0.0 /* timestamp */));
220  reply_context_ = ppapi::host::ReplyMessageContext();
221}
222
223void PepperVideoSourceHost::Close() {
224  if (source_handler_.get() && !stream_url_.empty())
225    source_handler_->Close(stream_url_, frame_receiver_.get());
226
227  source_handler_.reset(NULL);
228  stream_url_.clear();
229}
230
231}  // namespace content
232