Bug 1376966 - [Fennec][HLS] Fix defects in GeckoHlsPlayer.java. r?kikuo draft
authorJames Cheng <jacheng@mozilla.com>
Fri, 07 Jul 2017 16:41:31 +0800
changeset 605982 0c3cd0e635a346fc911a8e5b1efc84e7f8e9d159
parent 605258 c4996f2db43a883b8a642f00435f90f40fef1dfe
child 636645 1b22269e7d1767ecf4dd30f14d7ffa97abd0bdf4
push id67571
push userbmo:jacheng@mozilla.com
push dateMon, 10 Jul 2017 06:34:42 +0000
reviewerskikuo
bugs1376966
milestone56.0a1
Bug 1376966 - [Fennec][HLS] Fix defects in GeckoHlsPlayer.java. r?kikuo MozReview-Commit-ID: 5L4eluBOexY
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoHlsPlayer.java
--- 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