Bug 1320618 - Check if codec supports adaptive playback to determine the decoder support recycling or not. r=jolin
authorJames Cheng <jacheng@mozilla.com>
Wed, 30 Nov 2016 16:30:16 +0800
changeset 324921 803eb84eb9a613a2c50644162ee5fdd0adf5ec07
parent 324920 2bb4237ef57486be880f36451a7deb55b0c233f5
child 324922 68ee087eb530ab254cbaf48e3b59eeea96030267
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersjolin
bugs1320618
milestone53.0a1
Bug 1320618 - Check if codec supports adaptive playback to determine the decoder support recycling or not. r=jolin MozReview-Commit-ID: C3pI9NCUgiP
dom/media/platforms/android/MediaCodecDataDecoder.cpp
dom/media/platforms/android/MediaCodecDataDecoder.h
dom/media/platforms/android/RemoteDataDecoder.cpp
mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
mobile/android/base/java/org/mozilla/gecko/media/Codec.java
mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
widget/android/fennec/FennecJNIWrappers.cpp
widget/android/fennec/FennecJNIWrappers.h
--- a/dom/media/platforms/android/MediaCodecDataDecoder.cpp
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.cpp
@@ -118,17 +118,17 @@ public:
                                  presentationTimeUs,
                                  gfx::IntRect(0, 0,
                                               mConfig.mDisplay.width,
                                               mConfig.mDisplay.height));
     INVOKE_CALLBACK(Output, v);
     return NS_OK;
   }
 
-  bool SupportDecoderRecycling() const override { return true; }
+  bool SupportDecoderRecycling() const override { return mIsCodecSupportAdaptivePlayback; }
 
 protected:
   layers::ImageContainer* mImageContainer;
   const VideoInfo& mConfig;
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
   RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
 };
 
@@ -378,21 +378,25 @@ MediaCodecDataDecoder::InitDecoder(Surfa
 
   if (!mDecoder) {
     INVOKE_CALLBACK(Error,
                     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
     return NS_ERROR_FAILURE;
   }
 
   // Check if the video codec supports adaptive playback or not.
-  if (aSurface && java::HardwareCodecCapabilityUtils::CheckSupportsAdaptivePlayback(
-                    mDecoder, nsCString(TranslateMimeType(mMimeType)))) {
-      // TODO: may need to find a way to not use hard code to decide the max w/h.
-      mFormat->SetInteger(MediaFormat::KEY_MAX_WIDTH, 1920);
-      mFormat->SetInteger(MediaFormat::KEY_MAX_HEIGHT, 1080);
+  if (aSurface) {
+    mIsCodecSupportAdaptivePlayback =
+      java::HardwareCodecCapabilityUtils::CheckSupportsAdaptivePlayback(mDecoder,
+        nsCString(TranslateMimeType(mMimeType)));
+    if (mIsCodecSupportAdaptivePlayback) {
+        // TODO: may need to find a way to not use hard code to decide the max w/h.
+        mFormat->SetInteger(MediaFormat::KEY_MAX_WIDTH, 1920);
+        mFormat->SetInteger(MediaFormat::KEY_MAX_HEIGHT, 1080);
+    }
   }
 
   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, crypto, 0), rv);
   NS_ENSURE_SUCCESS(rv = mDecoder->Start(), rv);
--- a/dom/media/platforms/android/MediaCodecDataDecoder.h
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.h
@@ -123,13 +123,15 @@ protected:
 
   ModuleState mState;
 
   SampleQueue mQueue;
   // Durations are stored in microseconds.
   std::deque<media::TimeUnit> mDurations;
 
   nsString mDrmStubId;
+
+  bool mIsCodecSupportAdaptivePlayback = false;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -216,17 +216,17 @@ public:
 
     mJavaDecoder = CodecProxy::Create(mFormat,
                                       mSurfaceTexture->JavaSurface(),
                                       mJavaCallbacks,
                                       mDrmStubId);
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
-
+    mIsCodecSupportAdaptivePlayback = mJavaDecoder->IsAdaptivePlaybackSupported();
     mInputDurations.Clear();
 
     return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
   }
 
   void Flush() override
   {
     mInputDurations.Clear();
@@ -240,17 +240,17 @@ public:
   }
 
   void Input(MediaRawData* aSample) override
   {
     RemoteDataDecoder::Input(aSample);
     mInputDurations.Put(aSample->mDuration);
   }
 
-  bool SupportDecoderRecycling() const override { return true; }
+  bool SupportDecoderRecycling() const override { return mIsCodecSupportAdaptivePlayback; }
 
 private:
   class DurationQueue {
   public:
 
     void Clear()
     {
       mValues.clear();
@@ -276,16 +276,17 @@ private:
   private:
     std::deque<int64_t> mValues;
   };
 
   layers::ImageContainer* mImageContainer;
   const VideoInfo& mConfig;
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
   DurationQueue mInputDurations;
+  bool mIsCodecSupportAdaptivePlayback = false;
 };
 
 class RemoteAudioDecoder final : public RemoteDataDecoder
 {
 public:
   RemoteAudioDecoder(const AudioInfo& aConfig,
                      MediaFormat::Param aFormat,
                      MediaDataDecoderCallback* aCallback,
--- a/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
+++ b/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
@@ -9,16 +9,17 @@ 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, in String drmStubId);
+    boolean isAdaptivePlaybackSupported();
     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
@@ -20,16 +20,17 @@ public interface AsyncCodec {
         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, MediaCrypto crypto, int flags);
+    public abstract boolean isAdaptivePlaybackSupported(String mimeType);
     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);
