FileDocCategorySizeDatePackage
SubtitleController.javaAPI DocAndroid 5.1 API17266Thu Mar 12 22:22:30 GMT 2015android.media

SubtitleController

public class SubtitleController extends Object
The subtitle controller provides the architecture to display subtitles for a media source. It allows specifying which tracks to display, on which anchor to display them, and also allows adding external, out-of-band subtitle tracks.
hide

Fields Summary
private MediaTimeProvider
mTimeProvider
private Vector
mRenderers
private Vector
mTracks
private SubtitleTrack
mSelectedTrack
private boolean
mShowing
private android.view.accessibility.CaptioningManager
mCaptioningManager
private android.os.Handler
mHandler
private static final int
WHAT_SHOW
private static final int
WHAT_HIDE
private static final int
WHAT_SELECT_TRACK
private static final int
WHAT_SELECT_DEFAULT_TRACK
private final Handler.Callback
mCallback
private CaptioningManager.CaptioningChangeListener
mCaptioningChangeListener
private boolean
mTrackIsExplicit
private boolean
mVisibilityIsExplicit
private Anchor
mAnchor
private Listener
mListener
Constructors Summary
public SubtitleController(android.content.Context context, MediaTimeProvider timeProvider, Listener listener)
Creates a subtitle controller for a media playback object that implements the MediaTimeProvider interface.

param
timeProvider


                         
     
             
             
              
        mTimeProvider = timeProvider;
        mListener = listener;

        mRenderers = new Vector<Renderer>();
        mShowing = false;
        mTracks = new Vector<SubtitleTrack>();
        mCaptioningManager =
            (CaptioningManager)context.getSystemService(Context.CAPTIONING_SERVICE);
    
Methods Summary
public SubtitleTrackaddTrack(MediaFormat format)
Adds a new, external subtitle track to the manager.

param
format the format of the track that will include at least the MIME type {@link MediaFormat@KEY_MIME}.
return
the created {@link SubtitleTrack} object

        synchronized(mRenderers) {
            for (Renderer renderer: mRenderers) {
                if (renderer.supports(format)) {
                    SubtitleTrack track = renderer.createTrack(format);
                    if (track != null) {
                        synchronized(mTracks) {
                            if (mTracks.size() == 0) {
                                mCaptioningManager.addCaptioningChangeListener(
                                        mCaptioningChangeListener);
                            }
                            mTracks.add(track);
                        }
                        return track;
                    }
                }
            }
        }
        return null;
    
private voidcheckAnchorLooper()

        assert mHandler != null : "Should have a looper already";
        assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper";
    
private voiddoHide()

        mVisibilityIsExplicit = true;
        if (mSelectedTrack != null) {
            mSelectedTrack.hide();
        }
        mShowing = false;
    
