Bug 1350253 - Provide GeckoHlsDemuxerWrapper & corresponding generated JNI files as the glue for HLSDemuxer and GeckoHlsPlayer. draft
authorKilik Kuo <kikuo@mozilla.com>
Thu, 25 May 2017 20:48:03 +0800
changeset 584412 7111d29eff59c7576a276a23cf9f8e508f19598f
parent 584399 c4bdd6c56c7928dc64aa9375ca8459f7f53bf32d
child 630372 0c9be91577e6ce1bcd75570e55894275d785d045
push id60731
push userbmo:kikuo@mozilla.com
push dateThu, 25 May 2017 13:13:45 +0000
bugs1350253
milestone55.0a1
Bug 1350253 - Provide GeckoHlsDemuxerWrapper & corresponding generated JNI files as the glue for HLSDemuxer and GeckoHlsPlayer. MozReview-Commit-ID: 62cnGQsmdNS
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsDemuxerWrapper.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -461,16 +461,17 @@ gvjar.sources += [geckoview_thirdparty_s
     'java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
     'java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
 ]]
 
 if CONFIG['MOZ_ANDROID_HLS_SUPPORT']:
     gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
         'media/GeckoAudioInfo.java',
         'media/GeckoHlsAudioRenderer.java',
+        'media/GeckoHlsDemuxerWrapper.java',
         'media/GeckoHlsPlayer.java',
         'media/GeckoHlsRendererBase.java',
         'media/GeckoHlsSample.java',
         'media/GeckoHlsVideoRenderer.java',
         'media/GeckoVideoInfo.java',
         'media/Utils.java',
     ]]
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsDemuxerWrapper.java
@@ -0,0 +1,200 @@
+/* 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.util.Log;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.util.MimeTypes;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+
+public final class GeckoHlsDemuxerWrapper {
+    private static final String LOGTAG = "GeckoHlsDemuxerWrapper";
+    private static final boolean DEBUG = true;
+
+    // NOTE : These TRACK definitions should be synced with Gecko.
+    public enum TrackType {
+        UNDEFINED(0),
+        AUDIO(1),
+        VIDEO(2),
+        TEXT(3);
+        private int mType;
+        private TrackType(int type) {
+            mType = type;
+        }
+        public int value() {
+            return mType;
+        }
+    }
+
+    private GeckoHlsPlayer mPlayer = null;
+
+    public static class HlsDemuxerCallbacks extends JNIObject
+        implements GeckoHlsPlayer.DemuxerCallbacks {
+
+        @WrapForJNI(calledFrom = "gecko")
+        HlsDemuxerCallbacks() {}
+
+        @Override
+        @WrapForJNI
+        public native void onInitialized(boolean hasAudio, boolean hasVideo);
+
+        @Override
+        @WrapForJNI
+        public native void onError(int errorCode);
+
+        @Override // JNIObject
+        protected void disposeNative() {
+            throw new UnsupportedOperationException();
+        }
+    } // HlsDemuxerCallbacks
+
+    private static void assertTrue(boolean condition) {
+        if (DEBUG && !condition) {
+            throw new AssertionError("Expected condition to be true");
+        }
+    }
+
+    private GeckoHlsPlayer.TrackType getPlayerTrackType(int trackType) {
+        if (trackType == TrackType.AUDIO.value()) {
+            return GeckoHlsPlayer.TrackType.AUDIO;
+        } else if (trackType == TrackType.VIDEO.value()) {
+            return GeckoHlsPlayer.TrackType.VIDEO;
+        } else if (trackType == TrackType.TEXT.value()) {
+            return GeckoHlsPlayer.TrackType.TEXT;
+        }
+        return GeckoHlsPlayer.TrackType.UNDEFINED;
+    }
+
+    @WrapForJNI
+    public long getBuffered() {
+        assertTrue(mPlayer != null);
+        return mPlayer.getBufferedPosition();
+    }
+
+    @WrapForJNI(calledFrom = "gecko")
+    public static GeckoHlsDemuxerWrapper create(GeckoHlsPlayer player,
+                                                GeckoHlsPlayer.DemuxerCallbacks callback) {
+        return new GeckoHlsDemuxerWrapper(player, callback);
+    }
+
+    @WrapForJNI
+    public int getNumberOfTracks(int trackType) {
+        int tracks = mPlayer != null ? mPlayer.getNumberOfTracks(getPlayerTrackType(trackType)) : 0;
+        if (DEBUG) Log.d(LOGTAG, "[GetNumberOfTracks] type : " + trackType + ", num = " + tracks);
+        return tracks;
+    }
+
+    @WrapForJNI
+    public GeckoAudioInfo getAudioInfo(int index) {
+        assertTrue(mPlayer != null);
+
+        if (DEBUG) Log.d(LOGTAG, "[getAudioInfo] formatIndex : " + index);
+        Format fmt = mPlayer.getAudioTrackFormat(index);
+        if (fmt == null) {
+            return null;
+        }
+        /* According to https://github.com/google/ExoPlayer/blob
+         * /d979469659861f7fe1d39d153b90bdff1ab479cc/library/core/src/main
+         * /java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java#L221-L224,
+         * if the input audio format is not raw, exoplayer would assure that
+         * the sample's pcm encoding bitdepth is 16.
+         * For HLS content, it should always be 16.
+         */
+        assertTrue(!MimeTypes.AUDIO_RAW.equals(fmt.sampleMimeType));
+        // For HLS content, csd-0 is enough.
+        byte[] csd = fmt.initializationData.isEmpty() ? null : fmt.initializationData.get(0);
+        GeckoAudioInfo aInfo = new GeckoAudioInfo(fmt.sampleRate, fmt.channelCount,
+                                                  16, 0, mPlayer.getDuration(),
+                                                  fmt.sampleMimeType, csd);
+        return aInfo;
+    }
+
+    @WrapForJNI
+    public GeckoVideoInfo getVideoInfo(int index) {
+        assertTrue(mPlayer != null);
+
+        if (DEBUG) Log.d(LOGTAG, "[getVideoInfo] formatIndex : " + index);
+        Format fmt = mPlayer.getVideoTrackFormat(index);
+        if (fmt == null) {
+            return null;
+        }
+        GeckoVideoInfo vInfo = new GeckoVideoInfo(fmt.width, fmt.height,
+                                                  fmt.width, fmt.height,
+                                                  fmt.rotationDegrees, fmt.stereoMode,
+                                                  mPlayer.getDuration(), fmt.sampleMimeType,
+                                                  null, null);
+        return vInfo;
+    }
+
+    @WrapForJNI
+    public boolean seek(long seekTime) {
+        // seekTime : microseconds.
+        assertTrue(mPlayer != null);
+        if (DEBUG) Log.d(LOGTAG, "seek  : " + seekTime + " (Us)");
+        return mPlayer.seek(seekTime);
+    }
+
+    GeckoHlsDemuxerWrapper(GeckoHlsPlayer player,
+                           GeckoHlsPlayer.DemuxerCallbacks callback) {
+        if (DEBUG) Log.d(LOGTAG, "Constructing GeckoHlsDemuxerWrapper ...");
+        assertTrue(callback != null);
+        assertTrue(player != null);
+        try {
+            this.mPlayer = player;
+            this.mPlayer.addDemuxerWrapperCallbackListener(callback);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Constructing GeckoHlsDemuxerWrapper ... error", e);
+            callback.onError(GeckoHlsPlayer.DemuxerError.UNKNOWN.code());
+        }
+    }
+
+    @WrapForJNI
+    private GeckoHlsSample[] getSamples(int mediaType, int number) {
+        ConcurrentLinkedQueue<GeckoHlsSample> samples = null;
+        // getA/VSamples will always return a non-null instance.
+        if (mediaType == TrackType.VIDEO.value()) {
+            samples = mPlayer.getVideoSamples(number);
+        } else if (mediaType == TrackType.AUDIO.value()) {
+            samples = mPlayer.getAudioSamples(number);
+        }
+
+        assertTrue(samples.size() <= number);
+        return samples.toArray(new GeckoHlsSample[samples.size()]);
+    }
+
+    @WrapForJNI
+    private long getNextKeyFrameTime() {
+        assertTrue(mPlayer != null);
+        return mPlayer.getNextKeyFrameTime();
+    }
+
+    @WrapForJNI
+    private boolean isLiveStream() {
+        assertTrue(mPlayer != null);
+        return mPlayer.isLiveStream();
+    }
+
+    @WrapForJNI // Called when native object is destroyed.
+    private void destroy() {
+        if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed.");
+        if (mPlayer != null) {
+            release();
+        }
+    }
+
+    private void release() {
+        assertTrue(mPlayer != null);
+        if (DEBUG) Log.d(LOGTAG, "release GeckoHlsPlayer...");
+        mPlayer.release();
+        mPlayer = null;
+    }
+}
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -498,16 +498,35 @@ const JNINativeMethod CodecProxy::Native
             ::template Wrap<&Impl::OnOutput>),
 
     mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t>(
             mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t, Impl>
             ::template Wrap<&Impl::OnOutputFormatChanged>)
 };
 
 template<class Impl>
