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/media/webrtc/video_destination_handler.h" 6 7 #include <string> 8 9 #include "base/base64.h" 10 #include "base/logging.h" 11 #include "base/rand_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "content/renderer/media/media_stream.h" 14 #include "content/renderer/media/media_stream_registry_interface.h" 15 #include "content/renderer/media/media_stream_video_track.h" 16 #include "content/renderer/pepper/ppb_image_data_impl.h" 17 #include "content/renderer/render_thread_impl.h" 18 #include "media/video/capture/video_capture_types.h" 19 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" 20 #include "third_party/WebKit/public/platform/WebURL.h" 21 #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" 22 #include "third_party/libyuv/include/libyuv/convert.h" 23 #include "url/gurl.h" 24 25 namespace content { 26 27 class PpFrameWriter::FrameWriterDelegate 28 : public base::RefCountedThreadSafe<FrameWriterDelegate> { 29 public: 30 FrameWriterDelegate( 31 const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy, 32 const VideoCaptureDeliverFrameCB& new_frame_callback); 33 34 void DeliverFrame(const scoped_refptr<media::VideoFrame>& frame, 35 const media::VideoCaptureFormat& format); 36 private: 37 friend class base::RefCountedThreadSafe<FrameWriterDelegate>; 38 virtual ~FrameWriterDelegate(); 39 40 void DeliverFrameOnIO(const scoped_refptr<media::VideoFrame>& frame, 41 const media::VideoCaptureFormat& format); 42 43 scoped_refptr<base::MessageLoopProxy> io_message_loop_; 44 VideoCaptureDeliverFrameCB new_frame_callback_; 45 }; 46 47 PpFrameWriter::FrameWriterDelegate::FrameWriterDelegate( 48 const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy, 49 const VideoCaptureDeliverFrameCB& new_frame_callback) 50 : io_message_loop_(io_message_loop_proxy), 51 new_frame_callback_(new_frame_callback) { 52 } 53 54 PpFrameWriter::FrameWriterDelegate::~FrameWriterDelegate() { 55 } 56 57 void PpFrameWriter::FrameWriterDelegate::DeliverFrame( 58 const scoped_refptr<media::VideoFrame>& frame, 59 const media::VideoCaptureFormat& format) { 60 io_message_loop_->PostTask( 61 FROM_HERE, 62 base::Bind(&FrameWriterDelegate::DeliverFrameOnIO, 63 this, frame, format)); 64 } 65 66 void PpFrameWriter::FrameWriterDelegate::DeliverFrameOnIO( 67 const scoped_refptr<media::VideoFrame>& frame, 68 const media::VideoCaptureFormat& format) { 69 DCHECK(io_message_loop_->BelongsToCurrentThread()); 70 // The local time when this frame is generated is unknown so give a null 71 // value to |estimated_capture_time|. 72 new_frame_callback_.Run(frame, format, base::TimeTicks()); 73 } 74 75 PpFrameWriter::PpFrameWriter() { 76 DVLOG(3) << "PpFrameWriter ctor"; 77 } 78 79 PpFrameWriter::~PpFrameWriter() { 80 DVLOG(3) << "PpFrameWriter dtor"; 81 } 82 83 void PpFrameWriter::GetCurrentSupportedFormats( 84 int max_requested_width, 85 int max_requested_height, 86 double max_requested_frame_rate, 87 const VideoCaptureDeviceFormatsCB& callback) { 88 DCHECK(CalledOnValidThread()); 89 DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()"; 90 // Since the input is free to change the resolution at any point in time 91 // the supported formats are unknown. 92 media::VideoCaptureFormats formats; 93 callback.Run(formats); 94 } 95 96 void PpFrameWriter::StartSourceImpl( 97 const media::VideoCaptureFormat& format, 98 const VideoCaptureDeliverFrameCB& frame_callback) { 99 DCHECK(CalledOnValidThread()); 100 DCHECK(!delegate_.get()); 101 DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; 102 delegate_ = new FrameWriterDelegate(io_message_loop(), frame_callback); 103 OnStartDone(MEDIA_DEVICE_OK); 104 } 105 106 void PpFrameWriter::StopSourceImpl() { 107 DCHECK(CalledOnValidThread()); 108 } 109 110 void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, 111 int64 time_stamp_ns) { 112 DCHECK(CalledOnValidThread()); 113 DVLOG(3) << "PpFrameWriter::PutFrame()"; 114 115 if (!image_data) { 116 LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; 117 return; 118 } 119 ImageDataAutoMapper mapper(image_data); 120 if (!mapper.is_valid()) { 121 LOG(ERROR) << "PpFrameWriter::PutFrame - " 122 << "The image could not be mapped and is unusable."; 123 return; 124 } 125 const SkBitmap* bitmap = image_data->GetMappedBitmap(); 126 if (!bitmap) { 127 LOG(ERROR) << "PpFrameWriter::PutFrame - " 128 << "The image_data's mapped bitmap is NULL."; 129 return; 130 } 131 132 const gfx::Size frame_size(bitmap->width(), bitmap->height()); 133 134 if (state() != MediaStreamVideoSource::STARTED) 135 return; 136 137 const base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( 138 time_stamp_ns / base::Time::kNanosecondsPerMicrosecond); 139 140 // TODO(perkj): It would be more efficient to use I420 here. Using YV12 will 141 // force a copy into a tightly packed I420 frame in 142 // WebRtcVideoCapturerAdapter before the frame is delivered to libJingle. 143 // crbug/359587. 144 scoped_refptr<media::VideoFrame> new_frame = 145 frame_pool_.CreateFrame(media::VideoFrame::YV12, frame_size, 146 gfx::Rect(frame_size), frame_size, timestamp); 147 media::VideoCaptureFormat format( 148 frame_size, 149 MediaStreamVideoSource::kUnknownFrameRate, 150 media::PIXEL_FORMAT_YV12); 151 152 libyuv::BGRAToI420(reinterpret_cast<uint8*>(bitmap->getPixels()), 153 bitmap->rowBytes(), 154 new_frame->data(media::VideoFrame::kYPlane), 155 new_frame->stride(media::VideoFrame::kYPlane), 156 new_frame->data(media::VideoFrame::kUPlane), 157 new_frame->stride(media::VideoFrame::kUPlane), 158 new_frame->data(media::VideoFrame::kVPlane), 159 new_frame->stride(media::VideoFrame::kVPlane), 160 frame_size.width(), frame_size.height()); 161 162 delegate_->DeliverFrame(new_frame, format); 163 } 164 165 // PpFrameWriterProxy is a helper class to make sure the user won't use 166 // PpFrameWriter after it is released (IOW its owner - WebMediaStreamSource - 167 // is released). 168 class PpFrameWriterProxy : public FrameWriterInterface { 169 public: 170 explicit PpFrameWriterProxy(const base::WeakPtr<PpFrameWriter>& writer) 171 : writer_(writer) { 172 DCHECK(writer_ != NULL); 173 } 174 175 virtual ~PpFrameWriterProxy() {} 176 177 virtual void PutFrame(PPB_ImageData_Impl* image_data, 178 int64 time_stamp_ns) OVERRIDE { 179 writer_->PutFrame(image_data, time_stamp_ns); 180 } 181 182 private: 183 base::WeakPtr<PpFrameWriter> writer_; 184 185 DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); 186 }; 187 188 bool VideoDestinationHandler::Open( 189 MediaStreamRegistryInterface* registry, 190 const std::string& url, 191 FrameWriterInterface** frame_writer) { 192 DVLOG(3) << "VideoDestinationHandler::Open"; 193 blink::WebMediaStream stream; 194 if (registry) { 195 stream = registry->GetMediaStream(url); 196 } else { 197 stream = 198 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); 199 } 200 if (stream.isNull()) { 201 LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; 202 return false; 203 } 204 205 // Create a new native video track and add it to |stream|. 206 std::string track_id; 207 // According to spec, a media stream source's id should be unique per 208 // application. There's no easy way to strictly achieve that. The id 209 // generated with this method should be unique for most of the cases but 210 // theoretically it's possible we can get an id that's duplicated with the 211 // existing sources. 212 base::Base64Encode(base::RandBytesAsString(64), &track_id); 213 214 PpFrameWriter* writer = new PpFrameWriter(); 215 216 // Create a new webkit video track. 217 blink::WebMediaStreamSource webkit_source; 218 blink::WebMediaStreamSource::Type type = 219 blink::WebMediaStreamSource::TypeVideo; 220 blink::WebString webkit_track_id = base::UTF8ToUTF16(track_id); 221 webkit_source.initialize(webkit_track_id, type, webkit_track_id); 222 webkit_source.setExtraData(writer); 223 224 blink::WebMediaConstraints constraints; 225 constraints.initialize(); 226 bool track_enabled = true; 227 228 stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( 229 writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), 230 track_enabled)); 231 232 *frame_writer = new PpFrameWriterProxy(writer->AsWeakPtr()); 233 return true; 234 } 235 236 } // namespace content 237