michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: michael@0: #if defined(PR_LOG) michael@0: #error "This file must be #included before any IPDL-generated files or other files that #include prlog.h" michael@0: #endif michael@0: michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrefBranch.h" michael@0: michael@0: #include "CSFLog.h" michael@0: #include "prenv.h" michael@0: michael@0: #ifdef PR_LOGGING michael@0: static PRLogModuleInfo* michael@0: GetUserMediaLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("GetUserMedia"); michael@0: return sLog; michael@0: } michael@0: #endif michael@0: michael@0: #include "MediaEngineWebRTC.h" michael@0: #include "ImageContainer.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: #include "MediaEngineTabVideoSource.h" michael@0: #include "nsITabSource.h" michael@0: #include "MediaTrackConstraints.h" michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidJNIWrapper.h" michael@0: #include "AndroidBridge.h" michael@0: #endif michael@0: michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args) michael@0: michael@0: namespace mozilla { michael@0: michael@0: MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) michael@0: : mMutex("mozilla::MediaEngineWebRTC") michael@0: , mVideoEngine(nullptr) michael@0: , mVoiceEngine(nullptr) michael@0: , mVideoEngineInit(false) michael@0: , mAudioEngineInit(false) michael@0: , mHasTabVideoSource(false) michael@0: { michael@0: #ifndef MOZ_B2G_CAMERA michael@0: nsCOMPtr compMgr; michael@0: NS_GetComponentRegistrar(getter_AddRefs(compMgr)); michael@0: if (compMgr) { michael@0: compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource); michael@0: } michael@0: #else michael@0: AsyncLatencyLogger::Get()->AddRef(); michael@0: #endif michael@0: // XXX michael@0: gFarendObserver = new AudioOutputObserver(); michael@0: } michael@0: michael@0: void michael@0: MediaEngineWebRTC::EnumerateVideoDevices(nsTArray >* aVSources) michael@0: { michael@0: #ifdef MOZ_B2G_CAMERA michael@0: MutexAutoLock lock(mMutex); michael@0: michael@0: /** michael@0: * We still enumerate every time, in case a new device was plugged in since michael@0: * the last call. TODO: Verify that WebRTC actually does deal with hotplugging michael@0: * new devices (with or without new engine creation) and accordingly adjust. michael@0: * Enumeration is not neccessary if GIPS reports the same set of devices michael@0: * for a given instance of the engine. Likewise, if a device was plugged out, michael@0: * mVideoSources must be updated. michael@0: */ michael@0: int num = 0; michael@0: nsresult result; michael@0: result = ICameraControl::GetNumberOfCameras(num); michael@0: if (num <= 0 || result != NS_OK) { michael@0: return; michael@0: } michael@0: michael@0: for (int i = 0; i < num; i++) { michael@0: nsCString cameraName; michael@0: result = ICameraControl::GetCameraName(i, cameraName); michael@0: if (result != NS_OK) { michael@0: continue; michael@0: } michael@0: michael@0: nsRefPtr vSource; michael@0: NS_ConvertUTF8toUTF16 uuid(cameraName); michael@0: if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) { michael@0: // We've already seen this device, just append. michael@0: aVSources->AppendElement(vSource.get()); michael@0: } else { michael@0: vSource = new MediaEngineWebRTCVideoSource(i); michael@0: mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. michael@0: aVSources->AppendElement(vSource); michael@0: } michael@0: } michael@0: michael@0: return; michael@0: #else michael@0: ScopedCustomReleasePtr ptrViEBase; michael@0: ScopedCustomReleasePtr ptrViECapture; michael@0: michael@0: // We spawn threads to handle gUM runnables, so we must protect the member vars michael@0: MutexAutoLock lock(mMutex); michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); michael@0: michael@0: // get the JVM michael@0: JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); michael@0: michael@0: if (webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context) != 0) { michael@0: LOG(("VieCapture:SetAndroidObjects Failed")); michael@0: return; michael@0: } michael@0: #endif michael@0: if (!mVideoEngine) { michael@0: if (!(mVideoEngine = webrtc::VideoEngine::Create())) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: PRLogModuleInfo *logs = GetWebRTCLogInfo(); michael@0: if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { michael@0: // no need to a critical section or lock here michael@0: gWebrtcTraceLoggingOn = 1; michael@0: michael@0: const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); michael@0: if (!file) { michael@0: file = "WebRTC.log"; michael@0: } michael@0: michael@0: LOG(("%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level)); michael@0: michael@0: mVideoEngine->SetTraceFilter(logs->level); michael@0: mVideoEngine->SetTraceFile(file); michael@0: } michael@0: michael@0: ptrViEBase = webrtc::ViEBase::GetInterface(mVideoEngine); michael@0: if (!ptrViEBase) { michael@0: return; michael@0: } michael@0: michael@0: if (!mVideoEngineInit) { michael@0: if (ptrViEBase->Init() < 0) { michael@0: return; michael@0: } michael@0: mVideoEngineInit = true; michael@0: } michael@0: michael@0: ptrViECapture = webrtc::ViECapture::GetInterface(mVideoEngine); michael@0: if (!ptrViECapture) { michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * We still enumerate every time, in case a new device was plugged in since michael@0: * the last call. TODO: Verify that WebRTC actually does deal with hotplugging michael@0: * new devices (with or without new engine creation) and accordingly adjust. michael@0: * Enumeration is not neccessary if GIPS reports the same set of devices michael@0: * for a given instance of the engine. Likewise, if a device was plugged out, michael@0: * mVideoSources must be updated. michael@0: */ michael@0: int num = ptrViECapture->NumberOfCaptureDevices(); michael@0: if (num <= 0) { michael@0: return; michael@0: } michael@0: michael@0: for (int i = 0; i < num; i++) { michael@0: const unsigned int kMaxDeviceNameLength = 128; // XXX FIX! michael@0: const unsigned int kMaxUniqueIdLength = 256; michael@0: char deviceName[kMaxDeviceNameLength]; michael@0: char uniqueId[kMaxUniqueIdLength]; michael@0: michael@0: // paranoia michael@0: deviceName[0] = '\0'; michael@0: uniqueId[0] = '\0'; michael@0: int error = ptrViECapture->GetCaptureDevice(i, deviceName, michael@0: sizeof(deviceName), uniqueId, michael@0: sizeof(uniqueId)); michael@0: michael@0: if (error) { michael@0: LOG((" VieCapture:GetCaptureDevice: Failed %d", michael@0: ptrViEBase->LastError() )); michael@0: continue; michael@0: } michael@0: #ifdef DEBUG michael@0: LOG((" Capture Device Index %d, Name %s", i, deviceName)); michael@0: michael@0: webrtc::CaptureCapability cap; michael@0: int numCaps = ptrViECapture->NumberOfCapabilities(uniqueId, kMaxUniqueIdLength); michael@0: LOG(("Number of Capabilities %d", numCaps)); michael@0: for (int j = 0; j < numCaps; j++) { michael@0: if (ptrViECapture->GetCaptureCapability(uniqueId, kMaxUniqueIdLength, michael@0: j, cap ) != 0 ) { michael@0: break; michael@0: } michael@0: LOG(("type=%d width=%d height=%d maxFPS=%d", michael@0: cap.rawType, cap.width, cap.height, cap.maxFPS )); michael@0: } michael@0: #endif michael@0: michael@0: if (uniqueId[0] == '\0') { michael@0: // In case a device doesn't set uniqueId! michael@0: strncpy(uniqueId, deviceName, sizeof(uniqueId)); michael@0: uniqueId[sizeof(uniqueId)-1] = '\0'; // strncpy isn't safe michael@0: } michael@0: michael@0: nsRefPtr vSource; michael@0: NS_ConvertUTF8toUTF16 uuid(uniqueId); michael@0: if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) { michael@0: // We've already seen this device, just append. michael@0: aVSources->AppendElement(vSource.get()); michael@0: } else { michael@0: vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i); michael@0: mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. michael@0: aVSources->AppendElement(vSource); michael@0: } michael@0: } michael@0: michael@0: if (mHasTabVideoSource) michael@0: aVSources->AppendElement(new MediaEngineTabVideoSource()); michael@0: michael@0: return; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MediaEngineWebRTC::EnumerateAudioDevices(nsTArray >* aASources) michael@0: { michael@0: ScopedCustomReleasePtr ptrVoEBase; michael@0: ScopedCustomReleasePtr ptrVoEHw; michael@0: // We spawn threads to handle gUM runnables, so we must protect the member vars michael@0: MutexAutoLock lock(mMutex); michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); michael@0: michael@0: // get the JVM michael@0: JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); michael@0: JNIEnv *env = GetJNIForThread(); michael@0: michael@0: if (webrtc::VoiceEngine::SetAndroidObjects(jvm, env, (void*)context) != 0) { michael@0: LOG(("VoiceEngine:SetAndroidObjects Failed")); michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: if (!mVoiceEngine) { michael@0: mVoiceEngine = webrtc::VoiceEngine::Create(); michael@0: if (!mVoiceEngine) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: PRLogModuleInfo *logs = GetWebRTCLogInfo(); michael@0: if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { michael@0: // no need to a critical section or lock here michael@0: gWebrtcTraceLoggingOn = 1; michael@0: michael@0: const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); michael@0: if (!file) { michael@0: file = "WebRTC.log"; michael@0: } michael@0: michael@0: LOG(("Logging webrtc to %s level %d", __FUNCTION__, file, logs->level)); michael@0: michael@0: mVoiceEngine->SetTraceFilter(logs->level); michael@0: mVoiceEngine->SetTraceFile(file); michael@0: } michael@0: michael@0: ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); michael@0: if (!ptrVoEBase) { michael@0: return; michael@0: } michael@0: michael@0: if (!mAudioEngineInit) { michael@0: if (ptrVoEBase->Init() < 0) { michael@0: return; michael@0: } michael@0: mAudioEngineInit = true; michael@0: } michael@0: michael@0: ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine); michael@0: if (!ptrVoEHw) { michael@0: return; michael@0: } michael@0: michael@0: int nDevices = 0; michael@0: ptrVoEHw->GetNumOfRecordingDevices(nDevices); michael@0: for (int i = 0; i < nDevices; i++) { michael@0: // We use constants here because GetRecordingDeviceName takes char[128]. michael@0: char deviceName[128]; michael@0: char uniqueId[128]; michael@0: // paranoia; jingle doesn't bother with this michael@0: deviceName[0] = '\0'; michael@0: uniqueId[0] = '\0'; michael@0: michael@0: int error = ptrVoEHw->GetRecordingDeviceName(i, deviceName, uniqueId); michael@0: if (error) { michael@0: LOG((" VoEHardware:GetRecordingDeviceName: Failed %d", michael@0: ptrVoEBase->LastError() )); michael@0: continue; michael@0: } michael@0: michael@0: if (uniqueId[0] == '\0') { michael@0: // Mac and Linux don't set uniqueId! michael@0: MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia michael@0: strcpy(uniqueId,deviceName); // safe given assert and initialization/error-check michael@0: } michael@0: michael@0: nsRefPtr aSource; michael@0: NS_ConvertUTF8toUTF16 uuid(uniqueId); michael@0: if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) { michael@0: // We've already seen this device, just append. michael@0: aASources->AppendElement(aSource.get()); michael@0: } else { michael@0: aSource = new MediaEngineWebRTCAudioSource( michael@0: mVoiceEngine, i, deviceName, uniqueId michael@0: ); michael@0: mAudioSources.Put(uuid, aSource); // Hashtable takes ownership. michael@0: aASources->AppendElement(aSource); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MediaEngineWebRTC::Shutdown() michael@0: { michael@0: // This is likely paranoia michael@0: MutexAutoLock lock(mMutex); michael@0: michael@0: if (mVideoEngine) { michael@0: mVideoSources.Clear(); michael@0: webrtc::VideoEngine::Delete(mVideoEngine); michael@0: } michael@0: michael@0: if (mVoiceEngine) { michael@0: mAudioSources.Clear(); michael@0: webrtc::VoiceEngine::Delete(mVoiceEngine); michael@0: } michael@0: michael@0: mVideoEngine = nullptr; michael@0: mVoiceEngine = nullptr; michael@0: } michael@0: michael@0: }