summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/android/AndroidDecoderModule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/android/AndroidDecoderModule.cpp')
-rw-r--r--dom/media/platforms/android/AndroidDecoderModule.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp
new file mode 100644
index 000000000..3bf5cbf09
--- /dev/null
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -0,0 +1,227 @@
+/* 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 "AndroidDecoderModule.h"
+#include "AndroidBridge.h"
+
+#include "MediaCodecDataDecoder.h"
+#include "RemoteDataDecoder.h"
+
+#include "MediaInfo.h"
+#include "VPXDecoder.h"
+
+#include "MediaPrefs.h"
+#include "OpusDecoder.h"
+#include "VorbisDecoder.h"
+
+#include "nsPromiseFlatString.h"
+#include "nsIGfxInfo.h"
+
+#include "prlog.h"
+
+#include <jni.h>
+
+#undef LOG
+#define LOG(arg, ...) MOZ_LOG(sAndroidDecoderModuleLog, \
+ mozilla::LogLevel::Debug, ("AndroidDecoderModule(%p)::%s: " arg, \
+ this, __func__, ##__VA_ARGS__))
+
+using namespace mozilla;
+using namespace mozilla::gl;
+using namespace mozilla::java::sdk;
+using media::TimeUnit;
+
+namespace mozilla {
+
+mozilla::LazyLogModule sAndroidDecoderModuleLog("AndroidDecoderModule");
+
+static const char*
+TranslateMimeType(const nsACString& aMimeType)
+{
+ if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
+ return "video/x-vnd.on2.vp8";
+ } else if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9)) {
+ return "video/x-vnd.on2.vp9";
+ }
+ return PromiseFlatCString(aMimeType).get();
+}
+
+static bool
+GetFeatureStatus(int32_t aFeature)
+{
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
+ nsCString discardFailureId;
+ if (!gfxInfo || NS_FAILED(gfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
+ return false;
+ }
+ return status == nsIGfxInfo::FEATURE_STATUS_OK;
+};
+
+CryptoInfo::LocalRef
+GetCryptoInfoFromSample(const MediaRawData* aSample)
+{
+ auto& cryptoObj = aSample->mCrypto;
+
+ if (!cryptoObj.mValid) {
+ return nullptr;
+ }
+
+ CryptoInfo::LocalRef cryptoInfo;
+ nsresult rv = CryptoInfo::New(&cryptoInfo);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ uint32_t numSubSamples =
+ std::min<uint32_t>(cryptoObj.mPlainSizes.Length(), cryptoObj.mEncryptedSizes.Length());
+
+ uint32_t totalSubSamplesSize = 0;
+ for (auto& size : cryptoObj.mEncryptedSizes) {
+ totalSubSamplesSize += size;
+ }
+
+ // mPlainSizes is uint16_t, need to transform to uint32_t first.
+ nsTArray<uint32_t> plainSizes;
+ for (auto& size : cryptoObj.mPlainSizes) {
+ totalSubSamplesSize += size;
+ plainSizes.AppendElement(size);
+ }
+
+ uint32_t codecSpecificDataSize = aSample->Size() - totalSubSamplesSize;
+ // Size of codec specific data("CSD") for Android MediaCodec usage should be
+ // included in the 1st plain size.
+ plainSizes[0] += codecSpecificDataSize;
+
+ static const int kExpectedIVLength = 16;
+ auto tempIV(cryptoObj.mIV);
+ auto tempIVLength = tempIV.Length();
+ MOZ_ASSERT(tempIVLength <= kExpectedIVLength);
+ for (size_t i = tempIVLength; i < kExpectedIVLength; i++) {
+ // Padding with 0
+ tempIV.AppendElement(0);
+ }
+
+ auto numBytesOfPlainData = mozilla::jni::IntArray::New(
+ reinterpret_cast<int32_t*>(&plainSizes[0]),
+ plainSizes.Length());
+
+ auto numBytesOfEncryptedData =
+ mozilla::jni::IntArray::New(reinterpret_cast<const int32_t*>(&cryptoObj.mEncryptedSizes[0]),
+ cryptoObj.mEncryptedSizes.Length());
+ auto iv = mozilla::jni::ByteArray::New(reinterpret_cast<int8_t*>(&tempIV[0]),
+ tempIV.Length());
+ auto keyId = mozilla::jni::ByteArray::New(reinterpret_cast<const int8_t*>(&cryptoObj.mKeyId[0]),
+ cryptoObj.mKeyId.Length());
+ cryptoInfo->Set(numSubSamples,
+ numBytesOfPlainData,
+ numBytesOfEncryptedData,
+ keyId,
+ iv,
+ MediaCodec::CRYPTO_MODE_AES_CTR);
+
+ return cryptoInfo;
+}
+
+bool
+AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
+ DecoderDoctorDiagnostics* aDiagnostics) const
+{
+ if (!AndroidBridge::Bridge() ||
+ AndroidBridge::Bridge()->GetAPIVersion() < 16) {
+ return false;
+ }
+
+ if (aMimeType.EqualsLiteral("video/mp4") ||
+ aMimeType.EqualsLiteral("video/avc")) {
+ return true;
+ }
+
+ // When checking "audio/x-wav", CreateDecoder can cause a JNI ERROR by
+ // Accessing a stale local reference leading to a SIGSEGV crash.
+ // To avoid this we check for wav types here.
+ if (aMimeType.EqualsLiteral("audio/x-wav") ||
+ aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
+ aMimeType.EqualsLiteral("audio/wave; codecs=6") ||
+ aMimeType.EqualsLiteral("audio/wave; codecs=7") ||
+ aMimeType.EqualsLiteral("audio/wave; codecs=65534")) {
+ return false;
+ }
+
+ if ((VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8) &&
+ !GetFeatureStatus(nsIGfxInfo::FEATURE_VP8_HW_DECODE)) ||
+ (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP9) &&
+ !GetFeatureStatus(nsIGfxInfo::FEATURE_VP9_HW_DECODE))) {
+ return false;
+ }
+
+ // Prefer the gecko decoder for opus and vorbis; stagefright crashes
+ // on content demuxed from mp4.
+ if (OpusDataDecoder::IsOpus(aMimeType) ||
+ VorbisDataDecoder::IsVorbis(aMimeType)) {
+ LOG("Rejecting audio of type %s", aMimeType.Data());
+ return false;
+ }
+
+ return java::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
+ nsCString(TranslateMimeType(aMimeType)));
+}
+
+already_AddRefed<MediaDataDecoder>
+AndroidDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
+{
+ MediaFormat::LocalRef format;
+
+ const VideoInfo& config = aParams.VideoConfig();
+ NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
+ TranslateMimeType(config.mMimeType),
+ config.mDisplay.width,
+ config.mDisplay.height,
+ &format), nullptr);
+
+ RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
+ RemoteDataDecoder::CreateVideoDecoder(config,
+ format,
+ aParams.mCallback,
+ aParams.mImageContainer) :
+ MediaCodecDataDecoder::CreateVideoDecoder(config,
+ format,
+ aParams.mCallback,
+ aParams.mImageContainer);
+
+ return decoder.forget();
+}
+
+already_AddRefed<MediaDataDecoder>
+AndroidDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
+{
+ const AudioInfo& config = aParams.AudioConfig();
+ MOZ_ASSERT(config.mBitDepth == 16, "We only handle 16-bit audio!");
+
+ MediaFormat::LocalRef format;
+
+ LOG("CreateAudioFormat with mimeType=%s, mRate=%d, channels=%d",
+ config.mMimeType.Data(), config.mRate, config.mChannels);
+
+ NS_ENSURE_SUCCESS(MediaFormat::CreateAudioFormat(
+ config.mMimeType,
+ config.mRate,
+ config.mChannels,
+ &format), nullptr);
+
+ RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
+ RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback) :
+ MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
+
+ return decoder.forget();
+}
+
+PlatformDecoderModule::ConversionRequired
+AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
+{
+ if (aConfig.IsVideo()) {
+ return ConversionRequired::kNeedAnnexB;
+ }
+ return ConversionRequired::kNeedNone;
+}
+
+} // mozilla