• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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