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