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