Home | History | Annotate | Download | only in webrtc
      1 /*
      2  * libjingle
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     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/media/webrtc/webrtcvideoframe.h"
     36 #include "talk/media/webrtc/webrtcvideoframefactory.h"
     37 #include "webrtc/base/arraysize.h"
     38 #include "webrtc/base/bind.h"
     39 #include "webrtc/base/checks.h"
     40 #include "webrtc/base/criticalsection.h"
     41 #include "webrtc/base/logging.h"
     42 #include "webrtc/base/safe_conversions.h"
     43 #include "webrtc/base/thread.h"
     44 #include "webrtc/base/timeutils.h"
     45 
     46 #include "webrtc/base/win32.h"  // Need this to #include the impl files.
     47 #include "webrtc/modules/video_capture/video_capture_factory.h"
     48 #include "webrtc/system_wrappers/include/field_trial.h"
     49 
     50 namespace cricket {
     51 
     52 struct kVideoFourCCEntry {
     53   uint32_t fourcc;
     54   webrtc::RawVideoType webrtc_type;
     55 };
     56 
     57 // This indicates our format preferences and defines a mapping between
     58 // webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
     59 static kVideoFourCCEntry kSupportedFourCCs[] = {
     60   { FOURCC_I420, webrtc::kVideoI420 },   // 12 bpp, no conversion.
     61   { FOURCC_YV12, webrtc::kVideoYV12 },   // 12 bpp, no conversion.
     62   { FOURCC_YUY2, webrtc::kVideoYUY2 },   // 16 bpp, fast conversion.
     63   { FOURCC_UYVY, webrtc::kVideoUYVY },   // 16 bpp, fast conversion.
     64   { FOURCC_NV12, webrtc::kVideoNV12 },   // 12 bpp, fast conversion.
     65   { FOURCC_NV21, webrtc::kVideoNV21 },   // 12 bpp, fast conversion.
     66   { FOURCC_MJPG, webrtc::kVideoMJPEG },  // compressed, slow conversion.
     67   { FOURCC_ARGB, webrtc::kVideoARGB },   // 32 bpp, slow conversion.
     68   { FOURCC_24BG, webrtc::kVideoRGB24 },  // 24 bpp, slow conversion.
     69 };
     70 
     71 class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
     72  public:
     73   virtual webrtc::VideoCaptureModule* Create(int id, const char* device) {
     74     return webrtc::VideoCaptureFactory::Create(id, device);
     75   }
     76   virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
     77     return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
     78   }
     79   virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
     80     delete info;
     81   }
     82 };
     83 
     84 static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
     85                                VideoFormat* format) {
     86   uint32_t fourcc = 0;
     87   for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
     88     if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
     89       fourcc = kSupportedFourCCs[i].fourcc;
     90       break;
     91     }
     92   }
     93   if (fourcc == 0) {
     94     return false;
     95   }
     96 
     97   format->fourcc = fourcc;
     98   format->width = cap.width;
     99   format->height = cap.height;
    100   format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
    101   return true;
    102 }
    103 
    104 static bool FormatToCapability(const VideoFormat& format,
    105                                webrtc::VideoCaptureCapability* cap) {
    106   webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
    107   for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
    108     if (kSupportedFourCCs[i].fourcc == format.fourcc) {
    109       webrtc_type = kSupportedFourCCs[i].webrtc_type;
    110       break;
    111     }
    112   }
    113   if (webrtc_type == webrtc::kVideoUnknown) {
    114     return false;
    115   }
    116 
    117   cap->width = format.width;
    118   cap->height = format.height;
    119   cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
    120   cap->expectedCaptureDelay = 0;
    121   cap->rawType = webrtc_type;
    122   cap->codecType = webrtc::kVideoCodecUnknown;
    123   cap->interlaced = false;
    124   return true;
    125 }
    126 
    127 ///////////////////////////////////////////////////////////////////////////
    128 // Implementation of class WebRtcVideoCapturer
    129 ///////////////////////////////////////////////////////////////////////////
    130 
    131 WebRtcVideoCapturer::WebRtcVideoCapturer()
    132     : factory_(new WebRtcVcmFactory),
    133       module_(nullptr),
    134       captured_frames_(0),
    135       start_thread_(nullptr),
    136       async_invoker_(nullptr) {
    137   set_frame_factory(new WebRtcVideoFrameFactory());
    138 }
    139 
    140 WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
    141     : factory_(factory),
    142       module_(nullptr),
    143       captured_frames_(0),
    144       start_thread_(nullptr),
    145       async_invoker_(nullptr) {
    146   set_frame_factory(new WebRtcVideoFrameFactory());
    147 }
    148 
    149 WebRtcVideoCapturer::~WebRtcVideoCapturer() {
    150   if (module_) {
    151     module_->Release();
    152   }
    153 }
    154 
    155 bool WebRtcVideoCapturer::Init(const Device& device) {
    156   RTC_DCHECK(!start_thread_);
    157   if (module_) {
    158     LOG(LS_ERROR) << "The capturer is already initialized";
    159     return false;
    160   }
    161 
    162   webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
    163   if (!info) {
    164     return false;
    165   }
    166 
    167   // Find the desired camera, by name.
    168   // In the future, comparing IDs will be more robust.
    169   // TODO(juberti): Figure what's needed to allow this.
    170   int num_cams = info->NumberOfDevices();
    171   char vcm_id[256] = "";
    172   bool found = false;
    173   for (int index = 0; index < num_cams; ++index) {
    174     char vcm_name[256];
    175     if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
    176                             arraysize(vcm_id)) != -1) {
    177       if (device.name == reinterpret_cast<char*>(vcm_name)) {
    178         found = true;
    179         break;
    180       }
    181     }
    182   }
    183   if (!found) {
    184     LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
    185     factory_->DestroyDeviceInfo(info);
    186     return false;
    187   }
    188 
    189   // Enumerate the supported formats.
    190   // TODO(juberti): Find out why this starts/stops the camera...
    191   std::vector<VideoFormat> supported;
    192   int32_t num_caps = info->NumberOfCapabilities(vcm_id);
    193   for (int32_t i = 0; i < num_caps; ++i) {
    194     webrtc::VideoCaptureCapability cap;
    195     if (info->GetCapability(vcm_id, i, cap) != -1) {
    196       VideoFormat format;
    197       if (CapabilityToFormat(cap, &format)) {
    198         supported.push_back(format);
    199       } else {
    200         LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
    201                         << cap.rawType;
    202       }
    203     }
    204   }
    205   factory_->DestroyDeviceInfo(info);
    206 
    207   if (supported.empty()) {
    208     LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
    209     return false;
    210   }
    211 
    212   module_ = factory_->Create(0, vcm_id);
    213   if (!module_) {
    214     LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
    215     return false;
    216   }
    217 
    218   // It is safe to change member attributes now.
    219   module_->AddRef();
    220   SetId(device.id);
    221   SetSupportedFormats(supported);
    222 
    223   // Ensure these 2 have the same value.
    224   SetApplyRotation(module_->GetApplyRotation());
    225 
    226   return true;
    227 }
    228 
    229 bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
    230   RTC_DCHECK(!start_thread_);
    231   if (module_) {
    232     LOG(LS_ERROR) << "The capturer is already initialized";
    233     return false;
    234   }
    235   if (!module) {
    236     LOG(LS_ERROR) << "Invalid VCM supplied";
    237     return false;
    238   }
    239   // TODO(juberti): Set id and formats.
    240   (module_ = module)->AddRef();
    241   return true;
    242 }
    243 
    244 bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
    245                                                VideoFormat* best_format) {
    246   if (!best_format) {
    247     return false;
    248   }
    249 
    250   if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
    251     // We maybe using a manually injected VCM which doesn't support enum.
    252     // Use the desired format as the best format.
    253     best_format->width = desired.width;
    254     best_format->height = desired.height;
    255     best_format->fourcc = FOURCC_I420;
    256     best_format->interval = desired.interval;
    257     LOG(LS_INFO) << "Failed to find best capture format,"
    258                  << " fall back to the requested format "
    259                  << best_format->ToString();
    260   }
    261   return true;
    262 }
    263 bool WebRtcVideoCapturer::SetApplyRotation(bool enable) {
    264   // Can't take lock here as this will cause deadlock with
    265   // OnIncomingCapturedFrame. In fact, the whole method, including methods it
    266   // calls, can't take lock.
    267   RTC_DCHECK(module_);
    268 
    269   const std::string group_name =
    270       webrtc::field_trial::FindFullName("WebRTC-CVO");
    271 
    272   if (group_name == "Disabled") {
    273     return true;
    274   }
    275 
    276   if (!VideoCapturer::SetApplyRotation(enable)) {
    277     return false;
    278   }
    279   return module_->SetApplyRotation(enable);
    280 }
    281 
    282 CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
    283   if (!module_) {
    284     LOG(LS_ERROR) << "The capturer has not been initialized";
    285     return CS_NO_DEVICE;
    286   }
    287   if (start_thread_) {
    288     LOG(LS_ERROR) << "The capturer is already running";
    289     RTC_DCHECK(start_thread_->IsCurrent())
    290         << "Trying to start capturer on different threads";
    291     return CS_FAILED;
    292   }
    293 
    294   start_thread_ = rtc::Thread::Current();
    295   RTC_DCHECK(!async_invoker_);
    296   async_invoker_.reset(new rtc::AsyncInvoker());
    297   captured_frames_ = 0;
    298 
    299   SetCaptureFormat(&capture_format);
    300 
    301   webrtc::VideoCaptureCapability cap;
    302   if (!FormatToCapability(capture_format, &cap)) {
    303     LOG(LS_ERROR) << "Invalid capture format specified";
    304     return CS_FAILED;
    305   }
    306 
    307   uint32_t start = rtc::Time();
    308   module_->RegisterCaptureDataCallback(*this);
    309   if (module_->StartCapture(cap) != 0) {
    310     LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
    311     module_->DeRegisterCaptureDataCallback();
    312     async_invoker_.reset();
    313     SetCaptureFormat(nullptr);
    314     start_thread_ = nullptr;
    315     return CS_FAILED;
    316   }
    317 
    318   LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
    319                << capture_format.ToString() << ", elapsed time "
    320                << rtc::TimeSince(start) << " ms";
    321 
    322   SetCaptureState(CS_RUNNING);
    323   return CS_STARTING;
    324 }
    325 
    326 void WebRtcVideoCapturer::Stop() {
    327   if (!start_thread_) {
    328     LOG(LS_ERROR) << "The capturer is already stopped";
    329     return;
    330   }
    331   RTC_DCHECK(start_thread_);
    332   RTC_DCHECK(start_thread_->IsCurrent());
    333   RTC_DCHECK(async_invoker_);
    334   if (IsRunning()) {
    335     // The module is responsible for OnIncomingCapturedFrame being called, if
    336     // we stop it we will get no further callbacks.
    337     module_->StopCapture();
    338   }
    339   module_->DeRegisterCaptureDataCallback();
    340 
    341   // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
    342   double drop_ratio = 0.0;
    343   LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
    344                << captured_frames_ << " frames and dropping "
    345                << drop_ratio << "%";
    346 
    347   // Clear any pending async invokes (that OnIncomingCapturedFrame may have
    348   // caused).
    349   async_invoker_.reset();
    350 
    351   SetCaptureFormat(NULL);
    352   start_thread_ = nullptr;
    353   SetCaptureState(CS_STOPPED);
    354 }
    355 
    356 bool WebRtcVideoCapturer::IsRunning() {
    357   return (module_ != NULL && module_->CaptureStarted());
    358 }
    359 
    360 bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
    361   if (!fourccs) {
    362     return false;
    363   }
    364 
    365   fourccs->clear();
    366   for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
    367     fourccs->push_back(kSupportedFourCCs[i].fourcc);
    368   }
    369   return true;
    370 }
    371 
    372 void WebRtcVideoCapturer::OnIncomingCapturedFrame(
    373     const int32_t id,
    374     const webrtc::VideoFrame& sample) {
    375   // This can only happen between Start() and Stop().
    376   RTC_DCHECK(start_thread_);
    377   RTC_DCHECK(async_invoker_);
    378   if (start_thread_->IsCurrent()) {
    379     SignalFrameCapturedOnStartThread(sample);
    380   } else {
    381     // This currently happens on with at least VideoCaptureModuleV4L2 and
    382     // possibly other implementations of WebRTC's VideoCaptureModule.
    383     // In order to maintain the threading contract with the upper layers and
    384     // consistency with other capturers such as in Chrome, we need to do a
    385     // thread hop.
    386     // Note that Stop() can cause the async invoke call to be cancelled.
    387     async_invoker_->AsyncInvoke<void>(
    388         start_thread_,
    389         // Note that Bind captures by value, so there's an intermediate copy
    390         // of sample.
    391         rtc::Bind(&WebRtcVideoCapturer::SignalFrameCapturedOnStartThread, this,
    392                   sample));
    393   }
    394 }
    395 
    396 void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
    397                                                 const int32_t delay) {
    398   LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
    399 }
    400 
    401 void WebRtcVideoCapturer::SignalFrameCapturedOnStartThread(
    402     const webrtc::VideoFrame& frame) {
    403   // This can only happen between Start() and Stop().
    404   RTC_DCHECK(start_thread_);
    405   RTC_DCHECK(start_thread_->IsCurrent());
    406   RTC_DCHECK(async_invoker_);
    407 
    408   ++captured_frames_;
    409   // Log the size and pixel aspect ratio of the first captured frame.
    410   if (1 == captured_frames_) {
    411     LOG(LS_INFO) << "Captured frame size "
    412                  << frame.width() << "x" << frame.height()
    413                  << ". Expected format " << GetCaptureFormat()->ToString();
    414   }
    415 
    416   // Signal down stream components on captured frame.
    417   // The CapturedFrame class doesn't support planes. We have to ExtractBuffer
    418   // to one block for it.
    419   size_t length =
    420       webrtc::CalcBufferSize(webrtc::kI420, frame.width(), frame.height());
    421   capture_buffer_.resize(length);
    422   // TODO(magjed): Refactor the WebRtcCapturedFrame to avoid memory copy or
    423   // take over ownership of the buffer held by |frame| if that's possible.
    424   webrtc::ExtractBuffer(frame, length, &capture_buffer_[0]);
    425   WebRtcCapturedFrame webrtc_frame(frame, &capture_buffer_[0], length);
    426   SignalFrameCaptured(this, &webrtc_frame);
    427 }
    428 
    429 // WebRtcCapturedFrame
    430 WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::VideoFrame& sample,
    431                                          void* buffer,
    432                                          size_t length) {
    433   width = sample.width();
    434   height = sample.height();
    435   fourcc = FOURCC_I420;
    436   // TODO(hellner): Support pixel aspect ratio (for OSX).
    437   pixel_width = 1;
    438   pixel_height = 1;
    439   // Convert units from VideoFrame RenderTimeMs to CapturedFrame (nanoseconds).
    440   time_stamp = sample.render_time_ms() * rtc::kNumNanosecsPerMillisec;
    441   data_size = rtc::checked_cast<uint32_t>(length);
    442   data = buffer;
    443   rotation = sample.rotation();
    444 }
    445 
    446 }  // namespace cricket
    447 
    448 #endif  // HAVE_WEBRTC_VIDEO
    449