1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webrtc/MediaEngineWebRTC.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,357 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#ifdef MOZ_LOGGING 1.9 +#define FORCE_PR_LOG 1.10 +#endif 1.11 + 1.12 +#if defined(PR_LOG) 1.13 +#error "This file must be #included before any IPDL-generated files or other files that #include prlog.h" 1.14 +#endif 1.15 + 1.16 +#include "nsIPrefService.h" 1.17 +#include "nsIPrefBranch.h" 1.18 + 1.19 +#include "CSFLog.h" 1.20 +#include "prenv.h" 1.21 + 1.22 +#ifdef PR_LOGGING 1.23 +static PRLogModuleInfo* 1.24 +GetUserMediaLog() 1.25 +{ 1.26 + static PRLogModuleInfo *sLog; 1.27 + if (!sLog) 1.28 + sLog = PR_NewLogModule("GetUserMedia"); 1.29 + return sLog; 1.30 +} 1.31 +#endif 1.32 + 1.33 +#include "MediaEngineWebRTC.h" 1.34 +#include "ImageContainer.h" 1.35 +#include "nsIComponentRegistrar.h" 1.36 +#include "MediaEngineTabVideoSource.h" 1.37 +#include "nsITabSource.h" 1.38 +#include "MediaTrackConstraints.h" 1.39 + 1.40 +#ifdef MOZ_WIDGET_ANDROID 1.41 +#include "AndroidJNIWrapper.h" 1.42 +#include "AndroidBridge.h" 1.43 +#endif 1.44 + 1.45 +#undef LOG 1.46 +#define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args) 1.47 + 1.48 +namespace mozilla { 1.49 + 1.50 +MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) 1.51 + : mMutex("mozilla::MediaEngineWebRTC") 1.52 + , mVideoEngine(nullptr) 1.53 + , mVoiceEngine(nullptr) 1.54 + , mVideoEngineInit(false) 1.55 + , mAudioEngineInit(false) 1.56 + , mHasTabVideoSource(false) 1.57 +{ 1.58 +#ifndef MOZ_B2G_CAMERA 1.59 + nsCOMPtr<nsIComponentRegistrar> compMgr; 1.60 + NS_GetComponentRegistrar(getter_AddRefs(compMgr)); 1.61 + if (compMgr) { 1.62 + compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource); 1.63 + } 1.64 +#else 1.65 + AsyncLatencyLogger::Get()->AddRef(); 1.66 +#endif 1.67 + // XXX 1.68 + gFarendObserver = new AudioOutputObserver(); 1.69 +} 1.70 + 1.71 +void 1.72 +MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) 1.73 +{ 1.74 +#ifdef MOZ_B2G_CAMERA 1.75 + MutexAutoLock lock(mMutex); 1.76 + 1.77 + /** 1.78 + * We still enumerate every time, in case a new device was plugged in since 1.79 + * the last call. TODO: Verify that WebRTC actually does deal with hotplugging 1.80 + * new devices (with or without new engine creation) and accordingly adjust. 1.81 + * Enumeration is not neccessary if GIPS reports the same set of devices 1.82 + * for a given instance of the engine. Likewise, if a device was plugged out, 1.83 + * mVideoSources must be updated. 1.84 + */ 1.85 + int num = 0; 1.86 + nsresult result; 1.87 + result = ICameraControl::GetNumberOfCameras(num); 1.88 + if (num <= 0 || result != NS_OK) { 1.89 + return; 1.90 + } 1.91 + 1.92 + for (int i = 0; i < num; i++) { 1.93 + nsCString cameraName; 1.94 + result = ICameraControl::GetCameraName(i, cameraName); 1.95 + if (result != NS_OK) { 1.96 + continue; 1.97 + } 1.98 + 1.99 + nsRefPtr<MediaEngineWebRTCVideoSource> vSource; 1.100 + NS_ConvertUTF8toUTF16 uuid(cameraName); 1.101 + if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) { 1.102 + // We've already seen this device, just append. 1.103 + aVSources->AppendElement(vSource.get()); 1.104 + } else { 1.105 + vSource = new MediaEngineWebRTCVideoSource(i); 1.106 + mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. 1.107 + aVSources->AppendElement(vSource); 1.108 + } 1.109 + } 1.110 + 1.111 + return; 1.112 +#else 1.113 + ScopedCustomReleasePtr<webrtc::ViEBase> ptrViEBase; 1.114 + ScopedCustomReleasePtr<webrtc::ViECapture> ptrViECapture; 1.115 + 1.116 + // We spawn threads to handle gUM runnables, so we must protect the member vars 1.117 + MutexAutoLock lock(mMutex); 1.118 + 1.119 +#ifdef MOZ_WIDGET_ANDROID 1.120 + jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); 1.121 + 1.122 + // get the JVM 1.123 + JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); 1.124 + 1.125 + if (webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context) != 0) { 1.126 + LOG(("VieCapture:SetAndroidObjects Failed")); 1.127 + return; 1.128 + } 1.129 +#endif 1.130 + if (!mVideoEngine) { 1.131 + if (!(mVideoEngine = webrtc::VideoEngine::Create())) { 1.132 + return; 1.133 + } 1.134 + } 1.135 + 1.136 + PRLogModuleInfo *logs = GetWebRTCLogInfo(); 1.137 + if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { 1.138 + // no need to a critical section or lock here 1.139 + gWebrtcTraceLoggingOn = 1; 1.140 + 1.141 + const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); 1.142 + if (!file) { 1.143 + file = "WebRTC.log"; 1.144 + } 1.145 + 1.146 + LOG(("%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level)); 1.147 + 1.148 + mVideoEngine->SetTraceFilter(logs->level); 1.149 + mVideoEngine->SetTraceFile(file); 1.150 + } 1.151 + 1.152 + ptrViEBase = webrtc::ViEBase::GetInterface(mVideoEngine); 1.153 + if (!ptrViEBase) { 1.154 + return; 1.155 + } 1.156 + 1.157 + if (!mVideoEngineInit) { 1.158 + if (ptrViEBase->Init() < 0) { 1.159 + return; 1.160 + } 1.161 + mVideoEngineInit = true; 1.162 + } 1.163 + 1.164 + ptrViECapture = webrtc::ViECapture::GetInterface(mVideoEngine); 1.165 + if (!ptrViECapture) { 1.166 + return; 1.167 + } 1.168 + 1.169 + /** 1.170 + * We still enumerate every time, in case a new device was plugged in since 1.171 + * the last call. TODO: Verify that WebRTC actually does deal with hotplugging 1.172 + * new devices (with or without new engine creation) and accordingly adjust. 1.173 + * Enumeration is not neccessary if GIPS reports the same set of devices 1.174 + * for a given instance of the engine. Likewise, if a device was plugged out, 1.175 + * mVideoSources must be updated. 1.176 + */ 1.177 + int num = ptrViECapture->NumberOfCaptureDevices(); 1.178 + if (num <= 0) { 1.179 + return; 1.180 + } 1.181 + 1.182 + for (int i = 0; i < num; i++) { 1.183 + const unsigned int kMaxDeviceNameLength = 128; // XXX FIX! 1.184 + const unsigned int kMaxUniqueIdLength = 256; 1.185 + char deviceName[kMaxDeviceNameLength]; 1.186 + char uniqueId[kMaxUniqueIdLength]; 1.187 + 1.188 + // paranoia 1.189 + deviceName[0] = '\0'; 1.190 + uniqueId[0] = '\0'; 1.191 + int error = ptrViECapture->GetCaptureDevice(i, deviceName, 1.192 + sizeof(deviceName), uniqueId, 1.193 + sizeof(uniqueId)); 1.194 + 1.195 + if (error) { 1.196 + LOG((" VieCapture:GetCaptureDevice: Failed %d", 1.197 + ptrViEBase->LastError() )); 1.198 + continue; 1.199 + } 1.200 +#ifdef DEBUG 1.201 + LOG((" Capture Device Index %d, Name %s", i, deviceName)); 1.202 + 1.203 + webrtc::CaptureCapability cap; 1.204 + int numCaps = ptrViECapture->NumberOfCapabilities(uniqueId, kMaxUniqueIdLength); 1.205 + LOG(("Number of Capabilities %d", numCaps)); 1.206 + for (int j = 0; j < numCaps; j++) { 1.207 + if (ptrViECapture->GetCaptureCapability(uniqueId, kMaxUniqueIdLength, 1.208 + j, cap ) != 0 ) { 1.209 + break; 1.210 + } 1.211 + LOG(("type=%d width=%d height=%d maxFPS=%d", 1.212 + cap.rawType, cap.width, cap.height, cap.maxFPS )); 1.213 + } 1.214 +#endif 1.215 + 1.216 + if (uniqueId[0] == '\0') { 1.217 + // In case a device doesn't set uniqueId! 1.218 + strncpy(uniqueId, deviceName, sizeof(uniqueId)); 1.219 + uniqueId[sizeof(uniqueId)-1] = '\0'; // strncpy isn't safe 1.220 + } 1.221 + 1.222 + nsRefPtr<MediaEngineWebRTCVideoSource> vSource; 1.223 + NS_ConvertUTF8toUTF16 uuid(uniqueId); 1.224 + if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) { 1.225 + // We've already seen this device, just append. 1.226 + aVSources->AppendElement(vSource.get()); 1.227 + } else { 1.228 + vSource = new MediaEngineWebRTCVideoSource(mVideoEngine, i); 1.229 + mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. 1.230 + aVSources->AppendElement(vSource); 1.231 + } 1.232 + } 1.233 + 1.234 + if (mHasTabVideoSource) 1.235 + aVSources->AppendElement(new MediaEngineTabVideoSource()); 1.236 + 1.237 + return; 1.238 +#endif 1.239 +} 1.240 + 1.241 +void 1.242 +MediaEngineWebRTC::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) 1.243 +{ 1.244 + ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase; 1.245 + ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw; 1.246 + // We spawn threads to handle gUM runnables, so we must protect the member vars 1.247 + MutexAutoLock lock(mMutex); 1.248 + 1.249 +#ifdef MOZ_WIDGET_ANDROID 1.250 + jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef(); 1.251 + 1.252 + // get the JVM 1.253 + JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); 1.254 + JNIEnv *env = GetJNIForThread(); 1.255 + 1.256 + if (webrtc::VoiceEngine::SetAndroidObjects(jvm, env, (void*)context) != 0) { 1.257 + LOG(("VoiceEngine:SetAndroidObjects Failed")); 1.258 + return; 1.259 + } 1.260 +#endif 1.261 + 1.262 + if (!mVoiceEngine) { 1.263 + mVoiceEngine = webrtc::VoiceEngine::Create(); 1.264 + if (!mVoiceEngine) { 1.265 + return; 1.266 + } 1.267 + } 1.268 + 1.269 + PRLogModuleInfo *logs = GetWebRTCLogInfo(); 1.270 + if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { 1.271 + // no need to a critical section or lock here 1.272 + gWebrtcTraceLoggingOn = 1; 1.273 + 1.274 + const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); 1.275 + if (!file) { 1.276 + file = "WebRTC.log"; 1.277 + } 1.278 + 1.279 + LOG(("Logging webrtc to %s level %d", __FUNCTION__, file, logs->level)); 1.280 + 1.281 + mVoiceEngine->SetTraceFilter(logs->level); 1.282 + mVoiceEngine->SetTraceFile(file); 1.283 + } 1.284 + 1.285 + ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine); 1.286 + if (!ptrVoEBase) { 1.287 + return; 1.288 + } 1.289 + 1.290 + if (!mAudioEngineInit) { 1.291 + if (ptrVoEBase->Init() < 0) { 1.292 + return; 1.293 + } 1.294 + mAudioEngineInit = true; 1.295 + } 1.296 + 1.297 + ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine); 1.298 + if (!ptrVoEHw) { 1.299 + return; 1.300 + } 1.301 + 1.302 + int nDevices = 0; 1.303 + ptrVoEHw->GetNumOfRecordingDevices(nDevices); 1.304 + for (int i = 0; i < nDevices; i++) { 1.305 + // We use constants here because GetRecordingDeviceName takes char[128]. 1.306 + char deviceName[128]; 1.307 + char uniqueId[128]; 1.308 + // paranoia; jingle doesn't bother with this 1.309 + deviceName[0] = '\0'; 1.310 + uniqueId[0] = '\0'; 1.311 + 1.312 + int error = ptrVoEHw->GetRecordingDeviceName(i, deviceName, uniqueId); 1.313 + if (error) { 1.314 + LOG((" VoEHardware:GetRecordingDeviceName: Failed %d", 1.315 + ptrVoEBase->LastError() )); 1.316 + continue; 1.317 + } 1.318 + 1.319 + if (uniqueId[0] == '\0') { 1.320 + // Mac and Linux don't set uniqueId! 1.321 + MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia 1.322 + strcpy(uniqueId,deviceName); // safe given assert and initialization/error-check 1.323 + } 1.324 + 1.325 + nsRefPtr<MediaEngineWebRTCAudioSource> aSource; 1.326 + NS_ConvertUTF8toUTF16 uuid(uniqueId); 1.327 + if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) { 1.328 + // We've already seen this device, just append. 1.329 + aASources->AppendElement(aSource.get()); 1.330 + } else { 1.331 + aSource = new MediaEngineWebRTCAudioSource( 1.332 + mVoiceEngine, i, deviceName, uniqueId 1.333 + ); 1.334 + mAudioSources.Put(uuid, aSource); // Hashtable takes ownership. 1.335 + aASources->AppendElement(aSource); 1.336 + } 1.337 + } 1.338 +} 1.339 + 1.340 +void 1.341 +MediaEngineWebRTC::Shutdown() 1.342 +{ 1.343 + // This is likely paranoia 1.344 + MutexAutoLock lock(mMutex); 1.345 + 1.346 + if (mVideoEngine) { 1.347 + mVideoSources.Clear(); 1.348 + webrtc::VideoEngine::Delete(mVideoEngine); 1.349 + } 1.350 + 1.351 + if (mVoiceEngine) { 1.352 + mAudioSources.Clear(); 1.353 + webrtc::VoiceEngine::Delete(mVoiceEngine); 1.354 + } 1.355 + 1.356 + mVideoEngine = nullptr; 1.357 + mVoiceEngine = nullptr; 1.358 +} 1.359 + 1.360 +}