mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 11:15:34 +00:00
9e8bb0c379
MozReview-Commit-ID: 1RClh9UNQck --HG-- extra : rebase_source : 23e3f1b9e4ad42f0cff86d3903e2b0c7e5bc6a01
670 lines
19 KiB
C++
670 lines
19 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "MediaEngineDefault.h"
|
|
|
|
#include "ImageContainer.h"
|
|
#include "ImageTypes.h"
|
|
#include "Layers.h"
|
|
#include "MediaStreamGraph.h"
|
|
#include "MediaTrackConstraints.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIFile.h"
|
|
#include "nsIFilePicker.h"
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIPrefService.h"
|
|
#include "Tracing.h"
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
#include "nsISupportsUtils.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_WEBRTC
|
|
#include "YuvStamper.h"
|
|
#endif
|
|
|
|
#define DEFAULT_AUDIO_TIMER_MS 10
|
|
namespace mozilla {
|
|
|
|
using namespace mozilla::gfx;
|
|
|
|
/**
|
|
* Default video source.
|
|
*/
|
|
|
|
MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
|
|
: mTimer(nullptr)
|
|
, mMutex("MediaEngineDefaultVideoSource::mMutex")
|
|
{}
|
|
|
|
MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
|
|
{}
|
|
|
|
nsString
|
|
MediaEngineDefaultVideoSource::GetName() const
|
|
{
|
|
return NS_LITERAL_STRING(u"Default Video Device");
|
|
}
|
|
|
|
nsCString
|
|
MediaEngineDefaultVideoSource::GetUUID() const
|
|
{
|
|
return NS_LITERAL_CSTRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676");
|
|
}
|
|
|
|
uint32_t
|
|
MediaEngineDefaultVideoSource::GetBestFitnessDistance(
|
|
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
|
const nsString& aDeviceId) const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
uint32_t distance = 0;
|
|
#ifdef MOZ_WEBRTC
|
|
for (const auto* cs : aConstraintSets) {
|
|
distance = MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId);
|
|
break; // distance is read from first entry only
|
|
}
|
|
#endif
|
|
return distance;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
|
|
const MediaEnginePrefs &aPrefs,
|
|
const nsString& aDeviceId,
|
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
|
AllocationHandle** aOutHandle,
|
|
const char** aOutBadConstraint)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kReleased);
|
|
|
|
FlattenedConstraints c(aConstraints);
|
|
|
|
// Mock failure for automated tests.
|
|
if (c.mDeviceId.mIdeal.find(NS_LITERAL_STRING("bad device")) !=
|
|
c.mDeviceId.mIdeal.end()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// emulator debug is very, very slow; reduce load on it with smaller/slower fake video
|
|
mOpts = aPrefs;
|
|
mOpts.mWidth = c.mWidth.Get(aPrefs.mWidth ? aPrefs.mWidth :
|
|
#ifdef DEBUG
|
|
MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH/2
|
|
#else
|
|
MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH
|
|
#endif
|
|
);
|
|
mOpts.mHeight = c.mHeight.Get(aPrefs.mHeight ? aPrefs.mHeight :
|
|
#ifdef DEBUG
|
|
MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT/2
|
|
#else
|
|
MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT
|
|
#endif
|
|
);
|
|
mOpts.mWidth = std::max(160, std::min(mOpts.mWidth, 4096)) & ~1;
|
|
mOpts.mHeight = std::max(90, std::min(mOpts.mHeight, 2160)) & ~1;
|
|
*aOutHandle = nullptr;
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mState = kAllocated;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::Deallocate(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(!aHandle);
|
|
MOZ_ASSERT(!mImage);
|
|
MOZ_ASSERT(mState == kStopped || mState == kAllocated);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
if (mStream && IsTrackIDExplicit(mTrackID)) {
|
|
mStream->EndTrack(mTrackID);
|
|
mStream = nullptr;
|
|
mTrackID = TRACK_NONE;
|
|
}
|
|
mState = kReleased;
|
|
mImageContainer = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
static void AllocateSolidColorFrame(layers::PlanarYCbCrData& aData,
|
|
int aWidth, int aHeight,
|
|
int aY, int aCb, int aCr)
|
|
{
|
|
MOZ_ASSERT(!(aWidth&1));
|
|
MOZ_ASSERT(!(aHeight&1));
|
|
// Allocate a single frame with a solid color
|
|
int yLen = aWidth*aHeight;
|
|
int cbLen = yLen>>2;
|
|
int crLen = cbLen;
|
|
uint8_t* frame = (uint8_t*) malloc(yLen+cbLen+crLen);
|
|
memset(frame, aY, yLen);
|
|
memset(frame+yLen, aCb, cbLen);
|
|
memset(frame+yLen+cbLen, aCr, crLen);
|
|
|
|
aData.mYChannel = frame;
|
|
aData.mYSize = IntSize(aWidth, aHeight);
|
|
aData.mYStride = aWidth;
|
|
aData.mCbCrStride = aWidth>>1;
|
|
aData.mCbChannel = frame + yLen;
|
|
aData.mCrChannel = aData.mCbChannel + cbLen;
|
|
aData.mCbCrSize = IntSize(aWidth>>1, aHeight>>1);
|
|
aData.mPicX = 0;
|
|
aData.mPicY = 0;
|
|
aData.mPicSize = IntSize(aWidth, aHeight);
|
|
aData.mStereoMode = StereoMode::MONO;
|
|
}
|
|
|
|
static void ReleaseFrame(layers::PlanarYCbCrData& aData)
|
|
{
|
|
free(aData.mYChannel);
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::SetTrack(const RefPtr<const AllocationHandle>& aHandle,
|
|
const RefPtr<SourceMediaStream>& aStream,
|
|
TrackID aTrackID,
|
|
const PrincipalHandle& aPrincipal)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kAllocated);
|
|
MOZ_ASSERT(!mStream);
|
|
MOZ_ASSERT(mTrackID == TRACK_NONE);
|
|
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
mStream = aStream;
|
|
mTrackID = aTrackID;
|
|
}
|
|
aStream->AddTrack(aTrackID, 0, new VideoSegment(),
|
|
SourceMediaStream::ADDTRACK_QUEUED);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::Start(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kAllocated || mState == kStopped);
|
|
MOZ_ASSERT(mStream, "SetTrack() must happen before Start()");
|
|
MOZ_ASSERT(IsTrackIDExplicit(mTrackID), "SetTrack() must happen before Start()");
|
|
|
|
mTimer = NS_NewTimer();
|
|
if (!mTimer) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!mImageContainer) {
|
|
mImageContainer =
|
|
layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS);
|
|
}
|
|
|
|
// Start timer for subsequent frames
|
|
uint32_t interval;
|
|
#if defined(MOZ_WIDGET_ANDROID) && defined(DEBUG)
|
|
// emulator debug is very, very slow and has problems dealing with realtime audio inputs
|
|
interval = 10 * (1000 / mOpts.mFPS);
|
|
#else
|
|
interval = 1000 / mOpts.mFPS;
|
|
#endif
|
|
mTimer->InitWithNamedFuncCallback([](nsITimer* aTimer, void* aClosure) {
|
|
RefPtr<MediaEngineDefaultVideoSource> source =
|
|
static_cast<MediaEngineDefaultVideoSource*>(aClosure);
|
|
source->GenerateFrame();
|
|
}, this, interval, nsITimer::TYPE_REPEATING_SLACK,
|
|
"MediaEngineDefaultVideoSource::GenerateFrame");
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mState = kStarted;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mState == kStopped || mState == kAllocated) {
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == kStarted);
|
|
MOZ_ASSERT(mTimer);
|
|
MOZ_ASSERT(mStream);
|
|
MOZ_ASSERT(IsTrackIDExplicit(mTrackID));
|
|
|
|
mTimer->Cancel();
|
|
mTimer = nullptr;
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
mImage = nullptr;
|
|
mState = kStopped;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultVideoSource::Reconfigure(
|
|
const RefPtr<AllocationHandle>& aHandle,
|
|
const dom::MediaTrackConstraints& aConstraints,
|
|
const MediaEnginePrefs &aPrefs,
|
|
const nsString& aDeviceId,
|
|
const char** aOutBadConstraint)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
MediaEngineDefaultVideoSource::GenerateFrame()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
// Update the target color
|
|
if (mCr <= 16) {
|
|
if (mCb < 240) {
|
|
mCb++;
|
|
} else {
|
|
mCr++;
|
|
}
|
|
} else if (mCb >= 240) {
|
|
if (mCr < 240) {
|
|
mCr++;
|
|
} else {
|
|
mCb--;
|
|
}
|
|
} else if (mCr >= 240) {
|
|
if (mCb > 16) {
|
|
mCb--;
|
|
} else {
|
|
mCr--;
|
|
}
|
|
} else {
|
|
mCr--;
|
|
}
|
|
|
|
// Allocate a single solid color image
|
|
RefPtr<layers::PlanarYCbCrImage> ycbcr_image = mImageContainer->CreatePlanarYCbCrImage();
|
|
layers::PlanarYCbCrData data;
|
|
AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
|
|
|
|
#ifdef MOZ_WEBRTC
|
|
uint64_t timestamp = PR_Now();
|
|
YuvStamper::Encode(mOpts.mWidth, mOpts.mHeight, mOpts.mWidth,
|
|
data.mYChannel,
|
|
reinterpret_cast<unsigned char*>(×tamp), sizeof(timestamp),
|
|
0, 0);
|
|
#endif
|
|
|
|
bool setData = ycbcr_image->CopyData(data);
|
|
MOZ_ASSERT(setData);
|
|
|
|
// SetData copies data, so we can free the frame
|
|
ReleaseFrame(data);
|
|
|
|
if (!setData) {
|
|
return;
|
|
}
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mImage = std::move(ycbcr_image);
|
|
}
|
|
|
|
void
|
|
MediaEngineDefaultVideoSource::Pull(const RefPtr<const AllocationHandle>& aHandle,
|
|
const RefPtr<SourceMediaStream>& aStream,
|
|
TrackID aTrackID,
|
|
StreamTime aDesiredTime,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
{
|
|
TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i",
|
|
aStream.get(), aTrackID);
|
|
// AppendFrame takes ownership of `segment`
|
|
VideoSegment segment;
|
|
|
|
RefPtr<layers::Image> image;
|
|
{
|
|
MutexAutoLock lock(mMutex);
|
|
// Started - append real image
|
|
// Stopped - append null
|
|
// Released - Track is ended, safe to ignore
|
|
// Can happen because NotifyPull comes from a stream listener
|
|
if (mState == kReleased) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(mState != kAllocated);
|
|
if (mState == kStarted) {
|
|
MOZ_ASSERT(mStream == aStream);
|
|
MOZ_ASSERT(mTrackID == aTrackID);
|
|
image = mImage;
|
|
}
|
|
}
|
|
|
|
StreamTime delta = aDesiredTime - aStream->GetEndOfAppendedData(aTrackID);
|
|
if (delta > 0) {
|
|
// nullptr images are allowed
|
|
IntSize size(mOpts.mWidth, mOpts.mHeight);
|
|
segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle);
|
|
// This can fail if either a) we haven't added the track yet, or b)
|
|
// we've removed or finished the track.
|
|
aStream->AppendToTrack(aTrackID, &segment);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Default audio source.
|
|
*/
|
|
|
|
MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
|
|
: mMutex("MediaEngineDefaultAudioSource::mMutex")
|
|
{}
|
|
|
|
MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
|
|
{}
|
|
|
|
nsString
|
|
MediaEngineDefaultAudioSource::GetName() const
|
|
{
|
|
return NS_LITERAL_STRING(u"Default Audio Device");
|
|
}
|
|
|
|
nsCString
|
|
MediaEngineDefaultAudioSource::GetUUID() const
|
|
{
|
|
return NS_LITERAL_CSTRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092");
|
|
}
|
|
|
|
uint32_t
|
|
MediaEngineDefaultAudioSource::GetBestFitnessDistance(
|
|
const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
|
|
const nsString& aDeviceId) const
|
|
{
|
|
uint32_t distance = 0;
|
|
#ifdef MOZ_WEBRTC
|
|
for (const auto* cs : aConstraintSets) {
|
|
distance = MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId);
|
|
break; // distance is read from first entry only
|
|
}
|
|
#endif
|
|
return distance;
|
|
}
|
|
|
|
bool
|
|
MediaEngineDefaultAudioSource::IsAvailable() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
return mState == kReleased;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
|
|
const MediaEnginePrefs &aPrefs,
|
|
const nsString& aDeviceId,
|
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
|
AllocationHandle** aOutHandle,
|
|
const char** aOutBadConstraint)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kReleased);
|
|
|
|
// Mock failure for automated tests.
|
|
if (aConstraints.mDeviceId.IsString() &&
|
|
aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000;
|
|
*aOutHandle = nullptr;
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mState = kAllocated;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::Deallocate(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(!aHandle);
|
|
MOZ_ASSERT(mState == kStopped || mState == kAllocated);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
if (mStream && IsTrackIDExplicit(mTrackID)) {
|
|
mStream->EndTrack(mTrackID);
|
|
mStream = nullptr;
|
|
mTrackID = TRACK_NONE;
|
|
}
|
|
mState = kReleased;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::SetTrack(const RefPtr<const AllocationHandle>& aHandle,
|
|
const RefPtr<SourceMediaStream>& aStream,
|
|
TrackID aTrackID,
|
|
const PrincipalHandle& aPrincipal)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kAllocated);
|
|
MOZ_ASSERT(!mStream);
|
|
MOZ_ASSERT(mTrackID == TRACK_NONE);
|
|
|
|
// AddAudioTrack will take ownership of segment
|
|
mStream = aStream;
|
|
mTrackID = aTrackID;
|
|
aStream->AddAudioTrack(aTrackID,
|
|
aStream->GraphRate(),
|
|
0,
|
|
new AudioSegment(),
|
|
SourceMediaStream::ADDTRACK_QUEUED);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::Start(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
MOZ_ASSERT(mState == kAllocated || mState == kStopped);
|
|
MOZ_ASSERT(mStream, "SetTrack() must happen before Start()");
|
|
MOZ_ASSERT(IsTrackIDExplicit(mTrackID), "SetTrack() must happen before Start()");
|
|
|
|
if (!mSineGenerator) {
|
|
// generate sine wave (default 1KHz)
|
|
mSineGenerator = new SineWaveGenerator(mStream->GraphRate(), mFreq);
|
|
}
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mState = kStarted;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::Stop(const RefPtr<const AllocationHandle>& aHandle)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mState == kStopped || mState == kAllocated) {
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == kStarted);
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
mState = kStopped;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
MediaEngineDefaultAudioSource::Reconfigure(
|
|
const RefPtr<AllocationHandle>& aHandle,
|
|
const dom::MediaTrackConstraints& aConstraints,
|
|
const MediaEnginePrefs &aPrefs,
|
|
const nsString& aDeviceId,
|
|
const char** aOutBadConstraint)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment,
|
|
TrackTicks aSamples,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
{
|
|
RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t));
|
|
int16_t* dest = static_cast<int16_t*>(buffer->Data());
|
|
|
|
mSineGenerator->generate(dest, aSamples);
|
|
AutoTArray<const int16_t*,1> channels;
|
|
channels.AppendElement(dest);
|
|
aSegment.AppendFrames(buffer.forget(), channels, aSamples, aPrincipalHandle);
|
|
}
|
|
|
|
void
|
|
MediaEngineDefaultAudioSource::Pull(const RefPtr<const AllocationHandle>& aHandle,
|
|
const RefPtr<SourceMediaStream>& aStream,
|
|
TrackID aTrackID,
|
|
StreamTime aDesiredTime,
|
|
const PrincipalHandle& aPrincipalHandle)
|
|
{
|
|
TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i",
|
|
aStream.get(), aTrackID);
|
|
AudioSegment segment;
|
|
// avoid accumulating rounding errors
|
|
TrackTicks desired = aStream->TimeToTicksRoundUp(aStream->GraphRate(), aDesiredTime);
|
|
TrackTicks delta = desired - mLastNotify;
|
|
mLastNotify += delta;
|
|
AppendToSegment(segment, delta, aPrincipalHandle);
|
|
aStream->AppendToTrack(aTrackID, &segment);
|
|
}
|
|
|
|
void
|
|
MediaEngineDefault::EnumerateDevices(uint64_t aWindowId,
|
|
dom::MediaSourceEnum aMediaSource,
|
|
MediaSinkEnum aMediaSink,
|
|
nsTArray<RefPtr<MediaDevice>>* aDevices)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
switch (aMediaSource) {
|
|
case dom::MediaSourceEnum::Camera: {
|
|
// Only supports camera video sources. See Bug 1038241.
|
|
|
|
// We once had code here to find a VideoSource with the same settings and
|
|
// re-use that. This is no longer possible since the resolution gets set
|
|
// in Allocate().
|
|
|
|
nsTArray<RefPtr<MediaEngineSource>>*
|
|
devicesForThisWindow = mVSources.LookupOrAdd(aWindowId);
|
|
auto newSource = MakeRefPtr<MediaEngineDefaultVideoSource>();
|
|
devicesForThisWindow->AppendElement(newSource);
|
|
aDevices->AppendElement(MakeRefPtr<MediaDevice>(
|
|
newSource,
|
|
newSource->GetName(),
|
|
NS_ConvertUTF8toUTF16(newSource->GetUUID())));
|
|
return;
|
|
}
|
|
case dom::MediaSourceEnum::Microphone: {
|
|
nsTArray<RefPtr<MediaEngineDefaultAudioSource>>*
|
|
devicesForThisWindow = mASources.LookupOrAdd(aWindowId);
|
|
for (const RefPtr<MediaEngineDefaultAudioSource>& source : *devicesForThisWindow) {
|
|
if (source->IsAvailable()) {
|
|
aDevices->AppendElement(MakeRefPtr<MediaDevice>(
|
|
source,
|
|
source->GetName(),
|
|
NS_ConvertUTF8toUTF16(source->GetUUID())));
|
|
}
|
|
}
|
|
|
|
if (aDevices->IsEmpty()) {
|
|
// All streams are currently busy, just make a new one.
|
|
auto newSource = MakeRefPtr<MediaEngineDefaultAudioSource>();
|
|
devicesForThisWindow->AppendElement(newSource);
|
|
aDevices->AppendElement(MakeRefPtr<MediaDevice>(
|
|
newSource,
|
|
newSource->GetName(),
|
|
NS_ConvertUTF8toUTF16(newSource->GetUUID())));
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unsupported source type");
|
|
return;
|
|
}
|
|
|
|
if (aMediaSink == MediaSinkEnum::Speaker) {
|
|
NS_WARNING("No default implementation for MediaSinkEnum::Speaker");
|
|
}
|
|
}
|
|
|
|
void
|
|
MediaEngineDefault::ReleaseResourcesForWindow(uint64_t aWindowId)
|
|
{
|
|
nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* audioDevicesForThisWindow =
|
|
mASources.Get(aWindowId);
|
|
|
|
if (audioDevicesForThisWindow) {
|
|
for (const RefPtr<MediaEngineDefaultAudioSource>& source :
|
|
*audioDevicesForThisWindow) {
|
|
source->Shutdown();
|
|
}
|
|
}
|
|
|
|
mASources.Remove(aWindowId);
|
|
|
|
nsTArray<RefPtr<MediaEngineSource>>* videoDevicesForThisWindow =
|
|
mVSources.Get(aWindowId);
|
|
|
|
if (videoDevicesForThisWindow) {
|
|
for (const RefPtr<MediaEngineSource>& source :
|
|
*videoDevicesForThisWindow) {
|
|
source->Shutdown();
|
|
}
|
|
}
|
|
|
|
mVSources.Remove(aWindowId);
|
|
}
|
|
|
|
void
|
|
MediaEngineDefault::Shutdown()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
for (auto iter = mVSources.Iter(); !iter.Done(); iter.Next()) {
|
|
for (const RefPtr<MediaEngineSource>& source : *iter.UserData()) {
|
|
if (source) {
|
|
source->Shutdown();
|
|
}
|
|
}
|
|
}
|
|
for (auto iter = mASources.Iter(); !iter.Done(); iter.Next()) {
|
|
for (const RefPtr<MediaEngineDefaultAudioSource>& source : *iter.UserData()) {
|
|
if (source) {
|
|
source->Shutdown();
|
|
}
|
|
}
|
|
}
|
|
mVSources.Clear();
|
|
mASources.Clear();
|
|
};
|
|
|
|
} // namespace mozilla
|