| /* | 
 |  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "webrtc/video_engine/vie_input_manager.h" | 
 |  | 
 | #include <assert.h> | 
 |  | 
 | #include "webrtc/common_types.h" | 
 | #include "webrtc/modules/video_capture/include/video_capture_factory.h" | 
 | #include "webrtc/modules/video_coding/main/interface/video_coding.h" | 
 | #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" | 
 | #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | 
 | #include "webrtc/system_wrappers/interface/logging.h" | 
 | #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" | 
 | #include "webrtc/video_engine/include/vie_errors.h" | 
 | #include "webrtc/video_engine/vie_capturer.h" | 
 | #include "webrtc/video_engine/vie_defines.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | ViEInputManager::ViEInputManager(const int engine_id, const Config& config) | 
 |     : config_(config), | 
 |       engine_id_(engine_id), | 
 |       map_cs_(CriticalSectionWrapper::CreateCriticalSection()), | 
 |       device_info_cs_(CriticalSectionWrapper::CreateCriticalSection()), | 
 |       vie_frame_provider_map_(), | 
 |       capture_device_info_(NULL), | 
 |       module_process_thread_(NULL) { | 
 |   for (int idx = 0; idx < kViEMaxCaptureDevices; idx++) { | 
 |     free_capture_device_id_[idx] = true; | 
 |   } | 
 | } | 
 |  | 
 | ViEInputManager::~ViEInputManager() { | 
 |   for (FrameProviderMap::iterator it = vie_frame_provider_map_.begin(); | 
 |        it != vie_frame_provider_map_.end(); | 
 |        ++it) { | 
 |     delete it->second; | 
 |   } | 
 |  | 
 |   delete capture_device_info_; | 
 | } | 
 | void ViEInputManager::SetModuleProcessThread( | 
 |     ProcessThread* module_process_thread) { | 
 |   assert(!module_process_thread_); | 
 |   module_process_thread_ = module_process_thread; | 
 | } | 
 |  | 
 | int ViEInputManager::NumberOfCaptureDevices() { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   return capture_device_info_->NumberOfDevices(); | 
 | } | 
 |  | 
 | int ViEInputManager::GetDeviceName(uint32_t device_number, | 
 |                                    char* device_nameUTF8, | 
 |                                    uint32_t device_name_length, | 
 |                                    char* device_unique_idUTF8, | 
 |                                    uint32_t device_unique_idUTF8Length) { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   return capture_device_info_->GetDeviceName(device_number, device_nameUTF8, | 
 |                                              device_name_length, | 
 |                                              device_unique_idUTF8, | 
 |                                              device_unique_idUTF8Length); | 
 | } | 
 |  | 
 | int ViEInputManager::NumberOfCaptureCapabilities( | 
 |   const char* device_unique_idUTF8) { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   return capture_device_info_->NumberOfCapabilities(device_unique_idUTF8); | 
 | } | 
 |  | 
 | int ViEInputManager::GetCaptureCapability( | 
 |     const char* device_unique_idUTF8, | 
 |     const uint32_t device_capability_number, | 
 |     CaptureCapability& capability) { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   VideoCaptureCapability module_capability; | 
 |   int result = capture_device_info_->GetCapability(device_unique_idUTF8, | 
 |                                                    device_capability_number, | 
 |                                                    module_capability); | 
 |   if (result != 0) | 
 |     return result; | 
 |  | 
 |   // Copy from module type to public type. | 
 |   capability.expectedCaptureDelay = module_capability.expectedCaptureDelay; | 
 |   capability.height = module_capability.height; | 
 |   capability.width = module_capability.width; | 
 |   capability.interlaced = module_capability.interlaced; | 
 |   capability.rawType = module_capability.rawType; | 
 |   capability.codecType = module_capability.codecType; | 
 |   capability.maxFPS = module_capability.maxFPS; | 
 |   return result; | 
 | } | 
 |  | 
 | int ViEInputManager::GetOrientation(const char* device_unique_idUTF8, | 
 |                                     RotateCapturedFrame& orientation) { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   VideoCaptureRotation module_orientation; | 
 |   int result = capture_device_info_->GetOrientation(device_unique_idUTF8, | 
 |                                                     module_orientation); | 
 |   // Copy from module type to public type. | 
 |   switch (module_orientation) { | 
 |     case kCameraRotate0: | 
 |       orientation = RotateCapturedFrame_0; | 
 |       break; | 
 |     case kCameraRotate90: | 
 |       orientation = RotateCapturedFrame_90; | 
 |       break; | 
 |     case kCameraRotate180: | 
 |       orientation = RotateCapturedFrame_180; | 
 |       break; | 
 |     case kCameraRotate270: | 
 |       orientation = RotateCapturedFrame_270; | 
 |       break; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | int ViEInputManager::DisplayCaptureSettingsDialogBox( | 
 |     const char* device_unique_idUTF8, | 
 |     const char* dialog_titleUTF8, | 
 |     void* parent_window, | 
 |     uint32_t positionX, | 
 |     uint32_t positionY) { | 
 |   CriticalSectionScoped cs(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   return capture_device_info_->DisplayCaptureSettingsDialogBox( | 
 |            device_unique_idUTF8, dialog_titleUTF8, parent_window, positionX, | 
 |            positionY); | 
 | } | 
 |  | 
 | int ViEInputManager::CreateCaptureDevice( | 
 |     const char* device_unique_idUTF8, | 
 |     const uint32_t device_unique_idUTF8Length, | 
 |     int& capture_id) { | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |  | 
 |   // Make sure the device is not already allocated. | 
 |   for (FrameProviderMap::iterator it = vie_frame_provider_map_.begin(); | 
 |        it != vie_frame_provider_map_.end(); | 
 |        ++it) { | 
 |     // Make sure this is a capture device. | 
 |     if (it->first >= kViECaptureIdBase && it->first <= kViECaptureIdMax) { | 
 |       ViECapturer* vie_capture = static_cast<ViECapturer*>(it->second); | 
 |       assert(vie_capture); | 
 |       // TODO(mflodman) Can we change input to avoid this cast? | 
 |       const char* device_name = | 
 |           reinterpret_cast<const char*>(vie_capture->CurrentDeviceName()); | 
 |       if (strncmp(device_name, device_unique_idUTF8, | 
 |                   strlen(device_name)) == 0) { | 
 |         return kViECaptureDeviceAlreadyAllocated; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Make sure the device name is valid. | 
 |   bool found_device = false; | 
 |   CriticalSectionScoped cs_devinfo(device_info_cs_.get()); | 
 |   if (capture_device_info_ == NULL) | 
 |     capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( | 
 |         ViEModuleId(engine_id_)); | 
 |   assert(capture_device_info_); | 
 |   for (uint32_t device_index = 0; | 
 |        device_index < capture_device_info_->NumberOfDevices(); ++device_index) { | 
 |     if (device_unique_idUTF8Length > kVideoCaptureUniqueNameLength) { | 
 |       // User's string length is longer than the max. | 
 |       return -1; | 
 |     } | 
 |  | 
 |     char found_name[kVideoCaptureDeviceNameLength] = ""; | 
 |     char found_unique_name[kVideoCaptureUniqueNameLength] = ""; | 
 |     capture_device_info_->GetDeviceName(device_index, found_name, | 
 |                                         kVideoCaptureDeviceNameLength, | 
 |                                         found_unique_name, | 
 |                                         kVideoCaptureUniqueNameLength); | 
 |  | 
 |     // TODO(mflodman) Can we change input to avoid this cast? | 
 |     const char* cast_id = reinterpret_cast<const char*>(device_unique_idUTF8); | 
 |     if (strncmp(cast_id, reinterpret_cast<const char*>(found_unique_name), | 
 |                 strlen(cast_id)) == 0) { | 
 |       found_device = true; | 
 |       break; | 
 |     } | 
 |   } | 
 |   if (!found_device) { | 
 |     LOG(LS_ERROR) << "Capture device not found: " << device_unique_idUTF8; | 
 |     return kViECaptureDeviceDoesNotExist; | 
 |   } | 
 |  | 
 |   int newcapture_id = 0; | 
 |   if (!GetFreeCaptureId(&newcapture_id)) { | 
 |     LOG(LS_ERROR) << "All capture devices already allocated."; | 
 |     return kViECaptureDeviceMaxNoDevicesAllocated; | 
 |   } | 
 |   ViECapturer* vie_capture = ViECapturer::CreateViECapture( | 
 |       newcapture_id, engine_id_, config_, device_unique_idUTF8, | 
 |       device_unique_idUTF8Length, *module_process_thread_); | 
 |   if (!vie_capture) { | 
 |     ReturnCaptureId(newcapture_id); | 
 |     return kViECaptureDeviceUnknownError; | 
 |   } | 
 |  | 
 |   vie_frame_provider_map_[newcapture_id] = vie_capture; | 
 |   capture_id = newcapture_id; | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEInputManager::CreateCaptureDevice(VideoCaptureModule* capture_module, | 
 |                                          int& capture_id) { | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |   int newcapture_id = 0; | 
 |   if (!GetFreeCaptureId(&newcapture_id)) { | 
 |     LOG(LS_ERROR) << "All capture devices already allocated."; | 
 |     return kViECaptureDeviceMaxNoDevicesAllocated; | 
 |   } | 
 |  | 
 |   ViECapturer* vie_capture = ViECapturer::CreateViECapture( | 
 |       newcapture_id, engine_id_, config_, | 
 |       capture_module, *module_process_thread_); | 
 |   if (!vie_capture) { | 
 |     ReturnCaptureId(newcapture_id); | 
 |     return kViECaptureDeviceUnknownError; | 
 |   } | 
 |   vie_frame_provider_map_[newcapture_id] = vie_capture; | 
 |   capture_id = newcapture_id; | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEInputManager::DestroyCaptureDevice(const int capture_id) { | 
 |   ViECapturer* vie_capture = NULL; | 
 |   { | 
 |     // We need exclusive access to the object to delete it. | 
 |     // Take this write lock first since the read lock is taken before map_cs_. | 
 |     ViEManagerWriteScoped wl(this); | 
 |     CriticalSectionScoped cs(map_cs_.get()); | 
 |  | 
 |     vie_capture = ViECapturePtr(capture_id); | 
 |     if (!vie_capture) { | 
 |       LOG(LS_ERROR) << "No such capture device id: " << capture_id; | 
 |       return -1; | 
 |     } | 
 |     uint32_t num_callbacks = | 
 |         vie_capture->NumberOfRegisteredFrameCallbacks(); | 
 |     if (num_callbacks > 0) { | 
 |       LOG(LS_WARNING) << num_callbacks << " still registered to capture id " | 
 |                       << capture_id << " when destroying capture device."; | 
 |     } | 
 |     vie_frame_provider_map_.erase(capture_id); | 
 |     ReturnCaptureId(capture_id); | 
 |     // Leave cs before deleting the capture object. This is because deleting the | 
 |     // object might cause deletions of renderers so we prefer to not have a lock | 
 |     // at that time. | 
 |   } | 
 |   delete vie_capture; | 
 |   return 0; | 
 | } | 
 |  | 
 | int ViEInputManager::CreateExternalCaptureDevice( | 
 |     ViEExternalCapture*& external_capture, | 
 |     int& capture_id) { | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |  | 
 |   int newcapture_id = 0; | 
 |   if (GetFreeCaptureId(&newcapture_id) == false) { | 
 |     LOG(LS_ERROR) << "All capture devices already allocated."; | 
 |     return kViECaptureDeviceMaxNoDevicesAllocated; | 
 |   } | 
 |  | 
 |   ViECapturer* vie_capture = ViECapturer::CreateViECapture( | 
 |       newcapture_id, engine_id_, config_, NULL, 0, *module_process_thread_); | 
 |   if (!vie_capture) { | 
 |     ReturnCaptureId(newcapture_id); | 
 |     return kViECaptureDeviceUnknownError; | 
 |   } | 
 |  | 
 |   vie_frame_provider_map_[newcapture_id] = vie_capture; | 
 |   capture_id = newcapture_id; | 
 |   external_capture = vie_capture; | 
 |   return 0; | 
 | } | 
 |  | 
 | bool ViEInputManager::GetFreeCaptureId(int* freecapture_id) { | 
 |   for (int id = 0; id < kViEMaxCaptureDevices; id++) { | 
 |     if (free_capture_device_id_[id]) { | 
 |       // We found a free capture device id. | 
 |       free_capture_device_id_[id] = false; | 
 |       *freecapture_id = id + kViECaptureIdBase; | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void ViEInputManager::ReturnCaptureId(int capture_id) { | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |   if (capture_id >= kViECaptureIdBase && | 
 |       capture_id < kViEMaxCaptureDevices + kViECaptureIdBase) { | 
 |     free_capture_device_id_[capture_id - kViECaptureIdBase] = true; | 
 |   } | 
 |   return; | 
 | } | 
 |  | 
 | ViEFrameProviderBase* ViEInputManager::ViEFrameProvider( | 
 |     const ViEFrameCallback* capture_observer) const { | 
 |   assert(capture_observer); | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |  | 
 |   for (FrameProviderMap::const_iterator it = vie_frame_provider_map_.begin(); | 
 |        it != vie_frame_provider_map_.end(); | 
 |        ++it) { | 
 |     if (it->second->IsFrameCallbackRegistered(capture_observer)) | 
 |       return it->second; | 
 |   } | 
 |  | 
 |   // No capture device set for this channel. | 
 |   return NULL; | 
 | } | 
 |  | 
 | ViEFrameProviderBase* ViEInputManager::ViEFrameProvider(int provider_id) const { | 
 |   CriticalSectionScoped cs(map_cs_.get()); | 
 |  | 
 |   FrameProviderMap::const_iterator it = | 
 |       vie_frame_provider_map_.find(provider_id); | 
 |   if (it == vie_frame_provider_map_.end()) | 
 |     return NULL; | 
 |   return it->second; | 
 | } | 
 |  | 
 | ViECapturer* ViEInputManager::ViECapturePtr(int capture_id) const { | 
 |   if (!(capture_id >= kViECaptureIdBase && | 
 |         capture_id <= kViECaptureIdBase + kViEMaxCaptureDevices)) { | 
 |     LOG(LS_ERROR) << "Capture device doesn't exist " << capture_id << "."; | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   return static_cast<ViECapturer*>(ViEFrameProvider(capture_id)); | 
 | } | 
 |  | 
 | ViEInputManagerScoped::ViEInputManagerScoped( | 
 |     const ViEInputManager& vie_input_manager) | 
 |     : ViEManagerScopedBase(vie_input_manager) { | 
 | } | 
 |  | 
 | ViECapturer* ViEInputManagerScoped::Capture(int capture_id) const { | 
 |   return static_cast<const ViEInputManager*>(vie_manager_)->ViECapturePtr( | 
 |       capture_id); | 
 | } | 
 |  | 
 | ViEFrameProviderBase* ViEInputManagerScoped::FrameProvider( | 
 |     const ViEFrameCallback* capture_observer) const { | 
 |   return static_cast<const ViEInputManager*>(vie_manager_)->ViEFrameProvider( | 
 |       capture_observer); | 
 | } | 
 |  | 
 | ViEFrameProviderBase* ViEInputManagerScoped::FrameProvider( | 
 |     int provider_id) const { | 
 |   return static_cast<const ViEInputManager*>(vie_manager_)->ViEFrameProvider( | 
 |       provider_id); | 
 | } | 
 |  | 
 | }  // namespace webrtc |