| 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/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/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
| 12 #include "base/strings/utf_string_conversions.h" |
| 12 #include "content/renderer/media/media_stream.h" | 13 #include "content/renderer/media/media_stream.h" |
| 13 #include "content/renderer/media/media_stream_dependency_factory.h" | 14 #include "content/renderer/media/media_stream_dependency_factory.h" |
| 14 #include "content/renderer/media/media_stream_registry_interface.h" | 15 #include "content/renderer/media/media_stream_registry_interface.h" |
| 16 #include "content/renderer/media/media_stream_video_track.h" |
| 15 #include "content/renderer/pepper/ppb_image_data_impl.h" | 17 #include "content/renderer/pepper/ppb_image_data_impl.h" |
| 16 #include "content/renderer/render_thread_impl.h" | 18 #include "content/renderer/render_thread_impl.h" |
| 19 #include "media/video/capture/video_capture_types.h" |
| 17 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" | 20 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" |
| 18 #include "third_party/WebKit/public/platform/WebURL.h" | 21 #include "third_party/WebKit/public/platform/WebURL.h" |
| 19 #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" |
| 20 #include "url/gurl.h" | 24 #include "url/gurl.h" |
| 21 | 25 |
| 22 using cricket::CaptureState; | |
| 23 using cricket::VideoFormat; | |
| 24 using webrtc::VideoTrackInterface; | |
| 25 using webrtc::VideoTrackVector; | |
| 26 | |
| 27 static const cricket::FourCC kEffectColorFormat = cricket::FOURCC_BGRA; | |
| 28 | |
| 29 namespace content { | 26 namespace content { |
| 30 | 27 |
| 31 PpFrameWriter::PpFrameWriter() | 28 PpFrameWriter::PpFrameWriter(MediaStreamDependencyFactory* factory) |
| 32 : started_(false) {} | 29 : MediaStreamVideoSource(factory), first_frame_received_(false) { |
| 33 | 30 DVLOG(3) << "PpFrameWriter ctor"; |
| 34 PpFrameWriter::~PpFrameWriter() {} | |
| 35 | |
| 36 CaptureState PpFrameWriter::Start(const VideoFormat& capture_format) { | |
| 37 base::AutoLock auto_lock(lock_); | |
| 38 if (started_) { | |
| 39 LOG(ERROR) << "PpFrameWriter::Start - " | |
| 40 << "Got a StartCapture when already started!"; | |
| 41 return cricket::CS_FAILED; | |
| 42 } | |
| 43 started_ = true; | |
| 44 return cricket::CS_STARTING; | |
| 45 } | 31 } |
| 46 | 32 |
| 47 void PpFrameWriter::Stop() { | 33 PpFrameWriter::~PpFrameWriter() { |
| 48 base::AutoLock auto_lock(lock_); | 34 DVLOG(3) << "PpFrameWriter dtor"; |
| 49 started_ = false; | |
| 50 SignalStateChange(this, cricket::CS_STOPPED); | |
| 51 } | 35 } |
| 52 | 36 |
| 53 bool PpFrameWriter::IsRunning() { | 37 void PpFrameWriter::GetCurrentSupportedFormats(int max_requested_width, |
| 54 return started_; | 38 int max_requested_height) { |
| 39 DCHECK(CalledOnValidThread()); |
| 40 DVLOG(3) << "PpFrameWriter::GetCurrentSupportedFormats()"; |
| 41 if (format_.IsValid()) { |
| 42 media::VideoCaptureFormats formats; |
| 43 formats.push_back(format_); |
| 44 OnSupportedFormats(formats); |
| 45 } |
| 55 } | 46 } |
| 56 | 47 |
| 57 bool PpFrameWriter::GetPreferredFourccs(std::vector<uint32>* fourccs) { | 48 void PpFrameWriter::StartSourceImpl( |
| 58 if (!fourccs) { | 49 const media::VideoCaptureParams& params) { |
| 59 LOG(ERROR) << "PpFrameWriter::GetPreferredFourccs - " | 50 DCHECK(CalledOnValidThread()); |
| 60 << "fourccs is NULL."; | 51 DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; |
| 61 return false; | 52 OnStartDone(true); |
| 62 } | |
| 63 // The effects plugin output BGRA. | |
| 64 fourccs->push_back(kEffectColorFormat); | |
| 65 return true; | |
| 66 } | 53 } |
| 67 | 54 |
| 68 bool PpFrameWriter::GetBestCaptureFormat(const VideoFormat& desired, | 55 void PpFrameWriter::StopSourceImpl() { |
| 69 VideoFormat* best_format) { | 56 DCHECK(CalledOnValidThread()); |
| 70 if (!best_format) { | |
| 71 LOG(ERROR) << "PpFrameWriter::GetBestCaptureFormat - " | |
| 72 << "best_format is NULL."; | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 // Use the desired format as the best format. | |
| 77 best_format->width = desired.width; | |
| 78 best_format->height = desired.height; | |
| 79 best_format->fourcc = kEffectColorFormat; | |
| 80 best_format->interval = desired.interval; | |
| 81 return true; | |
| 82 } | |
| 83 | |
| 84 bool PpFrameWriter::IsScreencast() const { | |
| 85 return false; | |
| 86 } | 57 } |
| 87 | 58 |
| 88 void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, | 59 void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, |
| 89 int64 time_stamp_ns) { | 60 int64 time_stamp_ns) { |
| 90 base::AutoLock auto_lock(lock_); | 61 DCHECK(CalledOnValidThread()); |
| 91 // This assumes the handler of the SignalFrameCaptured won't call Start/Stop. | 62 DVLOG(3) << "PpFrameWriter::PutFrame()"; |
| 92 // TODO(ronghuawu): Avoid the using of lock. One way is to post this call to | 63 |
| 93 // libjingle worker thread, which will require an extra copy of |image_data|. | |
| 94 // However if pepper host can hand over the ownership of |image_data| | |
| 95 // then we can avoid this extra copy. | |
| 96 if (!started_) { | |
| 97 LOG(ERROR) << "PpFrameWriter::PutFrame - " | |
| 98 << "Called when capturer is not started."; | |
| 99 return; | |
| 100 } | |
| 101 if (!image_data) { | 64 if (!image_data) { |
| 102 LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; | 65 LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data."; |
| 103 return; | 66 return; |
| 104 } | 67 } |
| 105 ImageDataAutoMapper mapper(image_data); | 68 ImageDataAutoMapper mapper(image_data); |
| 106 if (!mapper.is_valid()) { | 69 if (!mapper.is_valid()) { |
| 107 LOG(ERROR) << "PpFrameWriter::PutFrame - " | 70 LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| 108 << "The image could not be mapped and is unusable."; | 71 << "The image could not be mapped and is unusable."; |
| 109 return; | 72 return; |
| 110 } | 73 } |
| 111 const SkBitmap* bitmap = image_data->GetMappedBitmap(); | 74 const SkBitmap* bitmap = image_data->GetMappedBitmap(); |
| 112 if (!bitmap) { | 75 if (!bitmap) { |
| 113 LOG(ERROR) << "PpFrameWriter::PutFrame - " | 76 LOG(ERROR) << "PpFrameWriter::PutFrame - " |
| 114 << "The image_data's mapped bitmap is NULL."; | 77 << "The image_data's mapped bitmap is NULL."; |
| 115 return; | 78 return; |
| 116 } | 79 } |
| 117 | 80 |
| 118 cricket::CapturedFrame frame; | 81 const gfx::Size frame_size(bitmap->width(), bitmap->height()); |
| 119 frame.elapsed_time = 0; | 82 |
| 120 frame.time_stamp = time_stamp_ns; | 83 if (!first_frame_received_) { |
| 121 frame.pixel_height = 1; | 84 first_frame_received_ = true; |
| 122 frame.pixel_width = 1; | 85 format_ = media::VideoCaptureFormat( |
| 123 frame.width = bitmap->width(); | 86 frame_size, |
| 124 frame.height = bitmap->height(); | 87 MediaStreamVideoSource::kDefaultFrameRate, |
| 125 if (image_data->format() == PP_IMAGEDATAFORMAT_BGRA_PREMUL) { | 88 media::PIXEL_FORMAT_I420); |
| 126 frame.fourcc = cricket::FOURCC_BGRA; | 89 if (state() == MediaStreamVideoSource::RETRIEVING_CAPABILITIES) { |
| 127 } else { | 90 media::VideoCaptureFormats formats; |
| 128 LOG(ERROR) << "PpFrameWriter::PutFrame - Got RGBA which is not supported."; | 91 formats.push_back(format_); |
| 92 OnSupportedFormats(formats); |
| 93 } |
| 94 } |
| 95 |
| 96 if (state() != MediaStreamVideoSource::STARTED) |
| 129 return; | 97 return; |
| 130 } | |
| 131 frame.data_size = bitmap->getSize(); | |
| 132 frame.data = bitmap->getPixels(); | |
| 133 | 98 |
| 134 // This signals to libJingle that a new VideoFrame is available. | 99 const base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds( |
| 135 // libJingle have no assumptions on what thread this signal come from. | 100 time_stamp_ns / talk_base::kNumNanosecsPerMillisec); |
| 136 SignalFrameCaptured(this, &frame); | 101 |
| 102 scoped_refptr<media::VideoFrame> new_frame = |
| 103 frame_pool_.CreateFrame(media::VideoFrame::I420, frame_size, |
| 104 gfx::Rect(frame_size), frame_size, timestamp); |
| 105 |
| 106 libyuv::BGRAToI420(reinterpret_cast<uint8*>(bitmap->getPixels()), |
| 107 bitmap->rowBytes(), |
| 108 new_frame->data(media::VideoFrame::kYPlane), |
| 109 new_frame->stride(media::VideoFrame::kYPlane), |
| 110 new_frame->data(media::VideoFrame::kUPlane), |
| 111 new_frame->stride(media::VideoFrame::kUPlane), |
| 112 new_frame->data(media::VideoFrame::kVPlane), |
| 113 new_frame->stride(media::VideoFrame::kVPlane), |
| 114 frame_size.width(), frame_size.height()); |
| 115 |
| 116 DeliverVideoFrame(new_frame); |
| 137 } | 117 } |
| 138 | 118 |
| 139 // PpFrameWriterProxy is a helper class to make sure the user won't use | 119 // PpFrameWriterProxy is a helper class to make sure the user won't use |
| 140 // PpFrameWriter after it is released (IOW its owner - WebMediaStreamTrack - | 120 // PpFrameWriter after it is released (IOW its owner - WebMediaStreamSource - |
| 141 // is released). | 121 // is released). |
| 142 class PpFrameWriterProxy : public FrameWriterInterface { | 122 class PpFrameWriterProxy : public FrameWriterInterface { |
| 143 public: | 123 public: |
| 144 PpFrameWriterProxy(VideoTrackInterface* track, | 124 explicit PpFrameWriterProxy(const base::WeakPtr<PpFrameWriter>& writer) |
| 145 PpFrameWriter* writer) | 125 : writer_(writer) { |
| 146 : track_(track), | |
| 147 writer_(writer) { | |
| 148 DCHECK(writer_ != NULL); | 126 DCHECK(writer_ != NULL); |
| 149 } | 127 } |
| 150 | 128 |
| 151 virtual ~PpFrameWriterProxy() {} | 129 virtual ~PpFrameWriterProxy() {} |
| 152 | 130 |
| 153 virtual void PutFrame(PPB_ImageData_Impl* image_data, | 131 virtual void PutFrame(PPB_ImageData_Impl* image_data, |
| 154 int64 time_stamp_ns) OVERRIDE { | 132 int64 time_stamp_ns) OVERRIDE { |
| 155 writer_->PutFrame(image_data, time_stamp_ns); | 133 writer_->PutFrame(image_data, time_stamp_ns); |
| 156 } | 134 } |
| 157 | 135 |
| 158 private: | 136 private: |
| 159 scoped_refptr<VideoTrackInterface> track_; | 137 base::WeakPtr<PpFrameWriter> writer_; |
| 160 PpFrameWriter* writer_; | |
| 161 | 138 |
| 162 DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); | 139 DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy); |
| 163 }; | 140 }; |
| 164 | 141 |
| 165 bool VideoDestinationHandler::Open( | 142 bool VideoDestinationHandler::Open( |
| 166 MediaStreamDependencyFactory* factory, | 143 MediaStreamDependencyFactory* factory, |
| 167 MediaStreamRegistryInterface* registry, | 144 MediaStreamRegistryInterface* registry, |
| 168 const std::string& url, | 145 const std::string& url, |
| 169 FrameWriterInterface** frame_writer) { | 146 FrameWriterInterface** frame_writer) { |
| 147 DVLOG(3) << "VideoDestinationHandler::Open"; |
| 170 if (!factory) { | 148 if (!factory) { |
| 171 factory = RenderThreadImpl::current()->GetMediaStreamDependencyFactory(); | 149 factory = RenderThreadImpl::current()->GetMediaStreamDependencyFactory(); |
| 172 DCHECK(factory != NULL); | 150 DCHECK(factory != NULL); |
| 173 } | 151 } |
| 174 blink::WebMediaStream stream; | 152 blink::WebMediaStream stream; |
| 175 if (registry) { | 153 if (registry) { |
| 176 stream = registry->GetMediaStream(url); | 154 stream = registry->GetMediaStream(url); |
| 177 } else { | 155 } else { |
| 178 stream = | 156 stream = |
| 179 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); | 157 blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url)); |
| 180 } | 158 } |
| 181 if (stream.isNull() || !stream.extraData()) { | 159 if (stream.isNull()) { |
| 182 LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; | 160 LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url; |
| 183 return false; | 161 return false; |
| 184 } | 162 } |
| 185 | 163 |
| 186 // Create a new native video track and add it to |stream|. | 164 // Create a new native video track and add it to |stream|. |
| 187 std::string track_id; | 165 std::string track_id; |
| 188 // According to spec, a media stream track's id should be globally unique. | 166 // According to spec, a media stream source's id should be unique per |
| 189 // There's no easy way to strictly achieve that. The id generated with this | 167 // application. There's no easy way to strictly achieve that. The id |
| 190 // method should be unique for most of the cases but theoretically it's | 168 // generated with this method should be unique for most of the cases but |
| 191 // possible we can get an id that's duplicated with the existing tracks. | 169 // theoretically it's possible we can get an id that's duplicated with the |
| 170 // existing sources. |
| 192 base::Base64Encode(base::RandBytesAsString(64), &track_id); | 171 base::Base64Encode(base::RandBytesAsString(64), &track_id); |
| 193 PpFrameWriter* writer = new PpFrameWriter(); | 172 PpFrameWriter* writer = new PpFrameWriter(factory); |
| 194 if (!factory->AddNativeVideoMediaTrack(track_id, &stream, writer)) { | |
| 195 delete writer; | |
| 196 return false; | |
| 197 } | |
| 198 | 173 |
| 199 // Gets a handler to the native video track, which owns the |writer|. | 174 // Create a new webkit video track. |
| 200 webrtc::MediaStreamInterface* native_stream = MediaStream::GetAdapter(stream); | 175 blink::WebMediaStreamSource webkit_source; |
| 201 DCHECK(native_stream); | 176 blink::WebMediaStreamSource::Type type = |
| 202 VideoTrackVector video_tracks = native_stream->GetVideoTracks(); | 177 blink::WebMediaStreamSource::TypeVideo; |
| 203 // Currently one supports one video track per media stream. | 178 blink::WebString webkit_track_id = base::UTF8ToUTF16(track_id); |
| 204 DCHECK(video_tracks.size() == 1); | 179 webkit_source.initialize(webkit_track_id, type, webkit_track_id); |
| 180 webkit_source.setExtraData(writer); |
| 205 | 181 |
| 206 *frame_writer = new PpFrameWriterProxy(video_tracks[0].get(), writer); | 182 blink::WebMediaConstraints constraints; |
| 183 constraints.initialize(); |
| 184 bool track_enabled = true; |
| 185 |
| 186 stream.addTrack(MediaStreamVideoTrack::CreateVideoTrack( |
| 187 writer, constraints, MediaStreamVideoSource::ConstraintsCallback(), |
| 188 track_enabled, factory)); |
| 189 |
| 190 *frame_writer = new PpFrameWriterProxy(writer->AsWeakPtr()); |
| 207 return true; | 191 return true; |
| 208 } | 192 } |
| 209 | 193 |
| 210 } // namespace content | 194 } // namespace content |
| 211 | 195 |
| OLD | NEW |