--- a/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
@@ -198,16 +198,18 @@ import java.util.concurrent.ConcurrentLi
    }
 
     private volatile ICodecCallbacks mCallbacks;
     private AsyncCodec mCodec;
     private InputProcessor mInputProcessor;
     private volatile boolean mFlushing = false;
     private SamplePool mSamplePool;
     private Queue<Sample> mSentOutputs = new ConcurrentLinkedQueue<>();
+    // Value will be updated after configure called.
+    private volatile boolean mIsAdaptivePlaybackSupported = false;
 
     public synchronized void setCallbacks(ICodecCallbacks callbacks) throws RemoteException {
         mCallbacks = callbacks;
         callbacks.asBinder().linkToDeath(this, 0);
     }
 
     // IBinder.DeathRecipient
     @Override
@@ -249,29 +251,47 @@ import java.util.concurrent.ConcurrentLi
 
             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);
+
+            // Video decoder should config with adaptive playback capability.
+            if (surface != null) {
+                mIsAdaptivePlaybackSupported = codec.isAdaptivePlaybackSupported(
+                                                   fmt.getString(MediaFormat.KEY_MIME));
+                if (mIsAdaptivePlaybackSupported) {
+                    if (DEBUG) Log.d(LOGTAG, "codec supports adaptive playback  = " + mIsAdaptivePlaybackSupported);
+                    // TODO: may need to find a way to not use hard code to decide the max w/h.
+                    fmt.setInteger(MediaFormat.KEY_MAX_WIDTH, 1920);
+                    fmt.setInteger(MediaFormat.KEY_MAX_HEIGHT, 1080);
+                }
+            }
+
             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();
             return false;
         }
     }
 
+    @Override
+    public synchronized boolean isAdaptivePlaybackSupported() {
+        return mIsAdaptivePlaybackSupported;
+    }
+
     private void releaseCodec() {
         mInputProcessor.reset();
         try {
             mCodec.release();
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
         mCodec = null;
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -126,16 +126,31 @@ public final class CodecProxy {
             return true;
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
     }
 
     @WrapForJNI
+    public synchronized boolean isAdaptivePlaybackSupported()
+    {
+      if (mRemote == null) {
+          Log.e(LOGTAG, "cannot check isAdaptivePlaybackSupported with an ended codec");
+          return false;
+      }
+      try {
+            return mRemote.isAdaptivePlaybackSupported();
+        } catch (RemoteException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    @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);
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -293,28 +293,24 @@ final class JellyBeanAsyncCodec implemen
         mCallbackSender = new CallbackSender(looper, callbacks);
         if (DEBUG) Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender);
     }
 
     @Override
     public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
         assertCallbacks();
 
-        // Video decoder should config with adaptive playback capability.
-        if (surface != null) {
-            if (HardwareCodecCapabilityUtils.checkSupportsAdaptivePlayback(
-                    mCodec, format.getString(MediaFormat.KEY_MIME))) {
-                // TODO: may need to find a way to not use hard code to decide the max w/h.
-                format.setInteger(MediaFormat.KEY_MAX_WIDTH, 1920);
-                format.setInteger(MediaFormat.KEY_MAX_HEIGHT, 1080);
-            }
-        }
         mCodec.configure(format, surface, crypto, flags);
     }
 
+    @Override
+    public boolean isAdaptivePlaybackSupported(String mimeType) {
+        return HardwareCodecCapabilityUtils.checkSupportsAdaptivePlayback(mCodec, mimeType);
+    }
+
     private void assertCallbacks() {
         if (mCallbackSender == null) {
             throw new IllegalStateException(LOGTAG + ": callback must be supplied with setCallbacks().");
         }
     }
 
     @Override
     public void start() {
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -200,16 +200,24 @@ auto CodecProxy::Flush() const -> bool
 constexpr char CodecProxy::Input_t::name[];
 constexpr char CodecProxy::Input_t::signature[];
 
 auto CodecProxy::Input(mozilla::jni::ByteBuffer::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2) const -> bool
 {
     return mozilla::jni::Method<Input_t>::Call(CodecProxy::mCtx, nullptr, a0, a1, a2);
 }
 
+constexpr char CodecProxy::IsAdaptivePlaybackSupported_t::name[];
+constexpr char CodecProxy::IsAdaptivePlaybackSupported_t::signature[];
+
+auto CodecProxy::IsAdaptivePlaybackSupported() const -> bool
+{
+    return mozilla::jni::Method<IsAdaptivePlaybackSupported_t>::Call(CodecProxy::mCtx, nullptr);
+}
+
 constexpr char CodecProxy::Release_t::name[];
 constexpr char CodecProxy::Release_t::signature[];
 
 auto CodecProxy::Release() const -> bool
 {
     return mozilla::jni::Method<Release_t>::Call(CodecProxy::mCtx, nullptr);
 }
 
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -722,16 +722,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     auto Input(mozilla::jni::ByteBuffer::Param, mozilla::jni::Object::Param, mozilla::jni::Object::Param) const -> bool;
 
+    struct IsAdaptivePlaybackSupported_t {
+        typedef CodecProxy Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "isAdaptivePlaybackSupported";
+        static constexpr char signature[] =
+                "()Z";
+        static const bool isStatic = false;
+        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;
+    };
+
+    auto IsAdaptivePlaybackSupported() const -> bool;
+
     struct Release_t {
         typedef CodecProxy Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "release";
         static constexpr char signature[] =
                 "()Z";