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