Loading media/java/android/media/MediaTimeProvider.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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; /** @hide */ public interface MediaTimeProvider { // we do not allow negative media time /** * Presentation time value if no timed event notification is requested. */ public final static long NO_TIME = -1; /** * Cancels all previous notification request from this listener if any. It * registers the listener to get seek and stop notifications. If timeUs is * not negative, it also registers the listener for a timed event * notification when the presentation time reaches (becomes greater) than * the value specified. This happens immediately if the current media time * is larger than or equal to timeUs. * * @param timeUs presentation time to get timed event callback at (or * {@link #NO_TIME}) */ public void notifyAt(long timeUs, OnMediaTimeListener listener); /** * Cancels all previous notification request from this listener if any. It * registers the listener to get seek and stop notifications. If the media * is stopped, the listener will immediately receive a stop notification. * Otherwise, it will receive a timed event notificaton. */ public void scheduleUpdate(OnMediaTimeListener listener); /** * Cancels all previous notification request from this listener if any. */ public void cancelNotifications(OnMediaTimeListener listener); /** * Get the current presentation time. * * @param precise Whether getting a precise time is important. This is * more costly. * @param monotonic Whether returned time should be monotonic: that is, * greater than or equal to the last returned time. Don't * always set this to true. E.g. this has undesired * consequences if the media is seeked between calls. * @throw IllegalStateException if the media is not initialized */ public long getCurrentTimeUs(boolean precise, boolean monotonic) throws IllegalStateException; /** @hide */ public static interface OnMediaTimeListener { /** * Called when the registered time was reached naturally. * * @param timeUs current media time */ void onTimedEvent(long timeUs); /** * Called when the media time changed due to seeking. * * @param timeUs current media time */ void onSeek(long timeUs); /** * Called when the playback stopped. This is not called on pause, only * on full stop, at which point there is no further current media time. */ void onStop(); } } media/java/android/media/SubtitleController.java 0 → 100644 +309 −0 Original line number Diff line number Diff line /* * 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; /** * 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>(); } /** * @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 = CaptioningManager.getLocale(mContext.getContentResolver()); 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 (CaptioningManager.isEnabled(mContext.getContentResolver())) { 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; } Loading
media/java/android/media/MediaTimeProvider.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * 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; /** @hide */ public interface MediaTimeProvider { // we do not allow negative media time /** * Presentation time value if no timed event notification is requested. */ public final static long NO_TIME = -1; /** * Cancels all previous notification request from this listener if any. It * registers the listener to get seek and stop notifications. If timeUs is * not negative, it also registers the listener for a timed event * notification when the presentation time reaches (becomes greater) than * the value specified. This happens immediately if the current media time * is larger than or equal to timeUs. * * @param timeUs presentation time to get timed event callback at (or * {@link #NO_TIME}) */ public void notifyAt(long timeUs, OnMediaTimeListener listener); /** * Cancels all previous notification request from this listener if any. It * registers the listener to get seek and stop notifications. If the media * is stopped, the listener will immediately receive a stop notification. * Otherwise, it will receive a timed event notificaton. */ public void scheduleUpdate(OnMediaTimeListener listener); /** * Cancels all previous notification request from this listener if any. */ public void cancelNotifications(OnMediaTimeListener listener); /** * Get the current presentation time. * * @param precise Whether getting a precise time is important. This is * more costly. * @param monotonic Whether returned time should be monotonic: that is, * greater than or equal to the last returned time. Don't * always set this to true. E.g. this has undesired * consequences if the media is seeked between calls. * @throw IllegalStateException if the media is not initialized */ public long getCurrentTimeUs(boolean precise, boolean monotonic) throws IllegalStateException; /** @hide */ public static interface OnMediaTimeListener { /** * Called when the registered time was reached naturally. * * @param timeUs current media time */ void onTimedEvent(long timeUs); /** * Called when the media time changed due to seeking. * * @param timeUs current media time */ void onSeek(long timeUs); /** * Called when the playback stopped. This is not called on pause, only * on full stop, at which point there is no further current media time. */ void onStop(); } }
media/java/android/media/SubtitleController.java 0 → 100644 +309 −0 Original line number Diff line number Diff line /* * 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; /** * 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>(); } /** * @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 = CaptioningManager.getLocale(mContext.getContentResolver()); 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 (CaptioningManager.isEnabled(mContext.getContentResolver())) { 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; }