LCOV - code coverage report
Current view: top level - dom/media/webrtc - MediaEngineWebRTC.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 167 1.2 %
Date: 2017-07-14 16:53:18 Functions: 2 9 22.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=8 et ft=cpp : */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsIPrefService.h"
       8             : #include "nsIPrefBranch.h"
       9             : 
      10             : #include "CSFLog.h"
      11             : #include "prenv.h"
      12             : 
      13             : #include "mozilla/Logging.h"
      14             : 
      15             : static mozilla::LazyLogModule sGetUserMediaLog("GetUserMedia");
      16             : 
      17             : #include "MediaEngineWebRTC.h"
      18             : #include "ImageContainer.h"
      19             : #include "nsIComponentRegistrar.h"
      20             : #include "MediaEngineTabVideoSource.h"
      21             : #include "MediaEngineRemoteVideoSource.h"
      22             : #include "CamerasChild.h"
      23             : #include "nsITabSource.h"
      24             : #include "MediaTrackConstraints.h"
      25             : 
      26             : #ifdef MOZ_WIDGET_ANDROID
      27             : #include "VideoEngine.h"
      28             : #include "AndroidJNIWrapper.h"
      29             : #include "AndroidBridge.h"
      30             : #endif
      31             : 
      32             : #undef LOG
      33             : #define LOG(args) MOZ_LOG(sGetUserMediaLog, mozilla::LogLevel::Debug, args)
      34             : 
      35             : namespace mozilla {
      36             : 
      37             : // statics from AudioInputCubeb
      38             : nsTArray<int>* AudioInputCubeb::mDeviceIndexes;
      39             : int AudioInputCubeb::mDefaultDevice = -1;
      40             : nsTArray<nsCString>* AudioInputCubeb::mDeviceNames;
      41             : cubeb_device_collection AudioInputCubeb::mDevices = { nullptr, 0 };
      42             : bool AudioInputCubeb::mAnyInUse = false;
      43           3 : StaticMutex AudioInputCubeb::sMutex;
      44             : uint32_t AudioInputCubeb::sUserChannelCount = 0;
      45             : 
      46             : // AudioDeviceID is an annoying opaque value that's really a string
      47             : // pointer, and is freed when the cubeb_device_collection is destroyed
      48             : 
      49           0 : void AudioInputCubeb::UpdateDeviceList()
      50             : {
      51           0 :   cubeb* cubebContext = CubebUtils::GetCubebContext();
      52           0 :   if (!cubebContext) {
      53           0 :     return;
      54             :   }
      55             : 
      56           0 :   cubeb_device_collection devices = { nullptr, 0 };
      57             : 
      58           0 :   if (CUBEB_OK != cubeb_enumerate_devices(cubebContext,
      59             :                                           CUBEB_DEVICE_TYPE_INPUT,
      60             :                                           &devices)) {
      61           0 :     return;
      62             :   }
      63             : 
      64           0 :   for (auto& device_index : (*mDeviceIndexes)) {
      65           0 :     device_index = -1; // unmapped
      66             :   }
      67             :   // We keep all the device names, but wipe the mappings and rebuild them
      68             : 
      69             :   // Calculate translation from existing mDevices to new devices. Note we
      70             :   // never end up with less devices than before, since people have
      71             :   // stashed indexes.
      72             :   // For some reason the "fake" device for automation is marked as DISABLED,
      73             :   // so white-list it.
      74           0 :   mDefaultDevice = -1;
      75           0 :   for (uint32_t i = 0; i < devices.count; i++) {
      76           0 :     LOG(("Cubeb device %u: type 0x%x, state 0x%x, name %s, id %p",
      77             :          i, devices.device[i].type, devices.device[i].state,
      78             :          devices.device[i].friendly_name, devices.device[i].device_id));
      79           0 :     if (devices.device[i].type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
      80           0 :         (devices.device[i].state == CUBEB_DEVICE_STATE_ENABLED ||
      81           0 :          (devices.device[i].state == CUBEB_DEVICE_STATE_DISABLED &&
      82           0 :           devices.device[i].friendly_name &&
      83           0 :           strcmp(devices.device[i].friendly_name, "Sine source at 440 Hz") == 0)))
      84             :     {
      85           0 :       auto j = mDeviceNames->IndexOf(devices.device[i].device_id);
      86           0 :       if (j != nsTArray<nsCString>::NoIndex) {
      87             :         // match! update the mapping
      88           0 :         (*mDeviceIndexes)[j] = i;
      89             :       } else {
      90             :         // new device, add to the array
      91           0 :         mDeviceIndexes->AppendElement(i);
      92           0 :         mDeviceNames->AppendElement(devices.device[i].device_id);
      93           0 :         j = mDeviceIndexes->Length()-1;
      94             :       }
      95           0 :       if (devices.device[i].preferred & CUBEB_DEVICE_PREF_VOICE) {
      96             :         // There can be only one... we hope
      97           0 :         NS_ASSERTION(mDefaultDevice == -1, "multiple default cubeb input devices!");
      98           0 :         mDefaultDevice = j;
      99             :       }
     100             :     }
     101             :   }
     102           0 :   LOG(("Cubeb default input device %d", mDefaultDevice));
     103           0 :   StaticMutexAutoLock lock(sMutex);
     104             :   // swap state
     105           0 :   cubeb_device_collection_destroy(cubebContext, &mDevices);
     106           0 :   mDevices = devices;
     107             : }
     108             : 
     109           0 : MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
     110             :   : mMutex("mozilla::MediaEngineWebRTC"),
     111             :     mVoiceEngine(nullptr),
     112             :     mAudioInput(nullptr),
     113           0 :     mFullDuplex(aPrefs.mFullDuplex),
     114           0 :     mHasTabVideoSource(false)
     115             : {
     116           0 :   nsCOMPtr<nsIComponentRegistrar> compMgr;
     117           0 :   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
     118           0 :   if (compMgr) {
     119           0 :     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
     120             :   }
     121             : 
     122           0 :   camera::GetChildAndCall(
     123             :     &camera::CamerasChild::AddDeviceChangeCallback,
     124           0 :     this);
     125           0 : }
     126             : 
     127             : void
     128           0 : MediaEngineWebRTC::SetFakeDeviceChangeEvents()
     129             : {
     130           0 :   camera::GetChildAndCall(
     131           0 :     &camera::CamerasChild::SetFakeDeviceChangeEvents);
     132           0 : }
     133             : 
     134             : void
     135           0 : MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
     136             :                                          nsTArray<RefPtr<MediaEngineVideoSource> >* aVSources)
     137             : {
     138             :   // We spawn threads to handle gUM runnables, so we must protect the member vars
     139           0 :   MutexAutoLock lock(mMutex);
     140             : 
     141           0 :   mozilla::camera::CaptureEngine capEngine = mozilla::camera::InvalidEngine;
     142             : 
     143             : #ifdef MOZ_WIDGET_ANDROID
     144             :   // get the JVM
     145             :   JavaVM* jvm;
     146             :   JNIEnv* const env = jni::GetEnvForThread();
     147             :   MOZ_ALWAYS_TRUE(!env->GetJavaVM(&jvm));
     148             : 
     149             :   if (!jvm || mozilla::camera::VideoEngine::SetAndroidObjects(jvm)) {
     150             :     LOG(("VideoEngine::SetAndroidObjects Failed"));
     151             :     return;
     152             :   }
     153             : #endif
     154           0 :   bool scaryKind = false; // flag sources with cross-origin exploit potential
     155             : 
     156           0 :   switch (aMediaSource) {
     157             :     case dom::MediaSourceEnum::Window:
     158           0 :       capEngine = mozilla::camera::WinEngine;
     159           0 :       break;
     160             :     case dom::MediaSourceEnum::Application:
     161           0 :       capEngine = mozilla::camera::AppEngine;
     162           0 :       break;
     163             :     case dom::MediaSourceEnum::Screen:
     164           0 :       capEngine = mozilla::camera::ScreenEngine;
     165           0 :       scaryKind = true;
     166           0 :       break;
     167             :     case dom::MediaSourceEnum::Browser:
     168           0 :       capEngine = mozilla::camera::BrowserEngine;
     169           0 :       scaryKind = true;
     170           0 :       break;
     171             :     case dom::MediaSourceEnum::Camera:
     172           0 :       capEngine = mozilla::camera::CameraEngine;
     173           0 :       break;
     174             :     default:
     175             :       // BOOM
     176           0 :       MOZ_CRASH("No valid video engine");
     177             :       break;
     178             :   }
     179             : 
     180             :   /**
     181             :    * We still enumerate every time, in case a new device was plugged in since
     182             :    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
     183             :    * new devices (with or without new engine creation) and accordingly adjust.
     184             :    * Enumeration is not neccessary if GIPS reports the same set of devices
     185             :    * for a given instance of the engine. Likewise, if a device was plugged out,
     186             :    * mVideoSources must be updated.
     187             :    */
     188             :   int num;
     189           0 :   num = mozilla::camera::GetChildAndCall(
     190             :     &mozilla::camera::CamerasChild::NumberOfCaptureDevices,
     191           0 :     capEngine);
     192             : 
     193           0 :   for (int i = 0; i < num; i++) {
     194             :     char deviceName[MediaEngineSource::kMaxDeviceNameLength];
     195             :     char uniqueId[MediaEngineSource::kMaxUniqueIdLength];
     196           0 :     bool scarySource = false;
     197             : 
     198             :     // paranoia
     199           0 :     deviceName[0] = '\0';
     200           0 :     uniqueId[0] = '\0';
     201             :     int error;
     202             : 
     203           0 :     error =  mozilla::camera::GetChildAndCall(
     204             :       &mozilla::camera::CamerasChild::GetCaptureDevice,
     205             :       capEngine,
     206             :       i, deviceName,
     207           0 :       sizeof(deviceName), uniqueId,
     208           0 :       sizeof(uniqueId),
     209           0 :       &scarySource);
     210           0 :     if (error) {
     211           0 :       LOG(("camera:GetCaptureDevice: Failed %d", error ));
     212           0 :       continue;
     213             :     }
     214             : #ifdef DEBUG
     215           0 :     LOG(("  Capture Device Index %d, Name %s", i, deviceName));
     216             : 
     217           0 :     webrtc::CaptureCapability cap;
     218           0 :     int numCaps = mozilla::camera::GetChildAndCall(
     219             :       &mozilla::camera::CamerasChild::NumberOfCapabilities,
     220             :       capEngine,
     221           0 :       uniqueId);
     222           0 :     LOG(("Number of Capabilities %d", numCaps));
     223           0 :     for (int j = 0; j < numCaps; j++) {
     224           0 :       if (mozilla::camera::GetChildAndCall(
     225             :             &mozilla::camera::CamerasChild::GetCaptureCapability,
     226             :             capEngine,
     227             :             uniqueId,
     228             :             j, cap) != 0) {
     229           0 :        break;
     230             :       }
     231           0 :       LOG(("type=%d width=%d height=%d maxFPS=%d",
     232             :            cap.rawType, cap.width, cap.height, cap.maxFPS ));
     233             :     }
     234             : #endif
     235             : 
     236           0 :     if (uniqueId[0] == '\0') {
     237             :       // In case a device doesn't set uniqueId!
     238           0 :       strncpy(uniqueId, deviceName, sizeof(uniqueId));
     239           0 :       uniqueId[sizeof(uniqueId)-1] = '\0'; // strncpy isn't safe
     240             :     }
     241             : 
     242           0 :     RefPtr<MediaEngineVideoSource> vSource;
     243           0 :     NS_ConvertUTF8toUTF16 uuid(uniqueId);
     244           0 :     if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
     245             :       // We've already seen this device, just refresh and append.
     246           0 :       static_cast<MediaEngineRemoteVideoSource*>(vSource.get())->Refresh(i);
     247           0 :       aVSources->AppendElement(vSource.get());
     248             :     } else {
     249             :       vSource = new MediaEngineRemoteVideoSource(i, capEngine, aMediaSource,
     250           0 :                                                  scaryKind || scarySource);
     251           0 :       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
     252           0 :       aVSources->AppendElement(vSource);
     253             :     }
     254             :   }
     255             : 
     256           0 :   if (mHasTabVideoSource || dom::MediaSourceEnum::Browser == aMediaSource) {
     257           0 :     aVSources->AppendElement(new MediaEngineTabVideoSource());
     258             :   }
     259           0 : }
     260             : 
     261             : bool
     262           0 : MediaEngineWebRTC::SupportsDuplex()
     263             : {
     264           0 :   return mFullDuplex;
     265             : }
     266             : 
     267             : void
     268           0 : MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
     269             :                                          nsTArray<RefPtr<MediaEngineAudioSource> >* aASources)
     270             : {
     271           0 :   ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
     272             :   // We spawn threads to handle gUM runnables, so we must protect the member vars
     273           0 :   MutexAutoLock lock(mMutex);
     274             : 
     275           0 :   if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     276             :     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
     277           0 :       new MediaEngineWebRTCAudioCaptureSource(nullptr);
     278           0 :     aASources->AppendElement(audioCaptureSource);
     279           0 :     return;
     280             :   }
     281             : 
     282             : #ifdef MOZ_WIDGET_ANDROID
     283             :   jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
     284             : 
     285             :   // get the JVM
     286             :   JavaVM* jvm;
     287             :   JNIEnv* const env = jni::GetEnvForThread();
     288             :   MOZ_ALWAYS_TRUE(!env->GetJavaVM(&jvm));
     289             : 
     290             :   if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) {
     291             :     LOG(("VoiceEngine:SetAndroidObjects Failed"));
     292             :     return;
     293             :   }
     294             : #endif
     295             : 
     296           0 :   if (!mVoiceEngine) {
     297           0 :     mVoiceEngine = webrtc::VoiceEngine::Create(/*mConfig*/);
     298           0 :     if (!mVoiceEngine) {
     299           0 :       return;
     300             :     }
     301             :   }
     302             : 
     303           0 :   ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
     304           0 :   if (!ptrVoEBase) {
     305           0 :     return;
     306             :   }
     307             : 
     308             :   // Always re-init the voice engine, since if we close the last use we
     309             :   // DeInitEngine() and Terminate(), which shuts down Process() - but means
     310             :   // we have to Init() again before using it.  Init() when already inited is
     311             :   // just a no-op, so call always.
     312           0 :   if (ptrVoEBase->Init() < 0) {
     313           0 :     return;
     314             :   }
     315             : 
     316           0 :   if (!mAudioInput) {
     317           0 :     if (SupportsDuplex()) {
     318             :       // The platform_supports_full_duplex.
     319           0 :       mAudioInput = new mozilla::AudioInputCubeb(mVoiceEngine);
     320             :     } else {
     321           0 :       mAudioInput = new mozilla::AudioInputWebRTC(mVoiceEngine);
     322             :     }
     323             :   }
     324             : 
     325           0 :   int nDevices = 0;
     326           0 :   mAudioInput->GetNumOfRecordingDevices(nDevices);
     327             :   int i;
     328             : #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
     329             :   i = 0; // Bug 1037025 - let the OS handle defaulting for now on android/b2g
     330             : #else
     331             :   // -1 is "default communications device" depending on OS in webrtc.org code
     332           0 :   i = -1;
     333             : #endif
     334           0 :   for (; i < nDevices; i++) {
     335             :     // We use constants here because GetRecordingDeviceName takes char[128].
     336             :     char deviceName[128];
     337             :     char uniqueId[128];
     338             :     // paranoia; jingle doesn't bother with this
     339           0 :     deviceName[0] = '\0';
     340           0 :     uniqueId[0] = '\0';
     341             : 
     342           0 :     int error = mAudioInput->GetRecordingDeviceName(i, deviceName, uniqueId);
     343           0 :     if (error) {
     344           0 :       LOG((" VoEHardware:GetRecordingDeviceName: Failed %d", error));
     345           0 :       continue;
     346             :     }
     347             : 
     348           0 :     if (uniqueId[0] == '\0') {
     349             :       // Mac and Linux don't set uniqueId!
     350             :       MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia
     351           0 :       strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check
     352             :     }
     353             : 
     354           0 :     RefPtr<MediaEngineAudioSource> aSource;
     355           0 :     NS_ConvertUTF8toUTF16 uuid(uniqueId);
     356           0 :     if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) {
     357             :       // We've already seen this device, just append.
     358           0 :       aASources->AppendElement(aSource.get());
     359             :     } else {
     360           0 :       AudioInput* audioinput = mAudioInput;
     361           0 :       if (SupportsDuplex()) {
     362             :         // The platform_supports_full_duplex.
     363             : 
     364             :         // For cubeb, it has state (the selected ID)
     365             :         // XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this
     366             :         // XXX Small window where the device list/index could change!
     367           0 :         audioinput = new mozilla::AudioInputCubeb(mVoiceEngine, i);
     368             :       }
     369             :       aSource = new MediaEngineWebRTCMicrophoneSource(mVoiceEngine, audioinput,
     370           0 :                                                       i, deviceName, uniqueId);
     371           0 :       mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
     372           0 :       aASources->AppendElement(aSource);
     373             :     }
     374             :   }
     375             : }
     376             : 
     377             : void
     378           0 : MediaEngineWebRTC::Shutdown()
     379             : {
     380             :   // This is likely paranoia
     381           0 :   MutexAutoLock lock(mMutex);
     382             : 
     383           0 :   if (camera::GetCamerasChildIfExists()) {
     384           0 :     camera::GetChildAndCall(
     385           0 :       &camera::CamerasChild::RemoveDeviceChangeCallback, this);
     386             :   }
     387             : 
     388           0 :   LOG(("%s", __FUNCTION__));
     389             :   // Shutdown all the sources, since we may have dangling references to the
     390             :   // sources in nsDOMUserMediaStreams waiting for GC/CC
     391           0 :   for (auto iter = mVideoSources.Iter(); !iter.Done(); iter.Next()) {
     392           0 :     MediaEngineVideoSource* source = iter.UserData();
     393           0 :     if (source) {
     394           0 :       source->Shutdown();
     395             :     }
     396             :   }
     397           0 :   for (auto iter = mAudioSources.Iter(); !iter.Done(); iter.Next()) {
     398           0 :     MediaEngineAudioSource* source = iter.UserData();
     399           0 :     if (source) {
     400           0 :       source->Shutdown();
     401             :     }
     402             :   }
     403           0 :   mVideoSources.Clear();
     404           0 :   mAudioSources.Clear();
     405             : 
     406           0 :   if (mVoiceEngine) {
     407           0 :     mVoiceEngine->SetTraceCallback(nullptr);
     408           0 :     webrtc::VoiceEngine::Delete(mVoiceEngine);
     409             :   }
     410             : 
     411           0 :   mVoiceEngine = nullptr;
     412             : 
     413           0 :   mozilla::camera::Shutdown();
     414           0 :   AudioInputCubeb::CleanupGlobalData();
     415           0 : }
     416             : 
     417           9 : }

Generated by: LCOV version 1.13