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