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.tv.tuner.exoplayer; 18 19 import android.content.Context; 20 import android.media.AudioFormat; 21 import android.media.MediaCodec.CryptoException; 22 import android.media.PlaybackParams; 23 import android.os.Handler; 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.MediaFormat; 34 import com.google.android.exoplayer.TrackRenderer; 35 import com.google.android.exoplayer.audio.AudioCapabilities; 36 import com.google.android.exoplayer.audio.AudioTrack; 37 import com.google.android.exoplayer.upstream.DataSource; 38 import com.android.tv.common.SoftPreconditions; 39 import com.android.tv.tuner.data.Cea708Data; 40 import com.android.tv.tuner.data.Cea708Data.CaptionEvent; 41 import com.android.tv.tuner.data.TunerChannel; 42 import com.android.tv.tuner.exoplayer.ac3.Ac3PassthroughTrackRenderer; 43 import com.android.tv.tuner.exoplayer.ac3.Ac3TrackRenderer; 44 import com.android.tv.tuner.source.TsDataSource; 45 import com.android.tv.tuner.source.TsDataSourceManager; 46 import com.android.tv.tuner.tvinput.EventDetector; 47 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 51 /** 52 * MPEG-2 TS stream player implementation using ExoPlayer. 53 */ 54 public class MpegTsPlayer implements ExoPlayer.Listener, MediaCodecVideoTrackRenderer.EventListener, 55 Ac3PassthroughTrackRenderer.EventListener, Ac3TrackRenderer.Ac3EventListener { 56 private int mCaptionServiceNumber = Cea708Data.EMPTY_SERVICE_NUMBER; 57 58 /** 59 * Interface definition for building specific track renderers. 60 */ 61 public interface RendererBuilder { buildRenderers(MpegTsPlayer mpegTsPlayer, DataSource dataSource, RendererBuilderCallback callback)62 void buildRenderers(MpegTsPlayer mpegTsPlayer, DataSource dataSource, 63 RendererBuilderCallback callback); 64 } 65 66 /** 67 * Interface definition for {@link RendererBuilder#buildRenderers} to notify the result. 68 */ 69 public interface RendererBuilderCallback { onRenderers(String[][] trackNames, TrackRenderer[] renderers)70 void onRenderers(String[][] trackNames, TrackRenderer[] renderers); onRenderersError(Exception e)71 void onRenderersError(Exception e); 72 } 73 74 /** 75 * Interface definition for a callback to be notified of changes in player state. 76 */ 77 public interface Listener { onStateChanged(boolean playWhenReady, int playbackState)78 void onStateChanged(boolean playWhenReady, int playbackState); onError(Exception e)79 void onError(Exception e); onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio)80 void onVideoSizeChanged(int width, int height, 81 float pixelWidthHeightRatio); onDrawnToSurface(MpegTsPlayer player, Surface surface)82 void onDrawnToSurface(MpegTsPlayer player, Surface surface); onAudioUnplayable()83 void onAudioUnplayable(); onSmoothTrickplayForceStopped()84 void onSmoothTrickplayForceStopped(); 85 } 86 87 /** 88 * Interface definition for a callback to be notified of changes on video display. 89 */ 90 public interface VideoEventListener { 91 /** 92 * Notifies the caption event. 93 */ onEmitCaptionEvent(CaptionEvent event)94 void onEmitCaptionEvent(CaptionEvent event); 95 96 /** 97 * Notifies the discovered caption service number. 98 */ onDiscoverCaptionServiceNumber(int serviceNumber)99 void onDiscoverCaptionServiceNumber(int serviceNumber); 100 } 101 102 public static final int RENDERER_COUNT = 3; 103 public static final int MIN_BUFFER_MS = 0; 104 public static final int MIN_REBUFFER_MS = 500; 105 106 @IntDef({TRACK_TYPE_VIDEO, TRACK_TYPE_AUDIO, TRACK_TYPE_TEXT}) 107 @Retention(RetentionPolicy.SOURCE) 108 public @interface TrackType {} 109 public static final int TRACK_TYPE_VIDEO = 0; 110 public static final int TRACK_TYPE_AUDIO = 1; 111 public static final int TRACK_TYPE_TEXT = 2; 112 113 @IntDef({RENDERER_BUILDING_STATE_IDLE, RENDERER_BUILDING_STATE_BUILDING, 114 RENDERER_BUILDING_STATE_BUILT}) 115 @Retention(RetentionPolicy.SOURCE) 116 public @interface RendererBuildingState {} 117 private static final int RENDERER_BUILDING_STATE_IDLE = 1; 118 private static final int RENDERER_BUILDING_STATE_BUILDING = 2; 119 private static final int RENDERER_BUILDING_STATE_BUILT = 3; 120 121 private static final float MAX_SMOOTH_TRICKPLAY_SPEED = 9.0f; 122 private static final float MIN_SMOOTH_TRICKPLAY_SPEED = 0.1f; 123 124 private final RendererBuilder mRendererBuilder; 125 private final ExoPlayer mPlayer; 126 private final Handler mMainHandler; 127 private final AudioCapabilities mAudioCapabilities; 128 private final TsDataSourceManager mSourceManager; 129 130 private Listener mListener; 131 @RendererBuildingState private int mRendererBuildingState; 132 133 private Surface mSurface; 134 private TsDataSource mDataSource; 135 private InternalRendererBuilderCallback mBuilderCallback; 136 private TrackRenderer mVideoRenderer; 137 private TrackRenderer mAudioRenderer; 138 private Cea708TextTrackRenderer mTextRenderer; 139 private final Cea708TextTrackRenderer.CcListener mCcListener; 140 private VideoEventListener mVideoEventListener; 141 private boolean mTrickplayRunning; 142 private float mVolume; 143 144 /** 145 * Creates MPEG2-TS stream player. 146 * 147 * @param rendererBuilder the builder of track renderers 148 * @param handler the handler for the playback events in track renderers 149 * @param sourceManager the manager for {@link DataSource} 150 * @param capabilities the {@link AudioCapabilities} of the current device 151 * @param listener the listener for playback state changes 152 */ MpegTsPlayer(RendererBuilder rendererBuilder, Handler handler, TsDataSourceManager sourceManager, AudioCapabilities capabilities, Listener listener)153 public MpegTsPlayer(RendererBuilder rendererBuilder, Handler handler, 154 TsDataSourceManager sourceManager, AudioCapabilities capabilities, 155 Listener listener) { 156 mRendererBuilder = rendererBuilder; 157 mPlayer = ExoPlayer.Factory.newInstance(RENDERER_COUNT, MIN_BUFFER_MS, MIN_REBUFFER_MS); 158 mPlayer.addListener(this); 159 mMainHandler = handler; 160 mAudioCapabilities = capabilities; 161 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 162 mCcListener = new MpegTsCcListener(); 163 mSourceManager = sourceManager; 164 mListener = listener; 165 } 166 167 /** 168 * Sets the video event listener. 169 * 170 * @param videoEventListener the listener for video events 171 */ setVideoEventListener(VideoEventListener videoEventListener)172 public void setVideoEventListener(VideoEventListener videoEventListener) { 173 mVideoEventListener = videoEventListener; 174 } 175 176 /** 177 * Sets the closed caption service number. 178 * 179 * @param captionServiceNumber the service number of CEA-708 closed caption 180 */ setCaptionServiceNumber(int captionServiceNumber)181 public void setCaptionServiceNumber(int captionServiceNumber) { 182 mCaptionServiceNumber = captionServiceNumber; 183 if (mTextRenderer != null) { 184 mPlayer.sendMessage(mTextRenderer, 185 Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber); 186 } 187 } 188 189 /** 190 * Sets the surface for the player. 191 * 192 * @param surface the {@link Surface} to render video 193 */ setSurface(Surface surface)194 public void setSurface(Surface surface) { 195 mSurface = surface; 196 pushSurface(false); 197 } 198 199 /** 200 * Returns the current surface of the player. 201 */ getSurface()202 public Surface getSurface() { 203 return mSurface; 204 } 205 206 /** 207 * Clears the surface and waits until the surface is being cleaned. 208 */ blockingClearSurface()209 public void blockingClearSurface() { 210 mSurface = null; 211 pushSurface(true); 212 } 213 214 /** 215 * Creates renderers and {@link DataSource} and initializes player. 216 * @param context a {@link Context} instance 217 * @param channel to play 218 * @param eventListener for program information which will be scanned from MPEG2-TS stream 219 * @return true when everything is created and initialized well, false otherwise 220 */ prepare(Context context, TunerChannel channel, EventDetector.EventListener eventListener)221 public boolean prepare(Context context, TunerChannel channel, 222 EventDetector.EventListener eventListener) { 223 TsDataSource source = null; 224 if (channel != null) { 225 source = mSourceManager.createDataSource(context, channel, eventListener); 226 if (source == null) { 227 return false; 228 } 229 } 230 mDataSource = source; 231 if (mRendererBuildingState == RENDERER_BUILDING_STATE_BUILT) { 232 mPlayer.stop(); 233 } 234 if (mBuilderCallback != null) { 235 mBuilderCallback.cancel(); 236 } 237 mRendererBuildingState = RENDERER_BUILDING_STATE_BUILDING; 238 mBuilderCallback = new InternalRendererBuilderCallback(); 239 mRendererBuilder.buildRenderers(this, source, mBuilderCallback); 240 return true; 241 } 242 243 /** 244 * Returns {@link TsDataSource} which provides MPEG2-TS stream. 245 */ getDataSource()246 public TsDataSource getDataSource() { 247 return mDataSource; 248 } 249 onRenderers(TrackRenderer[] renderers)250 private void onRenderers(TrackRenderer[] renderers) { 251 mBuilderCallback = null; 252 for (int i = 0; i < RENDERER_COUNT; i++) { 253 if (renderers[i] == null) { 254 // Convert a null renderer to a dummy renderer. 255 renderers[i] = new DummyTrackRenderer(); 256 } 257 } 258 mVideoRenderer = renderers[TRACK_TYPE_VIDEO]; 259 mAudioRenderer = renderers[TRACK_TYPE_AUDIO]; 260 mTextRenderer = (Cea708TextTrackRenderer) renderers[TRACK_TYPE_TEXT]; 261 mTextRenderer.setCcListener(mCcListener); 262 mPlayer.sendMessage( 263 mTextRenderer, Cea708TextTrackRenderer.MSG_SERVICE_NUMBER, mCaptionServiceNumber); 264 mRendererBuildingState = RENDERER_BUILDING_STATE_BUILT; 265 pushSurface(false); 266 mPlayer.prepare(renderers); 267 pushTrackSelection(TRACK_TYPE_VIDEO, true); 268 pushTrackSelection(TRACK_TYPE_AUDIO, true); 269 pushTrackSelection(TRACK_TYPE_TEXT, true); 270 } 271 onRenderersError(Exception e)272 private void onRenderersError(Exception e) { 273 mBuilderCallback = null; 274 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 275 if (mListener != null) { 276 mListener.onError(e); 277 } 278 } 279 280 /** 281 * Sets the player state to pause or play. 282 * 283 * @param playWhenReady sets the player state to being ready to play when {@code true}, 284 * sets the player state to being paused when {@code false} 285 * 286 */ setPlayWhenReady(boolean playWhenReady)287 public void setPlayWhenReady(boolean playWhenReady) { 288 mPlayer.setPlayWhenReady(playWhenReady); 289 stopSmoothTrickplay(false); 290 } 291 292 /** 293 * Returns true, if trickplay is supported. 294 */ supportSmoothTrickPlay(float playbackSpeed)295 public boolean supportSmoothTrickPlay(float playbackSpeed) { 296 return playbackSpeed > MIN_SMOOTH_TRICKPLAY_SPEED 297 && playbackSpeed < MAX_SMOOTH_TRICKPLAY_SPEED; 298 } 299 300 /** 301 * Starts trickplay. It'll be reset, if {@link #seekTo} or {@link #setPlayWhenReady} is called. 302 */ startSmoothTrickplay(PlaybackParams playbackParams)303 public void startSmoothTrickplay(PlaybackParams playbackParams) { 304 SoftPreconditions.checkState(supportSmoothTrickPlay(playbackParams.getSpeed())); 305 mPlayer.setPlayWhenReady(true); 306 mTrickplayRunning = true; 307 if (mAudioRenderer instanceof Ac3PassthroughTrackRenderer) { 308 mPlayer.sendMessage(mAudioRenderer, Ac3PassthroughTrackRenderer.MSG_SET_PLAYBACK_SPEED, 309 playbackParams.getSpeed()); 310 } else { 311 mPlayer.sendMessage(mAudioRenderer, 312 MediaCodecAudioTrackRenderer.MSG_SET_PLAYBACK_PARAMS, 313 playbackParams); 314 } 315 } 316 stopSmoothTrickplay(boolean calledBySeek)317 private void stopSmoothTrickplay(boolean calledBySeek) { 318 if (mTrickplayRunning) { 319 mTrickplayRunning = false; 320 if (mAudioRenderer instanceof Ac3PassthroughTrackRenderer) { 321 mPlayer.sendMessage(mAudioRenderer, 322 Ac3PassthroughTrackRenderer.MSG_SET_PLAYBACK_SPEED, 323 1.0f); 324 } else { 325 mPlayer.sendMessage(mAudioRenderer, 326 MediaCodecAudioTrackRenderer.MSG_SET_PLAYBACK_PARAMS, 327 new PlaybackParams().setSpeed(1.0f)); 328 } 329 if (!calledBySeek) { 330 mPlayer.seekTo(mPlayer.getCurrentPosition()); 331 } 332 } 333 } 334 335 /** 336 * Seeks to the specified position of the current playback. 337 * 338 * @param positionMs the specified position in milli seconds. 339 */ seekTo(long positionMs)340 public void seekTo(long positionMs) { 341 mPlayer.seekTo(positionMs); 342 stopSmoothTrickplay(true); 343 } 344 345 /** 346 * Releases the player. 347 */ release()348 public void release() { 349 if (mDataSource != null) { 350 mSourceManager.releaseDataSource(mDataSource); 351 mDataSource = null; 352 } 353 if (mBuilderCallback != null) { 354 mBuilderCallback.cancel(); 355 mBuilderCallback = null; 356 } 357 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 358 mSurface = null; 359 mListener = null; 360 mPlayer.release(); 361 } 362 363 /** 364 * Returns the current status of the player. 365 */ getPlaybackState()366 public int getPlaybackState() { 367 if (mRendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) { 368 return ExoPlayer.STATE_PREPARING; 369 } 370 return mPlayer.getPlaybackState(); 371 } 372 373 /** 374 * Returns {@code true} when the player is prepared to play, {@code false} otherwise. 375 */ isPrepared()376 public boolean isPrepared() { 377 int state = getPlaybackState(); 378 return state == ExoPlayer.STATE_READY || state == ExoPlayer.STATE_BUFFERING; 379 } 380 381 /** 382 * Returns {@code true} when the player is being ready to play, {@code false} otherwise. 383 */ isPlaying()384 public boolean isPlaying() { 385 int state = getPlaybackState(); 386 return (state == ExoPlayer.STATE_READY || state == ExoPlayer.STATE_BUFFERING) 387 && mPlayer.getPlayWhenReady(); 388 } 389 390 /** 391 * Returns {@code true} when the player is buffering, {@code false} otherwise. 392 */ isBuffering()393 public boolean isBuffering() { 394 return getPlaybackState() == ExoPlayer.STATE_BUFFERING; 395 } 396 397 /** 398 * Returns the current position of the playback in milli seconds. 399 */ getCurrentPosition()400 public long getCurrentPosition() { 401 return mPlayer.getCurrentPosition(); 402 } 403 404 /** 405 * Returns the total duration of the playback. 406 */ getDuration()407 public long getDuration() { 408 return mPlayer.getDuration(); 409 } 410 411 /** 412 * Returns {@code true} when the player is being ready to play, 413 * {@code false} when the player is paused. 414 */ getPlayWhenReady()415 public boolean getPlayWhenReady() { 416 return mPlayer.getPlayWhenReady(); 417 } 418 419 /** 420 * Sets the volume of the audio. 421 * 422 * @param volume see also {@link AudioTrack#setVolume(float)} 423 */ setVolume(float volume)424 public void setVolume(float volume) { 425 mVolume = volume; 426 if (mAudioRenderer instanceof Ac3PassthroughTrackRenderer) { 427 mPlayer.sendMessage(mAudioRenderer, Ac3PassthroughTrackRenderer.MSG_SET_VOLUME, volume); 428 } else { 429 mPlayer.sendMessage(mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, 430 volume); 431 } 432 } 433 434 /** 435 * Enables or disables audio. 436 * 437 * @param enable enables the audio when {@code true}, disables otherwise. 438 */ setAudioTrack(boolean enable)439 public void setAudioTrack(boolean enable) { 440 if (mAudioRenderer instanceof Ac3PassthroughTrackRenderer) { 441 mPlayer.sendMessage(mAudioRenderer, Ac3PassthroughTrackRenderer.MSG_SET_AUDIO_TRACK, 442 enable ? 1 : 0); 443 } else { 444 mPlayer.sendMessage(mAudioRenderer, MediaCodecAudioTrackRenderer.MSG_SET_VOLUME, 445 enable ? mVolume : 0.0f); 446 } 447 } 448 449 /** 450 * Returns {@code true} when AC3 audio can be played, {@code false} otherwise. 451 */ isAc3Playable()452 public boolean isAc3Playable() { 453 return mAudioCapabilities != null 454 && mAudioCapabilities.supportsEncoding(AudioFormat.ENCODING_AC3); 455 } 456 457 /** 458 * Notifies when the audio cannot be played by the current device. 459 */ onAudioUnplayable()460 public void onAudioUnplayable() { 461 if (mListener != null) { 462 mListener.onAudioUnplayable(); 463 } 464 } 465 466 /** 467 * Returns {@code true} if the player has any video track, {@code false} otherwise. 468 */ hasVideo()469 public boolean hasVideo() { 470 return mPlayer.getTrackCount(TRACK_TYPE_VIDEO) > 0; 471 } 472 473 /** 474 * Returns {@code true} if the player has any audio trock, {@code false} otherwise. 475 */ hasAudio()476 public boolean hasAudio() { 477 return mPlayer.getTrackCount(TRACK_TYPE_AUDIO) > 0; 478 } 479 480 /** 481 * Returns the number of tracks exposed by the specified renderer. 482 */ getTrackCount(int rendererIndex)483 public int getTrackCount(int rendererIndex) { 484 return mPlayer.getTrackCount(rendererIndex); 485 } 486 487 /** 488 * Selects a track for the specified renderer. 489 */ setSelectedTrack(int rendererIndex, int trackIndex)490 public void setSelectedTrack(int rendererIndex, int trackIndex) { 491 if (trackIndex >= getTrackCount(rendererIndex)) { 492 return; 493 } 494 mPlayer.setSelectedTrack(rendererIndex, trackIndex); 495 } 496 497 /** 498 * Gets the main handler of the player. 499 */ getMainHandler()500 /* package */ Handler getMainHandler() { 501 return mMainHandler; 502 } 503 504 @Override onPlayerStateChanged(boolean playWhenReady, int state)505 public void onPlayerStateChanged(boolean playWhenReady, int state) { 506 if (mListener == null) { 507 return; 508 } 509 mListener.onStateChanged(playWhenReady, state); 510 if (state == ExoPlayer.STATE_READY && mPlayer.getTrackCount(TRACK_TYPE_VIDEO) > 0 511 && playWhenReady) { 512 MediaFormat format = mPlayer.getTrackFormat(TRACK_TYPE_VIDEO, 0); 513 mListener.onVideoSizeChanged(format.width, 514 format.height, format.pixelWidthHeightRatio); 515 } 516 } 517 518 @Override onPlayerError(ExoPlaybackException exception)519 public void onPlayerError(ExoPlaybackException exception) { 520 mRendererBuildingState = RENDERER_BUILDING_STATE_IDLE; 521 if (mListener != null) { 522 mListener.onError(exception); 523 } 524 } 525 526 @Override onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio)527 public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, 528 float pixelWidthHeightRatio) { 529 if (mListener != null) { 530 mListener.onVideoSizeChanged(width, height, pixelWidthHeightRatio); 531 } 532 } 533 534 @Override onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs)535 public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, 536 long initializationDurationMs) { 537 // Do nothing. 538 } 539 540 @Override onDecoderInitializationError(DecoderInitializationException e)541 public void onDecoderInitializationError(DecoderInitializationException e) { 542 // Do nothing. 543 } 544 545 @Override onAudioTrackInitializationError(AudioTrack.InitializationException e)546 public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { 547 if (mListener != null) { 548 mListener.onAudioUnplayable(); 549 } 550 } 551 552 @Override onAudioTrackWriteError(AudioTrack.WriteException e)553 public void onAudioTrackWriteError(AudioTrack.WriteException e) { 554 // Do nothing. 555 } 556 557 @Override onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs)558 public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, 559 long elapsedSinceLastFeedMs) { 560 // Do nothing. 561 } 562 563 @Override onCryptoError(CryptoException e)564 public void onCryptoError(CryptoException e) { 565 // Do nothing. 566 } 567 568 @Override onPlayWhenReadyCommitted()569 public void onPlayWhenReadyCommitted() { 570 // Do nothing. 571 } 572 573 @Override onDrawnToSurface(Surface surface)574 public void onDrawnToSurface(Surface surface) { 575 if (mListener != null) { 576 mListener.onDrawnToSurface(this, surface); 577 } 578 } 579 580 @Override onDroppedFrames(int count, long elapsed)581 public void onDroppedFrames(int count, long elapsed) { 582 if (mTrickplayRunning && mListener != null) { 583 mListener.onSmoothTrickplayForceStopped(); 584 } 585 } 586 587 @Override onAudioTrackSetPlaybackParamsError(IllegalArgumentException e)588 public void onAudioTrackSetPlaybackParamsError(IllegalArgumentException e) { 589 if (mTrickplayRunning && mListener != null) { 590 mListener.onSmoothTrickplayForceStopped(); 591 } 592 } 593 pushSurface(boolean blockForSurfacePush)594 private void pushSurface(boolean blockForSurfacePush) { 595 if (mRendererBuildingState != RENDERER_BUILDING_STATE_BUILT) { 596 return; 597 } 598 599 if (blockForSurfacePush) { 600 mPlayer.blockingSendMessage( 601 mVideoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, mSurface); 602 } else { 603 mPlayer.sendMessage( 604 mVideoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, mSurface); 605 } 606 } 607 pushTrackSelection(@rackType int type, boolean allowRendererEnable)608 private void pushTrackSelection(@TrackType int type, boolean allowRendererEnable) { 609 if (mRendererBuildingState != RENDERER_BUILDING_STATE_BUILT) { 610 return; 611 } 612 mPlayer.setSelectedTrack(type, allowRendererEnable ? 0 : -1); 613 } 614 615 private class MpegTsCcListener implements Cea708TextTrackRenderer.CcListener { 616 617 @Override emitEvent(CaptionEvent captionEvent)618 public void emitEvent(CaptionEvent captionEvent) { 619 if (mVideoEventListener != null) { 620 mVideoEventListener.onEmitCaptionEvent(captionEvent); 621 } 622 } 623 624 @Override discoverServiceNumber(int serviceNumber)625 public void discoverServiceNumber(int serviceNumber) { 626 if (mVideoEventListener != null) { 627 mVideoEventListener.onDiscoverCaptionServiceNumber(serviceNumber); 628 } 629 } 630 } 631 632 private class InternalRendererBuilderCallback implements RendererBuilderCallback { 633 private boolean canceled; 634 cancel()635 public void cancel() { 636 canceled = true; 637 } 638 639 @Override onRenderers(String[][] trackNames, TrackRenderer[] renderers)640 public void onRenderers(String[][] trackNames, TrackRenderer[] renderers) { 641 if (!canceled) { 642 MpegTsPlayer.this.onRenderers(renderers); 643 } 644 } 645 646 @Override onRenderersError(Exception e)647 public void onRenderersError(Exception e) { 648 if (!canceled) { 649 MpegTsPlayer.this.onRenderersError(e); 650 } 651 } 652 } 653 } 654