~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/libjingle/talk/session/phone/webrtcvideocapturer.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libjingle
 
2
// Copyright 2004--2011 Google Inc.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//  1. Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//  2. Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//  3. The name of the author may not be used to endorse or promote products
 
13
//     derived from this software without specific prior written permission.
 
14
//
 
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
//
 
26
// Implementation of class WebRtcVideoCapturer.
 
27
 
 
28
#ifdef HAVE_CONFIG_H
 
29
#include <config.h>
 
30
#endif
 
31
 
 
32
#ifdef HAVE_WEBRTC_VIDEO
 
33
#include "talk/session/phone/webrtcvideocapturer.h"
 
34
#include "talk/base/logging.h"
 
35
#include "talk/base/thread.h"
 
36
#include "talk/base/timeutils.h"
 
37
#include "talk/session/phone/webrtcvideoframe.h"
 
38
 
 
39
#include "talk/base/win32.h"  // Need this to #include the impl files
 
40
#ifdef WEBRTC_RELATIVE_PATH
 
41
#include "modules/video_capture/main/interface/video_capture_factory.h"
 
42
#else
 
43
#include "third_party/webrtc/files/include/video_capture_factory.h"
 
44
#endif
 
45
 
 
46
namespace cricket {
 
47
 
 
48
struct kVideoFourCCEntry {
 
49
  uint32 fourcc;
 
50
  webrtc::RawVideoType webrtc_type;
 
51
};
 
52
 
 
53
// This indicates our format preferences and defines a mapping between
 
54
// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
 
55
static kVideoFourCCEntry kSupportedFourCCs[] = {
 
56
  { FOURCC_I420, webrtc::kVideoI420 },   // 12 bpp, no conversion
 
57
  { FOURCC_YV12, webrtc::kVideoYV12 },   // 12 bpp, no conversion
 
58
  { FOURCC_NV12, webrtc::kVideoNV12 },   // 12 bpp, fast conversion
 
59
  { FOURCC_NV21, webrtc::kVideoNV21 },   // 12 bpp, fast conversion
 
60
  { FOURCC_YUY2, webrtc::kVideoYUY2 },   // 16 bpp, fast conversion
 
61
  { FOURCC_UYVY, webrtc::kVideoUYVY },   // 16 bpp, fast conversion
 
62
  { FOURCC_MJPG, webrtc::kVideoMJPEG },  // compressed, slow conversion
 
63
  { FOURCC_ARGB, webrtc::kVideoARGB },   // 32 bpp, slow conversion
 
64
  { FOURCC_24BG, webrtc::kVideoRGB24 },  // 32 bpp, slow conversion
 
65
};
 
66
 
 
67
class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
 
68
 public:
 
69
  virtual webrtc::VideoCaptureModule* Create(int id,
 
70
                                             const char* device) {
 
71
    return webrtc::VideoCaptureFactory::Create(id, device);
 
72
  }
 
73
  virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
 
74
    return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
 
75
  }
 
76
  virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
 
77
    delete info;
 
78
  }
 
79
};
 
80
 
 
81
static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
 
82
                               VideoFormat* format) {
 
83
  uint32 fourcc = 0;
 
84
  for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
 
85
    if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
 
86
      fourcc = kSupportedFourCCs[i].fourcc;
 
87
      break;
 
88
    }
 
89
  }
 
90
  if (fourcc == 0) {
 
91
    return false;
 
92
  }
 
93
 
 
94
  format->fourcc = fourcc;
 
95
  format->width = cap.width;
 
96
  format->height = cap.height;
 
97
  format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
 
98
  return true;
 
99
}
 
100
 
 
101
static bool FormatToCapability(const VideoFormat& format,
 
102
                               webrtc::VideoCaptureCapability* cap) {
 
103
  webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
 
104
  for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
 
105
    if (kSupportedFourCCs[i].fourcc == format.fourcc) {
 
106
      webrtc_type = kSupportedFourCCs[i].webrtc_type;
 
107
      break;
 
108
    }
 
109
  }
 
110
  if (webrtc_type == webrtc::kVideoUnknown) {
 
111
    return false;
 
112
  }
 
113
 
 
114
  cap->width = format.width;
 
115
  cap->height = format.height;
 
116
  cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
 
117
  cap->expectedCaptureDelay = 0;
 
118
  cap->rawType = webrtc_type;
 
119
  cap->codecType = webrtc::kVideoCodecUnknown;
 
120
  cap->interlaced = false;
 
121
  return true;
 
122
}
 
123
 
 
124
///////////////////////////////////////////////////////////////////////////
 
125
// Implementation of class WebRtcVideoCapturer
 
126
///////////////////////////////////////////////////////////////////////////
 
127
 
 
128
WebRtcVideoCapturer::WebRtcVideoCapturer()
 
