Bug 1265755 - Support encoder case for CodecProxy; r?jolin draft
authorMunro Mengjue Chiang <mchiang@mozilla.com>
Thu, 30 Mar 2017 08:51:12 +0800
changeset 554275 25e80ccf3d9a39d2d761ed2db5c5ff927ca6bfd6
parent 553393 620d4c13a6a4f5eeb741bb3a970a8b9a7532d297
child 554276 62088af683e8a186bd9d6f827821f0d20b700be1
child 554379 1428c9291ee885a9baa612989dd0c58df0a13c00
push id51891
push userbmo:mchiang@mozilla.com
push dateFri, 31 Mar 2017 09:50:14 +0000
reviewersjolin
bugs1265755
milestone55.0a1
Bug 1265755 - Support encoder case for CodecProxy; r?jolin MozReview-Commit-ID: 4cEZQs8spo7
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/FormatParam.java
mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
widget/android/fennec/FennecJNIWrappers.cpp
widget/android/fennec/FennecJNIWrappers.h
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -191,17 +191,18 @@ public:
 
     // Register native methods.
     JavaCallbacksSupport::Init();
 
     mJavaCallbacks = CodecProxy::NativeCallbacks::New();
     JavaCallbacksSupport::AttachNative(
       mJavaCallbacks, mozilla::MakeUnique<CallbacksSupport>(this));
 
-    mJavaDecoder = CodecProxy::Create(mFormat,
+    mJavaDecoder = CodecProxy::Create(false, // false indicates to create a decoder and true denotes encoder
+                                      mFormat,
                                       mSurfaceTexture->JavaSurface(),
                                       mJavaCallbacks,
                                       mDrmStubId);
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                                           __func__);
     }
     mIsCodecSupportAdaptivePlayback =
@@ -271,17 +272,17 @@ public:
     // Register native methods.
     JavaCallbacksSupport::Init();
 
     mJavaCallbacks = CodecProxy::NativeCallbacks::New();
     JavaCallbacksSupport::AttachNative(
       mJavaCallbacks, mozilla::MakeUnique<CallbacksSupport>(this));
 
     mJavaDecoder =
-      CodecProxy::Create(mFormat, nullptr, mJavaCallbacks, mDrmStubId);
+      CodecProxy::Create(false, mFormat, nullptr, mJavaCallbacks, mDrmStubId);
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                                           __func__);
     }
 
     return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
   }
 
