--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsPlayer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsPlayer.java
@@ -135,26 +135,27 @@ public class GeckoHlsPlayer implements B
}
}
protected void checkInitDone() {
if (mIsDemuxerInitDone) {
return;
}
assertTrue(mDemuxerCallbacks != null);
- assertTrue(mTracksInfo != null);
if (DEBUG) {
Log.d(LOGTAG, "[checkInitDone] VReady:" + mTracksInfo.videoReady() +
",AReady:" + mTracksInfo.audioReady() +
",hasV:" + mTracksInfo.hasVideo() +
",hasA:" + mTracksInfo.hasAudio());
}
if (mTracksInfo.videoReady() && mTracksInfo.audioReady()) {
- mDemuxerCallbacks.onInitialized(mTracksInfo.hasAudio(), mTracksInfo.hasVideo());
+ if (mDemuxerCallbacks != null) {
+ mDemuxerCallbacks.onInitialized(mTracksInfo.hasAudio(), mTracksInfo.hasVideo());
+ }
mIsDemuxerInitDone = true;
}
}
public final class ComponentEventDispatcher {
public void onDataArrived(final int trackType) {
assertTrue(mMainHandler != null);
assertTrue(mComponentListener != null);
@@ -202,51 +203,75 @@ public class GeckoHlsPlayer implements B
});
}
}
}
public final class ComponentListener {
// General purpose implementation
- public synchronized void onDataArrived(int trackType) {
- if (DEBUG) { Log.d(LOGTAG, "[CB][onDataArrived] id " + mPlayerId); }
- if (!mIsPlayerInitDone) {
- return;
+ public void onDataArrived(int trackType) {
+ synchronized (GeckoHlsPlayer.this) {
+ if (DEBUG) { Log.d(LOGTAG, "[CB][onDataArrived] id " + mPlayerId); }
+ if (!mIsPlayerInitDone) {
+ return;
+ }
+ assertTrue(mResourceCallbacks != null);
+ assertTrue(mTracksInfo != null);
+
+ if (mTracksInfo == null || mResourceCallbacks == null) {
+ Log.e(LOGTAG, "Encounter an abnormal calling sequence.");
+ return;
+ }
+ mTracksInfo.onDataArrived(trackType);
+ mResourceCallbacks.onDataArrived();
+
+ checkInitDone();
}
- assertTrue(mResourceCallbacks != null);
- assertTrue(mTracksInfo != null);
- mTracksInfo.onDataArrived(trackType);
- mResourceCallbacks.onDataArrived();
- checkInitDone();
}
- public synchronized void onVideoInputFormatChanged(Format format) {
- if (DEBUG) {
- Log.d(LOGTAG, "[CB] onVideoInputFormatChanged [" + format + "]");
- Log.d(LOGTAG, "[CB] SampleMIMEType [" +
- format.sampleMimeType + "], ContainerMIMEType [" +
- format.containerMimeType + "], id : " + mPlayerId);
+ public void onVideoInputFormatChanged(Format format) {
+ synchronized (GeckoHlsPlayer.this) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "[CB] onVideoInputFormatChanged [" + format + "]");
+ Log.d(LOGTAG, "[CB] SampleMIMEType [" +
+ format.sampleMimeType + "], ContainerMIMEType [" +
+ format.containerMimeType + "], id : " + mPlayerId);
+ }
+ if (!mIsPlayerInitDone) {
+ return;
+ }
+ assertTrue(mTracksInfo != null);
+
+ if (mTracksInfo == null) {
+ Log.e(LOGTAG, "Encounter a abnormal calling sequence. mTracksInfo is null");
+ return;
+ }
+ mTracksInfo.onVideoInfoUpdated();
+ checkInitDone();
}
- if (!mIsPlayerInitDone) {
- return;
- }
- assertTrue(mTracksInfo != null);
- mTracksInfo.onVideoInfoUpdated();
- checkInitDone();
}
- public synchronized void onAudioInputFormatChanged(Format format) {
- if (DEBUG) { Log.d(LOGTAG, "[CB] onAudioInputFormatChanged [" + format + "], mPlayerId :" + mPlayerId); }
- if (!mIsPlayerInitDone) {
- return;
+ public void onAudioInputFormatChanged(Format format) {
+ synchronized (GeckoHlsPlayer.this) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "[CB] onAudioInputFormatChanged [" + format + "], mPlayerId :" + mPlayerId);
+ }
+ if (!mIsPlayerInitDone) {
+ return;
+ }
+ assertTrue(mTracksInfo != null);
+
+ if (mTracksInfo == null) {
+ Log.e(LOGTAG, "Encounter a abnormal calling sequence. mTracksInfo is null");
+ return;
+ }
+ mTracksInfo.onAudioInfoUpdated();
+ checkInitDone();
}
- assertTrue(mTracksInfo != null);
- mTracksInfo.onAudioInfoUpdated();
- checkInitDone();
}
}
private DataSource.Factory buildDataSourceFactory(Context ctx, DefaultBandwidthMeter bandwidthMeter) {
return new DefaultDataSourceFactory(ctx, bandwidthMeter,
buildHttpDataSourceFactory(bandwidthMeter));
}
@@ -255,23 +280,22 @@ public class GeckoHlsPlayer implements B
AppConstants.USER_AGENT_FENNEC_MOBILE,
bandwidthMeter /* listener */,
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true /* allowCrossProtocolRedirects */
);
}
- private long getDuration() {
- assertTrue(mPlayer != null);
- if (isLiveStream()) {
- return 0L;
+ private synchronized long getDuration() {
+ long duration = 0L;
+ // Value returned by getDuration() is in milliseconds.
+ if (mPlayer != null && !isLiveStream()) {
+ duration = Math.max(0L, mPlayer.getDuration() * 1000L);
}
- // Value returned by getDuration() is in milliseconds.
- long duration = Math.max(0L, mPlayer.getDuration() * 1000L);
if (DEBUG) { Log.d(LOGTAG, "getDuration : " + duration + "(Us)"); }
return duration;
}
// To make sure that each player has a unique id, GeckoHlsPlayer should be
// created only from synchronized APIs in GeckoPlayerFactory.
public GeckoHlsPlayer() {
mPlayerId = sPlayerId.incrementAndGet();
@@ -294,26 +318,26 @@ public class GeckoHlsPlayer implements B
@Override
public void addDemuxerWrapperCallbackListener(BaseHlsPlayer.DemuxerCallbacks callback) {
if (DEBUG) { Log.d(LOGTAG, " addDemuxerWrapperCallbackListener ..."); }
mDemuxerCallbacks = callback;
}
@Override
- public void onLoadingChanged(boolean isLoading) {
+ public synchronized void onLoadingChanged(boolean isLoading) {
if (DEBUG) { Log.d(LOGTAG, "loading [" + isLoading + "]"); }
if (!isLoading) {
// To update buffered position.
mComponentEventDispatcher.onDataArrived(C.TRACK_TYPE_DEFAULT);
}
}
@Override
- public void onPlayerStateChanged(boolean playWhenReady, int state) {
+ public synchronized void onPlayerStateChanged(boolean playWhenReady, int state) {
if (DEBUG) { Log.d(LOGTAG, "state [" + playWhenReady + ", " + getStateString(state) + "]"); }
if (state == ExoPlayer.STATE_READY) {
mPlayer.setPlayWhenReady(true);
}
}
@Override
public void onPositionDiscontinuity() {
@@ -324,17 +348,17 @@ public class GeckoHlsPlayer implements B
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
if (DEBUG) {
Log.d(LOGTAG, "playbackParameters " +
String.format("[speed=%.2f, pitch=%.2f]", playbackParameters.speed, playbackParameters.pitch));
}
}
@Override
- public void onPlayerError(ExoPlaybackException e) {
+ public synchronized void onPlayerError(ExoPlaybackException e) {
if (DEBUG) { Log.e(LOGTAG, "playerFailed" , e); }
mIsPlayerInitDone = false;
if (mResourceCallbacks != null) {
mResourceCallbacks.onError(ResourceError.PLAYER.code());
}
if (mDemuxerCallbacks != null) {
mDemuxerCallbacks.onError(DemuxerError.PLAYER.code());
}
@@ -343,62 +367,62 @@ public class GeckoHlsPlayer implements B
@Override
public synchronized void onTracksChanged(TrackGroupArray ignored, TrackSelectionArray trackSelections) {
if (DEBUG) {
Log.d(LOGTAG, "onTracksChanged : TGA[" + ignored +
"], TSA[" + trackSelections + "]");
MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo == null) {
- Log.d(LOGTAG, "Tracks []");
- return;
+ Log.d(LOGTAG, "Tracks []");
+ return;
}
Log.d(LOGTAG, "Tracks [");
// Log tracks associated to renderers.
for (int rendererIndex = 0; rendererIndex < mappedTrackInfo.length; rendererIndex++) {
- TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
- TrackSelection trackSelection = trackSelections.get(rendererIndex);
- if (rendererTrackGroups.length > 0) {
- Log.d(LOGTAG, " Renderer:" + rendererIndex + " [");
- for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) {
- TrackGroup trackGroup = rendererTrackGroups.get(groupIndex);
- String adaptiveSupport = getAdaptiveSupportString(trackGroup.length,
- mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
- Log.d(LOGTAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
- for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
- String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
- String formatSupport = getFormatSupportString(
- mappedTrackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
- Log.d(LOGTAG, " " + status + " Track:" + trackIndex +
- ", " + Format.toLogString(trackGroup.getFormat(trackIndex)) +
- ", supported=" + formatSupport);
- }
- Log.d(LOGTAG, " ]");
+ TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
+ TrackSelection trackSelection = trackSelections.get(rendererIndex);
+ if (rendererTrackGroups.length > 0) {
+ Log.d(LOGTAG, " Renderer:" + rendererIndex + " [");
+ for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) {
+ TrackGroup trackGroup = rendererTrackGroups.get(groupIndex);
+ String adaptiveSupport = getAdaptiveSupportString(trackGroup.length,
+ mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
+ Log.d(LOGTAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
+ for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
+ String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
+ String formatSupport = getFormatSupportString(
+ mappedTrackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
+ Log.d(LOGTAG, " " + status + " Track:" + trackIndex + ", " +
+ Format.toLogString(trackGroup.getFormat(trackIndex)) +
+ ", supported=" + formatSupport);
+ }
+ Log.d(LOGTAG, " ]");
+ }
+ Log.d(LOGTAG, " ]");
}
- Log.d(LOGTAG, " ]");
- }
}
// Log tracks not associated with a renderer.
TrackGroupArray unassociatedTrackGroups = mappedTrackInfo.getUnassociatedTrackGroups();
if (unassociatedTrackGroups.length > 0) {
- Log.d(LOGTAG, " Renderer:None [");
- for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) {
- Log.d(LOGTAG, " Group:" + groupIndex + " [");
- TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex);
- for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
- String status = getTrackStatusString(false);
- String formatSupport = getFormatSupportString(
- RendererCapabilities.FORMAT_UNSUPPORTED_TYPE);
- Log.d(LOGTAG, " " + status + " Track:" + trackIndex +
+ Log.d(LOGTAG, " Renderer:None [");
+ for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) {
+ Log.d(LOGTAG, " Group:" + groupIndex + " [");
+ TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex);
+ for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
+ String status = getTrackStatusString(false);
+ String formatSupport = getFormatSupportString(
+ RendererCapabilities.FORMAT_UNSUPPORTED_TYPE);
+ Log.d(LOGTAG, " " + status + " Track:" + trackIndex +
", " + Format.toLogString(trackGroup.getFormat(trackIndex)) +
", supported=" + formatSupport);
+ }
+ Log.d(LOGTAG, " ]");
}
- Log.d(LOGTAG, " ]");
- }
- Log.d(LOGTAG, " ]");
+ Log.d(LOGTAG, " ]");
}
Log.d(LOGTAG, "]");
}
mTracksInfo = null;
int numVideoTracks = 0;
int numAudioTracks = 0;
for (int j = 0; j < ignored.length; j++) {
TrackGroup tg = ignored.get(j);
@@ -414,17 +438,17 @@ public class GeckoHlsPlayer implements B
}
}
}
}
mTracksInfo = new HlsMediaTracksInfo(numVideoTracks, numAudioTracks);
}
@Override
- public void onTimelineChanged(Timeline timeline, Object manifest) {
+ public synchronized void onTimelineChanged(Timeline timeline, Object manifest) {
// For now, we use the interface ExoPlayer.getDuration() for gecko,
// so here we create local variable 'window' & 'peroid' to obtain
// the dynamic duration.
// See. http://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/Timeline.html
// for further information.
Timeline.Window window = new Timeline.Window();
mIsTimelineStatic = !timeline.isEmpty()
&& !timeline.getWindow(timeline.getWindowCount() - 1, window).isDynamic;
@@ -573,20 +597,19 @@ public class GeckoHlsPlayer implements B
return mARenderer != null ? mARenderer.getQueuedSamples(number) :
new ConcurrentLinkedQueue<GeckoHLSSample>();
} else {
return new ConcurrentLinkedQueue<GeckoHLSSample>();
}
}
@Override
- public long getBufferedPosition() {
- assertTrue(mPlayer != null);
+ public synchronized long getBufferedPosition() {
// Value returned by getBufferedPosition() is in milliseconds.
- long bufferedPos = Math.max(0L, mPlayer.getBufferedPosition() * 1000L);
+ long bufferedPos = mPlayer == null ? 0L : Math.max(0L, mPlayer.getBufferedPosition() * 1000L);
if (DEBUG) { Log.d(LOGTAG, "getBufferedPosition : " + bufferedPos + "(Us)"); }
return bufferedPos;
}
@Override
public synchronized int getNumberOfTracks(TrackType trackType) {
if (DEBUG) { Log.d(LOGTAG, "getNumberOfTracks : type " + trackType); }
assertTrue(mTracksInfo != null);
@@ -643,17 +666,17 @@ public class GeckoHlsPlayer implements B
byte[] csd = fmt.initializationData.isEmpty() ? null : fmt.initializationData.get(0);
GeckoAudioInfo aInfo = new GeckoAudioInfo(fmt.sampleRate, fmt.channelCount,
16, 0, getDuration(),
fmt.sampleMimeType, csd);
return aInfo;
}
@Override
- public boolean seek(long positionUs) {
+ public synchronized boolean seek(long positionUs) {
// positionUs : microseconds.
// NOTE : 1) It's not possible to seek media by tracktype via ExoPlayer Interface.
// 2) positionUs is samples PTS from MFR, we need to re-adjust it
// for ExoPlayer by subtracting sample start time.
// 3) Time unit for ExoPlayer.seek() is milliseconds.
try {
// TODO : Gather Timeline Period / Window information to develop
// complete timeline, and seekTime should be inside the duration.
@@ -667,17 +690,19 @@ public class GeckoHlsPlayer implements B
}
if (DEBUG) {
Log.d(LOGTAG, "seeking : " + positionUs / 1000 +
" (ms); startTime : " + startTime / 1000 + " (ms)");
}
assertTrue(startTime != Long.MAX_VALUE);
mPlayer.seekTo(positionUs / 1000 - startTime / 1000);
} catch (Exception e) {
- mDemuxerCallbacks.onError(DemuxerError.UNKNOWN.code());
+ if (mDemuxerCallbacks != null) {
+ mDemuxerCallbacks.onError(DemuxerError.UNKNOWN.code());
+ }
return false;
}
return true;
}
@Override
public long getNextKeyFrameTime() {
long nextKeyFrameTime = mVRenderer != null