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