Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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
#include "PEMFactory.h"
#include "PlatformEncoderModule.h"
#ifdef MOZ_APPLEMEDIA
# include "AppleEncoderModule.h"
#endif
#ifdef MOZ_WIDGET_ANDROID
# include "AndroidEncoderModule.h"
#endif
#ifdef XP_WIN
# include "WMFEncoderModule.h"
#endif
#ifdef MOZ_FFMPEG
# include "FFmpegRuntimeLinker.h"
#endif
#include "FFVPXRuntimeLinker.h"
#include "GMPEncoderModule.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/gfx/gfxVars.h"
namespace mozilla {
LazyLogModule sPEMLog("PlatformEncoderModule");
#define LOGE(fmt, ...) \
MOZ_LOG(sPEMLog, mozilla::LogLevel::Error, \
("[PEMFactory] %s: " fmt, __func__, ##__VA_ARGS__))
#define LOG(fmt, ...) \
MOZ_LOG(sPEMLog, mozilla::LogLevel::Debug, \
("[PEMFactory] %s: " fmt, __func__, ##__VA_ARGS__))
PEMFactory::PEMFactory() {
gfx::gfxVars::Initialize();
#ifdef MOZ_APPLEMEDIA
RefPtr<PlatformEncoderModule> m(new AppleEncoderModule());
mCurrentPEMs.AppendElement(m);
#endif
#ifdef MOZ_WIDGET_ANDROID
mCurrentPEMs.AppendElement(new AndroidEncoderModule());
#endif
#ifdef XP_WIN
mCurrentPEMs.AppendElement(new WMFEncoderModule());
#endif
if (StaticPrefs::media_ffmpeg_encoder_enabled()) {
if (RefPtr<PlatformEncoderModule> pem =
FFVPXRuntimeLinker::CreateEncoder()) {
mCurrentPEMs.AppendElement(pem);
}
}
#ifdef MOZ_FFMPEG
if (StaticPrefs::media_ffmpeg_enabled() &&
StaticPrefs::media_ffmpeg_encoder_enabled()) {
if (RefPtr<PlatformEncoderModule> pem =
FFmpegRuntimeLinker::CreateEncoder()) {
mCurrentPEMs.AppendElement(pem);
}
}
#endif
if (StaticPrefs::media_gmp_encoder_enabled()) {
auto pem = MakeRefPtr<GMPEncoderModule>();
if (StaticPrefs::media_gmp_encoder_preferred()) {
mCurrentPEMs.InsertElementAt(0, std::move(pem));
} else {
mCurrentPEMs.AppendElement(std::move(pem));
}
}
}
already_AddRefed<MediaDataEncoder> PEMFactory::CreateEncoder(
const EncoderConfig& aConfig, const RefPtr<TaskQueue>& aTaskQueue) {
RefPtr<PlatformEncoderModule> m = FindPEM(aConfig);
if (!m) {
return nullptr;
}
return aConfig.IsVideo() ? m->CreateVideoEncoder(aConfig, aTaskQueue)
: nullptr;
}
RefPtr<PlatformEncoderModule::CreateEncoderPromise>
PEMFactory::CreateEncoderAsync(const EncoderConfig& aConfig,
const RefPtr<TaskQueue>& aTaskQueue) {
return CheckAndMaybeCreateEncoder(aConfig, 0, aTaskQueue);
}
RefPtr<PlatformEncoderModule::CreateEncoderPromise>
PEMFactory::CheckAndMaybeCreateEncoder(const EncoderConfig& aConfig,
uint32_t aIndex,
const RefPtr<TaskQueue>& aTaskQueue) {
for (uint32_t i = aIndex; i < mCurrentPEMs.Length(); i++) {
if (!mCurrentPEMs[i]->Supports(aConfig)) {
continue;
}
return CreateEncoderWithPEM(mCurrentPEMs[i], aConfig, aTaskQueue)
->Then(
GetCurrentSerialEventTarget(), __func__,
[](RefPtr<MediaDataEncoder>&& aEncoder) {
return PlatformEncoderModule::CreateEncoderPromise::
CreateAndResolve(std::move(aEncoder), __func__);
},
[self = RefPtr{this}, i, config = aConfig, aTaskQueue,
&aConfig](const MediaResult& aError) mutable {
// Try the next PEM.
return self->CheckAndMaybeCreateEncoder(aConfig, i + 1,
aTaskQueue);
});
}
return PlatformEncoderModule::CreateEncoderPromise::CreateAndReject(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
nsPrintfCString("Error no encoder found for %s",
GetCodecTypeString(aConfig.mCodec))
.get()),
__func__);
}
RefPtr<PlatformEncoderModule::CreateEncoderPromise>
PEMFactory::CreateEncoderWithPEM(PlatformEncoderModule* aPEM,
const EncoderConfig& aConfig,
const RefPtr<TaskQueue>& aTaskQueue) {
MOZ_ASSERT(aPEM);
MediaResult result = NS_OK;
if (aConfig.IsAudio()) {
return aPEM->AsyncCreateEncoder(aConfig, aTaskQueue)
->Then(
GetCurrentSerialEventTarget(), __func__,
[config = aConfig](RefPtr<MediaDataEncoder>&& aEncoder) {
RefPtr<MediaDataEncoder> decoder = std::move(aEncoder);
return PlatformEncoderModule::CreateEncoderPromise::
CreateAndResolve(decoder, __func__);
},
[](const MediaResult& aError) {
return PlatformEncoderModule::CreateEncoderPromise::
CreateAndReject(aError, __func__);
});
}
if (!aConfig.IsVideo()) {
return PlatformEncoderModule::CreateEncoderPromise::CreateAndReject(
MediaResult(
NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL(
"Encoder configuration error, expected audio or video.")),
__func__);
}
return aPEM->AsyncCreateEncoder(aConfig, aTaskQueue);
}
bool PEMFactory::Supports(const EncoderConfig& aConfig) const {
RefPtr<PlatformEncoderModule> found;
for (const auto& m : mCurrentPEMs) {
if (m->Supports(aConfig)) {
// TODO name
LOG("Checking if %s supports codec %s: yes", m->GetName(),
GetCodecTypeString(aConfig.mCodec));
return true;
}
LOG("Checking if %s supports codec %s: no", m->GetName(),
GetCodecTypeString(aConfig.mCodec));
}
return false;
}
bool PEMFactory::SupportsCodec(CodecType aCodec) const {
for (const auto& m : mCurrentPEMs) {
if (m->SupportsCodec(aCodec)) {
// TODO name
LOG("Checking if %s supports codec %d: yes", m->GetName(),
static_cast<int>(aCodec));
return true;
}
LOG("Checking if %s supports codec %d: no", m->GetName(),
static_cast<int>(aCodec));
}
LOG("No PEM support %d", static_cast<int>(aCodec));
return false;
}
already_AddRefed<PlatformEncoderModule> PEMFactory::FindPEM(
const EncoderConfig& aConfig) const {
RefPtr<PlatformEncoderModule> found;
for (const auto& m : mCurrentPEMs) {
if (m->Supports(aConfig)) {
found = m;
break;
}
}
return found.forget();
}
} // namespace mozilla
#undef LOGE
#undef LOG