diff options
| -rw-r--r-- | media/java/android/media/tv/TvInputManager.java | 272 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvView.java | 120 |
2 files changed, 259 insertions, 133 deletions
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 78714d2..51bd205 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -159,12 +159,12 @@ public final class TvInputManager { private final Object mLock = new Object(); - // @GuardedBy(mLock) + // @GuardedBy("mLock") private final List<TvInputCallbackRecord> mCallbackRecords = new LinkedList<TvInputCallbackRecord>(); // A mapping from TV input ID to the state of corresponding input. - // @GuardedBy(mLock) + // @GuardedBy("mLock") private final Map<String, Integer> mStateMap = new ArrayMap<String, Integer>(); // A mapping from the sequence number of a session to its SessionCallbackRecord. @@ -207,7 +207,7 @@ public final class TvInputManager { /** * This is called when the channel of this session is changed by the underlying TV input - * with out any {@link TvInputManager.Session#tune(Uri)} request. + * without any {@link TvInputManager.Session#tune(Uri)} request. * * @param session A {@link TvInputManager.Session} associated with this callback. * @param channelUri The URI of a channel. @@ -227,7 +227,7 @@ public final class TvInputManager { /** * This is called when a track for a given type is selected. * - * @param session A {@link TvInputManager.Session} associated with this callback + * @param session A {@link TvInputManager.Session} associated with this callback. * @param type The type of the selected track. The type can be * {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or * {@link TvTrackInfo#TYPE_SUBTITLE}. @@ -238,6 +238,18 @@ public final class TvInputManager { } /** + * This is invoked when the video size has been changed. It is also called when the first + * time video size information becomes available after the session is tuned to a specific + * channel. + * + * @param session A {@link TvInputManager.Session} associated with this callback. + * @param width The width of the video. + * @param height The height of the video. + */ + public void onVideoSizeChanged(Session session, int width, int height) { + } + + /** * This is called when the video is available, so the TV input starts the playback. * * @param session A {@link TvInputManager.Session} associated with this callback. @@ -312,13 +324,13 @@ public final class TvInputManager { private final Handler mHandler; private Session mSession; - public SessionCallbackRecord(SessionCallback sessionCallback, + SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) { mSessionCallback = sessionCallback; mHandler = handler; } - public void postSessionCreated(final Session session) { + void postSessionCreated(final Session session) { mSession = session; mHandler.post(new Runnable() { @Override @@ -328,7 +340,7 @@ public final class TvInputManager { }); } - public void postSessionReleased() { + void postSessionReleased() { mHandler.post(new Runnable() { @Override public void run() { @@ -337,7 +349,7 @@ public final class TvInputManager { }); } - public void postChannelRetuned(final Uri channelUri) { + void postChannelRetuned(final Uri channelUri) { mHandler.post(new Runnable() { @Override public void run() { @@ -346,49 +358,34 @@ public final class TvInputManager { }); } - public void postTracksChanged(final List<TvTrackInfo> tracks) { + void postTracksChanged(final List<TvTrackInfo> tracks) { mHandler.post(new Runnable() { @Override public void run() { - mSession.mAudioTracks.clear(); - mSession.mVideoTracks.clear(); - mSession.mSubtitleTracks.clear(); - for (TvTrackInfo track : tracks) { - if (track.getType() == TvTrackInfo.TYPE_AUDIO) { - mSession.mAudioTracks.add(track); - } else if (track.getType() == TvTrackInfo.TYPE_VIDEO) { - mSession.mVideoTracks.add(track); - } else if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) { - mSession.mSubtitleTracks.add(track); - } else { - // Silently ignore. - } - } mSessionCallback.onTracksChanged(mSession, tracks); } }); } - public void postTrackSelected(final int type, final String trackId) { + void postTrackSelected(final int type, final String trackId) { mHandler.post(new Runnable() { @Override public void run() { - if (type == TvTrackInfo.TYPE_AUDIO) { - mSession.mSelectedAudioTrackId = trackId; - } else if (type == TvTrackInfo.TYPE_VIDEO) { - mSession.mSelectedVideoTrackId = trackId; - } else if (type == TvTrackInfo.TYPE_SUBTITLE) { - mSession.mSelectedSubtitleTrackId = trackId; - } else { - // Silently ignore. - return; - } mSessionCallback.onTrackSelected(mSession, type, trackId); } }); } - public void postVideoAvailable() { + void postVideoSizeChanged(final int width, final int height) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onVideoSizeChanged(mSession, width, height); + } + }); + } + + void postVideoAvailable() { mHandler.post(new Runnable() { @Override public void run() { @@ -397,7 +394,7 @@ public final class TvInputManager { }); } - public void postVideoUnavailable(final int reason) { + void postVideoUnavailable(final int reason) { mHandler.post(new Runnable() { @Override public void run() { @@ -406,7 +403,7 @@ public final class TvInputManager { }); } - public void postContentAllowed() { + void postContentAllowed() { mHandler.post(new Runnable() { @Override public void run() { @@ -415,7 +412,7 @@ public final class TvInputManager { }); } - public void postContentBlocked(final TvContentRating rating) { + void postContentBlocked(final TvContentRating rating) { mHandler.post(new Runnable() { @Override public void run() { @@ -424,7 +421,7 @@ public final class TvInputManager { }); } - public void postLayoutSurface(final int left, final int top, final int right, + void postLayoutSurface(final int left, final int top, final int right, final int bottom) { mHandler.post(new Runnable() { @Override @@ -434,7 +431,7 @@ public final class TvInputManager { }); } - public void postSessionEvent(final String eventType, final Bundle eventArgs) { + void postSessionEvent(final String eventType, final Bundle eventArgs) { mHandler.post(new Runnable() { @Override public void run() { @@ -610,7 +607,10 @@ public final class TvInputManager { Log.e(TAG, "Callback not found for seq " + seq); return; } - record.postTracksChanged(tracks); + if (record.mSession.updateTracks(tracks)) { + record.postTracksChanged(tracks); + postVideoSizeChangedIfNeededLocked(record); + } } } @@ -622,7 +622,17 @@ public final class TvInputManager { Log.e(TAG, "Callback not found for seq " + seq); return; } - record.postTrackSelected(type, trackId); + if (record.mSession.updateTrackSelection(type, trackId)) { + record.postTrackSelected(type, trackId); + postVideoSizeChangedIfNeededLocked(record); + } + } + } + + private void postVideoSizeChangedIfNeededLocked(SessionCallbackRecord record) { + TvTrackInfo track = record.mSession.getVideoTrackToNotify(); + if (track != null) { + record.postVideoSizeChanged(track.getVideoWidth(), track.getVideoHeight()); } } @@ -778,7 +788,7 @@ public final class TvInputManager { } /** - * Returns the state of a given TV input. It retuns one of the following: + * Returns the state of a given TV input. It returns one of the following: * <ul> * <li>{@link #INPUT_STATE_CONNECTED} * <li>{@link #INPUT_STATE_CONNECTED_STANDBY} @@ -1133,12 +1143,24 @@ public final class TvInputManager { private IBinder mToken; private TvInputEventSender mSender; private InputChannel mChannel; + + private final Object mTrackLock = new Object(); + // @GuardedBy("mTrackLock") private final List<TvTrackInfo> mAudioTracks = new ArrayList<TvTrackInfo>(); + // @GuardedBy("mTrackLock") private final List<TvTrackInfo> mVideoTracks = new ArrayList<TvTrackInfo>(); + // @GuardedBy("mTrackLock") private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<TvTrackInfo>(); + // @GuardedBy("mTrackLock") private String mSelectedAudioTrackId; + // @GuardedBy("mTrackLock") private String mSelectedVideoTrackId; + // @GuardedBy("mTrackLock") private String mSelectedSubtitleTrackId; + // @GuardedBy("mTrackLock") + private int mVideoWidth; + // @GuardedBy("mTrackLock") + private int mVideoHeight; private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) { @@ -1273,12 +1295,16 @@ public final class TvInputManager { Log.w(TAG, "The session has been already released"); return; } - mAudioTracks.clear(); - mVideoTracks.clear(); - mSubtitleTracks.clear(); - mSelectedAudioTrackId = null; - mSelectedVideoTrackId = null; - mSelectedSubtitleTrackId = null; + synchronized (mTrackLock) { + mAudioTracks.clear(); + mVideoTracks.clear(); + mSubtitleTracks.clear(); + mSelectedAudioTrackId = null; + mSelectedVideoTrackId = null; + mSelectedSubtitleTrackId = null; + mVideoWidth = 0; + mVideoHeight = 0; + } try { mService.tune(mToken, channelUri, params, mUserId); } catch (RemoteException e) { @@ -1314,23 +1340,25 @@ public final class TvInputManager { * @see #getTracks */ public void selectTrack(int type, String trackId) { - if (type == TvTrackInfo.TYPE_AUDIO) { - if (trackId != null && !containsTrack(mAudioTracks, trackId)) { - Log.w(TAG, "Invalid audio trackId: " + trackId); - return; - } - } else if (type == TvTrackInfo.TYPE_VIDEO) { - if (trackId != null && !containsTrack(mVideoTracks, trackId)) { - Log.w(TAG, "Invalid video trackId: " + trackId); - return; - } - } else if (type == TvTrackInfo.TYPE_SUBTITLE) { - if (trackId != null && !containsTrack(mSubtitleTracks, trackId)) { - Log.w(TAG, "Invalid subtitle trackId: " + trackId); - return; + synchronized (mTrackLock) { + if (type == TvTrackInfo.TYPE_AUDIO) { + if (trackId != null && !containsTrack(mAudioTracks, trackId)) { + Log.w(TAG, "Invalid audio trackId: " + trackId); + return; + } + } else if (type == TvTrackInfo.TYPE_VIDEO) { + if (trackId != null && !containsTrack(mVideoTracks, trackId)) { + Log.w(TAG, "Invalid video trackId: " + trackId); + return; + } + } else if (type == TvTrackInfo.TYPE_SUBTITLE) { + if (trackId != null && !containsTrack(mSubtitleTracks, trackId)) { + Log.w(TAG, "Invalid subtitle trackId: " + trackId); + return; + } + } else { + throw new IllegalArgumentException("invalid type: " + type); } - } else { - throw new IllegalArgumentException("invalid type: " + type); } if (mToken == null) { Log.w(TAG, "The session has been already released"); @@ -1361,21 +1389,23 @@ public final class TvInputManager { * @return the list of tracks for the given type. */ public List<TvTrackInfo> getTracks(int type) { - if (type == TvTrackInfo.TYPE_AUDIO) { - if (mAudioTracks == null) { - return null; - } - return mAudioTracks; - } else if (type == TvTrackInfo.TYPE_VIDEO) { - if (mVideoTracks == null) { - return null; - } - return mVideoTracks; - } else if (type == TvTrackInfo.TYPE_SUBTITLE) { - if (mSubtitleTracks == null) { - return null; + synchronized (mTrackLock) { + if (type == TvTrackInfo.TYPE_AUDIO) { + if (mAudioTracks == null) { + return null; + } + return new ArrayList<TvTrackInfo>(mAudioTracks); + } else if (type == TvTrackInfo.TYPE_VIDEO) { + if (mVideoTracks == null) { + return null; + } + return new ArrayList<TvTrackInfo>(mVideoTracks); + } else if (type == TvTrackInfo.TYPE_SUBTITLE) { + if (mSubtitleTracks == null) { + return null; + } + return new ArrayList<TvTrackInfo>(mSubtitleTracks); } - return mSubtitleTracks; } throw new IllegalArgumentException("invalid type: " + type); } @@ -1388,17 +1418,89 @@ public final class TvInputManager { * @see #selectTrack */ public String getSelectedTrack(int type) { - if (type == TvTrackInfo.TYPE_AUDIO) { - return mSelectedAudioTrackId; - } else if (type == TvTrackInfo.TYPE_VIDEO) { - return mSelectedVideoTrackId; - } else if (type == TvTrackInfo.TYPE_SUBTITLE) { - return mSelectedSubtitleTrackId; + synchronized (mTrackLock) { + if (type == TvTrackInfo.TYPE_AUDIO) { + return mSelectedAudioTrackId; + } else if (type == TvTrackInfo.TYPE_VIDEO) { + return mSelectedVideoTrackId; + } else if (type == TvTrackInfo.TYPE_SUBTITLE) { + return mSelectedSubtitleTrackId; + } } throw new IllegalArgumentException("invalid type: " + type); } /** + * Responds to onTracksChanged() and updates the internal track information. Returns true if + * there is an update. + */ + boolean updateTracks(List<TvTrackInfo> tracks) { + synchronized (mTrackLock) { + mAudioTracks.clear(); + mVideoTracks.clear(); + mSubtitleTracks.clear(); + for (TvTrackInfo track : tracks) { + if (track.getType() == TvTrackInfo.TYPE_AUDIO) { + mAudioTracks.add(track); + } else if (track.getType() == TvTrackInfo.TYPE_VIDEO) { + mVideoTracks.add(track); + } else if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) { + mSubtitleTracks.add(track); + } + } + return !mAudioTracks.isEmpty() || !mVideoTracks.isEmpty() + || !mSubtitleTracks.isEmpty(); + } + } + + /** + * Responds to onTrackSelected() and updates the internal track selection information. + * Returns true if there is an update. + */ + boolean updateTrackSelection(int type, String trackId) { + synchronized (mTrackLock) { + if (type == TvTrackInfo.TYPE_AUDIO && trackId != mSelectedAudioTrackId) { + mSelectedAudioTrackId = trackId; + return true; + } else if (type == TvTrackInfo.TYPE_VIDEO && trackId != mSelectedVideoTrackId) { + mSelectedVideoTrackId = trackId; + return true; + } else if (type == TvTrackInfo.TYPE_SUBTITLE + && trackId != mSelectedSubtitleTrackId) { + mSelectedSubtitleTrackId = trackId; + return true; + } + } + return false; + } + + /** + * Returns the new/updated video track that contains new video size information. Returns + * null if there is no video track to notify. Subsequent calls of this method results in a + * non-null video track returned only by the first call and null returned by following + * calls. The caller should immediately notify of the video size change upon receiving the + * track. + */ + TvTrackInfo getVideoTrackToNotify() { + synchronized (mTrackLock) { + if (!mVideoTracks.isEmpty() && mSelectedVideoTrackId != null) { + for (TvTrackInfo track : mVideoTracks) { + if (track.getId().equals(mSelectedVideoTrackId)) { + int videoWidth = track.getVideoWidth(); + int videoHeight = track.getVideoHeight(); + if (mVideoWidth != videoWidth || mVideoHeight != videoHeight) { + mVideoWidth = videoWidth; + mVideoHeight = videoHeight; + return track; + } + } + } + } + } + return null; + } + + /** * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle) * TvInputService.Session.appPrivateCommand()} on the current TvView. * diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 92a2a39..f9d84c1 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -59,8 +59,6 @@ public class TvView extends ViewGroup { private static final String TAG = "TvView"; private static final boolean DEBUG = false; - private static final int VIDEO_SIZE_VALUE_UNKNOWN = 0; - private static final int ZORDER_MEDIA = 0; private static final int ZORDER_MEDIA_OVERLAY = 1; private static final int ZORDER_ON_TOP = 2; @@ -69,7 +67,7 @@ public class TvView extends ViewGroup { private static final int CAPTION_ENABLED = 1; private static final int CAPTION_DISABLED = 2; - private static final WeakReference<TvView> NULL_TV_VIEW = new WeakReference(null); + private static final WeakReference<TvView> NULL_TV_VIEW = new WeakReference<>(null); private static final Object sMainTvViewLock = new Object(); private static WeakReference<TvView> sMainTvView = NULL_TV_VIEW; @@ -86,8 +84,6 @@ public class TvView extends ViewGroup { private OnUnhandledInputEventListener mOnUnhandledInputEventListener; private boolean mHasStreamVolume; private float mStreamVolume; - private int mVideoWidth = VIDEO_SIZE_VALUE_UNKNOWN; - private int mVideoHeight = VIDEO_SIZE_VALUE_UNKNOWN; private int mCaptionEnabled; private String mAppPrivateCommandAction; private Bundle mAppPrivateCommandData; @@ -200,7 +196,7 @@ public class TvView extends ViewGroup { @SystemApi public void setMain() { synchronized (sMainTvViewLock) { - sMainTvView = new WeakReference(this); + sMainTvView = new WeakReference<>(this); if (hasWindowFocus() && mSession != null) { mSession.setMain(); } @@ -294,7 +290,7 @@ public class TvView extends ViewGroup { } synchronized (sMainTvViewLock) { if (sMainTvView.get() == null) { - sMainTvView = new WeakReference(this); + sMainTvView = new WeakReference<>(this); } } if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) { @@ -716,19 +712,8 @@ public class TvView extends ViewGroup { } /** - * This is invoked when the view is tuned to a specific channel and starts decoding video - * stream from there. It is also called later when the video size is changed. - * - * @param inputId The ID of the TV input bound to this view. - * @param width The width of the video. - * @param height The height of the video. - */ - public void onVideoSizeChanged(String inputId, int width, int height) { - } - - /** * This is invoked when the channel of this TvView is changed by the underlying TV input - * with out any {@link TvView#tune(String, Uri)} request. + * without any {@link TvView#tune(String, Uri)} request. * * @param inputId The ID of the TV input bound to this view. * @param channelUri The URI of a channel. @@ -758,6 +743,18 @@ public class TvView extends ViewGroup { } /** + * This is invoked when the video size has been changed. It is also called when the first + * time video size information becomes available after this view is tuned to a specific + * channel. + * + * @param inputId The ID of the TV input bound to this view. + * @param width The width of the video. + * @param height The height of the video. + */ + public void onVideoSizeChanged(String inputId, int width, int height) { + } + + /** * This is called when the video is available, so the TV input starts the playback. * * @param inputId The ID of the TV input bound to this view. @@ -841,16 +838,17 @@ public class TvView extends ViewGroup { @Override public void onSessionCreated(Session session) { + if (DEBUG) { + Log.d(TAG, "onSessionCreated()"); + } if (this != mSessionCallback) { + Log.w(TAG, "onSessionCreated - session already created"); // This callback is obsolete. if (session != null) { session.release(); } return; } - if (DEBUG) { - Log.d(TAG, "onSessionCreated()"); - } mSession = session; if (session != null) { synchronized (sMainTvViewLock) { @@ -891,7 +889,11 @@ public class TvView extends ViewGroup { @Override public void onSessionReleased(Session session) { + if (DEBUG) { + Log.d(TAG, "onSessionReleased()"); + } if (this != mSessionCallback) { + Log.w(TAG, "onSessionReleased - session not created"); return; } mOverlayViewCreated = false; @@ -905,12 +907,13 @@ public class TvView extends ViewGroup { @Override public void onChannelRetuned(Session session, Uri channelUri) { - if (this != mSessionCallback) { - return; - } if (DEBUG) { Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")"); } + if (this != mSessionCallback) { + Log.w(TAG, "onChannelRetuned - session not created"); + return; + } if (mCallback != null) { mCallback.onChannelRetuned(mInputId, channelUri); } @@ -918,12 +921,13 @@ public class TvView extends ViewGroup { @Override public void onTracksChanged(Session session, List<TvTrackInfo> tracks) { + if (DEBUG) { + Log.d(TAG, "onTracksChanged(" + tracks + ")"); + } if (this != mSessionCallback) { + Log.w(TAG, "onTracksChanged - session not created"); return; } - if (DEBUG) { - Log.d(TAG, "onTracksChanged()"); - } if (mCallback != null) { mCallback.onTracksChanged(mInputId, tracks); } @@ -931,26 +935,41 @@ public class TvView extends ViewGroup { @Override public void onTrackSelected(Session session, int type, String trackId) { + if (DEBUG) { + Log.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")"); + } if (this != mSessionCallback) { + Log.w(TAG, "onTrackSelected - session not created"); return; } - if (DEBUG) { - Log.d(TAG, "onTrackSelected()"); - } - // TODO: Update the video size when the type is TYPE_VIDEO. if (mCallback != null) { mCallback.onTrackSelected(mInputId, type, trackId); } } @Override - public void onVideoAvailable(Session session) { + public void onVideoSizeChanged(Session session, int width, int height) { + if (DEBUG) { + Log.d(TAG, "onVideoSizeChanged()"); + } if (this != mSessionCallback) { + Log.w(TAG, "onVideoSizeChanged - session not created"); return; } + if (mCallback != null) { + mCallback.onVideoSizeChanged(mInputId, width, height); + } + } + + @Override + public void onVideoAvailable(Session session) { if (DEBUG) { Log.d(TAG, "onVideoAvailable()"); } + if (this != mSessionCallback) { + Log.w(TAG, "onVideoAvailable - session not created"); + return; + } if (mCallback != null) { mCallback.onVideoAvailable(mInputId); } @@ -958,12 +977,13 @@ public class TvView extends ViewGroup { @Override public void onVideoUnavailable(Session session, int reason) { + if (DEBUG) { + Log.d(TAG, "onVideoUnavailable(reason=" + reason + ")"); + } if (this != mSessionCallback) { + Log.w(TAG, "onVideoUnavailable - session not created"); return; } - if (DEBUG) { - Log.d(TAG, "onVideoUnavailable(" + reason + ")"); - } if (mCallback != null) { mCallback.onVideoUnavailable(mInputId, reason); } @@ -971,12 +991,13 @@ public class TvView extends ViewGroup { @Override public void onContentAllowed(Session session) { - if (this != mSessionCallback) { - return; - } if (DEBUG) { Log.d(TAG, "onContentAllowed()"); } + if (this != mSessionCallback) { + Log.w(TAG, "onContentAllowed - session not created"); + return; + } if (mCallback != null) { mCallback.onContentAllowed(mInputId); } @@ -984,12 +1005,13 @@ public class TvView extends ViewGroup { @Override public void onContentBlocked(Session session, TvContentRating rating) { + if (DEBUG) { + Log.d(TAG, "onContentBlocked(rating=" + rating + ")"); + } if (this != mSessionCallback) { + Log.w(TAG, "onContentBlocked - session not created"); return; } - if (DEBUG) { - Log.d(TAG, "onContentBlocked()"); - } if (mCallback != null) { mCallback.onContentBlocked(mInputId, rating); } @@ -997,13 +1019,14 @@ public class TvView extends ViewGroup { @Override public void onLayoutSurface(Session session, int left, int top, int right, int bottom) { - if (this != mSessionCallback) { - return; - } if (DEBUG) { Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom + ",)"); } + if (this != mSessionCallback) { + Log.w(TAG, "onLayoutSurface - session not created"); + return; + } mSurfaceViewLeft = left; mSurfaceViewTop = top; mSurfaceViewRight = right; @@ -1014,12 +1037,13 @@ public class TvView extends ViewGroup { @Override public void onSessionEvent(Session session, String eventType, Bundle eventArgs) { - if (this != mSessionCallback) { - return; - } if (DEBUG) { Log.d(TAG, "onSessionEvent(" + eventType + ")"); } + if (this != mSessionCallback) { + Log.w(TAG, "onSessionEvent - session not created"); + return; + } if (mCallback != null) { mCallback.onEvent(mInputId, eventType, eventArgs); } |
