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