1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.usbtuner.exoplayer; 18 19 import android.media.AudioFormat; 20 import android.media.MediaCodec.CryptoException; 21 import android.media.MediaDataSource; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.support.annotation.IntDef; 25 import android.view.Surface; 26 27 import com.google.android.exoplayer.DummyTrackRenderer; 28 import com.google.android.exoplayer.ExoPlaybackException; 29 import com.google.android.exoplayer.ExoPlayer; 30 import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; 31 import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; 32 import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 33 import com.google.android.exoplayer.TrackRenderer; 34 import com.google.android.exoplayer.audio.AudioCapabilities; 35 import com.google.android.exoplayer.audio.AudioTrack; 36 import com.android.usbtuner.data.Cea708Data; 37 import com.android.usbtuner.data.Cea708Data.CaptionEvent; 38 import com.android.usbtuner.exoplayer.Cea708TextTrackRenderer.CcListener; 39 import com.android.usbtuner.exoplayer.ac3.Ac3TrackRenderer; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 44 /** 45 * MPEG-2 TS stream player implementation using ExoPlayer. 46 */ 47 public class MpegTsPlayer implements ExoPlayer.Listener, 48 MediaCodecVideoTrackRenderer.EventListener, Ac3TrackRenderer.EventListener { 49 private int mCaptionServiceNumber = Cea708Data.EMPTY_SERVICE_NUMBER; 50 51 /** 52 * Interface definition for building specific track renderers. 53 */ 54 public interface RendererBuilder { buildRenderers(MpegTsPlayer mpegTsPlayer, MediaDataSource dataSource, RendererBuilderCallback callback)55 void buildRenderers(MpegTsPlayer mpegTsPlayer, MediaDataSource dataSource, 56 RendererBuilderCallback callback); 57 } 58 59 /** 60 * Interface definition for {@link RendererBuilder#buildRenderers} to notify the result. 61 */ 62 public interface RendererBuilderCallback { onRenderers(String[][] trackNames, TrackRenderer[] renderers)63 void onRenderers(String[][] trackNames, TrackRenderer[] renderers); onRenderersError(Exception e)64 void onRenderersError(Exception e); 65 } 66 67 /** 68 * Interface definition for a callback to be notified of changes in player state. 69 */ 70 public interface Listener { onStateChanged(int generation, boolean playWhenReady, int playbackState)71 void onStateChanged(int generation, boolean playWhenReady, int playbackState); onError(int generation, Exception e)72 void onError(int generation, Exception e); onVideoSizeChanged(int generation, int width, int height, float pixelWidthHeightRatio)73 void onVideoSizeChanged(int generation, int width, int height, 74 float pixelWidthHeightRatio); onDrawnToSurface(MpegTsPlayer player, Surface surface)75 void onDrawnToSurface(MpegTsPlayer player, Surface surface); onAudioUnplayable(int generation)76 void onAudioUnplayable(int generation); 77 } 78 79 /** 80 * Interface definition for a callback to be notified of changes on video display. 81 */ 82 public interface VideoEventListener { 83 /** 84 * Notifies the caption event. 85 */ onEmitCaptionEvent(CaptionEvent event)86 void onEmitCaptionEvent(CaptionEvent event); 87 88 /** 89 * Notifies the discovered caption service number. 90 */ onDiscoverCaptionServiceNumber(int serviceNumber)91 void onDiscoverCaptionServiceNumber(int serviceNumber); 92 } 93 94 // Constants pulled into this class for convenience. 95 @IntDef({STATE_IDLE, STATE_PREPARING, STATE_BUFFERING, STATE_READY, STATE_ENDED}) 96 @Retention(RetentionPolicy.SOURCE) 97 public @interface PlaybackState {} 98 public static final int STATE_IDLE = ExoPlayer.STATE_IDLE; 99 public static final int STATE_PREPARING = ExoPlayer.STATE_PREPARING; 100 public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING; 101 public static final int STATE_READY = ExoPlayer.STATE_READY; 102 public static final int STATE_ENDED = ExoPlayer.STATE_ENDED; 103 104 public static final int RENDERER_COUNT = 3; 105 public static final int MIN_BUFFER_MS = 200; 106 public static final int MIN_REBUFFER_MS = 500; 107 108 @IntDef({TRACK_TYPE_VIDEO, TRACK_TYPE_AUDIO, TRACK_TYPE_TEXT}) 109 @Retention(RetentionPolicy.SOURCE) 110 public @interface TrackType {} 111 public static final int TRACK_TYPE_VIDEO = 0; 112 public static final int TRACK_TYPE_AUDIO = 1; 113 public static final int TRACK_TYPE_TEXT = 2; 114 115 @IntDef({RENDERER_BUILDING_STATE_IDLE, RENDERER_BUILDING_STATE_BUILDING, 116 RENDERER_BUILDING_STATE_BUILT}) 117 @Retention(RetentionPolicy.SOURCE) 118 public @interface RendererBuildingState {} 119 private static final int RENDERER_BUILDING_STATE_IDLE = 1; 120 private static final int RENDERER_BUILDING_STATE_BUILDING = 2; 121 private static final int RENDERER_BUILDING_STATE_BUILT = 3; 122 123 private final RendererBuilder mRendererBuilder; 124 private final ExoPlayer mPlayer; 125 private final Handler mMainHandler; 126 private final int mPlayerGeneration; 127 private final AudioCapabilities mAudioCapabilities; 128 129 private Listener mListener; 130 @RendererBuildingState private int mRendererBuildingState; 131 @PlaybackState private int mLastReportedPlaybackState; 132 private boolean mLastReportedPlayWhenReady; 133 134 private Surface mSurface; 135 private InternalRendererBuilderCallback mBuilderCallback; 136 private TrackRenderer mVideoRenderer; 137 private TrackRenderer mAudioRenderer; 138 139 private String[][] mTrackNames; 140 private int[] mSelectedTracks; 141 142 private Cea708TextTrackRenderer mTextRenderer; 143 private CcListener mCcListener; 144 private VideoEventListener mVideoEventListener; 145 MpegTsPlayer(int playerGeneration, RendererBuilder rendererBuilder, Handler handler, AudioCapabilities capabilities, Listener listener)146 public MpegTsPlayer(int playerGeneration, RendererBuilder rendererBuilder, Handler handler, 147 AudioCapabilities capabilities, Listener listener) { 148 mRendererBuilder = rendererBuilder; 149 mPlayer = ExoPlayer.Factory.newInstance(RENDERER_COUNT, MIN_BUFFER_MS, MIN_REBUFFER_MS); 150 mPlayer.addListener(this); 151 mMainHandler = handler; 152 mPlayerGeneration = playerGeneration; 153 mAudioCapabilities = capabilities; 154 mLastReportedPlaybackState = STATE_IDLE; 155 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 156 mSelectedTracks = new int[RENDERER_COUNT]; 157 mCcListener = new MpegTsCcListener(); 158 mListener = listener; 159 } 160 setVideoEventListener(VideoEventListener videoEventListener)161 public void setVideoEventListener(VideoEventListener videoEventListener) { 162 mVideoEventListener = videoEventListener; 163 } 164 setCaptionServiceNumber(int captionServiceNumber)165 public void setCaptionServiceNumber(int captionServiceNumber) { 166 mCaptionServiceNumber = captionServiceNumber; 167 if (mTextRenderer != null) { 168 mPlayer.sendMessage(mTextRenderer, 169 Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber); 170 } 171 } 172 setSurface(Surface surface)173 public void setSurface(Surface surface) { 174 mSurface = surface; 175 pushSurface(false); 176 } 177 getSurface()178 public Surface getSurface() { 179 return mSurface; 180 } 181 blockingClearSurface()182 public void blockingClearSurface() { 183 mSurface = null; 184 pushSurface(true); 185 } 186 getTracks(int type)187 public String[] getTracks(int type) { 188 return mTrackNames == null ? null : mTrackNames[type]; 189 } 190 getSelectedTrackIndex(int type)191 public int getSelectedTrackIndex(int type) { 192 return mSelectedTracks[type]; 193 } 194 selectTrack(int type, int index)195 public void selectTrack(int type, int index) { 196 if (mSelectedTracks[type] == index) { 197 return; 198 } 199 mSelectedTracks[type] = index; 200 pushTrackSelection(type, true); 201 } 202 prepare(MediaDataSource source)203 public void prepare(MediaDataSource source) { 204 if (mRendererBuildingState == RENDERER_BUILDING_STATE_BUILT) { 205 mPlayer.stop(); 206 } 207 if (mBuilderCallback != null) { 208 mBuilderCallback.cancel(); 209 } 210 mRendererBuildingState = RENDERER_BUILDING_STATE_BUILDING; 211 maybeReportPlayerState(); 212 mBuilderCallback = new InternalRendererBuilderCallback(); 213 mRendererBuilder.buildRenderers(this, source, mBuilderCallback); 214 } 215 onRenderers(String[][] trackNames, TrackRenderer[] renderers)216 /* package */ void onRenderers(String[][] trackNames, TrackRenderer[] renderers) { 217 mBuilderCallback = null; 218 219 // Normalize the results. 220 if (trackNames == null) { 221 trackNames = new String[RENDERER_COUNT][]; 222 } 223 for (int i = 0; i < RENDERER_COUNT; i++) { 224 if (renderers[i] == null) { 225 // Convert a null renderer to a dummy renderer. 226 renderers[i] = new DummyTrackRenderer(); 227 } 228 } 229 mVideoRenderer = renderers[TRACK_TYPE_VIDEO]; 230 mAudioRenderer = renderers[TRACK_TYPE_AUDIO]; 231 mTextRenderer = (Cea708TextTrackRenderer) renderers[TRACK_TYPE_TEXT]; 232 mTextRenderer.setCcListener(mCcListener); 233 mPlayer.sendMessage( 234 mTextRenderer, Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber); 235 mTrackNames = trackNames; 236 mRendererBuildingState = RENDERER_BUILDING_STATE_BUILT; 237 pushSurface(false); 238 mPlayer.prepare(renderers); 239 pushTrackSelection(TRACK_TYPE_VIDEO, true); 240 pushTrackSelection(TRACK_TYPE_AUDIO, true); 241 pushTrackSelection(TRACK_TYPE_TEXT, true); 242 } 243 onRenderersError(Exception e)244 /* package */ void onRenderersError(Exception e) { 245 mBuilderCallback = null; 246 if (mListener != null) { 247 mListener.onError(mPlayerGeneration, e); 248 } 249 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 250 maybeReportPlayerState(); 251 } 252 setPlayWhenReady(boolean playWhenReady)253 public void setPlayWhenReady(boolean playWhenReady) { 254 mPlayer.setPlayWhenReady(playWhenReady); 255 } 256 seekTo(long positionMs)257 public void seekTo(long positionMs) { 258 mPlayer.seekTo(positionMs); 259 } 260 release()261 public void release() { 262 if (mBuilderCallback != null) { 263 mBuilderCallback.cancel(); 264 mBuilderCallback = null; 265 } 266 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 267 mSurface = null; 268 mListener = null; 269 mPlayer.release(); 270 } 271 getPlaybackState()272 @PlaybackState public int getPlaybackState() { 273 if (mRendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) { 274 return STATE_PREPARING; 275 } 276 return mPlayer.getPlaybackState(); 277 } 278 isPlaying()279 public boolean isPlaying() { 280 @PlaybackState int state = getPlaybackState(); 281 return (state == STATE_READY || state == STATE_BUFFERING) 282 && mPlayer.getPlayWhenReady(); 283 } 284 isBuffering()285 public boolean isBuffering() { 286 return getPlaybackState() == STATE_BUFFERING; 287 } 288 getCurrentPosition()289 public long getCurrentPosition() { 290 return mPlayer.getCurrentPosition(); 291 } 292 getDuration()293 public long getDuration() { 294 return mPlayer.getDuration(); 295 } 296 getBufferedPercentage()297 public int getBufferedPercentage() { 298 return mPlayer.getBufferedPercentage(); 299 } 300 getPlayWhenReady()301 public boolean getPlayWhenReady() { 302 return mPlayer.getPlayWhenReady(); 303 } 304 setVolume(float volume)305 public void setVolume(float volume) { 306 mPlayer.sendMessage(mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, volume); 307 } 308 setAudioTrack(boolean enable)309 public void setAudioTrack(boolean enable) { 310 mPlayer.sendMessage(mAudioRenderer, Ac3TrackRenderer.MSG_SET_AUDIO_TRACK, enable ? 1 : 0); 311 } 312 isAc3Playable()313 public boolean isAc3Playable() { 314 return mAudioCapabilities != null 315 && mAudioCapabilities.supportsEncoding(AudioFormat.ENCODING_AC3); 316 } 317 onAudioUnplayable()318 public void onAudioUnplayable() { 319 if (mListener != null) { 320 mListener.onAudioUnplayable(mPlayerGeneration); 321 } 322 } 323 getPlaybackLooper()324 /* package */ Looper getPlaybackLooper() { 325 return mPlayer.getPlaybackLooper(); 326 } 327 getMainHandler()328 /* package */ Handler getMainHandler() { 329 return mMainHandler; 330 } 331 332 @Override onPlayerStateChanged(boolean playWhenReady, int state)333 public void onPlayerStateChanged(boolean playWhenReady, int state) { 334 maybeReportPlayerState(); 335 } 336 337 @Override onPlayerError(ExoPlaybackException exception)338 public void onPlayerError(ExoPlaybackException exception) { 339 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 340 if (mListener != null) { 341 mListener.onError(mPlayerGeneration, exception); 342 } 343 } 344 345 @Override onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio)346 public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 347 float pixelWidthHeightRatio) { 348 if (mListener != null) { 349 mListener.onVideoSizeChanged(mPlayerGeneration, width, height, pixelWidthHeightRatio); 350 } 351 } 352 353 @Override onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs)354 public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, 355 long initializationDurationMs) { 356 // TODO 357 } 358 359 @Override onDecoderInitializationError(DecoderInitializationException e)360 public void onDecoderInitializationError(DecoderInitializationException e) { 361 // Do nothing. 362 } 363 364 @Override onAudioTrackInitializationError(AudioTrack.InitializationException e)365 public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { 366 onAudioUnplayable(); 367 } 368 369 @Override onAudioTrackWriteError(AudioTrack.WriteException e)370 public void onAudioTrackWriteError(AudioTrack.WriteException e) { 371 // Do nothing. 372 } 373 374 @Override onCryptoError(CryptoException e)375 public void onCryptoError(CryptoException e) { 376 // Do nothing. 377 } 378 379 @Override onPlayWhenReadyCommitted()380 public void onPlayWhenReadyCommitted() { 381 // Do nothing. 382 } 383 384 @Override onDrawnToSurface(Surface surface)385 public void onDrawnToSurface(Surface surface) { 386 if (mListener != null) { 387 mListener.onDrawnToSurface(this, surface); 388 } 389 } 390 391 @Override onDroppedFrames(int count, long elapsed)392 public void onDroppedFrames(int count, long elapsed) { 393 // Do nothing. 394 } 395 maybeReportPlayerState()396 private void maybeReportPlayerState() { 397 boolean playWhenReady = mPlayer.getPlayWhenReady(); 398 @PlaybackState int playbackState = getPlaybackState(); 399 if (mLastReportedPlayWhenReady != playWhenReady 400 || mLastReportedPlaybackState != playbackState) { 401 if (mListener != null) { 402 if (playbackState == STATE_ENDED) { 403 mListener.onStateChanged(mPlayerGeneration, playWhenReady, STATE_ENDED); 404 } 405 else if (playbackState == STATE_READY) { 406 mListener.onStateChanged(mPlayerGeneration, playWhenReady, STATE_READY); 407 } 408 } 409 mLastReportedPlayWhenReady = playWhenReady; 410 mLastReportedPlaybackState = playbackState; 411 } 412 } 413 pushSurface(boolean blockForSurfacePush)414 private void pushSurface(boolean blockForSurfacePush) { 415 if (mRendererBuildingState != RENDERER_BUILDING_STATE_BUILT) { 416 return; 417 } 418 419 if (blockForSurfacePush) { 420 mPlayer.blockingSendMessage( 421 mVideoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, mSurface); 422 } else { 423 mPlayer.sendMessage( 424 mVideoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, mSurface); 425 } 426 } 427 pushTrackSelection(@rackType int type, boolean allowRendererEnable)428 private void pushTrackSelection(@TrackType int type, boolean allowRendererEnable) { 429 if (mRendererBuildingState != RENDERER_BUILDING_STATE_BUILT) { 430 return; 431 } 432 mPlayer.setSelectedTrack(type, allowRendererEnable ? 0 : -1); 433 } 434 435 private class MpegTsCcListener implements CcListener { 436 437 @Override emitEvent(CaptionEvent captionEvent)438 public void emitEvent(CaptionEvent captionEvent) { 439 if (mVideoEventListener != null) { 440 mVideoEventListener.onEmitCaptionEvent(captionEvent); 441 } 442 } 443 444 @Override discoverServiceNumber(int serviceNumber)445 public void discoverServiceNumber(int serviceNumber) { 446 if (mVideoEventListener != null) { 447 mVideoEventListener.onDiscoverCaptionServiceNumber(serviceNumber); 448 } 449 } 450 } 451 452 private class InternalRendererBuilderCallback implements RendererBuilderCallback { 453 private boolean canceled; 454 cancel()455 public void cancel() { 456 canceled = true; 457 } 458 459 @Override onRenderers(String[][] trackNames, TrackRenderer[] renderers)460 public void onRenderers(String[][] trackNames, TrackRenderer[] renderers) { 461 if (!canceled) { 462 MpegTsPlayer.this.onRenderers(trackNames, renderers); 463 } 464 } 465 466 @Override onRenderersError(Exception e)467 public void onRenderersError(Exception e) { 468 if (!canceled) { 469 MpegTsPlayer.this.onRenderersError(e); 470 } 471 } 472 } 473 } 474