--- a/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
+++ b/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
@@ -19,9 +19,10 @@ interface ICodec {
     void stop();
     void flush();
     void release();
 
     Sample dequeueInput(int size);
     oneway void queueInput(in Sample sample);
 
     oneway void releaseOutput(in Sample sample, in boolean render);
+    oneway void setRates(in int newBitRate);
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -30,11 +30,12 @@ public interface AsyncCodec {
     public abstract void stop();
     public abstract void flush();
     // Must be called after flush().
     public abstract void resumeReceivingInputs();
     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 setRates(int newBitRate);
     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
@@ -334,17 +334,17 @@ import java.util.concurrent.ConcurrentLi
         if (mCodec != null) {
             if (DEBUG) { Log.d(LOGTAG, "release existing codec: " + mCodec); }
             releaseCodec();
         }
 
         if (DEBUG) { Log.d(LOGTAG, "configure " + this); }
 
         MediaFormat fmt = format.asFormat();
-        String codecName = getDecoderForFormat(fmt);
+        String codecName = getCodecForFormat(fmt, flags == MediaCodec.CONFIGURE_FLAG_ENCODE ? true : false);
         if (codecName == null) {
             Log.e(LOGTAG, "FAIL: cannot find codec");
             return false;
         }
 
         try {
             AsyncCodec codec = AsyncCodecFactory.create(codecName);
 
@@ -396,25 +396,25 @@ import java.util.concurrent.ConcurrentLi
 
             mCodec.release();
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
         mCodec = null;
     }
 
-    private String getDecoderForFormat(MediaFormat format) {
+    private String getCodecForFormat(MediaFormat format, boolean isEncoder) {
         String mime = format.getString(MediaFormat.KEY_MIME);
         if (mime == null) {
             return null;
         }
         int numCodecs = MediaCodecList.getCodecCount();
         for (int i = 0; i < numCodecs; i++) {
             MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
-            if (info.isEncoder()) {
+            if (info.isEncoder() == !isEncoder) {
                 continue;
             }
             String[] types = info.getSupportedTypes();
             for (String t : types) {
                 if (t.equalsIgnoreCase(mime)) {
                     return info.getName();
                 }
             }
@@ -489,16 +489,25 @@ import java.util.concurrent.ConcurrentLi
     }
 
     @Override
     public synchronized void queueInput(Sample sample) throws RemoteException {
         mInputProcessor.onSample(sample);
     }
 
     @Override
+    public synchronized void setRates(int newBitRate) {
+        try {
+            mCodec.setRates(newBitRate);
+        } catch (Exception e) {
+            reportError(Error.FATAL, e);
+        }
+    }
+
+    @Override
     public synchronized void releaseOutput(Sample sample, boolean render) {
         try {
             mOutputProcessor.onRelease(sample, render);
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -22,16 +22,17 @@ import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 // Proxy class of ICodec binder.
 public final class CodecProxy {
     private static final String LOGTAG = "GeckoRemoteCodecProxy";
     private static final boolean DEBUG = false;
 
     private ICodec mRemote;
+    private boolean mIsEncoder;
     private FormatParam mFormat;
     private Surface mOutputSurface;
     private CallbacksForwarder mCallbacks;
     private String mRemoteDrmStubId;
     private Queue<Sample> mSurfaceOutputs = new ConcurrentLinkedQueue<>();
 
     public interface Callbacks {
         void onInputExhausted();
@@ -97,41 +98,44 @@ public final class CodecProxy {
         }
 
         private void setEndOfInput(boolean end) {
             mEndOfInput = end;
         }
     }
 
     @WrapForJNI
-    public static CodecProxy create(MediaFormat format,
+    public static CodecProxy create(boolean isEncoder,
+                                    MediaFormat format,
                                     Surface surface,
                                     Callbacks callbacks,
                                     String drmStubId) {
-        return RemoteManager.getInstance().createCodec(format, surface, callbacks, drmStubId);
+        return RemoteManager.getInstance().createCodec(isEncoder, format, surface, callbacks, drmStubId);
     }
 
-    public static CodecProxy createCodecProxy(MediaFormat format,
+    public static CodecProxy createCodecProxy(boolean isEncoder,
+                                              MediaFormat format,
                                               Surface surface,
                                               Callbacks callbacks,
                                               String drmStubId) {
-        return new CodecProxy(format, surface, callbacks, drmStubId);
+        return new CodecProxy(isEncoder, format, surface, callbacks, drmStubId);
     }
 
-    private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks, String drmStubId) {
+    private CodecProxy(boolean isEncoder, MediaFormat format, Surface surface, Callbacks callbacks, String drmStubId) {
+        mIsEncoder = isEncoder;
         mFormat = new FormatParam(format);
         mOutputSurface = surface;
         mRemoteDrmStubId = drmStubId;
         mCallbacks = new CallbacksForwarder(callbacks);
     }
 
     boolean init(ICodec remote) {
         try {
             remote.setCallbacks(mCallbacks);
-            if (!remote.configure(mFormat, mOutputSurface, 0, mRemoteDrmStubId)) {
+            if (!remote.configure(mFormat, mOutputSurface, mIsEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0, mRemoteDrmStubId)) {
                 return false;
             }
             remote.start();
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
 
@@ -253,16 +257,42 @@ public final class CodecProxy {
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
         return true;
     }
 
     @WrapForJNI
+    public synchronized boolean setRates(int newBitRate) {
+        if (!mIsEncoder) {
+            Log.w(LOGTAG, "this api is encoder-only");
+            return false;
+        }
+
+        if (android.os.Build.VERSION.SDK_INT < 19) {
+            Log.w(LOGTAG, "this api was added in API level 19");
+            return false;
+        }
+
+        if (mRemote == null) {
+            Log.w(LOGTAG, "codec already ended");
+            return true;
+        }
+
+        try {
+            mRemote.setRates(newBitRate);
+        } catch (RemoteException e) {
+            Log.e(LOGTAG, "remote fail to set rates:" + newBitRate);
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    @WrapForJNI
     public synchronized boolean releaseOutput(Sample sample, boolean render) {
         if (!mSurfaceOutputs.remove(sample)) {
             if (mRemote != null) Log.w(LOGTAG, "already released: " + sample);
             return true;
         }
 
         if (mRemote == null) {
             Log.w(LOGTAG, "codec already ended");
--- a/mobile/android/base/java/org/mozilla/gecko/media/FormatParam.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/FormatParam.java
@@ -14,16 +14,21 @@ import java.nio.ByteBuffer;
 /** A wrapper to make {@link MediaFormat} parcelable.
  *  Supports following keys:
  *  <ul>
  *  <li>{@link MediaFormat#KEY_MIME}</li>
  *  <li>{@link MediaFormat#KEY_WIDTH}</li>
  *  <li>{@link MediaFormat#KEY_HEIGHT}</li>
  *  <li>{@link MediaFormat#KEY_CHANNEL_COUNT}</li>
  *  <li>{@link MediaFormat#KEY_SAMPLE_RATE}</li>
+ *  <li>{@link MediaFormat#KEY_BIT_RATE}</li>
+ *  <li>{@link MediaFormat#KEY_BITRATE_MODE}</li>
+ *  <li>{@link MediaFormat#KEY_COLOR_FORMAT}</li>
+ *  <li>{@link MediaFormat#KEY_FRAME_RATE}</li>
+ *  <li>{@link MediaFormat#KEY_I_FRAME_INTERVAL}</li>
  *  <li>"csd-0"</li>
  *  <li>"csd-1"</li>
  *  </ul>
  */
 public final class FormatParam implements Parcelable {
     // Keys for codec specific config bits not exposed in {@link MediaFormat}.
     private static final String KEY_CONFIG_0 = "csd-0";
     private static final String KEY_CONFIG_1 = "csd-1";
@@ -89,16 +94,36 @@ public final class FormatParam implement
         if (bundle.containsKey(KEY_CONFIG_0)) {
             mFormat.setByteBuffer(KEY_CONFIG_0,
                     ByteBuffer.wrap(bundle.getByteArray(KEY_CONFIG_0)));
         }
         if (bundle.containsKey(KEY_CONFIG_1)) {
             mFormat.setByteBuffer(KEY_CONFIG_1,
                     ByteBuffer.wrap(bundle.getByteArray((KEY_CONFIG_1))));
         }
+        if (bundle.containsKey(MediaFormat.KEY_BIT_RATE)) {
+            mFormat.setInteger(MediaFormat.KEY_BIT_RATE,
+                    bundle.getInt(MediaFormat.KEY_BIT_RATE));
+        }
+        if (bundle.containsKey(MediaFormat.KEY_BITRATE_MODE)) {
+            mFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+                    bundle.getInt(MediaFormat.KEY_BITRATE_MODE));
+        }
+        if (bundle.containsKey(MediaFormat.KEY_COLOR_FORMAT)) {
+            mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                    bundle.getInt(MediaFormat.KEY_COLOR_FORMAT));
+        }
+        if (bundle.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+            mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,
+                    bundle.getInt(MediaFormat.KEY_FRAME_RATE));
+        }
+        if (bundle.containsKey(MediaFormat.KEY_I_FRAME_INTERVAL)) {
+            mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,
+                    bundle.getInt(MediaFormat.KEY_I_FRAME_INTERVAL));
+        }
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeBundle(toBundle());
     }
 
     private Bundle toBundle() {
@@ -123,11 +148,26 @@ public final class FormatParam implement
             bundle.putByteArray(KEY_CONFIG_0,
                 Sample.byteArrayFromBuffer(bytes, 0, bytes.capacity()));
         }
         if (mFormat.containsKey(KEY_CONFIG_1)) {
             ByteBuffer bytes = mFormat.getByteBuffer(KEY_CONFIG_1);
             bundle.putByteArray(KEY_CONFIG_1,
                 Sample.byteArrayFromBuffer(bytes, 0, bytes.capacity()));
         }
+        if (mFormat.containsKey(MediaFormat.KEY_BIT_RATE)) {
+            bundle.putInt(MediaFormat.KEY_BIT_RATE, mFormat.getInteger(MediaFormat.KEY_BIT_RATE));
+        }
+        if (mFormat.containsKey(MediaFormat.KEY_BITRATE_MODE)) {
+            bundle.putInt(MediaFormat.KEY_BITRATE_MODE, mFormat.getInteger(MediaFormat.KEY_BITRATE_MODE));
+        }
+        if (mFormat.containsKey(MediaFormat.KEY_COLOR_FORMAT)) {
+            bundle.putInt(MediaFormat.KEY_COLOR_FORMAT, mFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT));
+        }
+        if (mFormat.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+            bundle.putInt(MediaFormat.KEY_FRAME_RATE, mFormat.getInteger(MediaFormat.KEY_FRAME_RATE));
+        }
+        if (mFormat.containsKey(MediaFormat.KEY_I_FRAME_INTERVAL)) {
+            bundle.putInt(MediaFormat.KEY_I_FRAME_INTERVAL, mFormat.getInteger(MediaFormat.KEY_I_FRAME_INTERVAL));
+        }
         return bundle;
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -8,16 +8,17 @@ import org.mozilla.gecko.util.HardwareCo
 
 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.os.Bundle;
 import android.util.Log;
 import android.view.Surface;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
 // Implement async API using MediaCodec sync mode (API v16).
 // This class uses internal worker thread/handler (mBufferPoller) to poll
@@ -328,21 +329,36 @@ final class JellyBeanAsyncCodec implemen
     @Override
     public void resumeReceivingInputs() {
         for (int i = 0; i < mInputBuffers.length; i++) {
             mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
         }
     }
 
     @Override
+    public final void setRates(int newBitRate) {
+        if (android.os.Build.VERSION.SDK_INT >= 19) {
+            Bundle params = new Bundle();
+            params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, newBitRate * 1000);
+            mCodec.setParameters(params);
+        }
+    }
+
+    @Override
     public final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags) {
         assertCallbacks();
 
         mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
 
+        if ((android.os.Build.VERSION.SDK_INT >= 19) && ((flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0)) {
+            Bundle params = new Bundle();
+            params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
+            mCodec.setParameters(params);
+        }
+
         try {
             mCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
         } catch (IllegalStateException e) {
             e.printStackTrace();
             mCallbackSender.notifyError(ERROR_CODEC);
             return;
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
@@ -104,27 +104,28 @@ public final class RemoteManager impleme
         if (mRemote != null) {
             return true;
         }
 
         if (DEBUG) Log.d(LOGTAG, "init remote manager " + this);
         return mConnection.connect();
     }
 
-    public synchronized CodecProxy createCodec(MediaFormat format,
+    public synchronized CodecProxy createCodec(boolean isEncoder,
+                                               MediaFormat format,
                                                Surface surface,
                                                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, drmStubId);
+            CodecProxy proxy = CodecProxy.createCodecProxy(isEncoder, format, surface, callbacks, drmStubId);
             if (proxy.init(remote)) {
                 mProxies.add(proxy);
                 return proxy;
             } else {
                 return null;
             }
         } catch (RemoteException e) {
             e.printStackTrace();
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -179,19 +179,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, mozilla::jni::String::Param a3) -> CodecProxy::LocalRef
+auto CodecProxy::Create(bool a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2, mozilla::jni::Object::Param a3, mozilla::jni::String::Param a4) -> CodecProxy::LocalRef
 {
-    return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2, a3);
+    return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2, a3, a4);
 }
 
 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);
@@ -224,16 +224,24 @@ auto CodecProxy::Release() const -> bool
 constexpr char CodecProxy::ReleaseOutput_t::name[];
 constexpr char CodecProxy::ReleaseOutput_t::signature[];
 
 auto CodecProxy::ReleaseOutput(mozilla::jni::Object::Param a0, bool a1) const -> bool
 {
     return mozilla::jni::Method<ReleaseOutput_t>::Call(CodecProxy::mCtx, nullptr, a0, a1);
 }
 
+constexpr char CodecProxy::SetRates_t::name[];
+constexpr char CodecProxy::SetRates_t::signature[];
+
+auto CodecProxy::SetRates(int32_t a0) const -> bool
+{
+    return mozilla::jni::Method<SetRates_t>::Call(CodecProxy::mCtx, nullptr, a0);
+}
+
 const char CodecProxy::NativeCallbacks::name[] =
         "org/mozilla/gecko/media/CodecProxy$NativeCallbacks";
 
 constexpr char CodecProxy::NativeCallbacks::New_t::name[];
 constexpr char CodecProxy::NativeCallbacks::New_t::signature[];
 
 auto CodecProxy::NativeCallbacks::New() -> NativeCallbacks::LocalRef
 {
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -663,33 +663,34 @@ public:
 
     class NativeCallbacks;
 
     struct Create_t {
         typedef CodecProxy Owner;
         typedef CodecProxy::LocalRef ReturnType;
         typedef CodecProxy::Param SetterType;
         typedef mozilla::jni::Args<
+                bool,
                 mozilla::jni::Object::Param,
                 mozilla::jni::Object::Param,
                 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;Ljava/lang/String;)Lorg/mozilla/gecko/media/CodecProxy;";
+                "(ZLandroid/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, mozilla::jni::String::Param) -> CodecProxy::LocalRef;
+    static auto Create(bool, 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[] =
@@ -781,16 +782,36 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     auto ReleaseOutput(mozilla::jni::Object::Param, bool) const -> bool;
 
+    struct SetRates_t {
+        typedef CodecProxy Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "setRates";
+        static constexpr char signature[] =
+                "(I)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 SetRates(int32_t) const -> bool;
+
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
 class CodecProxy::NativeCallbacks : public mozilla::jni::ObjectBase<NativeCallbacks>
 {
 public: