blob: bcfda4e84e8142d613ac00c3dbe6edfd3c66a563 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001// libjingle
2// Copyright 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#include "talk/media/webrtc/webrtcvideocapturer.h"
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#ifdef HAVE_WEBRTC_VIDEO
35#include "talk/base/logging.h"
36#include "talk/base/thread.h"
37#include "talk/base/timeutils.h"
38#include "talk/media/webrtc/webrtcvideoframe.h"
39
40#include "talk/base/win32.h" // Need this to #include the impl files.
41#include "webrtc/modules/video_capture/include/video_capture_factory.h"
42
43namespace cricket {
44
45struct kVideoFourCCEntry {
46 uint32 fourcc;
47 webrtc::RawVideoType webrtc_type;
48};
49
50// This indicates our format preferences and defines a mapping between
51// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
52static kVideoFourCCEntry kSupportedFourCCs[] = {
53 { FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
54 { FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
55 { FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
56 { FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
57 { FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
58 { FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
59 { FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
60 { FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
61 { FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
62};
63
64class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
65 public:
66 virtual webrtc::VideoCaptureModule* Create(int id, const char* device) {
67 return webrtc::VideoCaptureFactory::Create(id, device);
68 }
69 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
70 return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
71 }
72 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
73 delete info;
74 }
75};
76
77static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
78 VideoFormat* format) {
79 uint32 fourcc = 0;
80 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
81 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
82 fourcc = kSupportedFourCCs[i].fourcc;
83 break;
84 }
85 }
86 if (fourcc == 0) {
87 return false;
88 }
89
90 format->fourcc = fourcc;
91 format->width = cap.width;
92 format->height = cap.height;
93 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
94 return true;
95}
96
97static bool FormatToCapability(const VideoFormat& format,
98 webrtc::VideoCaptureCapability* cap) {
99 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
100 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
101 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
102 webrtc_type = kSupportedFourCCs[i].webrtc_type;
103 break;
104 }
105 }
106 if (webrtc_type == webrtc::kVideoUnknown) {
107 return false;
108 }
109
110 cap->width = format.width;
111 cap->height = format.height;
112 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
113 cap->expectedCaptureDelay = 0;
114 cap->rawType = webrtc_type;
115 cap->codecType = webrtc::kVideoCodecUnknown;
116 cap->interlaced = false;
117 return true;
118}
119
120///////////////////////////////////////////////////////////////////////////
121// Implementation of class WebRtcVideoCapturer
122///////////////////////////////////////////////////////////////////////////
123
124WebRtcVideoCapturer::WebRtcVideoCapturer()
125 : factory_(new WebRtcVcmFactory),
126 module_(NULL),
127 captured_frames_(0) {
128}
129
130WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
131 : factory_(factory),
132 module_(NULL),
133 captured_frames_(0) {
134}
135
136WebRtcVideoCapturer::~WebRtcVideoCapturer() {
137 if (module_) {
138 module_->Release();
139 }
140}
141
142bool WebRtcVideoCapturer::Init(const Device& device) {
143 if (module_) {
144 LOG(LS_ERROR) << "The capturer is already initialized";
145 return false;
146 }
147
148 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
149 if (!info) {
150 return false;
151 }
152
153 // Find the desired camera, by name.
154 // In the future, comparing IDs will be more robust.
155 // TODO(juberti): Figure what's needed to allow this.
156 int num_cams = info->NumberOfDevices();
157 char vcm_id[256] = "";
158 bool found = false;
159 for (int index = 0; index < num_cams; ++index) {
160 char vcm_name[256];
161 if (info->GetDeviceName(index, vcm_name, ARRAY_SIZE(vcm_name),
162 vcm_id, ARRAY_SIZE(vcm_id)) != -1) {
163 if (device.name == reinterpret_cast<char*>(vcm_name)) {
164 found = true;
165 break;
166 }
167 }
168 }
169 if (!found) {
170 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
171 factory_->DestroyDeviceInfo(info);
172 return false;
173 }
174
175 // Enumerate the supported formats.
176 // TODO(juberti): Find out why this starts/stops the camera...
177 std::vector<VideoFormat> supported;
178 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
179 for (int32_t i = 0; i < num_caps; ++i) {
180 webrtc::VideoCaptureCapability cap;
181 if (info->GetCapability(vcm_id, i, cap) != -1) {
182 VideoFormat format;
183 if (CapabilityToFormat(cap, &format)) {
184 supported.push_back(format);
185 } else {
186 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
187 << cap.rawType;
188 }
189 }
190 }
191 factory_->DestroyDeviceInfo(info);
192 if (supported.empty()) {
193 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
194 return false;
195 }
196
197 module_ = factory_->Create(0, vcm_id);
198 if (!module_) {
199 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
200 return false;
201 }
202
203 // It is safe to change member attributes now.
204 module_->AddRef();
205 SetId(device.id);
206 SetSupportedFormats(supported);
207 return true;
208}
209
210bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
211 if (module_) {
212 LOG(LS_ERROR) << "The capturer is already initialized";
213 return false;
214 }
215 if (!module) {
216 LOG(LS_ERROR) << "Invalid VCM supplied";
217 return false;
218 }
219 // TODO(juberti): Set id and formats.
220 (module_ = module)->AddRef();
221 return true;
222}
223
224bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
225 VideoFormat* best_format) {
226 if (!best_format) {
227 return false;
228 }
229
230 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
231 // We maybe using a manually injected VCM which doesn't support enum.
232 // Use the desired format as the best format.
233 best_format->width = desired.width;
234 best_format->height = desired.height;
235 best_format->fourcc = FOURCC_I420;
236 best_format->interval = desired.interval;
237 LOG(LS_INFO) << "Failed to find best capture format,"
238 << " fall back to the requested format "
239 << best_format->ToString();
240 }
241 return true;
242}
243
244CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
245 if (!module_) {
246 LOG(LS_ERROR) << "The capturer has not been initialized";
247 return CS_NO_DEVICE;
248 }
249
250 // TODO(hellner): weird to return failure when it is in fact actually running.
251 if (IsRunning()) {
252 LOG(LS_ERROR) << "The capturer is already running";
253 return CS_FAILED;
254 }
255
256 SetCaptureFormat(&capture_format);
257
258 webrtc::VideoCaptureCapability cap;
259 if (!FormatToCapability(capture_format, &cap)) {
260 LOG(LS_ERROR) << "Invalid capture format specified";
261 return CS_FAILED;
262 }
263
264 std::string camera_id(GetId());
265 uint32 start = talk_base::Time();
266 if (module_->RegisterCaptureDataCallback(*this) != 0 ||
267 module_->StartCapture(cap) != 0) {
268 LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start";
269 return CS_FAILED;
270 }
271
272 LOG(LS_INFO) << "Camera '" << camera_id << "' started with format "
273 << capture_format.ToString() << ", elapsed time "
274 << talk_base::TimeSince(start) << " ms";
275
276 captured_frames_ = 0;
277 SetCaptureState(CS_RUNNING);
278 return CS_STARTING;
279}
280
281void WebRtcVideoCapturer::Stop() {
282 if (IsRunning()) {
283 talk_base::Thread::Current()->Clear(this);
284 module_->StopCapture();
285 module_->DeRegisterCaptureDataCallback();
286
287 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
288 double drop_ratio = 0.0;
289 std::string camera_id(GetId());
290 LOG(LS_INFO) << "Camera '" << camera_id << "' stopped after capturing "
291 << captured_frames_ << " frames and dropping "
292 << drop_ratio << "%";
293 }
294 SetCaptureFormat(NULL);
295}
296
297bool WebRtcVideoCapturer::IsRunning() {
298 return (module_ != NULL && module_->CaptureStarted());
299}
300
301bool WebRtcVideoCapturer::GetPreferredFourccs(
302 std::vector<uint32>* fourccs) {
303 if (!fourccs) {
304 return false;
305 }
306
307 fourccs->clear();
308 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
309 fourccs->push_back(kSupportedFourCCs[i].fourcc);
310 }
311 return true;
312}
313
314void WebRtcVideoCapturer::OnIncomingCapturedFrame(const int32_t id,
315 webrtc::I420VideoFrame& sample) {
316 ASSERT(IsRunning());
317
318 ++captured_frames_;
319 // Log the size and pixel aspect ratio of the first captured frame.
320 if (1 == captured_frames_) {
321 LOG(LS_INFO) << "Captured frame size "
322 << sample.width() << "x" << sample.height()
323 << ". Expected format " << GetCaptureFormat()->ToString();
324 }
325
326 // Signal down stream components on captured frame.
327 // The CapturedFrame class doesn't support planes. We have to ExtractBuffer
328 // to one block for it.
329 int length = webrtc::CalcBufferSize(webrtc::kI420,
330 sample.width(), sample.height());
331 if (!captured_frame_.get() ||
332 captured_frame_->length() != static_cast<size_t>(length)) {
333 captured_frame_.reset(new FrameBuffer(length));
334 }
335 // TODO(ronghuawu): Refactor the WebRtcVideoFrame to avoid memory copy.
336 webrtc::ExtractBuffer(sample, length,
337 reinterpret_cast<uint8_t*>(captured_frame_->data()));
338 WebRtcCapturedFrame frame(sample, captured_frame_->data(), length);
339 SignalFrameCaptured(this, &frame);
340}
341
342void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
343 const int32_t delay) {
344 LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
345}
346
347// WebRtcCapturedFrame
348WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::I420VideoFrame& sample,
349 void* buffer,
350 int length) {
351 width = sample.width();
352 height = sample.height();
353 fourcc = FOURCC_I420;
354 // TODO(hellner): Support pixel aspect ratio (for OSX).
355 pixel_width = 1;
356 pixel_height = 1;
357 // Convert units from VideoFrame RenderTimeMs to CapturedFrame (nanoseconds).
358 elapsed_time = sample.render_time_ms() * talk_base::kNumNanosecsPerMillisec;
359 time_stamp = elapsed_time;
360 data_size = length;
361 data = buffer;
362}
363
364} // namespace cricket
365
366#endif // HAVE_WEBRTC_VIDEO