• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/numerics/safe_conversions.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/libyuv/include/libyuv/convert.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 
23 using ppapi::host::HostMessageContext;
24 using ppapi::host::ReplyMessageContext;
25 
26 namespace content {
27 
FrameReceiver(const base::WeakPtr<PepperVideoSourceHost> & host)28 PepperVideoSourceHost::FrameReceiver::FrameReceiver(
29     const base::WeakPtr<PepperVideoSourceHost>& host)
30     : host_(host),
31       main_message_loop_proxy_(base::MessageLoopProxy::current()) {}
32 
~FrameReceiver()33 PepperVideoSourceHost::FrameReceiver::~FrameReceiver() {}
34 
GotFrame(const scoped_refptr<media::VideoFrame> & frame)35 bool PepperVideoSourceHost::FrameReceiver::GotFrame(
36     const scoped_refptr<media::VideoFrame>& frame) {
37   // It's not safe to access the host from this thread, so post a task to our
38   // main thread to transfer the new frame.
39   main_message_loop_proxy_->PostTask(
40       FROM_HERE, base::Bind(&FrameReceiver::OnGotFrame, this, frame));
41 
42   return true;
43 }
44 
OnGotFrame(const scoped_refptr<media::VideoFrame> & frame)45 void PepperVideoSourceHost::FrameReceiver::OnGotFrame(
46     const scoped_refptr<media::VideoFrame>& frame) {
47   if (host_.get()) {
48     // Hold a reference to the new frame and release the previous.
49     host_->last_frame_ = frame;
50 
51     if (host_->get_frame_pending_)
52       host_->SendGetFrameReply();
53   }
54 }
55 
PepperVideoSourceHost(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource)56 PepperVideoSourceHost::PepperVideoSourceHost(RendererPpapiHost* host,
57                                              PP_Instance instance,
58                                              PP_Resource resource)
59     : ResourceHost(host->GetPpapiHost(), instance, resource),
60       renderer_ppapi_host_(host),
61       source_handler_(new VideoSourceHandler(NULL)),
62       get_frame_pending_(false),
63       weak_factory_(this) {
64   frame_receiver_ = new FrameReceiver(weak_factory_.GetWeakPtr());
65 }
66 
~PepperVideoSourceHost()67 PepperVideoSourceHost::~PepperVideoSourceHost() { Close(); }
68 
OnResourceMessageReceived(const IPC::Message & msg,HostMessageContext * context)69 int32_t PepperVideoSourceHost::OnResourceMessageReceived(
70     const IPC::Message& msg,
71     HostMessageContext* context) {
72   PPAPI_BEGIN_MESSAGE_MAP(PepperVideoSourceHost, msg)
73     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoSource_Open,
74                                       OnHostMsgOpen)
75     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_GetFrame,
76                                         OnHostMsgGetFrame)
77     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_Close,
78                                         OnHostMsgClose)
79   PPAPI_END_MESSAGE_MAP()
80   return PP_ERROR_FAILED;
81 }
82 
OnHostMsgOpen(HostMessageContext * context,const std::string & stream_url)83 int32_t PepperVideoSourceHost::OnHostMsgOpen(HostMessageContext* context,
84                                              const std::string& stream_url) {
85   GURL gurl(stream_url);
86   if (!gurl.is_valid())
87     return PP_ERROR_BADARGUMENT;
88 
89   if (!source_handler_->Open(gurl.spec(), frame_receiver_.get()))
90     return PP_ERROR_BADARGUMENT;
91 
92   stream_url_ = gurl.spec();
93 
94   ReplyMessageContext reply_context = context->MakeReplyMessageContext();
95   reply_context.params.set_result(PP_OK);
96   host()->SendReply(reply_context, PpapiPluginMsg_VideoSource_OpenReply());
97   return PP_OK_COMPLETIONPENDING;
98 }
99 
OnHostMsgGetFrame(HostMessageContext * context)100 int32_t PepperVideoSourceHost::OnHostMsgGetFrame(HostMessageContext* context) {
101   if (!source_handler_.get())
102     return PP_ERROR_FAILED;
103   if (get_frame_pending_)
104     return PP_ERROR_INPROGRESS;
105 
106   reply_context_ = context->MakeReplyMessageContext();
107   get_frame_pending_ = true;
108 
109   // If a frame is ready, try to convert it and send the reply.
110   if (last_frame_.get())
111     SendGetFrameReply();
112 
113   return PP_OK_COMPLETIONPENDING;
114 }
115 
OnHostMsgClose(HostMessageContext * context)116 int32_t PepperVideoSourceHost::OnHostMsgClose(HostMessageContext* context) {
117   Close();
118   return PP_OK;
119 }
120 
SendGetFrameReply()121 void PepperVideoSourceHost::SendGetFrameReply() {
122   DCHECK(get_frame_pending_);
123   get_frame_pending_ = false;
124 
125   DCHECK(last_frame_.get());
126   scoped_refptr<media::VideoFrame> frame(last_frame_);
127   last_frame_ = NULL;
128 
129   const int dst_width = frame->visible_rect().width();
130   const int dst_height = frame->visible_rect().height();
131 
132   PP_ImageDataDesc image_desc;
133   IPC::PlatformFileForTransit image_handle;
134   uint32_t byte_count;
135   ppapi::ScopedPPResource resource(
136       ppapi::ScopedPPResource::PassRef(),
137       ppapi::proxy::PPB_ImageData_Proxy::CreateImageData(
138           pp_instance(),
139           ppapi::PPB_ImageData_Shared::SIMPLE,
140           PP_IMAGEDATAFORMAT_BGRA_PREMUL,
141           PP_MakeSize(dst_width, dst_height),
142           false /* init_to_zero */,
143           &image_desc,
144           &image_handle,
145           &byte_count));
146   if (!resource.get()) {
147     SendGetFrameErrorReply(PP_ERROR_FAILED);
148     return;
149   }
150 
151   ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_ImageData_API>
152       enter_resource(resource, false);
153   if (enter_resource.failed()) {
154     SendGetFrameErrorReply(PP_ERROR_FAILED);
155     return;
156   }
157 
158   PPB_ImageData_Impl* image_data =
159       static_cast<PPB_ImageData_Impl*>(enter_resource.object());
160   ImageDataAutoMapper mapper(image_data);
161   if (!mapper.is_valid()) {
162     SendGetFrameErrorReply(PP_ERROR_FAILED);
163     return;
164   }
165 
166   const SkBitmap* bitmap = image_data->GetMappedBitmap();
167   if (!bitmap) {
168     SendGetFrameErrorReply(PP_ERROR_FAILED);
169     return;
170   }
171   uint8_t* bitmap_pixels = static_cast<uint8_t*>(bitmap->getPixels());
172   if (!bitmap_pixels) {
173     SendGetFrameErrorReply(PP_ERROR_FAILED);
174     return;
175   }
176 
177   // Calculate that portion of the |frame| that should be copied into
178   // |bitmap|. If |frame| has been cropped,
179   // frame->coded_size() != frame->visible_rect().
180   const int src_width = frame->coded_size().width();
181   const int src_height = frame->coded_size().height();
182   DCHECK(src_width >= dst_width && src_height >= dst_height);
183 
184   const int horiz_crop = frame->visible_rect().x();
185   const int vert_crop = frame->visible_rect().y();
186 
187   const uint8* src_y = frame->data(media::VideoFrame::kYPlane) +
188                        (src_width * vert_crop + horiz_crop);
189   const int center = (src_width + 1) / 2;
190   const uint8* src_u = frame->data(media::VideoFrame::kUPlane) +
191                        (center * vert_crop + horiz_crop) / 2;
192   const uint8* src_v = frame->data(media::VideoFrame::kVPlane) +
193                        (center * vert_crop + horiz_crop) / 2;
194 
195   libyuv::I420ToBGRA(src_y,
196                      frame->stride(media::VideoFrame::kYPlane),
197                      src_u,
198                      frame->stride(media::VideoFrame::kUPlane),
199                      src_v,
200                      frame->stride(media::VideoFrame::kVPlane),
201                      bitmap_pixels,
202                      bitmap->rowBytes(),
203                      dst_width,
204                      dst_height);
205 
206   ppapi::HostResource host_resource;
207   host_resource.SetHostResource(pp_instance(), resource.get());
208 
209   // Convert a video timestamp to a PP_TimeTicks (a double, in seconds).
210   PP_TimeTicks timestamp = frame->timestamp().InSecondsF();
211 
212   ppapi::proxy::SerializedHandle serialized_handle;
213   serialized_handle.set_shmem(image_handle, byte_count);
214   reply_context_.params.AppendHandle(serialized_handle);
215 
216   host()->SendReply(reply_context_,
217                     PpapiPluginMsg_VideoSource_GetFrameReply(
218                         host_resource, image_desc, timestamp));
219 
220   reply_context_ = ppapi::host::ReplyMessageContext();
221 
222   // Keep a reference once we know this method succeeds.
223   resource.Release();
224 }
225 
SendGetFrameErrorReply(int32_t error)226 void PepperVideoSourceHost::SendGetFrameErrorReply(int32_t error) {
227   reply_context_.params.set_result(error);
228   host()->SendReply(
229       reply_context_,
230       PpapiPluginMsg_VideoSource_GetFrameReply(
231           ppapi::HostResource(), PP_ImageDataDesc(), 0.0 /* timestamp */));
232   reply_context_ = ppapi::host::ReplyMessageContext();
233 }
234 
Close()235 void PepperVideoSourceHost::Close() {
236   if (source_handler_.get() && !stream_url_.empty())
237     source_handler_->Close(frame_receiver_.get());
238 
239   source_handler_.reset(NULL);
240   stream_url_.clear();
241 }
242 
243 }  // namespace content
244