+class GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::Natives : public mozilla::jni::NativeImpl<HlsDemuxerCallbacks, Impl>
+{
+public:
+    static const JNINativeMethod methods[2];
+};
+
+template<class Impl>
+const JNINativeMethod GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::Natives<Impl>::methods[] = {
+
+    mozilla::jni::MakeNativeMethod<GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnError_t>(
+            mozilla::jni::NativeStub<GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnError_t, Impl>
+            ::template Wrap<&Impl::OnError>),
+
+    mozilla::jni::MakeNativeMethod<GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnInitialized_t>(
+            mozilla::jni::NativeStub<GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnInitialized_t, Impl>
+            ::template Wrap<&Impl::OnInitialized>)
+};
+
+template<class Impl>
 class MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives : public mozilla::jni::NativeImpl<NativeMediaDrmProxyCallbacks, Impl>
 {
 public:
     static const JNINativeMethod methods[7];
 };
 
 template<class Impl>
 const JNINativeMethod MediaDrmProxy::NativeMediaDrmProxyCallbacks::Natives<Impl>::methods[] = {
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1918,16 +1918,116 @@ auto GeckoAudioInfo::Profile() const -> 
 constexpr char GeckoAudioInfo::Rate_t::name[];
 constexpr char GeckoAudioInfo::Rate_t::signature[];
 
 auto GeckoAudioInfo::Rate() const -> int32_t
 {
     return mozilla::jni::Field<Rate_t>::Get(GeckoAudioInfo::mCtx, nullptr);
 }
 
+const char GeckoHlsDemuxerWrapper::name[] =
+        "org/mozilla/gecko/media/GeckoHlsDemuxerWrapper";
+
+constexpr char GeckoHlsDemuxerWrapper::Create_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::Create_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> GeckoHlsDemuxerWrapper::LocalRef
+{
+    return mozilla::jni::Method<Create_t>::Call(GeckoHlsDemuxerWrapper::Context(), nullptr, a0, a1);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::Destroy_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::Destroy_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::Destroy() const -> void
+{
+    return mozilla::jni::Method<Destroy_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetAudioInfo_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetAudioInfo_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetAudioInfo(int32_t a0) const -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<GetAudioInfo_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr, a0);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetBuffered_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetBuffered_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetBuffered() const -> int64_t
+{
+    return mozilla::jni::Method<GetBuffered_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetNextKeyFrameTime_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetNextKeyFrameTime_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetNextKeyFrameTime() const -> int64_t
+{
+    return mozilla::jni::Method<GetNextKeyFrameTime_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetNumberOfTracks_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetNumberOfTracks_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetNumberOfTracks(int32_t a0) const -> int32_t
+{
+    return mozilla::jni::Method<GetNumberOfTracks_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr, a0);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetSamples_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetSamples_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetSamples(int32_t a0, int32_t a1) const -> mozilla::jni::ObjectArray::LocalRef
+{
+    return mozilla::jni::Method<GetSamples_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr, a0, a1);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::GetVideoInfo_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::GetVideoInfo_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::GetVideoInfo(int32_t a0) const -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<GetVideoInfo_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr, a0);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::IsLiveStream_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::IsLiveStream_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::IsLiveStream() const -> bool
+{
+    return mozilla::jni::Method<IsLiveStream_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::Seek_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::Seek_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::Seek(int64_t a0) const -> bool
+{
+    return mozilla::jni::Method<Seek_t>::Call(GeckoHlsDemuxerWrapper::mCtx, nullptr, a0);
+}
+
+const char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::name[] =
+        "org/mozilla/gecko/media/GeckoHlsDemuxerWrapper$HlsDemuxerCallbacks";
+
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::New_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::New_t::signature[];
+
+auto GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::New() -> HlsDemuxerCallbacks::LocalRef
+{
+    return mozilla::jni::Constructor<New_t>::Call(HlsDemuxerCallbacks::Context(), nullptr);
+}
+
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnError_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnError_t::signature[];
+
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnInitialized_t::name[];
+constexpr char GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks::OnInitialized_t::signature[];
+
 const char GeckoHlsSample::name[] =
         "org/mozilla/gecko/media/GeckoHlsSample";
 
 constexpr char GeckoHlsSample::IsEOS_t::name[];
 constexpr char GeckoHlsSample::IsEOS_t::signature[];
 
 auto GeckoHlsSample::IsEOS() const -> bool
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -5537,16 +5537,297 @@ public:
 
     auto Rate() const -> int32_t;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
+class GeckoHlsDemuxerWrapper : public mozilla::jni::ObjectBase<GeckoHlsDemuxerWrapper>
+{
+public:
+    static const char name[];
+
+    explicit GeckoHlsDemuxerWrapper(const Context& ctx) : ObjectBase<GeckoHlsDemuxerWrapper>(ctx) {}
+
+    class HlsDemuxerCallbacks;
+
+    struct Create_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef GeckoHlsDemuxerWrapper::LocalRef ReturnType;
+        typedef GeckoHlsDemuxerWrapper::Param SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::Object::Param,
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "create";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/media/GeckoHlsPlayer;Lorg/mozilla/gecko/media/GeckoHlsPlayer$DemuxerCallbacks;)Lorg/mozilla/gecko/media/GeckoHlsDemuxerWrapper;";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto Create(mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> GeckoHlsDemuxerWrapper::LocalRef;
+
+    struct Destroy_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "destroy";
+        static constexpr char signature[] =
+                "()V";
+        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 Destroy() const -> void;
+
+    struct GetAudioInfo_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "getAudioInfo";
+        static constexpr char signature[] =
+                "(I)Lorg/mozilla/gecko/media/GeckoAudioInfo;";
+        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 GetAudioInfo(int32_t) const -> mozilla::jni::Object::LocalRef;
+
+    struct GetBuffered_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef int64_t ReturnType;
+        typedef int64_t SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "getBuffered";
+        static constexpr char signature[] =
+                "()J";
+        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 GetBuffered() const -> int64_t;
+
+    struct GetNextKeyFrameTime_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef int64_t ReturnType;
+        typedef int64_t SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "getNextKeyFrameTime";
+        static constexpr char signature[] =
+                "()J";
+        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 GetNextKeyFrameTime() const -> int64_t;
+
+    struct GetNumberOfTracks_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef int32_t ReturnType;
+        typedef int32_t SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "getNumberOfTracks";
+        static constexpr char signature[] =
+                "(I)I";
+        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 GetNumberOfTracks(int32_t) const -> int32_t;
+
+    struct GetSamples_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef mozilla::jni::ObjectArray::LocalRef ReturnType;
+        typedef mozilla::jni::ObjectArray::Param SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "getSamples";
+        static constexpr char signature[] =
+                "(II)[Lorg/mozilla/gecko/media/GeckoHlsSample;";
+        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 GetSamples(int32_t, int32_t) const -> mozilla::jni::ObjectArray::LocalRef;
+
+    struct GetVideoInfo_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "getVideoInfo";
+        static constexpr char signature[] =
+                "(I)Lorg/mozilla/gecko/media/GeckoVideoInfo;";
+        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 GetVideoInfo(int32_t) const -> mozilla::jni::Object::LocalRef;
+
+    struct IsLiveStream_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "isLiveStream";
+        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 IsLiveStream() const -> bool;
+
+    struct Seek_t {
+        typedef GeckoHlsDemuxerWrapper Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<
+                int64_t> Args;
+        static constexpr char name[] = "seek";
+        static constexpr char signature[] =
+                "(J)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 Seek(int64_t) const -> bool;
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+};
+
+class GeckoHlsDemuxerWrapper::HlsDemuxerCallbacks : public mozilla::jni::ObjectBase<HlsDemuxerCallbacks>
+{
+public:
+    static const char name[];
+
+    explicit HlsDemuxerCallbacks(const Context& ctx) : ObjectBase<HlsDemuxerCallbacks>(ctx) {}
+
+    struct New_t {
+        typedef HlsDemuxerCallbacks Owner;
+        typedef HlsDemuxerCallbacks::LocalRef ReturnType;
+        typedef HlsDemuxerCallbacks::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "<init>";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto New() -> HlsDemuxerCallbacks::LocalRef;
+
+    struct OnError_t {
+        typedef HlsDemuxerCallbacks Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "onError";
+        static constexpr char signature[] =
+                "(I)V";
+        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;
+    };
+
+    struct OnInitialized_t {
+        typedef HlsDemuxerCallbacks Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                bool,
+                bool> Args;
+        static constexpr char name[] = "onInitialized";
+        static constexpr char signature[] =
+                "(ZZ)V";
+        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;
+    };
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+    template<class Impl> class Natives;
+};
+
 class GeckoHlsSample : public mozilla::jni::ObjectBase<GeckoHlsSample>
 {
 public:
     static const char name[];
 
     explicit GeckoHlsSample(const Context& ctx) : ObjectBase<GeckoHlsSample>(ctx) {}
 
     struct IsEOS_t {