|  | /* | 
|  | *  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 |