129
    : factory_(new WebRtcVcmFactory),
 
130
      module_(NULL),
 
131
      captured_frames_(0) {
 
132
}
 
133
 
 
134
WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
 
135
    : factory_(factory),
 
136
      module_(NULL),
 
137
      captured_frames_(0) {
 
138
}
 
139
 
 
140
WebRtcVideoCapturer::~WebRtcVideoCapturer() {
 
141
  if (module_) {
 
142
    module_->Release();
 
143
  }
 
144
}
 
145
 
 
146
bool WebRtcVideoCapturer::Init(const Device& device) {
 
147
  if (module_) {
 
148
    LOG(LS_ERROR) << "The capturer is already initialized";
 
149
    return false;
 
150
  }
 
151
 
 
152
  webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
 
153
  if (!info) {
 
154
    return false;
 
155
  }
 
156
 
 
157
  // Find the desired camera, by name.
 
158
  // In the future, comparing IDs will be more robust.
 
159
  // TODO: Figure what's needed to allow this.
 
160
  int num_cams = info->NumberOfDevices();
 
161
  char vcm_id[256] = "";
 
162
  bool found = false;
 
163
  for (int index = 0; index < num_cams; ++index) {
 
164
    char vcm_name[256];
 
165
    if (info->GetDeviceName(index, vcm_name, ARRAY_SIZE(vcm_name),
 
166
                            vcm_id, ARRAY_SIZE(vcm_id)) != -1) {
 
167
      if (device.name == reinterpret_cast<char*>(vcm_name)) {
 
168
        found = true;
 
169
        break;
 
170
      }
 
171
    }
 
172
  }
 
173
  if (!found) {
 
174
    LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
 
175
    factory_->DestroyDeviceInfo(info);
 
176
    return false;
 
177
  }
 
178
 
 
179
  // Enumerate the supported formats.
 
180
  // TODO: Find out why this starts/stops the camera...
 
181
  std::vector<VideoFormat> supported;
 
182
  WebRtc_UWord32 num_caps = info->NumberOfCapabilities(vcm_id);
 
183
  for (WebRtc_UWord8 i = 0; i < num_caps; ++i) {
 
184
    webrtc::VideoCaptureCapability cap;
 
185
    if (info->GetCapability(vcm_id, i, cap) != -1) {
 
186
      VideoFormat format;
 
187
      if (CapabilityToFormat(cap, &format)) {
 
188
        supported.push_back(format);
 
189
      } else {
 
190
        LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
 
191
                        << cap.rawType;
 
192
      }
 
193
    }
 
194
  }
 
195
  factory_->DestroyDeviceInfo(info);
 
196
  if (supported.empty()) {
 
197
    LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
 
198
    return false;
 
199
  }
 
200
 
 
201
  module_ = factory_->Create(0, vcm_id);
 
202
  if (!module_) {
 
203
    LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
 
204
    return false;
 
205
  }
 
206
 
 
207
  // It is safe to change member attributes now.
 
208
  module_->AddRef();
 
209
  SetId(device.id);
 
210
  SetSupportedFormats(supported);
 
211
  return true;
 
212
}
 
213
 
 
214
bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
 
215
  if (module_) {
 
216
    LOG(LS_ERROR) << "The capturer is already initialized";
 
217
    return false;
 
218
  }
 
219
  if (!module) {
 
220
    LOG(LS_ERROR) << "Invalid VCM supplied";
 
221
    return false;
 
222
  }
 
223
  // TODO: Set id and formats.
 
224
  (module_ = module)->AddRef();
 
225
  return true;
 
226
}
 
227
 
 
228
bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
 
229
                                               VideoFormat* best_format) {
 
230
  if (!best_format) {
 
231
    return false;
 
232
  }
 
233
 
 
234
  if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
 
235
    // If the vcm has a list of the supported format, but didn't find the
 
236
    // best match, then we should return fail.
 
237
    if (GetSupportedFormats()) {
 
238
      return false;
 
239
    }
 
240
 
 
241
    // We maybe using a manually injected VCM which doesn't support enum.
 
242
    // Use the desired format as the best format.
 
243
    best_format->width = desired.width;
 
244
    best_format->height = desired.height;
 
245
    best_format->fourcc = FOURCC_I420;
 
246
    best_format->interval = desired.interval;
 
247
    LOG(LS_INFO) << "Failed to find best capture format,"
 
248
                 << " fall back to the requested format "
 
249
                 << best_format->ToString();
 
250
  }
 
251
  return true;
 
252
}
 
253
 
 
254
CaptureResult WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
 
255
  if (!module_) {
 
256
    LOG(LS_ERROR) << "The capturer has not been initialized";
 
257
    return CR_NO_DEVICE;
 
258
  }
 
259
 
 
260
  if (IsRunning()) {
 
261
    LOG(LS_ERROR) << "The capturer is already running";
 
262
    return CR_FAILURE;
 
263
  }
 
