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
19using cricket::CaptureState;
20using cricket::VideoFormat;
21using webrtc::VideoTrackInterface;
22using webrtc::VideoTrackVector;
23
24static const cricket::FourCC kEffectColorFormat = cricket::FOURCC_BGRA;
25
26namespace content {
27
28PpFrameWriter::PpFrameWriter()
29    : started_(false) {}
30
31PpFrameWriter::~PpFrameWriter() {}
32
33CaptureState 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
44void PpFrameWriter::Stop() {
45  base::AutoLock auto_lock(lock_);
46  started_ = false;
47  SignalStateChange(this, cricket::CS_STOPPED);
48}
49
50bool PpFrameWriter::IsRunning() {
51  return started_;
52}
53
54bool 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
65bool 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
81bool PpFrameWriter::IsScreencast() const {
82  return false;
83}
84
85void 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).
139class 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
162bool 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