--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -464,13 +464,21 @@ PDMFactory::GetDecoder(const TrackInfo&
}
}
return pdm.forget();
}
void
PDMFactory::SetCDMProxy(CDMProxy* aProxy)
{
+ MOZ_ASSERT(aProxy);
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (IsWidevineKeySystem(aProxy->KeySystem())) {
+ mEMEPDM = new AndroidDecoderModule(aProxy);
+ return;
+ }
+#endif
RefPtr<PDMFactory> m = new PDMFactory();
mEMEPDM = new EMEDecoderModule(aProxy, m);
}
} // namespace mozilla
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -114,16 +114,21 @@ GetCryptoInfoFromSample(const MediaRawDa
numBytesOfEncryptedData,
keyId,
iv,
MediaCodec::CRYPTO_MODE_AES_CTR);
return cryptoInfo;
}
+AndroidDecoderModule::AndroidDecoderModule(CDMProxy* aProxy)
+{
+ mProxy = static_cast<MediaDrmCDMProxy*>(aProxy);
+}
+
bool
AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
if (!AndroidBridge::Bridge() ||
AndroidBridge::Bridge()->GetAPIVersion() < 16) {
return false;
}
@@ -169,26 +174,34 @@ AndroidDecoderModule::CreateVideoDecoder
const VideoInfo& config = aParams.VideoConfig();
NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
TranslateMimeType(config.mMimeType),
config.mDisplay.width,
config.mDisplay.height,
&format), nullptr);
+ nsString drmStubId;
+ if (mProxy) {
+ drmStubId = mProxy->GetMediaDrmStubId();
+ }
+
RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
- RemoteDataDecoder::CreateVideoDecoder(config,
- format,
- aParams.mCallback,
- aParams.mImageContainer) :
- MediaCodecDataDecoder::CreateVideoDecoder(config,
- format,
- aParams.mCallback,
- aParams.mImageContainer);
-
+ RemoteDataDecoder::CreateVideoDecoder(config,
+ format,
+ aParams.mCallback,
+ aParams.mImageContainer,
+ drmStubId) :
+ MediaCodecDataDecoder::CreateVideoDecoder(config,
+ format,
+ aParams.mCallback,
+ aParams.mImageContainer,
+ drmStubId,
+ mProxy,
+ aParams.mTaskQueue);
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!");
@@ -199,20 +212,28 @@ AndroidDecoderModule::CreateAudioDecoder
config.mMimeType.Data(), config.mRate, config.mChannels);
NS_ENSURE_SUCCESS(MediaFormat::CreateAudioFormat(
config.mMimeType,
config.mRate,
config.mChannels,
&format), nullptr);
+ nsString drmStubId;
+ if (mProxy) {
+ drmStubId = mProxy->GetMediaDrmStubId();
+ }
RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
- RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback) :
- MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
-
+ RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback, drmStubId) :
+ MediaCodecDataDecoder::CreateAudioDecoder(config,
+ format,
+ aParams.mCallback,
+ drmStubId,
+ mProxy,
+ aParams.mTaskQueue);
return decoder.forget();
}
PlatformDecoderModule::ConversionRequired
AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
{
if (aConfig.IsVideo()) {
return ConversionRequired::kNeedAnnexB;
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -1,34 +1,38 @@
/* 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/. */
#ifndef AndroidDecoderModule_h_
#define AndroidDecoderModule_h_
#include "PlatformDecoderModule.h"
+#include "mozilla/MediaDrmCDMProxy.h"
namespace mozilla {
class AndroidDecoderModule : public PlatformDecoderModule {
public:
already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const CreateDecoderParams& aParams) override;
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const CreateDecoderParams& aParams) override;
- AndroidDecoderModule() {}
+ AndroidDecoderModule(CDMProxy* aProxy = nullptr);
virtual ~AndroidDecoderModule() {}
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const override;
ConversionRequired
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
+
+private:
+ RefPtr<MediaDrmCDMProxy> mProxy;
};
extern LazyLogModule sAndroidDecoderModuleLog;
} // namespace mozilla
#endif
--- a/dom/media/platforms/android/MediaCodecDataDecoder.cpp
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.cpp
@@ -51,19 +51,20 @@ CreateDecoder(const nsACString& aMimeTyp
}
class VideoDataDecoder : public MediaCodecDataDecoder
{
public:
VideoDataDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer)
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
- aFormat, aCallback)
+ aFormat, aCallback, aDrmStubId)
, mImageContainer(aImageContainer)
, mConfig(aConfig)
{
}
const char* GetDescriptionName() const override
{
@@ -76,17 +77,16 @@ public:
if (!mSurfaceTexture) {
NS_WARNING("Failed to create SurfaceTexture for video decode\n");
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
-
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
void Cleanup() override
{
}
nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
@@ -122,25 +122,69 @@ public:
INVOKE_CALLBACK(Output, v);
return NS_OK;
}
protected:
layers::ImageContainer* mImageContainer;
const VideoInfo& mConfig;
RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
+ RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
+
+
+class EMEVideoDataDecoder : public VideoDataDecoder {
+public:
+ EMEVideoDataDecoder(const VideoInfo& aConfig,
+ MediaFormat::Param aFormat,
+ MediaDataDecoderCallback* aCallback,
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId,
+ CDMProxy* aProxy,
+ TaskQueue* aTaskQueue)
+ : VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId)
+ , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
+ aTaskQueue, aProxy))
+ {
+ }
+
+ void Input(MediaRawData* aSample) override;
+ void Shutdown() override;
+
+private:
+ RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+};
+
+void
+EMEVideoDataDecoder::Input(MediaRawData* aSample)
+{
+ if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ return;
+ }
+ VideoDataDecoder::Input(aSample);
+}
+
+void
+EMEVideoDataDecoder::Shutdown()
+{
+ VideoDataDecoder::Shutdown();
+
+ mSamplesWaitingForKey->BreakCycles();
+ mSamplesWaitingForKey = nullptr;
+}
+
class AudioDataDecoder : public MediaCodecDataDecoder
{
public:
AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
- aFormat, aCallback)
+ aFormat, aCallback, aDrmStubId)
{
JNIEnv* const env = jni::GetEnvForThread();
jni::ByteBuffer::LocalRef buffer(env);
NS_ENSURE_SUCCESS_VOID(aFormat->GetByteBuffer(NS_LITERAL_STRING("csd-0"),
&buffer));
if (!buffer && aConfig.mCodecSpecificConfig->Length() >= 2) {
@@ -205,47 +249,109 @@ public:
Move(audio),
numChannels,
sampleRate);
INVOKE_CALLBACK(Output, data);
return NS_OK;
}
};
+class EMEAudioDataDecoder : public AudioDataDecoder {
+public:
+ EMEAudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
+ MediaDataDecoderCallback* aCallback, const nsString& aDrmStubId,
+ CDMProxy* aProxy, TaskQueue* aTaskQueue)
+ : AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId)
+ , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
+ aTaskQueue, aProxy))
+ {
+ }
+
+ void Input(MediaRawData* aSample) override;
+ void Shutdown() override;
+
+private:
+ RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+};
+
+void
+EMEAudioDataDecoder::Input(MediaRawData* aSample)
+{
+ if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+ return;
+ }
+ AudioDataDecoder::Input(aSample);
+}
+
+void
+EMEAudioDataDecoder::Shutdown()
+{
+ AudioDataDecoder::Shutdown();
+
+ mSamplesWaitingForKey->BreakCycles();
+ mSamplesWaitingForKey = nullptr;
+}
+
MediaDataDecoder*
MediaCodecDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
- MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ java::sdk::MediaFormat::Param aFormat,
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId,
+ CDMProxy* aProxy,
+ TaskQueue* aTaskQueue)
{
- return new AudioDataDecoder(aConfig, aFormat, aCallback);
+ if (!aProxy) {
+ return new AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId);
+ } else {
+ return new EMEAudioDataDecoder(aConfig,
+ aFormat,
+ aCallback,
+ aDrmStubId,
+ aProxy,
+ aTaskQueue);
+ }
}
MediaDataDecoder*
MediaCodecDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
- MediaFormat::Param aFormat,
+ java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer)
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId,
+ CDMProxy* aProxy,
+ TaskQueue* aTaskQueue)
{
- return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer);
+ if (!aProxy) {
+ return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
+ } else {
+ return new EMEVideoDataDecoder(aConfig,
+ aFormat,
+ aCallback,
+ aImageContainer,
+ aDrmStubId,
+ aProxy,
+ aTaskQueue);
+ }
}
MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId)
: mType(aType)
, mMimeType(aMimeType)
, mFormat(aFormat)
, mCallback(aCallback)
, mInputBuffers(nullptr)
, mOutputBuffers(nullptr)
, mMonitor("MediaCodecDataDecoder::mMonitor")
, mState(ModuleState::kDecoding)
+ , mDrmStubId(aDrmStubId)
{
-
}
MediaCodecDataDecoder::~MediaCodecDataDecoder()
{
Shutdown();
}
RefPtr<MediaDataDecoder::InitPromise>
@@ -268,18 +374,21 @@ MediaCodecDataDecoder::InitDecoder(Surfa
{
mDecoder = CreateDecoder(mMimeType);
if (!mDecoder) {
INVOKE_CALLBACK(Error,
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
return NS_ERROR_FAILURE;
}
+ MediaCrypto::LocalRef crypto = MediaDrmProxy::GetMediaCrypto(mDrmStubId);
+ bool hascrypto = !!crypto;
+ LOG("Has(%d) MediaCrypto (%s)", hascrypto, NS_ConvertUTF16toUTF8(mDrmStubId).get());
nsresult rv;
- NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, nullptr, 0), rv);
+ NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, crypto, 0), rv);
NS_ENSURE_SUCCESS(rv = mDecoder->Start(), rv);
NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv);
NS_ENSURE_SUCCESS(rv = ResetOutputBuffers(), rv);
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop);
rv = NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread), r);
--- a/dom/media/platforms/android/MediaCodecDataDecoder.h
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.h
@@ -18,22 +18,28 @@
namespace mozilla {
typedef std::deque<RefPtr<MediaRawData>> SampleQueue;
class MediaCodecDataDecoder : public MediaDataDecoder {
public:
static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback);
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId,
+ CDMProxy* aProxy,
+ TaskQueue* aTaskQueue);
static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer);
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId,
+ CDMProxy* aProxy,
+ TaskQueue* aTaskQueue);
virtual ~MediaCodecDataDecoder();
RefPtr<MediaDataDecoder::InitPromise> Init() override;
void Flush() override;
void Drain() override;
void Shutdown() override;
void Input(MediaRawData* aSample) override;
@@ -53,17 +59,18 @@ protected:
kShutdown
};
friend class AndroidDecoderModule;
MediaCodecDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
java::sdk::MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback);
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId);
static const char* ModuleStateStr(ModuleState aState);
virtual nsresult InitDecoder(java::sdk::Surface::Param aSurface);
virtual nsresult Output(java::sdk::BufferInfo::Param aInfo, void* aBuffer,
java::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
{
@@ -114,13 +121,15 @@ protected:
// Only these members are protected by mMonitor.
Monitor mMonitor;
ModuleState mState;
SampleQueue mQueue;
// Durations are stored in microseconds.
std::deque<media::TimeUnit> mDurations;
+
+ nsString mDrmStubId;
};
} // namespace mozilla
#endif
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -180,19 +180,20 @@ public:
private:
RemoteVideoDecoder* mDecoder;
};
RemoteVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer)
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId)
: RemoteDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
- aFormat, aCallback)
+ aFormat, aCallback, aDrmStubId)
, mImageContainer(aImageContainer)
, mConfig(aConfig)
{
}
RefPtr<InitPromise> Init() override
{
mSurfaceTexture = AndroidSurfaceTexture::Create();
@@ -208,17 +209,20 @@ public:
// Register native methods.
JavaCallbacksSupport::Init();
mJavaCallbacks = CodecProxy::NativeCallbacks::New();
JavaCallbacksSupport::AttachNative(mJavaCallbacks,
mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
- mJavaDecoder = CodecProxy::Create(mFormat, mSurfaceTexture->JavaSurface(), mJavaCallbacks);
+ mJavaDecoder = CodecProxy::Create(mFormat,
+ mSurfaceTexture->JavaSurface(),
+ mJavaCallbacks,
+ mDrmStubId);
if (mJavaDecoder == nullptr) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
mInputDurations.Clear();
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
@@ -276,20 +280,21 @@ private:
RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
DurationQueue mInputDurations;
};
class RemoteAudioDecoder final : public RemoteDataDecoder
{
public:
RemoteAudioDecoder(const AudioInfo& aConfig,
- MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ MediaFormat::Param aFormat,
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId)
: RemoteDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
- aFormat, aCallback)
+ aFormat, aCallback, aDrmStubId)
, mConfig(aConfig)
{
JNIEnv* const env = jni::GetEnvForThread();
bool formatHasCSD = false;
NS_ENSURE_SUCCESS_VOID(aFormat->ContainsKey(NS_LITERAL_STRING("csd-0"), &formatHasCSD));
if (!formatHasCSD && aConfig.mCodecSpecificConfig->Length() >= 2) {
@@ -306,17 +311,17 @@ public:
{
// Register native methods.
JavaCallbacksSupport::Init();
mJavaCallbacks = CodecProxy::NativeCallbacks::New();
JavaCallbacksSupport::AttachNative(mJavaCallbacks,
mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
- mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks);
+ mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks, mDrmStubId);
if (mJavaDecoder == nullptr) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
}
private:
@@ -405,38 +410,42 @@ private:
};
const AudioInfo& mConfig;
};
MediaDataDecoder*
RemoteDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId)
{
- return new RemoteAudioDecoder(aConfig, aFormat, aCallback);
+ return new RemoteAudioDecoder(aConfig, aFormat, aCallback, aDrmStubId);
}
MediaDataDecoder*
RemoteDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer)
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId)
{
- return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer);
+ return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
}
RemoteDataDecoder::RemoteDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback)
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId)
: mType(aType)
, mMimeType(aMimeType)
, mFormat(aFormat)
, mCallback(aCallback)
+ , mDrmStubId(aDrmStubId)
{
}
void
RemoteDataDecoder::Flush()
{
mJavaDecoder->Flush();
}
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -17,46 +17,50 @@
#include <deque>
namespace mozilla {
class RemoteDataDecoder : public MediaDataDecoder {
public:
static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback);
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId);
static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
- layers::ImageContainer* aImageContainer);
+ layers::ImageContainer* aImageContainer,
+ const nsString& aDrmStubId);
virtual ~RemoteDataDecoder() {}
void Flush() override;
void Drain() override;
void Shutdown() override;
void Input(MediaRawData* aSample) override;
const char* GetDescriptionName() const override
{
return "android remote decoder";
}
protected:
RemoteDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
java::sdk::MediaFormat::Param aFormat,
- MediaDataDecoderCallback* aCallback);
+ MediaDataDecoderCallback* aCallback,
+ const nsString& aDrmStubId);
MediaData::Type mType;
nsAutoCString mMimeType;
java::sdk::MediaFormat::GlobalRef mFormat;
MediaDataDecoderCallback* mCallback;
java::CodecProxy::GlobalRef mJavaDecoder;
java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
+ nsString mDrmStubId;
};
} // namespace mozilla
#endif
--- a/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
+++ b/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
@@ -8,17 +8,17 @@ package org.mozilla.gecko.media;
import android.os.Bundle;
import android.view.Surface;
import org.mozilla.gecko.media.FormatParam;
import org.mozilla.gecko.media.ICodecCallbacks;
import org.mozilla.gecko.media.Sample;
interface ICodec {
void setCallbacks(in ICodecCallbacks callbacks);
- boolean configure(in FormatParam format, inout Surface surface, int flags);
+ boolean configure(in FormatParam format, inout Surface surface, int flags, in String drmStubId);
oneway void start();
oneway void stop();
oneway void flush();
oneway void release();
Sample dequeueInput(int size);
oneway void queueInput(in Sample sample);
--- a/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -1,15 +1,17 @@
/* 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/. */
package org.mozilla.gecko.media;
import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CryptoInfo;
+import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.view.Surface;
import java.nio.ByteBuffer;
// A wrapper interface that mimics the new {@link android.media.MediaCodec}
// asynchronous mode API in Lollipop.
@@ -17,18 +19,19 @@ public interface AsyncCodec {
public interface Callbacks {
void onInputBufferAvailable(AsyncCodec codec, int index);
void onOutputBufferAvailable(AsyncCodec codec, int index, BufferInfo info);
void onError(AsyncCodec codec, int error);
void onOutputFormatChanged(AsyncCodec codec, MediaFormat format);
}
public abstract void setCallbacks(Callbacks callbacks, Handler handler);
- public abstract void configure(MediaFormat format, Surface surface, int flags);
+ public abstract void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags);
public abstract void start();
public abstract void stop();
public abstract void flush();
public abstract void release();
public abstract ByteBuffer getInputBuffer(int index);
public abstract ByteBuffer getOutputBuffer(int index);
public abstract void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
+ public abstract void queueSecureInputBuffer(int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
public abstract void releaseOutputBuffer(int index, boolean render);
}
--- a/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
@@ -2,16 +2,17 @@
* 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/. */
package org.mozilla.gecko.media;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.util.Log;
import android.view.Surface;
import java.io.IOException;
@@ -162,30 +163,36 @@ import java.util.concurrent.ConcurrentLi
private void feedSampleToBuffer() {
while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) {
int index = mAvailableInputBuffers.poll();
int len = 0;
Sample sample = mInputSamples.poll();
long pts = sample.info.presentationTimeUs;
int flags = sample.info.flags;
+ MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
if (!sample.isEOS() && sample.buffer != null) {
len = sample.info.size;
ByteBuffer buf = mCodec.getInputBuffer(index);
try {
sample.writeToByteBuffer(buf);
mCallbacks.onInputExhausted();
} catch (IOException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
mSamplePool.recycleInput(sample);
}
- mCodec.queueInputBuffer(index, 0, len, pts, flags);
+
+ if (cryptoInfo != null) {
+ mCodec.queueSecureInputBuffer(index, 0, cryptoInfo, pts, flags);
+ } else {
+ mCodec.queueInputBuffer(index, 0, len, pts, flags);
+ }
}
}
private synchronized void reset() {
mInputSamples.clear();
mAvailableInputBuffers.clear();
}
}
@@ -209,17 +216,20 @@ import java.util.concurrent.ConcurrentLi
try {
release();
} catch (RemoteException e) {
// Nowhere to report the error.
}
}
@Override
- public synchronized boolean configure(FormatParam format, Surface surface, int flags) throws RemoteException {
+ public synchronized boolean configure(FormatParam format,
+ Surface surface,
+ int flags,
+ String drmStubId) throws RemoteException {
if (mCallbacks == null) {
Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()");
return false;
}
if (mCodec != null) {
if (DEBUG) Log.d(LOGTAG, "release existing codec: " + mCodec);
releaseCodec();
@@ -231,18 +241,25 @@ import java.util.concurrent.ConcurrentLi
String codecName = getDecoderForFormat(fmt);
if (codecName == null) {
Log.e(LOGTAG, "FAIL: cannot find codec");
return false;
}
try {
AsyncCodec codec = AsyncCodecFactory.create(codecName);
+
+ MediaCrypto crypto = RemoteMediaDrmBridgeStub.getMediaCrypto(drmStubId);
+ if (DEBUG) {
+ boolean hasCrypto = crypto != null;
+ Log.d(LOGTAG, "configure mediacodec with crypto(" + hasCrypto + ") / Id :" + drmStubId);
+ }
+
codec.setCallbacks(new Callbacks(mCallbacks), null);
- codec.configure(fmt, surface, flags);
+ codec.configure(fmt, surface, crypto, flags);
mCodec = codec;
mInputProcessor = new InputProcessor();
mSamplePool = new SamplePool(codecName);
if (DEBUG) Log.d(LOGTAG, codec.toString() + " created");
return true;
} catch (Exception e) {
if (DEBUG) Log.d(LOGTAG, "FAIL: cannot create codec -- " + codecName);
e.printStackTrace();
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -23,16 +23,17 @@ import java.nio.ByteBuffer;
public final class CodecProxy {
private static final String LOGTAG = "GeckoRemoteCodecProxy";
private static final boolean DEBUG = false;
private ICodec mRemote;
private FormatParam mFormat;
private Surface mOutputSurface;
private CallbacksForwarder mCallbacks;
+ private String mRemoteDrmStubId;
public interface Callbacks {
void onInputExhausted();
void onOutputFormatChanged(MediaFormat format);
void onOutput(Sample output);
void onError(boolean fatal);
}
@@ -77,34 +78,41 @@ public final class CodecProxy {
}
public void reportError(boolean fatal) {
mCallbacks.onError(fatal);
}
}
@WrapForJNI
- public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
- return RemoteManager.getInstance().createCodec(format, surface, callbacks);
+ public static CodecProxy create(MediaFormat format,
+ Surface surface,
+ Callbacks callbacks,
+ String drmStubId) {
+ return RemoteManager.getInstance().createCodec(format, surface, callbacks, drmStubId);
}
- public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
- return new CodecProxy(format, surface, callbacks);
+ public static CodecProxy createCodecProxy(MediaFormat format,
+ Surface surface,
+ Callbacks callbacks,
+ String drmStubId) {
+ return new CodecProxy(format, surface, callbacks, drmStubId);
}
- private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
+ private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks, String drmStubId) {
mFormat = new FormatParam(format);
mOutputSurface = surface;
+ mRemoteDrmStubId = drmStubId;
mCallbacks = new CallbacksForwarder(callbacks);
}
boolean init(ICodec remote) {
try {
remote.setCallbacks(mCallbacks);
- remote.configure(mFormat, mOutputSurface, 0);
+ remote.configure(mFormat, mOutputSurface, 0, mRemoteDrmStubId);
remote.start();
} catch (RemoteException e) {
e.printStackTrace();
return false;
}
mRemote = remote;
return true;
@@ -123,17 +131,16 @@ public final class CodecProxy {
}
@WrapForJNI
public synchronized boolean input(ByteBuffer bytes, BufferInfo info, CryptoInfo cryptoInfo) {
if (mRemote == null) {
Log.e(LOGTAG, "cannot send input to an ended codec");
return false;
}
-
try {
Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ?
Sample.EOS : mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);
mRemote.queueInput(sample);
sample.dispose();
} catch (IOException e) {
e.printStackTrace();
return false;
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -1,15 +1,16 @@
/* 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/. */
package org.mozilla.gecko.media;
import android.media.MediaCodec;
+import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
@@ -289,20 +290,20 @@ final class JellyBeanAsyncCodec implemen
// This thread has no looper. Use poller thread.
looper = mBufferPoller.getLooper();
}
mCallbackSender = new CallbackSender(looper, callbacks);
if (DEBUG) Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender);
}
@Override
- public void configure(MediaFormat format, Surface surface, int flags) {
+ public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
assertCallbacks();
- mCodec.configure(format, surface, null, flags);
+ mCodec.configure(format, surface, crypto, flags);
}
private void assertCallbacks() {
if (mCallbackSender == null) {
throw new IllegalStateException(LOGTAG + ": callback must be supplied with setCallbacks().");
}
}
@@ -332,16 +333,38 @@ final class JellyBeanAsyncCodec implemen
return;
}
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
}
@Override
+ public final void queueSecureInputBuffer(int index,
+ int offset,
+ MediaCodec.CryptoInfo cryptoInfo,
+ long presentationTimeUs,
+ int flags) {
+ assertCallbacks();
+
+ mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+
+ try {
+ mCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ mCallbackSender.notifyError(ERROR_CODEC);
+ return;
+ }
+
+ mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
+ mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
+ }
+
+ @Override
public final void releaseOutputBuffer(int index, boolean render) {
assertCallbacks();
mCodec.releaseOutputBuffer(index, render);
}
@Override
public final ByteBuffer getInputBuffer(int index) {
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
@@ -115,24 +115,25 @@ public final class RemoteManager impleme
}
mConnectionLatch = null;
return ok;
}
public synchronized CodecProxy createCodec(MediaFormat format,
Surface surface,
- CodecProxy.Callbacks callbacks) {
+ CodecProxy.Callbacks callbacks,
+ String drmStubId) {
if (mRemote == null) {
if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
return null;
}
try {
ICodec remote = mRemote.createCodec();
- CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks);
+ CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks, drmStubId);
if (proxy.init(remote)) {
mProxies.add(proxy);
return proxy;
} else {
return null;
}
} catch (RemoteException e) {
e.printStackTrace();
--- a/widget/android/bindings/MediaCodec-classes.txt
+++ b/widget/android/bindings/MediaCodec-classes.txt
@@ -1,5 +1,6 @@
android.media.MediaCodec
android.media.MediaCodec$BufferInfo
android.media.MediaCodec$CryptoInfo
+android.media.MediaCrypto
android.media.MediaDrm$KeyStatus
android.media.MediaFormat
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -188,19 +188,19 @@ auto AudioFocusAgent::NotifyStoppedPlayi
}
const char CodecProxy::name[] =
"org/mozilla/gecko/media/CodecProxy";
constexpr char CodecProxy::Create_t::name[];
constexpr char CodecProxy::Create_t::signature[];
-auto CodecProxy::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2) -> CodecProxy::LocalRef
+auto CodecProxy::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2, mozilla::jni::String::Param a3) -> CodecProxy::LocalRef
{
- return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2);
+ return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2, a3);
}
constexpr char CodecProxy::Flush_t::name[];
constexpr char CodecProxy::Flush_t::signature[];
auto CodecProxy::Flush() const -> bool
{
return mozilla::jni::Method<Flush_t>::Call(CodecProxy::mCtx, nullptr);
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -717,30 +717,31 @@ public:
struct Create_t {
typedef CodecProxy Owner;
typedef CodecProxy::LocalRef ReturnType;
typedef CodecProxy::Param SetterType;
typedef mozilla::jni::Args<
mozilla::jni::Object::Param,
mozilla::jni::Object::Param,
- mozilla::jni::Object::Param> Args;
+ mozilla::jni::Object::Param,
+ mozilla::jni::String::Param> Args;
static constexpr char name[] = "create";
static constexpr char signature[] =
- "(Landroid/media/MediaFormat;Landroid/view/Surface;Lorg/mozilla/gecko/media/CodecProxy$Callbacks;)Lorg/mozilla/gecko/media/CodecProxy;";
+ "(Landroid/media/MediaFormat;Landroid/view/Surface;Lorg/mozilla/gecko/media/CodecProxy$Callbacks;Ljava/lang/String;)Lorg/mozilla/gecko/media/CodecProxy;";
static const bool isStatic = true;
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;
static const mozilla::jni::CallingThread callingThread =
mozilla::jni::CallingThread::ANY;
static const mozilla::jni::DispatchTarget dispatchTarget =
mozilla::jni::DispatchTarget::CURRENT;
};
- static auto Create(mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> CodecProxy::LocalRef;
+ static auto Create(mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::String::Param) -> CodecProxy::LocalRef;
struct Flush_t {
typedef CodecProxy Owner;
typedef bool ReturnType;
typedef bool SetterType;
typedef mozilla::jni::Args<> Args;
static constexpr char name[] = "flush";
static constexpr char signature[] =