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