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