summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHLSDemuxerWrapper.java
blob: cd732fe535d6c143baad1380e49a4954dc9a63e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* 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 java.util.concurrent.ConcurrentLinkedQueue;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.geckoview.BuildConfig;

public final class GeckoHLSDemuxerWrapper {
  private static final String LOGTAG = "GeckoHLSDemuxerWrapper";
  private static final boolean DEBUG = !BuildConfig.MOZILLA_OFFICIAL;

  // 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(final int type) {
      mType = type;
    }

    public int value() {
      return mType;
    }
  }

  private BaseHlsPlayer mPlayer = null;

  public static class Callbacks extends JNIObject implements BaseHlsPlayer.DemuxerCallbacks {
    @WrapForJNI(calledFrom = "gecko")
    Callbacks() {}

    @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();
    }
  } // Callbacks

  private static void assertTrue(final boolean condition) {
    if (DEBUG && !condition) {
      throw new AssertionError("Expected condition to be true");
    }
  }

  private BaseHlsPlayer.TrackType getPlayerTrackType(final int trackType) {
    if (trackType == TrackType.AUDIO.value()) {
      return BaseHlsPlayer.TrackType.AUDIO;
    } else if (trackType == TrackType.VIDEO.value()) {
      return BaseHlsPlayer.TrackType.VIDEO;
    } else if (trackType == TrackType.TEXT.value()) {
      return BaseHlsPlayer.TrackType.TEXT;
    }
    return BaseHlsPlayer.TrackType.UNDEFINED;
  }

  @WrapForJNI
  public long getBuffered() {
    assertTrue(mPlayer != null);
    return mPlayer.getBufferedPosition();
  }

  @WrapForJNI(calledFrom = "gecko")
  public static GeckoHLSDemuxerWrapper create(
      final int id, final BaseHlsPlayer.DemuxerCallbacks callback) {
    return new GeckoHLSDemuxerWrapper(id, callback);
  }

  @WrapForJNI
  public int getNumberOfTracks(final int trackType) {
    assertTrue(mPlayer != null);
    final int tracks = mPlayer.getNumberOfTracks(getPlayerTrackType(trackType));
    if (DEBUG) Log.d(LOGTAG, "[GetNumberOfTracks] type : " + trackType + ", num = " + tracks);
    return tracks;
  }

  @WrapForJNI
  public GeckoAudioInfo getAudioInfo(final int index) {
    assertTrue(mPlayer != null);
    if (DEBUG) Log.d(LOGTAG, "[getAudioInfo] formatIndex : " + index);
    final GeckoAudioInfo aInfo = mPlayer.getAudioInfo(index);
    return aInfo;
  }

  @WrapForJNI
  public GeckoVideoInfo getVideoInfo(final int index) {
    assertTrue(mPlayer != null);
    if (DEBUG) Log.d(LOGTAG, "[getVideoInfo] formatIndex : " + index);
    final GeckoVideoInfo vInfo = mPlayer.getVideoInfo(index);
    return vInfo;
  }

  @WrapForJNI
  public boolean seek(final long seekTime) {
    // seekTime : microseconds.
    assertTrue(mPlayer != null);
    if (DEBUG) Log.d(LOGTAG, "seek  : " + seekTime + " (Us)");
    return mPlayer.seek(seekTime);
  }

  GeckoHLSDemuxerWrapper(final int id, final BaseHlsPlayer.DemuxerCallbacks callback) {
    if (DEBUG) Log.d(LOGTAG, "Constructing GeckoHLSDemuxerWrapper ...");
    assertTrue(callback != null);
    try {
      mPlayer = GeckoPlayerFactory.getPlayer(id);
      if (mPlayer != null) {
        mPlayer.addDemuxerWrapperCallbackListener(callback);
      }
    } catch (final Exception e) {
      Log.e(LOGTAG, "Constructing GeckoHLSDemuxerWrapper ... error", e);
      callback.onError(BaseHlsPlayer.DemuxerError.UNKNOWN.code());
    }
  }

  @WrapForJNI
  private GeckoHLSSample[] getSamples(final int mediaType, final int number) {
    assertTrue(mPlayer != null);
    ConcurrentLinkedQueue<GeckoHLSSample> samples = null;
    // getA/VSamples will always return a non-null instance.
    samples = mPlayer.getSamples(getPlayerTrackType(mediaType), 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 BaseHlsPlayer...");
    GeckoPlayerFactory.removePlayer(mPlayer);
    mPlayer.release();
    mPlayer = null;
  }
}