blob: 2cf1b2db71b5161aa445c9ec845d8d6845855d3f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import java.util.Locale;
import java.util.Vector;
import android.content.Context;
import android.media.MediaPlayer.OnSubtitleDataListener;
import android.view.View;
import android.view.accessibility.CaptioningManager;
/**
* 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
*/
public class SubtitleController {
private Context mContext;
private MediaTimeProvider mTimeProvider;
private Vector<Renderer> mRenderers;
private Vector<SubtitleTrack> mTracks;
private SubtitleTrack mSelectedTrack;
private boolean mShowing;
private CaptioningManager mCaptioningManager;
/**
* Creates a subtitle controller for a media playback object that implements
* the MediaTimeProvider interface.
*
* @param timeProvider
*/
public SubtitleController(
Context context,
MediaTimeProvider timeProvider,
Listener listener) {
mContext = context;
mTimeProvider = timeProvider;
mListener = listener;
mRenderers = new Vector<Renderer>();
mShowing = false;
mTracks = new Vector<SubtitleTrack>();
mCaptioningManager =
(CaptioningManager)context.getSystemService(Context.CAPTIONING_SERVICE);
}
/**
* @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}.
*/
public SubtitleTrack[] getTracks() {
SubtitleTrack[] tracks = new SubtitleTrack[mTracks.size()];
mTracks.toArray(tracks);
return tracks;
}
/**
* @return the currently selected subtitle track
*/
public SubtitleTrack getSelectedTrack() {
return mSelectedTrack;
}
private View getSubtitleView() {
if (mSelectedTrack == null) {
return null;
}
return mSelectedTrack.getView();
}
/**
* 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.
*
* @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.
*/
public boolean selectTrack(SubtitleTrack track) {
if (track != null && !mTracks.contains(track)) {
return false;
}
mTrackIsExplicit = true;
if (mSelectedTrack == track) {
return true;
}
if (mSelectedTrack != null) {
mSelectedTrack.hide();
mSelectedTrack.setTimeProvider(null);
}
mSelectedTrack = track;
mAnchor.setSubtitleView(getSubtitleView());
if (mSelectedTrack != null) {
mSelectedTrack.setTimeProvider(mTimeProvider);
mSelectedTrack.show();
}
if (mListener != null) {
mListener.onSubtitleTrackSelected(track);
}
return true;
}
/**
* @return the default subtitle track based on system preferences, or null,
* if no such track exists in this manager.
*/
public SubtitleTrack getDefaultTrack() {
Locale locale = mCaptioningManager.getLocale();
for (SubtitleTrack track: mTracks) {
MediaFormat format = track.getFormat();
String language = format.getString(MediaFormat.KEY_LANGUAGE);
// TODO: select track with best renderer. For now, we select first
// track with local's language or first track if locale has none
if (locale == null ||
locale.getLanguage().equals("") ||
locale.getISO3Language().equals(language) ||
locale.getLanguage().equals(language)) {
return track;
}
}
return null;
}
private boolean mTrackIsExplicit = false;
private boolean mVisibilityIsExplicit = false;
/** @hide */
public void selectDefaultTrack() {
if (mTrackIsExplicit) {
return;
}
SubtitleTrack track = getDefaultTrack();
if (track != null) {
selectTrack(track);
mTrackIsExplicit = false;
if (!mVisibilityIsExplicit) {
if (mCaptioningManager.isEnabled()) {
show();
} else {
hide();
}
mVisibilityIsExplicit = false;
}
}
}
/** @hide */
public void reset() {
hide();
selectTrack(null);
mTracks.clear();
mTrackIsExplicit = false;
mVisibilityIsExplicit = false;
}
/**
* 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
*/
public SubtitleTrack addTrack(MediaFormat format) {
for (Renderer renderer: mRenderers) {
if (renderer.supports(format)) {
SubtitleTrack track = renderer.createTrack(format);
if (track != null) {
mTracks.add(track);
return track;
}
}
}
return null;
}
/**
* Show the selected (or default) subtitle track.
*/
public void show() {
mShowing = true;
mVisibilityIsExplicit = true;
if (mSelectedTrack != null) {
mSelectedTrack.show();
}
}
/**
* Hide the selected (or default) subtitle track.
*/
public void hide() {
mVisibilityIsExplicit = true;
if (mSelectedTrack != null) {
mSelectedTrack.hide();
}
mShowing = false;
}
/**
* Interface for supporting a single or multiple subtitle types in {@link
* MediaPlayer}.
*/
public abstract static class Renderer {
/**
* Called by {@link MediaPlayer}'s {@link SubtitleController} when a new
* subtitle track is detected, to see if it should use this object to
* parse and display this subtitle track.
*
* @param format the format of the track that will include at least
* the MIME type {@link MediaFormat@KEY_MIME}.
*
* @return true if and only if the track format is supported by this
* renderer
*/
public abstract boolean supports(MediaFormat format);
/**
* Called by {@link MediaPlayer}'s {@link SubtitleController} for each
* subtitle track that was detected and is supported by this object to
* create a {@link SubtitleTrack} object. This object will be created
* for each track that was found. If the track is selected for display,
* this object will be used to parse and display the track data.
*
* @param format the format of the track that will include at least
* the MIME type {@link MediaFormat@KEY_MIME}.
* @return a {@link SubtitleTrack} object that will be used to parse
* and render the subtitle track.
*/
public abstract SubtitleTrack createTrack(MediaFormat format);
}
/**
* Add support for a subtitle format in {@link MediaPlayer}.
*
* @param renderer a {@link SubtitleController.Renderer} object that adds
* support for a subtitle format.
*/
public void registerRenderer(Renderer renderer) {
// 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);
}
}
/**
* Subtitle anchor, an object that is able to display a subtitle view,
* e.g. a VideoView.
*/
public interface Anchor {
/**
* Anchor should set the subtitle view to the supplied view,
* or none, if the supplied view is null.
*
* @param view subtitle view, or null
*/
public void setSubtitleView(View view);
}
private Anchor mAnchor;
/** @hide */
public void setAnchor(Anchor anchor) {
if (mAnchor == anchor) {
return;
}
if (mAnchor != null) {
mAnchor.setSubtitleView(null);
}
mAnchor = anchor;
if (mAnchor != null) {
mAnchor.setSubtitleView(getSubtitleView());
}
}
public interface Listener {
/**
* Called when a subtitle track has been selected.
*
* @param track selected subtitle track or null
* @hide
*/
public void onSubtitleTrackSelected(SubtitleTrack track);
}
private Listener mListener;
}