summaryrefslogtreecommitdiff
path: root/chromium/content/renderer/media/video_destination_handler.cc
blob: 692efeff86fbd79b2920f34af93312c5a3226dd4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/video_destination_handler.h"

#include <string>

#include "base/base64.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "content/renderer/media/media_stream_dependency_factory.h"
#include "content/renderer/media/media_stream_registry_interface.h"
#include "content/renderer/pepper/ppb_image_data_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"

using cricket::CaptureState;
using cricket::VideoFormat;
using webrtc::VideoTrackInterface;
using webrtc::VideoTrackVector;

static const cricket::FourCC kEffectColorFormat = cricket::FOURCC_BGRA;

namespace content {

PpFrameWriter::PpFrameWriter()
    : started_(false) {}

PpFrameWriter::~PpFrameWriter() {}

CaptureState PpFrameWriter::Start(const VideoFormat& capture_format) {
  base::AutoLock auto_lock(lock_);
  if (started_) {
    LOG(ERROR) << "PpFrameWriter::Start - "
               << "Got a StartCapture when already started!";
    return cricket::CS_FAILED;
  }
  started_ = true;
  return cricket::CS_STARTING;
}

void PpFrameWriter::Stop() {
  base::AutoLock auto_lock(lock_);
  started_ = false;
  SignalStateChange(this, cricket::CS_STOPPED);
}

bool PpFrameWriter::IsRunning() {
  return started_;
}

bool PpFrameWriter::GetPreferredFourccs(std::vector<uint32>* fourccs) {
  if (!fourccs) {
    LOG(ERROR) << "PpFrameWriter::GetPreferredFourccs - "
               << "fourccs is NULL.";
    return false;
  }
  // The effects plugin output BGRA.
  fourccs->push_back(kEffectColorFormat);
  return true;
}

bool PpFrameWriter::GetBestCaptureFormat(const VideoFormat& desired,
                                         VideoFormat* best_format) {
  if (!best_format) {
    LOG(ERROR) << "PpFrameWriter::GetBestCaptureFormat - "
               << "best_format is NULL.";
    return false;
  }

  // Use the desired format as the best format.
  best_format->width = desired.width;
  best_format->height = desired.height;
  best_format->fourcc = kEffectColorFormat;
  best_format->interval = desired.interval;
  return true;
}

bool PpFrameWriter::IsScreencast() const {
  return false;
}

void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data,
                             int64 time_stamp_ns) {
  base::AutoLock auto_lock(lock_);
  // This assumes the handler of the SignalFrameCaptured won't call Start/Stop.
  // TODO(ronghuawu): Avoid the using of lock. One way is to post this call to
  // libjingle worker thread, which will require an extra copy of |image_data|.
  // However if pepper host can hand over the ownership of |image_data|
  // then we can avoid this extra copy.
  if (!started_) {
    LOG(ERROR) << "PpFrameWriter::PutFrame - "
               << "Called when capturer is not started.";
    return;
  }
  if (!image_data) {
    LOG(ERROR) << "PpFrameWriter::PutFrame - Called with NULL image_data.";
    return;
  }
  ImageDataAutoMapper mapper(image_data);
  if (!mapper.is_valid()) {
    LOG(ERROR) << "PpFrameWriter::PutFrame - "
               << "The image could not be mapped and is unusable.";
    return;
  }
  const SkBitmap* bitmap = image_data->GetMappedBitmap();
  if (!bitmap) {
    LOG(ERROR) << "PpFrameWriter::PutFrame - "
               << "The image_data's mapped bitmap is NULL.";
    return;
  }

  cricket::CapturedFrame frame;
  frame.elapsed_time = 0;
  frame.time_stamp = time_stamp_ns;
  frame.pixel_height = 1;
  frame.pixel_width = 1;
  frame.width = bitmap->width();
  frame.height = bitmap->height();
  if (image_data->format() == PP_IMAGEDATAFORMAT_BGRA_PREMUL) {
    frame.fourcc = cricket::FOURCC_BGRA;
  } else {
    LOG(ERROR) << "PpFrameWriter::PutFrame - Got RGBA which is not supported.";
    return;
  }
  frame.data_size = bitmap->getSize();
  frame.data = bitmap->getPixels();

  // This signals to libJingle that a new VideoFrame is available.
  // libJingle have no assumptions on what thread this signal come from.
  SignalFrameCaptured(this, &frame);
}

// PpFrameWriterProxy is a helper class to make sure the user won't use
// PpFrameWriter after it is released (IOW its owner - WebMediaStreamTrack -
// is released).
class PpFrameWriterProxy : public FrameWriterInterface {
 public:
  PpFrameWriterProxy(VideoTrackInterface* track,
                     PpFrameWriter* writer)
      : track_(track),
        writer_(writer) {
    DCHECK(writer_ != NULL);
  }

  virtual ~PpFrameWriterProxy() {}

  virtual void PutFrame(PPB_ImageData_Impl* image_data,
                        int64 time_stamp_ns) OVERRIDE {
    writer_->PutFrame(image_data, time_stamp_ns);
  }

 private:
  scoped_refptr<VideoTrackInterface> track_;
  PpFrameWriter* writer_;

  DISALLOW_COPY_AND_ASSIGN(PpFrameWriterProxy);
};

bool VideoDestinationHandler::Open(
    MediaStreamDependencyFactory* factory,
    MediaStreamRegistryInterface* registry,
    const std::string& url,
    FrameWriterInterface** frame_writer) {
  if (!factory) {
    factory = RenderThreadImpl::current()->GetMediaStreamDependencyFactory();
    DCHECK(factory != NULL);
  }
  blink::WebMediaStream stream;
  if (registry) {
    stream = registry->GetMediaStream(url);
  } else {
    stream =
        blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(GURL(url));
  }
  if (stream.isNull() || !stream.extraData()) {
    LOG(ERROR) << "VideoDestinationHandler::Open - invalid url: " << url;
    return false;
  }

  // Create a new native video track and add it to |stream|.
  std::string track_id;
  // According to spec, a media stream track's id should be globally unique.
  // There's no easy way to strictly achieve that. The id generated with this
  // method should be unique for most of the cases but theoretically it's
  // possible we can get an id that's duplicated with the existing tracks.
  base::Base64Encode(base::RandBytesAsString(64), &track_id);
  PpFrameWriter* writer = new PpFrameWriter();
  if (!factory->AddNativeVideoMediaTrack(track_id, &stream, writer)) {
    delete writer;
    return false;
  }

  // Gets a handler to the native video track, which owns the |writer|.
  MediaStreamExtraData* extra_data =
      static_cast<MediaStreamExtraData*>(stream.extraData());
  webrtc::MediaStreamInterface* native_stream = extra_data->stream().get();
  DCHECK(native_stream);
  VideoTrackVector video_tracks = native_stream->GetVideoTracks();
  // Currently one supports one video track per media stream.
  DCHECK(video_tracks.size() == 1);

  *frame_writer = new PpFrameWriterProxy(video_tracks[0].get(), writer);
  return true;
}

}  // namespace content