264
 
 
265
  SetCaptureFormat(&capture_format);
 
266
 
 
267
  webrtc::VideoCaptureCapability cap;
 
268
  if (!FormatToCapability(capture_format, &cap)) {
 
269
    LOG(LS_ERROR) << "Invalid capture format specified";
 
270
    return CR_FAILURE;
 
271
  }
 
272
 
 
273
  std::string camera_id(GetId());
 
274
  uint32 start = talk_base::Time();
 
275
  if (module_->RegisterCaptureDataCallback(*this) != 0 ||
 
276
      module_->StartCapture(cap) != 0) {
 
277
    LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start";
 
278
    return CR_FAILURE;
 
279
  }
 
280
 
 
281
  LOG(LS_INFO) << "Camera '" << camera_id << "' started with format "
 
282
               << capture_format.ToString() << ", elapsed time "
 
283
               << talk_base::TimeSince(start) << " ms";
 
284
 
 
285
  captured_frames_ = 0;
 
286
  talk_base::Thread::Current()->Post(this);
 
287
  return CR_PENDING;
 
288
}
 
289
 
 
290
void WebRtcVideoCapturer::Stop() {
 
291
  if (IsRunning()) {
 
292
    talk_base::Thread::Current()->Clear(this);
 
293
    module_->StopCapture();
 
294
    module_->DeRegisterCaptureDataCallback();
 
295
 
 
296
    // TODO: Determine if the VCM exposes any drop stats we can use.
 
297
    double drop_ratio = 0.0;
 
298
    std::string camera_id(GetId());
 
299
    LOG(LS_INFO) << "Camera '" << camera_id << "' stopped after capturing "
 
300
                 << captured_frames_ << " frames and dropping "
 
301
                 << drop_ratio << "%";
 
302
  }
 
303
  SetCaptureFormat(NULL);
 
304
}
 
305
 
 
306
bool WebRtcVideoCapturer::IsRunning() {
 
307
  return (module_ != NULL && module_->CaptureStarted());
 
308
}
 
309
 
 
310
bool WebRtcVideoCapturer::GetPreferredFourccs(
 
311
    std::vector<uint32>* fourccs) {
 
312
  if (!fourccs) {
 
313
    return false;
 
314
  }
 
315
 
 
316
  fourccs->clear();
 
317
  for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
 
318
    fourccs->push_back(kSupportedFourCCs[i].fourcc);
 
319
  }
 
320
  return true;
 
321
}
 
322
 
 
323
void WebRtcVideoCapturer::OnMessage(talk_base::Message* message) {
 
324
  // TODO: Fire SignalCaptureEvent appropriately.
 
325
  SignalStartResult(this, CR_SUCCESS);
 
326
}
 
327
 
 
328
void WebRtcVideoCapturer::OnIncomingCapturedFrame(const WebRtc_Word32 id,
 
329
    webrtc::VideoFrame& sample, webrtc::VideoCodecType codec_type) {
 
330
  ASSERT(IsRunning());
 
331
  ASSERT(codec_type == webrtc::kVideoCodecUnknown);
 
332
 
 
333
  ++captured_frames_;
 
334
  // Log the size and pixel aspect ratio of the first captured frame.
 
335
  if (1 == captured_frames_) {
 
336
    LOG(LS_INFO) << "Captured frame size "
 
337
                 << sample.Width() << "x" << sample.Height()
 
338
                 << ". Expected format " << GetCaptureFormat()->ToString();
 
339
  }
 
340
 
 
341
  // Signal down stream components on captured frame.
 
342
  WebRtcCapturedFrame frame(sample);
 
343
  SignalFrameCaptured(this, &frame);
 
344
}
 
345
 
 
346
void WebRtcVideoCapturer::OnCaptureDelayChanged(
 
347
    const WebRtc_Word32 id, const WebRtc_Word32 delay) {
 
348
  LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
 
349
}
 
350
 
 
351
// WebRtcCapturedFrame
 
352
WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::VideoFrame& sample) {
 
353
  width = sample.Width();
 
354
  height = sample.Height();
 
355
  fourcc = FOURCC_I420;
 
356
  pixel_width = 1;
 
357
  pixel_height = 1;
 
358
  // convert units from VideoFrame RenderTimeMs
 
359
  // to CapturedFrame (nanoseconds)
 
360
  elapsed_time = sample.RenderTimeMs() * talk_base::kNumNanosecsPerMillisec;
 
361
  time_stamp = elapsed_time;
 
362
  data_size = sample.Length();
 
363
  data = const_cast<WebRtc_UWord8*>(sample.Buffer());
 
364
}
 
365
 
 
366
}  // namespace cricket
 
367
 
 
368
#endif  // HAVE_WEBRTC_VIDEO