private voiddoSelectDefaultTrack()

        if (mTrackIsExplicit) {
            // If track selection is explicit, but visibility
            // is not, it falls back to the captioning setting
            if (!mVisibilityIsExplicit) {
                if (mCaptioningManager.isEnabled() ||
                    (mSelectedTrack != null &&
                     mSelectedTrack.getFormat().getInteger(
                            MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0)) {
                    show();
                } else if (mSelectedTrack != null && !mSelectedTrack.isTimedText()) {
                    hide();
                }
                mVisibilityIsExplicit = false;
            }
            return;
        }

        // We can have a default (forced) track even if captioning
        // is not enabled.  This is handled by getDefaultTrack().
        // Show this track unless subtitles were explicitly hidden.
        SubtitleTrack track = getDefaultTrack();
        if (track != null) {
            selectTrack(track);
            mTrackIsExplicit = false;
            if (!mVisibilityIsExplicit) {
                show();
                mVisibilityIsExplicit = false;
            }
        }
    
private voiddoSelectTrack(SubtitleTrack track)

        mTrackIsExplicit = true;
        if (mSelectedTrack == track) {
            return;
        }

        if (mSelectedTrack != null) {
            mSelectedTrack.hide();
            mSelectedTrack.setTimeProvider(null);
        }

        mSelectedTrack = track;
        if (mAnchor != null) {
            mAnchor.setSubtitleWidget(getRenderingWidget());
        }

        if (mSelectedTrack != null) {
            mSelectedTrack.setTimeProvider(mTimeProvider);
            mSelectedTrack.show();
        }

        if (mListener != null) {
            mListener.onSubtitleTrackSelected(track);
        }
    
private voiddoShow()

        mShowing = true;
        mVisibilityIsExplicit = true;
        if (mSelectedTrack != null) {
            mSelectedTrack.show();
        }
    
protected voidfinalize()

        mCaptioningManager.removeCaptioningChangeListener(
                mCaptioningChangeListener);
        super.finalize();
    
public SubtitleTrackgetDefaultTrack()

return
the default subtitle track based on system preferences, or null, if no such track exists in this manager. Supports HLS-flags: AUTOSELECT, FORCED & DEFAULT. 1. If captioning is disabled, only consider FORCED tracks. Otherwise, consider all tracks, but prefer non-FORCED ones. 2. If user selected "Default" caption language: a. If there is a considered track with DEFAULT=yes, returns that track (favor the first one in the current language if there are more than one default tracks, or the first in general if none of them are in the current language). b. Otherwise, if there is a track with AUTOSELECT=yes in the current language, return that one. c. If there are no default tracks, and no autoselectable tracks in the current language, return null. 3. If there is a track with the caption language, select that one. Prefer the one with AUTOSELECT=no. The default values for these flags are DEFAULT=no, AUTOSELECT=yes and FORCED=no.

        SubtitleTrack bestTrack = null;
        int bestScore = -1;

        Locale selectedLocale = mCaptioningManager.getLocale();
        Locale locale = selectedLocale;
        if (locale == null) {
            locale = Locale.getDefault();
        }
        boolean selectForced = !mCaptioningManager.isEnabled();

        synchronized(mTracks) {
            for (SubtitleTrack track: mTracks) {
                MediaFormat format = track.getFormat();
                String language = format.getString(MediaFormat.KEY_LANGUAGE);
                boolean forced =
                    format.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0;
                boolean autoselect =
                    format.getInteger(MediaFormat.KEY_IS_AUTOSELECT, 1) != 0;
                boolean is_default =
                    format.getInteger(MediaFormat.KEY_IS_DEFAULT, 0) != 0;

                boolean languageMatches =
                    (locale == null ||
                    locale.getLanguage().equals("") ||
                    locale.getISO3Language().equals(language) ||
                    locale.getLanguage().equals(language));
                // is_default is meaningless unless caption language is 'default'
                int score = (forced ? 0 : 8) +
                    (((selectedLocale == null) && is_default) ? 4 : 0) +
                    (autoselect ? 0 : 2) + (languageMatches ? 1 : 0);

                if (selectForced && !forced) {
                    continue;
                }

                // we treat null locale/language as matching any language
                if ((selectedLocale == null && is_default) ||
                    (languageMatches &&
                     (autoselect || forced || selectedLocale != null))) {
                    if (score > bestScore) {
                        bestScore = score;
                        bestTrack = track;
                    }
                }
            }
        }
        return bestTrack;
    
private android.media.SubtitleTrack.RenderingWidgetgetRenderingWidget()

        if (mSelectedTrack == null) {
            return null;
        }
        return mSelectedTrack.getRenderingWidget();
    
public SubtitleTrackgetSelectedTrack()

return
the currently selected subtitle track

        return mSelectedTrack;
    
public SubtitleTrack[]getTracks()

return
the available subtitle tracks for this media. These include the tracks found by {@link MediaPlayer} as well as any tracks added manually via {@link #addTrack}.

        synchronized(mTracks) {
            SubtitleTrack[] tracks = new SubtitleTrack[mTracks.size()];
            mTracks.toArray(tracks);
            return tracks;
        }
    
public booleanhasRendererFor(MediaFormat format)

hide

        synchronized(mRenderers) {
            // TODO how to get available renderers in the system
            for (Renderer renderer: mRenderers) {
                if (renderer.supports(format)) {
                    return true;
                }
            }
            return false;
        }
    
public voidhide()
Hide the selected (or default) subtitle track. Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}

        processOnAnchor(mHandler.obtainMessage(WHAT_HIDE));
    
private voidprocessOnAnchor(android.os.Message m)

        assert mHandler != null : "Should have a looper already";
        if (Looper.myLooper() == mHandler.getLooper()) {
            mHandler.dispatchMessage(m);
        } else {
            mHandler.sendMessage(m);
        }
    
public voidregisterRenderer(android.media.SubtitleController$Renderer renderer)
Add support for a subtitle format in {@link MediaPlayer}.

param
renderer a {@link SubtitleController.Renderer} object that adds support for a subtitle format.

        synchronized(mRenderers) {
            // TODO how to get available renderers in the system
            if (!mRenderers.contains(renderer)) {
                // TODO should added renderers override existing ones (to allow replacing?)
                mRenderers.add(renderer);
            }
        }
    
public voidreset()

hide
- must be called from anchor thread

        checkAnchorLooper();
        hide();
        selectTrack(null);
        mTracks.clear();
        mTrackIsExplicit = false;
        mVisibilityIsExplicit = false;
        mCaptioningManager.removeCaptioningChangeListener(
                mCaptioningChangeListener);
    
public voidselectDefaultTrack()

hide
- should be called from anchor thread


             
       
        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK));
    
public booleanselectTrack(SubtitleTrack track)
Selects a subtitle track. As a result, this track will receive in-band data from the {@link MediaPlayer}. However, this does not change the subtitle visibility. Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}

param
track The subtitle track to select. This must be one of the tracks in {@link #getTracks}.
return
true if the track was successfully selected.

        if (track != null && !mTracks.contains(track)) {
            return false;
        }

        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track));
        return true;
    
public voidsetAnchor(android.media.SubtitleController$Anchor anchor)

hide
- called from anchor's looper (if any, both when unsetting and setting)

        if (mAnchor == anchor) {
            return;
        }

        if (mAnchor != null) {
            checkAnchorLooper();
            mAnchor.setSubtitleWidget(null);
        }
        mAnchor = anchor;
        mHandler = null;
        if (mAnchor != null) {
            mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback);
            checkAnchorLooper();
            mAnchor.setSubtitleWidget(getRenderingWidget());
        }
    
public voidshow()
Show the selected (or default) subtitle track. Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}

        processOnAnchor(mHandler.obtainMessage(WHAT_SHOW));