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.
5 #include "content/renderer/pepper/pepper_video_source_host.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"
23 using ppapi::host::HostMessageContext
;
24 using ppapi::host::ReplyMessageContext
;
28 PepperVideoSourceHost::FrameReceiver::FrameReceiver(
29 const base::WeakPtr
<PepperVideoSourceHost
>& host
)
31 main_message_loop_proxy_(base::MessageLoopProxy::current()) {}
33 PepperVideoSourceHost::FrameReceiver::~FrameReceiver() {}
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
));
45 void PepperVideoSourceHost::FrameReceiver::OnGotFrame(
46 const scoped_refptr
<media::VideoFrame
>& frame
) {
48 // Hold a reference to the new frame and release the previous.
49 host_
->last_frame_
= frame
;
51 if (host_
->get_frame_pending_
)
52 host_
->SendGetFrameReply();
56 PepperVideoSourceHost::PepperVideoSourceHost(RendererPpapiHost
* host
,
59 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
60 renderer_ppapi_host_(host
),
61 source_handler_(new VideoSourceHandler(NULL
)),
62 get_frame_pending_(false),
64 frame_receiver_
= new FrameReceiver(weak_factory_
.GetWeakPtr());
67 PepperVideoSourceHost::~PepperVideoSourceHost() { Close(); }
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
,
75 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_GetFrame
,
77 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoSource_Close
,
79 PPAPI_END_MESSAGE_MAP()
80 return PP_ERROR_FAILED
;
83 int32_t PepperVideoSourceHost::OnHostMsgOpen(HostMessageContext
* context
,
84 const std::string
& stream_url
) {
85 GURL
gurl(stream_url
);
87 return PP_ERROR_BADARGUMENT
;
89 if (!source_handler_
->Open(gurl
.spec(), frame_receiver_
.get()))
90 return PP_ERROR_BADARGUMENT
;
92 stream_url_
= gurl
.spec();
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
;
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
;
106 reply_context_
= context
->MakeReplyMessageContext();
107 get_frame_pending_
= true;
109 // If a frame is ready, try to convert it and send the reply.
110 if (last_frame_
.get())
113 return PP_OK_COMPLETIONPENDING
;
116 int32_t PepperVideoSourceHost::OnHostMsgClose(HostMessageContext
* context
) {
121 void PepperVideoSourceHost::SendGetFrameReply() {
122 DCHECK(get_frame_pending_
);
123 get_frame_pending_
= false;
125 DCHECK(last_frame_
.get());
126 scoped_refptr
<media::VideoFrame
> frame(last_frame_
);
129 const int dst_width
= frame
->visible_rect().width();
130 const int dst_height
= frame
->visible_rect().height();
132 PP_ImageDataDesc image_desc
;
133 IPC::PlatformFileForTransit image_handle
;
135 ppapi::ScopedPPResource
resource(
136 ppapi::ScopedPPResource::PassRef(),
137 ppapi::proxy::PPB_ImageData_Proxy::CreateImageData(
139 ppapi::PPB_ImageData_Shared::SIMPLE
,
140 PP_IMAGEDATAFORMAT_BGRA_PREMUL
,
141 PP_MakeSize(dst_width
, dst_height
),
142 false /* init_to_zero */,
146 if (!resource
.get()) {
147 SendGetFrameErrorReply(PP_ERROR_FAILED
);
151 ppapi::thunk::EnterResourceNoLock
<ppapi::thunk::PPB_ImageData_API
>
152 enter_resource(resource
, false);
153 if (enter_resource
.failed()) {
154 SendGetFrameErrorReply(PP_ERROR_FAILED
);
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
);
166 const SkBitmap
* bitmap
= image_data
->GetMappedBitmap();
168 SendGetFrameErrorReply(PP_ERROR_FAILED
);
171 uint8_t* bitmap_pixels
= static_cast<uint8_t*>(bitmap
->getPixels());
172 if (!bitmap_pixels
) {
173 SendGetFrameErrorReply(PP_ERROR_FAILED
);
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
);
184 const int horiz_crop
= frame
->visible_rect().x();
185 const int vert_crop
= frame
->visible_rect().y();
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;
195 libyuv::I420ToBGRA(src_y
,
196 frame
->stride(media::VideoFrame::kYPlane
),
198 frame
->stride(media::VideoFrame::kUPlane
),
200 frame
->stride(media::VideoFrame::kVPlane
),
206 ppapi::HostResource host_resource
;
207 host_resource
.SetHostResource(pp_instance(), resource
.get());
209 // Convert a video timestamp to a PP_TimeTicks (a double, in seconds).
210 PP_TimeTicks timestamp
= frame
->timestamp().InSecondsF();
212 ppapi::proxy::SerializedHandle serialized_handle
;
213 serialized_handle
.set_shmem(image_handle
, byte_count
);
214 reply_context_
.params
.AppendHandle(serialized_handle
);
216 host()->SendReply(reply_context_
,
217 PpapiPluginMsg_VideoSource_GetFrameReply(
218 host_resource
, image_desc
, timestamp
));
220 reply_context_
= ppapi::host::ReplyMessageContext();
222 // Keep a reference once we know this method succeeds.
226 void PepperVideoSourceHost::SendGetFrameErrorReply(int32_t error
) {
227 reply_context_
.params
.set_result(error
);
230 PpapiPluginMsg_VideoSource_GetFrameReply(
231 ppapi::HostResource(), PP_ImageDataDesc(), 0.0 /* timestamp */));
232 reply_context_
= ppapi::host::ReplyMessageContext();
235 void PepperVideoSourceHost::Close() {
236 if (source_handler_
.get() && !stream_url_
.empty())
237 source_handler_
->Close(frame_receiver_
.get());
239 source_handler_
.reset(NULL
);
243 } // namespace content