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