Chromium Code Reviews| 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 DeliverFrameOnIO(const VideoCaptureDeliverFrameCB& frame_callback, |
| 42 const media::VideoCaptureFormat& format); | 47 uint8* data, int stride, int width, int height, |
| 48 int64 time_stamp_ns); | |
| 49 void FrameDelivered(const scoped_refptr<PPB_ImageData_Impl>& image_data); | |
| 43 | 50 |
| 44 scoped_refptr<base::MessageLoopProxy> io_message_loop_; | 51 scoped_refptr<base::MessageLoopProxy> io_message_loop_; |
| 45 VideoCaptureDeliverFrameCB new_frame_callback_; | 52 VideoCaptureDeliverFrameCB new_frame_callback_; |
| 53 | |
| 54 // |frame_pool_| is only used on the IO-thread. | |
| 55 media::VideoFramePool frame_pool_; | |
| 56 | |
| 57 // Used to DCHECK that we are called on the main render thread. | |
| 58 base::ThreadChecker thread_checker_; | |
| 46 }; | 59 }; |
| 47 | 60 |
| 48 PpFrameWriter::FrameWriterDelegate::FrameWriterDelegate( | 61 PpFrameWriter::FrameWriterDelegate::FrameWriterDelegate( |
| 49 const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy, | 62 const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy) |
| 50 const VideoCaptureDeliverFrameCB& new_frame_callback) | 63 : io_message_loop_(io_message_loop_proxy) { |
| 51 : io_message_loop_(io_message_loop_proxy), | |
| 52 new_frame_callback_(new_frame_callback) { | |
| 53 } | 64 } |
| 54 | 65 |
| 55 PpFrameWriter::FrameWriterDelegate::~FrameWriterDelegate() { | 66 PpFrameWriter::FrameWriterDelegate::~FrameWriterDelegate() { |
| 56 } | 67 } |
| 57 | 68 |
| 58 void PpFrameWriter::FrameWriterDelegate::DeliverFrame( | 69 void PpFrameWriter::FrameWriterDelegate::StartDeliver( |
| 59 const scoped_refptr<media::VideoFrame>& frame, | 70 const VideoCaptureDeliverFrameCB& frame_callback) { |
| 60 const media::VideoCaptureFormat& format) { | 71 DCHECK(thread_checker_.CalledOnValidThread()); |
| 61 io_message_loop_->PostTask( | 72 new_frame_callback_ = frame_callback; |
| 62 FROM_HERE, | |
| 63 base::Bind(&FrameWriterDelegate::DeliverFrameOnIO, | |
| 64 this, frame, format)); | |
| 65 } | 73 } |
| 66 | 74 |
| 67 void PpFrameWriter::FrameWriterDelegate::DeliverFrameOnIO( | 75 void PpFrameWriter::FrameWriterDelegate::StopDeliver() { |
| 68 const scoped_refptr<media::VideoFrame>& frame, | 76 DCHECK(thread_checker_.CalledOnValidThread()); |
| 69 const media::VideoCaptureFormat& format) { | 77 new_frame_callback_.Reset(); |
| 70 DCHECK(io_message_loop_->BelongsToCurrentThread()); | |
| 71 // The local time when this frame is generated is unknown so give a null | |
| 72 // value to |estimated_capture_time|. | |
| 73 new_frame_callback_.Run(frame, format, base::TimeTicks()); | |
| 74 } | 78 } |
| 75 | 79 |
| 76 PpFrameWriter::PpFrameWriter() { | 80 void PpFrameWriter::FrameWriterDelegate::DeliverFrame( |
| 77 DVLOG(3) << "PpFrameWriter ctor"; | 81 const scoped_refptr<PPB_ImageData_Impl>& image_data, |
| 78 } | 82 int64 time_stamp_ns) { |
| 79 | 83 DCHECK(thread_checker_.CalledOnValidThread()); |
| 80 PpFrameWriter::~PpFrameWriter() { | 84 TRACE_EVENT0("video", "PpFrameWriter::FrameWriterDelegate::DeliverFrame"); |
| 81 DVLOG(3) << "PpFrameWriter dtor"; | 85 if (new_frame_callback_.is_null()) |
| 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; | 86 return; |
| 120 } | 87 if (!image_data->Map()) { |
| 121 ImageDataAutoMapper mapper(image_data); | |
| 122 if (!mapper.is_valid()) { | |
| 123 LOG(ERROR) << "PpFrameWriter::PutFrame - " | 88 LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| 124 << "The image could not be mapped and is unusable."; | 89 << "The image could not be mapped and is unusable."; |
| 125 return; | 90 return; |
| 126 } | 91 } |
| 92 | |
| 127 const SkBitmap* bitmap = image_data->GetMappedBitmap(); | 93 const SkBitmap* bitmap = image_data->GetMappedBitmap(); |
| 128 if (!bitmap) { | 94 if (!bitmap) { |
| 129 LOG(ERROR) << "PpFrameWriter::PutFrame - " | 95 LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| 130 << "The image_data's mapped bitmap is NULL."; | 96 << "The image_data's mapped bitmap is NULL."; |
| 131 return; | 97 return; |
| 132 } | 98 } |
| 99 io_message_loop_->PostTaskAndReply( | |
| 100 FROM_HERE, | |
| 101 base::Bind(&FrameWriterDelegate::DeliverFrameOnIO, this, | |
| 102 new_frame_callback_, | |
|
tommi (sloooow) - chröme
2014/10/08 09:11:24
Are you sending a pointer to the callback to the I
perkj_chrome
2014/10/08 12:20:18
Changed so that StartDeliver post the cb to the io
| |
| 103 static_cast<uint8*>(bitmap->getPixels()), | |
| 104 bitmap->rowBytes(), | |
| 105 bitmap->width(), | |
| 106 bitmap->height(), | |
| 107 time_stamp_ns), | |
| 108 base::Bind(&FrameWriterDelegate::FrameDelivered, this, | |
| 109 image_data)); | |
| 110 } | |
| 133 | 111 |
| 134 const gfx::Size frame_size(bitmap->width(), bitmap->height()); | 112 void PpFrameWriter::FrameWriterDelegate::FrameDelivered( |
| 113 const scoped_refptr<PPB_ImageData_Impl>& image_data) { | |
| 114 image_data->Unmap(); | |
|
tommi (sloooow) - chröme
2014/10/08 09:11:24
should there be a thread check in this function?
perkj_chrome
2014/10/08 12:20:18
Done.
| |
| 115 } | |
| 135 | 116 |
| 136 if (state() != MediaStreamVideoSource::STARTED) | 117 void PpFrameWriter::FrameWriterDelegate::DeliverFrameOnIO( |
| 137 return; | 118 const VideoCaptureDeliverFrameCB& frame_callback, |
| 119 uint8* data, int stride, int width, int height, int64 time_stamp_ns) { | |
| 120 DCHECK(io_message_loop_->BelongsToCurrentThread()); | |
| 121 TRACE_EVENT0("video", "PpFrameWriter::FrameWriterDelegate::DeliverFrameOnIO"); | |
| 122 const gfx::Size frame_size(width, height); | |
| 138 | 123 |
| 139 const base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( | 124 const base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( |
| 140 time_stamp_ns / base::Time::kNanosecondsPerMicrosecond); | 125 time_stamp_ns / base::Time::kNanosecondsPerMicrosecond); |
| 141 | 126 |
| 142 // TODO(perkj): It would be more efficient to use I420 here. Using YV12 will | 127 // 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 | 128 // force a copy into a tightly packed I420 frame in |
| 144 // WebRtcVideoCapturerAdapter before the frame is delivered to libJingle. | 129 // WebRtcVideoCapturerAdapter before the frame is delivered to libJingle. |
| 145 // crbug/359587. | 130 // crbug/359587. |
| 146 scoped_refptr<media::VideoFrame> new_frame = | 131 scoped_refptr<media::VideoFrame> new_frame = |
| 147 frame_pool_.CreateFrame(media::VideoFrame::YV12, frame_size, | 132 frame_pool_.CreateFrame(media::VideoFrame::YV12, frame_size, |
| 148 gfx::Rect(frame_size), frame_size, timestamp); | 133 gfx::Rect(frame_size), frame_size, timestamp); |
| 149 media::VideoCaptureFormat format( | 134 media::VideoCaptureFormat format( |
| 150 frame_size, | 135 frame_size, |
| 151 MediaStreamVideoSource::kUnknownFrameRate, | 136 MediaStreamVideoSource::kUnknownFrameRate, |
| 152 media::PIXEL_FORMAT_YV12); | 137 media::PIXEL_FORMAT_YV12); |
| 153 | 138 |
| 154 libyuv::BGRAToI420(reinterpret_cast<uint8*>(bitmap->getPixels()), | 139 libyuv::BGRAToI420(data, |
| 155 bitmap->rowBytes(), | 140 stride, |
| 156 new_frame->data(media::VideoFrame::kYPlane), | 141 new_frame->data(media::VideoFrame::kYPlane), |
| 157 new_frame->stride(media::VideoFrame::kYPlane), | 142 new_frame->stride(media::VideoFrame::kYPlane), |
| 158 new_frame->data(media::VideoFrame::kUPlane), | 143 new_frame->data(media::VideoFrame::kUPlane), |
| 159 new_frame->stride(media::VideoFrame::kUPlane), | 144 new_frame->stride(media::VideoFrame::kUPlane), |
| 160 new_frame->data(media::VideoFrame::kVPlane), | 145 new_frame->data(media::VideoFrame::kVPlane), |
| 161 new_frame->stride(media::VideoFrame::kVPlane), | 146 new_frame->stride(media::VideoFrame::kVPlane), |
| 162 frame_size.width(), frame_size.height()); | 147 frame_size.width(), frame_size.height()); |
| 163 | 148 |
| 164 delegate_->DeliverFrame(new_frame, format); | 149 // The local time when this frame is generated is unknown so give a null |
| 150 // value to |estimated_capture_time|. | |
| 151 frame_callback.Run(new_frame, format, base::TimeTicks()); | |
| 165 } | 152 } |
| 166 | 153 |
| 167 // PpFrameWriterProxy is a helper class to make sure the user won't use | 154 PpFrameWriter::PpFrameWriter() { |
| 168 // PpFrameWriter after it is released (IOW its owner - WebMediaStreamSource - | 155 DVLOG(3) << "PpFrameWriter ctor"; |
| 169 // is released). | 156 delegate_ = new FrameWriterDelegate(io_message_loop()); |
|
tommi (sloooow) - chröme
2014/10/08 09:11:24
initializer list?
perkj_chrome
2014/10/08 12:20:18
I guess I can but I have a vague memory that you d
tommi (sloooow) - chröme
2014/10/08 14:50:36
oh, delegate_ is a base class member. If so, leav
| |
| 170 class PpFrameWriterProxy : public FrameWriterInterface { | 157 } |
| 171 public: | |
| 172 explicit PpFrameWriterProxy(const base::WeakPtr<PpFrameWriter>& writer) | |
| 173 : writer_(writer) { | |
| 174 DCHECK(writer_ != NULL); | |
| 175 } | |
| 176 | 158 |
| 177 virtual ~PpFrameWriterProxy() {} | 159 PpFrameWriter::~PpFrameWriter() { |
| 160 DVLOG(3) << "PpFrameWriter dtor"; | |
| 161 } | |
| 178 | 162 |
| 179 virtual void PutFrame(PPB_ImageData_Impl* image_data, | 163 VideoDestinationHandler::FrameWriterCallback |
| 180 int64 time_stamp_ns) OVERRIDE { | 164 PpFrameWriter::GetFrameWriterCallback() { |
| 181 writer_->PutFrame(image_data, time_stamp_ns); | 165 DCHECK(CalledOnValidThread()); |
| 182 } | 166 return base::Bind(&PpFrameWriter::FrameWriterDelegate::DeliverFrame, |
| 167 delegate_); | |
| 168 } | |
| 183 | 169 |
| 184 private: | 170 void PpFrameWriter::GetCurrentSupportedFormats( |
| 185 base::WeakPtr<PpFrameWriter> writer_; | 171 int max_requested_width, |
| 172 int max_requested_height, | |
| 173 double max_requested_frame_rate, | |
| 174 const VideoCaptureDeviceFormatsCB& callback) { | |
| 175 DCHECK(CalledOnValidThread()); | |
| 176 DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()"; | |
| 177 // Since the input is free to change the resolution at any point in time | |
| 178 // the supported formats are unknown. | |
| 179 media::VideoCaptureFormats formats; | |
| 180 callback.Run(formats); | |
| 181 } | |
| 186 | 182 |
| 187 DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); | 183 void PpFrameWriter::StartSourceImpl( |
| 188 }; | 184 const media::VideoCaptureFormat& format, |
| 185 const VideoCaptureDeliverFrameCB& frame_callback) { | |
| 186 DCHECK(CalledOnValidThread()); | |
| 187 DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; | |
| 188 delegate_->StartDeliver(frame_callback); | |
| 189 OnStartDone(MEDIA_DEVICE_OK); | |
| 190 } | |
| 191 | |
| 192 void PpFrameWriter::StopSourceImpl() { | |
| 193 DCHECK(CalledOnValidThread()); | |
| 194 delegate_->StopDeliver(); | |
| 195 } | |
| 189 | 196 |
| 190 bool VideoDestinationHandler::Open( | 197 bool VideoDestinationHandler::Open( |
| 191 MediaStreamRegistryInterface* registry, | 198 MediaStreamRegistryInterface* registry, |
| 192 const std::string& url, | 199 const std::string& url, |
| 193 FrameWriterInterface** frame_writer) { | 200 FrameWriterCallback* frame_writer) { |
| 194 DVLOG(3) << "VideoDestinationHandler::Open"; | 201 DVLOG(3) << "VideoDestinationHandler::Open"; |
| 195 blink::WebMediaStream stream; | 202 blink::WebMediaStream stream; |
| 196 if (registry) { | 203 if (registry) { |
| 197 stream = registry->GetMediaStream(url); | 204 stream = registry->GetMediaStream(url); |
| 198 } else { | 205 } else { |
| 199 stream = | 206 stream = |
| 200 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); | 207 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); |
| 201 } | 208 } |
| 202 if (stream.isNull()) { | 209 if (stream.isNull()) { |
| 203 LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; | 210 LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; |
| 204 return false; | 211 return false; |
| 205 } | 212 } |
| 206 | 213 |
| 207 // Create a new native video track and add it to |stream|. | 214 // Create a new native video track and add it to |stream|. |
| 208 std::string track_id; | 215 std::string track_id; |
| 209 // According to spec, a media stream source's id should be unique per | 216 // 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 | 217 // 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 | 218 // 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 | 219 // theoretically it's possible we can get an id that's duplicated with the |
| 213 // existing sources. | 220 // existing sources. |
| 214 base::Base64Encode(base::RandBytesAsString(64), &track_id); | 221 base::Base64Encode(base::RandBytesAsString(64), &track_id); |
| 215 | 222 |
| 216 PpFrameWriter* writer = new PpFrameWriter(); | 223 PpFrameWriter* writer = new PpFrameWriter(); |
| 224 *frame_writer = writer->GetFrameWriterCallback(); | |
| 217 | 225 |
| 218 // Create a new webkit video track. | 226 // Create a new webkit video track. |
| 219 blink::WebMediaStreamSource webkit_source; | 227 blink::WebMediaStreamSource webkit_source; |
| 220 blink::WebMediaStreamSource::Type type = | 228 blink::WebMediaStreamSource::Type type = |
| 221 blink::WebMediaStreamSource::TypeVideo; | 229 blink::WebMediaStreamSource::TypeVideo; |
| 222 blink::WebString webkit_track_id = base::UTF8ToUTF16(track_id); | 230 blink::WebString webkit_track_id = base::UTF8ToUTF16(track_id); |
| 223 webkit_source.initialize(webkit_track_id, type, webkit_track_id); | 231 webkit_source.initialize(webkit_track_id, type, webkit_track_id); |
| 224 webkit_source.setExtraData(writer); | 232 webkit_source.setExtraData(writer); |
| 225 | 233 |
| 226 blink::WebMediaConstraints constraints; | 234 blink::WebMediaConstraints constraints; |
| 227 constraints.initialize(); | 235 constraints.initialize(); |
| 228 bool track_enabled = true; | 236 bool track_enabled = true; |
| 229 | 237 |
| 230 stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( | 238 stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( |
| 231 writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), | 239 writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), |
| 232 track_enabled)); | 240 track_enabled)); |
| 233 | 241 |
| 234 *frame_writer = new PpFrameWriterProxy(writer->AsWeakPtr()); | |
| 235 return true; | 242 return true; |
| 236 } | 243 } |
| 237 | 244 |
| 238 } // namespace content | 245 } // namespace content |
| OLD | NEW |