/* * Copyright (c) 2011 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 "video_engine/vie_input_manager.h" #include #include "common_types.h" #include "modules/video_capture/main/interface/video_capture_factory.h" #include "modules/video_coding/main/interface/video_coding.h" #include "modules/video_coding/main/interface/video_coding_defines.h" #include "system_wrappers/interface/critical_section_wrapper.h" #include "system_wrappers/interface/rw_lock_wrapper.h" #include "system_wrappers/interface/trace.h" #include "video_engine/include/vie_errors.h" #include "video_engine/vie_capturer.h" #include "video_engine/vie_defines.h" #include "video_engine/vie_file_player.h" namespace webrtc { ViEInputManager::ViEInputManager(const int engine_id) : engine_id_(engine_id), map_cs_(*CriticalSectionWrapper::CreateCriticalSection()), vie_frame_provider_map_(), capture_device_info_(NULL), module_process_thread_(NULL) { WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); for (int idx = 0; idx < kViEMaxCaptureDevices; idx++) { free_capture_device_id_[idx] = true; } capture_device_info_ = VideoCaptureFactory::CreateDeviceInfo( ViEModuleId(engine_id_)); for (int idx = 0; idx < kViEMaxFilePlayers; idx++) { free_file_id_[idx] = true; } } ViEInputManager::~ViEInputManager() { WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); while (vie_frame_provider_map_.Size() != 0) { MapItem* item = vie_frame_provider_map_.First(); assert(item); ViEFrameProviderBase* frame_provider = static_cast(item->GetItem()); vie_frame_provider_map_.Erase(item); delete frame_provider; } delete &map_cs_; if (capture_device_info_) { delete capture_device_info_; capture_device_info_ = NULL; } } void ViEInputManager::SetModuleProcessThread( ProcessThread& module_process_thread) { assert(!module_process_thread_); module_process_thread_ = &module_process_thread; } int ViEInputManager::NumberOfCaptureDevices() { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); assert(capture_device_info_); return capture_device_info_->NumberOfDevices(); } int ViEInputManager::GetDeviceName(WebRtc_UWord32 device_number, WebRtc_UWord8* device_nameUTF8, WebRtc_UWord32 device_name_length, WebRtc_UWord8* device_unique_idUTF8, WebRtc_UWord32 device_unique_idUTF8Length) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_number: %d)", __FUNCTION__, device_number); 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 WebRtc_UWord8* device_unique_idUTF8) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); assert(capture_device_info_); return capture_device_info_->NumberOfCapabilities(device_unique_idUTF8); } int ViEInputManager::GetCaptureCapability( const WebRtc_UWord8* device_unique_idUTF8, const WebRtc_UWord32 device_capability_number, CaptureCapability& capability) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_unique_idUTF8: %s, device_capability_number: %d)", __FUNCTION__, device_unique_idUTF8, device_capability_number); 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 WebRtc_UWord8* device_unique_idUTF8, RotateCapturedFrame& orientation) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_unique_idUTF8: %s,)", __FUNCTION__, device_unique_idUTF8); 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; default: assert(!"Unknown enum"); } return result; } int ViEInputManager::DisplayCaptureSettingsDialogBox( const WebRtc_UWord8* device_unique_idUTF8, const WebRtc_UWord8* dialog_titleUTF8, void* parent_window, WebRtc_UWord32 positionX, WebRtc_UWord32 positionY) { assert(capture_device_info_); return capture_device_info_->DisplayCaptureSettingsDialogBox( device_unique_idUTF8, dialog_titleUTF8, parent_window, positionX, positionY); } int ViEInputManager::CreateCaptureDevice( const WebRtc_UWord8* device_unique_idUTF8, const WebRtc_UWord32 device_unique_idUTF8Length, int& capture_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_unique_id: %s)", __FUNCTION__, device_unique_idUTF8); CriticalSectionScoped cs(map_cs_); // Make sure the device is not already allocated. for (MapItem* item = vie_frame_provider_map_.First(); item != NULL; item = vie_frame_provider_map_.Next(item)) { // Make sure this is a capture device. if (item->GetId() >= kViECaptureIdBase && item->GetId() <= kViECaptureIdMax) { ViECapturer* vie_capture = static_cast(item->GetItem()); assert(vie_capture); // TODO(mflodman) Can we change input to avoid this cast? const char* device_name = reinterpret_cast(vie_capture->CurrentDeviceName()); if (strncmp(device_name, reinterpret_cast(device_unique_idUTF8), strlen(device_name)) == 0) { return kViECaptureDeviceAlreadyAllocated; } } } // Make sure the device name is valid. bool found_device = false; for (WebRtc_UWord32 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; } WebRtc_UWord8 found_name[kVideoCaptureDeviceNameLength] = ""; WebRtc_UWord8 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(device_unique_idUTF8); if (strncmp(cast_id, reinterpret_cast(found_unique_name), strlen(cast_id)) == 0) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, ViEId(engine_id_), "%s:%d Capture device was found by unique ID: %s. Returning", __FUNCTION__, __LINE__, device_unique_idUTF8); found_device = true; break; } } if (!found_device) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, ViEId(engine_id_), "%s:%d Capture device NOT found by unique ID: %s. Returning", __FUNCTION__, __LINE__, device_unique_idUTF8); return kViECaptureDeviceDoesNotExist; } int newcapture_id = 0; if (GetFreeCaptureId(newcapture_id) == false) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Maximum supported number of capture devices already in " "use", __FUNCTION__); return kViECaptureDeviceMaxNoDevicesAllocated; } ViECapturer* vie_capture = ViECapturer::CreateViECapture( newcapture_id, engine_id_, device_unique_idUTF8, device_unique_idUTF8Length, *module_process_thread_); if (!vie_capture) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not create capture module for %s", __FUNCTION__, device_unique_idUTF8); return kViECaptureDeviceUnknownError; } if (vie_frame_provider_map_.Insert(newcapture_id, vie_capture) != 0) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not insert capture module for %s", __FUNCTION__, device_unique_idUTF8); return kViECaptureDeviceUnknownError; } capture_id = newcapture_id; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_unique_id: %s, capture_id: %d)", __FUNCTION__, device_unique_idUTF8, capture_id); return 0; } int ViEInputManager::CreateCaptureDevice(VideoCaptureModule& capture_module, int& capture_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); CriticalSectionScoped cs(map_cs_); int newcapture_id = 0; if (!GetFreeCaptureId(newcapture_id)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Maximum supported number of capture devices already in " "use", __FUNCTION__); return kViECaptureDeviceMaxNoDevicesAllocated; } ViECapturer* vie_capture = ViECapturer::CreateViECapture( newcapture_id, engine_id_, capture_module, *module_process_thread_); if (!vie_capture) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could attach capture module.", __FUNCTION__); return kViECaptureDeviceUnknownError; } if (vie_frame_provider_map_.Insert(newcapture_id, vie_capture) != 0) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not insert capture module", __FUNCTION__); return kViECaptureDeviceUnknownError; } capture_id = newcapture_id; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s, capture_id: %d", __FUNCTION__, capture_id); return 0; } int ViEInputManager::DestroyCaptureDevice(const int capture_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(capture_id: %d)", __FUNCTION__, 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_); vie_capture = ViECapturePtr(capture_id); if (!vie_capture) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s(capture_id: %d) - No such capture device id", __FUNCTION__, capture_id); return -1; } WebRtc_UWord32 num_callbacks = vie_capture->NumberOfRegisteredFrameCallbacks(); if (num_callbacks > 0) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(engine_id_), "%s(capture_id: %d) - %u registered " "callbacks when destroying capture device", __FUNCTION__, capture_id, num_callbacks); } 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) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); CriticalSectionScoped cs(map_cs_); int newcapture_id = 0; if (GetFreeCaptureId(newcapture_id) == false) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Maximum supported number of capture devices already in " "use", __FUNCTION__); return kViECaptureDeviceMaxNoDevicesAllocated; } ViECapturer* vie_capture = ViECapturer::CreateViECapture( newcapture_id, engine_id_, NULL, 0, *module_process_thread_); if (!vie_capture) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not create capture module for external capture.", __FUNCTION__); return kViECaptureDeviceUnknownError; } if (vie_frame_provider_map_.Insert(newcapture_id, vie_capture) != 0) { ReturnCaptureId(newcapture_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not insert capture module for external capture.", __FUNCTION__); return kViECaptureDeviceUnknownError; } capture_id = newcapture_id; external_capture = vie_capture; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s, capture_id: %d)", __FUNCTION__, capture_id); return 0; } int ViEInputManager::CreateFilePlayer(const WebRtc_Word8* file_nameUTF8, const bool loop, const webrtc::FileFormats file_format, VoiceEngine* voe_ptr, int& file_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(device_unique_id: %s)", __FUNCTION__, file_nameUTF8); CriticalSectionScoped cs(map_cs_); int new_file_id = 0; if (GetFreeFileId(new_file_id) == false) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Maximum supported number of file players already in use", __FUNCTION__); return kViEFileMaxNoOfFilesOpened; } ViEFilePlayer* vie_file_player = ViEFilePlayer::CreateViEFilePlayer( new_file_id, engine_id_, file_nameUTF8, loop, file_format, *this, voe_ptr); if (!vie_file_player) { ReturnFileId(new_file_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not open file %s for playback", __FUNCTION__, file_nameUTF8); return kViEFileUnknownError; } if (vie_frame_provider_map_.Insert(new_file_id, vie_file_player) != 0) { ReturnCaptureId(new_file_id); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s: Could not insert file player for %s", __FUNCTION__, file_nameUTF8); delete vie_file_player; return kViEFileUnknownError; } file_id = new_file_id; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(filename: %s, file_id: %d)", __FUNCTION__, file_nameUTF8, new_file_id); return 0; } int ViEInputManager::DestroyFilePlayer(int file_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(file_id: %d)", __FUNCTION__, file_id); ViEFilePlayer* vie_file_player = 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_); vie_file_player = ViEFilePlayerPtr(file_id); if (!vie_file_player) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(engine_id_), "%s(file_id: %d) - No such file player", __FUNCTION__, file_id); return -1; } int num_callbacks = vie_file_player->NumberOfRegisteredFrameCallbacks(); if (num_callbacks > 0) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideo, ViEId(engine_id_), "%s(file_id: %d) - %u registered " "callbacks when destroying file player", __FUNCTION__, file_id, num_callbacks); } vie_frame_provider_map_.Erase(file_id); ReturnFileId(file_id); // Leave cs before deleting the file 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_file_player; return 0; } bool ViEInputManager::GetFreeCaptureId(int& freecapture_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); 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; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s: new id: %d", __FUNCTION__, freecapture_id); return true; } } return false; } void ViEInputManager::ReturnCaptureId(int capture_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(%d)", __FUNCTION__, capture_id); CriticalSectionScoped cs(map_cs_); if (capture_id >= kViECaptureIdBase && capture_id < kViEMaxCaptureDevices + kViECaptureIdBase) { free_capture_device_id_[capture_id - kViECaptureIdBase] = true; } return; } bool ViEInputManager::GetFreeFileId(int& free_file_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s", __FUNCTION__); for (int id = 0; id < kViEMaxFilePlayers; id++) { if (free_file_id_[id]) { // We found a free capture device id. free_file_id_[id] = false; free_file_id = id + kViEFileIdBase; WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s: new id: %d", __FUNCTION__, free_file_id); return true; } } return false; } void ViEInputManager::ReturnFileId(int file_id) { WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(engine_id_), "%s(%d)", __FUNCTION__, file_id); CriticalSectionScoped cs(map_cs_); if (file_id >= kViEFileIdBase && file_id < kViEMaxFilePlayers + kViEFileIdBase) { free_file_id_[file_id - kViEFileIdBase] = true; } return; } ViEFrameProviderBase* ViEInputManager::ViEFrameProvider( const ViEFrameCallback* capture_observer) const { assert(capture_observer); CriticalSectionScoped cs(map_cs_); for (MapItem* provider_item = vie_frame_provider_map_.First(); provider_item != NULL; provider_item = vie_frame_provider_map_.Next(provider_item)) { ViEFrameProviderBase* vie_frame_provider = static_cast(provider_item->GetItem()); assert(vie_frame_provider != NULL); if (vie_frame_provider->IsFrameCallbackRegistered(capture_observer)) { // We found it. return vie_frame_provider; } } // No capture device set for this channel. return NULL; } ViEFrameProviderBase* ViEInputManager::ViEFrameProvider(int provider_id) const { CriticalSectionScoped cs(map_cs_); MapItem* map_item = vie_frame_provider_map_.Find(provider_id); if (!map_item) { return NULL; } ViEFrameProviderBase* vie_frame_provider = static_cast(map_item->GetItem()); return vie_frame_provider; } ViECapturer* ViEInputManager::ViECapturePtr(int capture_id) const { if (!(capture_id >= kViECaptureIdBase && capture_id <= kViECaptureIdBase + kViEMaxCaptureDevices)) return NULL; CriticalSectionScoped cs(map_cs_); MapItem* map_item = vie_frame_provider_map_.Find(capture_id); if (!map_item) { return NULL; } ViECapturer* vie_capture = static_cast(map_item->GetItem()); return vie_capture; } void ViEInputManager::GetViECaptures(MapWrapper& vie_capture_map) { CriticalSectionScoped cs(map_cs_); if (vie_frame_provider_map_.Size() == 0) { return; } // Add all items to the map. for (MapItem* item = vie_frame_provider_map_.First(); item != NULL; item = vie_frame_provider_map_.Next(item)) { vie_capture_map.Insert(item->GetId(), item->GetItem()); } return; } ViEFilePlayer* ViEInputManager::ViEFilePlayerPtr(int file_id) const { if (file_id < kViEFileIdBase || file_id > kViEFileIdMax) { return NULL; } CriticalSectionScoped cs(map_cs_); MapItem* map_item = vie_frame_provider_map_.Find(file_id); if (!map_item) { return NULL; } ViEFilePlayer* vie_file_player = static_cast(map_item->GetItem()); return vie_file_player; } ViEInputManagerScoped::ViEInputManagerScoped( const ViEInputManager& vie_input_manager) : ViEManagerScopedBase(vie_input_manager) { } ViECapturer* ViEInputManagerScoped::Capture(int capture_id) const { return static_cast(vie_manager_)->ViECapturePtr( capture_id); } ViEFrameProviderBase* ViEInputManagerScoped::FrameProvider( const ViEFrameCallback* capture_observer) const { return static_cast(vie_manager_)->ViEFrameProvider( capture_observer); } ViEFrameProviderBase* ViEInputManagerScoped::FrameProvider( int provider_id) const { return static_cast(vie_manager_)->ViEFrameProvider( provider_id); } ViEFilePlayer* ViEInputManagerScoped::FilePlayer(int file_id) const { return static_cast(vie_manager_)->ViEFilePlayerPtr( file_id); } } // namespace webrtc