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 24 using ppapi::host::HostMessageContext; 25 using ppapi::host::ReplyMessageContext; 26 27 namespace content { 28 29 PepperVideoSourceHost::FrameReceiver::FrameReceiver( 30 const base::WeakPtr<PepperVideoSourceHost>& host) 31 : host_(host), 32 main_message_loop_proxy_(base::MessageLoopProxy::current()) { 33 } 34 35 PepperVideoSourceHost::FrameReceiver::~FrameReceiver() { 36 } 37 38 bool 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 51 void 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 62 PepperVideoSourceHost::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 74 PepperVideoSourceHost::~PepperVideoSourceHost() { 75 Close(); 76 } 77 78 int32_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 92 int32_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 109 int32_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 125 int32_t PepperVideoSourceHost::OnHostMsgClose(HostMessageContext* context) { 126 Close(); 127 return PP_OK; 128 } 129 130 void 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 213 void 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 223 void 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