diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/IRemoteControlClient.aidl | 7 | ||||
-rw-r--r-- | media/java/android/media/MediaFocusControl.java | 52 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 3 | ||||
-rw-r--r-- | media/java/android/media/RemoteControlClient.java | 118 | ||||
-rw-r--r-- | media/java/android/media/SoundPool.java | 2 | ||||
-rw-r--r-- | media/java/android/media/SubtitleController.java | 95 |
6 files changed, 244 insertions, 33 deletions
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl index 48079f2..999b8ba 100644 --- a/media/java/android/media/IRemoteControlClient.aidl +++ b/media/java/android/media/IRemoteControlClient.aidl @@ -41,6 +41,13 @@ oneway interface IRemoteControlClient void onInformationRequested(int generationId, int infoFlags); /** + * Notifies a remote control client that information for the given generation ID is + * requested for the given IRemoteControlDisplay alone. + * @param rcd the display to which current info should be sent + */ + void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h); + + /** * Sets the generation counter of the current client that is displayed on the remote control. */ void setCurrentClientGenerationId(int clientGeneration); diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index cf5be1b..2a7a731 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -139,6 +139,7 @@ public class MediaFocusControl implements OnFinished { private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7; private static final int MSG_RCC_SEEK_REQUEST = 8; private static final int MSG_RCC_UPDATE_METADATA = 9; + private static final int MSG_RCDISPLAY_INIT_INFO = 10; // sendMsg() flags /** If the msg is already queued, replace it with this one. */ @@ -214,6 +215,12 @@ public class MediaFocusControl implements OnFinished { case MSG_PROMOTE_RCC: onPromoteRcc(msg.arg1); break; + + case MSG_RCDISPLAY_INIT_INFO: + // msg.obj is guaranteed to be non null + onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/, + msg.arg1/*w*/, msg.arg2/*h*/); + break; } } } @@ -852,6 +859,11 @@ public class MediaFocusControl implements OnFinished { * Access protected by mCurrentRcLock. */ private IRemoteControlClient mCurrentRcClient = null; + /** + * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant + * if mCurrentRcClient is null + */ + private PendingIntent mCurrentRcClientIntent = null; private final static int RC_INFO_NONE = 0; private final static int RC_INFO_ALL = @@ -1446,6 +1458,7 @@ public class MediaFocusControl implements OnFinished { // tell the current client that it needs to send info try { + //TODO change name to informationRequestForAllDisplays() mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags); } catch (RemoteException e) { Log.e(TAG, "Current valid remote client is dead: "+e); @@ -1460,6 +1473,36 @@ public class MediaFocusControl implements OnFinished { } } + /** + * Called when processing MSG_RCDISPLAY_INIT_INFO event + * Causes the current RemoteControlClient to send its info (metadata, playstate...) to + * a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE. + */ + private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) { + synchronized(mRCStack) { + synchronized(mCurrentRcLock) { + if (mCurrentRcClient != null) { + if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); } + try { + // synchronously update the new RCD with the current client generation + // and matching PendingIntent + newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent, + false); + + // tell the current RCC that it needs to send info, but only to the new RCD + try { + mCurrentRcClient.informationRequestForDisplay(newRcd, w, h); + } catch (RemoteException e) { + Log.e(TAG, "Current valid remote client is dead: ", e); + mCurrentRcClient = null; + } + } catch (RemoteException e) { + Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e); + } + } + } + } + } /** * Helper function: @@ -1497,6 +1540,7 @@ public class MediaFocusControl implements OnFinished { infoFlagsAboutToBeUsed = RC_INFO_ALL; } mCurrentRcClient = rcse.mRcClient; + mCurrentRcClientIntent = rcse.mMediaIntent; } // will cause onRcDisplayUpdate() to be called in AudioService's handler thread mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, @@ -1923,8 +1967,12 @@ public class MediaFocusControl implements OnFinished { } } - // we have a new display, of which all the clients are now aware: have it be updated - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); + // we have a new display, of which all the clients are now aware: have it be + // initialized wih the current gen ID and the current client info, do not + // reset the information for the other (existing) displays + sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE, + w /*arg1*/, h /*arg2*/, + rcd /*obj*/, 0/*delay*/); } } } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index deba2cc..def9aa7 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1772,6 +1772,9 @@ public class MediaPlayer implements SubtitleController.Listener mSelectedSubtitleTrackIndex = -1; } setOnSubtitleDataListener(null); + if (track == null) { + return; + } for (int i = 0; i < mInbandSubtitleTracks.length; i++) { if (mInbandSubtitleTracks[i] == track) { Log.v(TAG, "Selecting subtitle track " + i); diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 7613c89..ab6bd70 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -515,13 +515,13 @@ public class RemoteControlClient mEditorArtwork = null; if (mMetadataChanged & mArtworkChanged) { // send to remote control display if conditions are met - sendMetadataWithArtwork_syncCacheLock(); + sendMetadataWithArtwork_syncCacheLock(null, 0, 0); } else if (mMetadataChanged) { // send to remote control display if conditions are met - sendMetadata_syncCacheLock(); + sendMetadata_syncCacheLock(null); } else if (mArtworkChanged) { // send to remote control display if conditions are met - sendArtwork_syncCacheLock(); + sendArtwork_syncCacheLock(null, 0, 0); } mApplied = true; } @@ -620,7 +620,7 @@ public class RemoteControlClient mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime(); // send to remote control display if conditions are met - sendPlaybackState_syncCacheLock(); + sendPlaybackState_syncCacheLock(null); // update AudioService sendAudioServiceNewPlaybackState_syncCacheLock(); @@ -705,7 +705,7 @@ public class RemoteControlClient mTransportControlFlags = transportControlFlags; // send to remote control display if conditions are met - sendTransportControlInfo_syncCacheLock(); + sendTransportControlInfo_syncCacheLock(null); } } @@ -791,7 +791,7 @@ public class RemoteControlClient mPositionUpdateListener = l; if (oldCapa != mPlaybackPositionCapabilities) { // tell RCDs that this RCC's playback position capabilities have changed - sendTransportControlInfo_syncCacheLock(); + sendTransportControlInfo_syncCacheLock(null); } } } @@ -813,7 +813,7 @@ public class RemoteControlClient mPositionProvider = l; if (oldCapa != mPlaybackPositionCapabilities) { // tell RCDs that this RCC's playback position capabilities have changed - sendTransportControlInfo_syncCacheLock(); + sendTransportControlInfo_syncCacheLock(null); } if ((mPositionProvider != null) && (mEventHandler != null) && playbackPositionShouldMove(mPlaybackState)) { @@ -1083,6 +1083,7 @@ public class RemoteControlClient */ private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() { + //TODO change name to informationRequestForAllDisplays() public void onInformationRequested(int generationId, int infoFlags) { // only post messages, we can't block here if (mEventHandler != null) { @@ -1096,12 +1097,30 @@ public class RemoteControlClient mEventHandler.removeMessages(MSG_REQUEST_METADATA); mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL); mEventHandler.removeMessages(MSG_REQUEST_ARTWORK); + mEventHandler.removeMessages(MSG_REQUEST_METADATA_ARTWORK); mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE)); + mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, null)); mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL)); - mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA)); - mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK)); + mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, null)); + mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, + 0, 0, null)); + } + } + + public void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h) { + // only post messages, we can't block here + if (mEventHandler != null) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, rcd)); + mEventHandler.sendMessage( + mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, rcd)); + if ((w > 0) && (h > 0)) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, w, h, rcd)); + } else { + mEventHandler.sendMessage( + mEventHandler.obtainMessage(MSG_REQUEST_METADATA, rcd)); + } } } @@ -1207,6 +1226,7 @@ public class RemoteControlClient private final static int MSG_POSITION_DRIFT_CHECK = 11; private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12; private final static int MSG_UPDATE_METADATA = 13; + private final static int MSG_REQUEST_METADATA_ARTWORK = 14; private class EventHandler extends Handler { public EventHandler(RemoteControlClient rcc, Looper looper) { @@ -1218,22 +1238,29 @@ public class RemoteControlClient switch(msg.what) { case MSG_REQUEST_PLAYBACK_STATE: synchronized (mCacheLock) { - sendPlaybackState_syncCacheLock(); + sendPlaybackState_syncCacheLock((IRemoteControlDisplay)msg.obj); } break; case MSG_REQUEST_METADATA: synchronized (mCacheLock) { - sendMetadata_syncCacheLock(); + sendMetadata_syncCacheLock((IRemoteControlDisplay)msg.obj); } break; case MSG_REQUEST_TRANSPORTCONTROL: synchronized (mCacheLock) { - sendTransportControlInfo_syncCacheLock(); + sendTransportControlInfo_syncCacheLock((IRemoteControlDisplay)msg.obj); } break; case MSG_REQUEST_ARTWORK: synchronized (mCacheLock) { - sendArtwork_syncCacheLock(); + sendArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj, + msg.arg1, msg.arg2); + } + break; + case MSG_REQUEST_METADATA_ARTWORK: + synchronized (mCacheLock) { + sendMetadataWithArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj, + msg.arg1, msg.arg2); } break; case MSG_NEW_INTERNAL_CLIENT_GEN: @@ -1272,8 +1299,19 @@ public class RemoteControlClient //=========================================================== // Communication with the IRemoteControlDisplay (the displays known to the system) - private void sendPlaybackState_syncCacheLock() { + private void sendPlaybackState_syncCacheLock(IRemoteControlDisplay target) { if (mCurrentClientGenId == mInternalClientGenId) { + if (target != null) { + try { + target.setPlaybackState(mInternalClientGenId, + mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, + mPlaybackSpeed); + } catch (RemoteException e) { + Log.e(TAG, "Error in setPlaybackState() for dead display " + target, e); + } + return; + } + // target == null implies all displays must be updated final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); @@ -1289,8 +1327,17 @@ public class RemoteControlClient } } - private void sendMetadata_syncCacheLock() { + private void sendMetadata_syncCacheLock(IRemoteControlDisplay target) { if (mCurrentClientGenId == mInternalClientGenId) { + if (target != null) { + try { + target.setMetadata(mInternalClientGenId, mMetadata); + } catch (RemoteException e) { + Log.e(TAG, "Error in setMetadata() for dead display " + target, e); + } + return; + } + // target == null implies all displays must be updated final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); @@ -1304,8 +1351,19 @@ public class RemoteControlClient } } - private void sendTransportControlInfo_syncCacheLock() { + private void sendTransportControlInfo_syncCacheLock(IRemoteControlDisplay target) { if (mCurrentClientGenId == mInternalClientGenId) { + if (target != null) { + try { + target.setTransportControlInfo(mInternalClientGenId, + mTransportControlFlags, mPlaybackPositionCapabilities); + } catch (RemoteException e) { + Log.e(TAG, "Error in setTransportControlFlags() for dead display " + target, + e); + } + return; + } + // target == null implies all displays must be updated final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); @@ -1321,9 +1379,15 @@ public class RemoteControlClient } } - private void sendArtwork_syncCacheLock() { + private void sendArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) { // FIXME modify to cache all requested sizes? if (mCurrentClientGenId == mInternalClientGenId) { + if (target != null) { + final DisplayInfoForClient di = new DisplayInfoForClient(target, w, h); + sendArtworkToDisplay(di); + return; + } + // target == null implies all displays must be updated final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { if (!sendArtworkToDisplay((DisplayInfoForClient) displayIterator.next())) { @@ -1353,9 +1417,23 @@ public class RemoteControlClient return true; } - private void sendMetadataWithArtwork_syncCacheLock() { + private void sendMetadataWithArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) { // FIXME modify to cache all requested sizes? if (mCurrentClientGenId == mInternalClientGenId) { + if (target != null) { + try { + if ((w > 0) && (h > 0)) { + Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, w, h); + target.setAllMetadata(mInternalClientGenId, mMetadata, artwork); + } else { + target.setMetadata(mInternalClientGenId, mMetadata); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in set(All)Metadata() for dead display " + target, e); + } + return; + } + // target == null implies all displays must be updated final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 5127479..06af5de 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -374,7 +374,7 @@ public class SoundPool { * Called when a sound has completed loading. * * @param soundPool SoundPool object from the load() method - * @param soundPool the sample ID of the sound loaded. + * @param sampleId the sample ID of the sound loaded. * @param status the status of the load operation (0 = success) */ public void onLoadComplete(SoundPool soundPool, int sampleId, int status); diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java index 8090561..13205bc 100644 --- a/media/java/android/media/SubtitleController.java +++ b/media/java/android/media/SubtitleController.java @@ -21,6 +21,9 @@ import java.util.Vector; import android.content.Context; import android.media.SubtitleTrack.RenderingWidget; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.view.accessibility.CaptioningManager; /** @@ -37,6 +40,34 @@ public class SubtitleController { private SubtitleTrack mSelectedTrack; private boolean mShowing; private CaptioningManager mCaptioningManager; + private Handler mHandler; + + private static final int WHAT_SHOW = 1; + private static final int WHAT_HIDE = 2; + private static final int WHAT_SELECT_TRACK = 3; + private static final int WHAT_SELECT_DEFAULT_TRACK = 4; + + private final Handler.Callback mCallback = new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case WHAT_SHOW: + doShow(); + return true; + case WHAT_HIDE: + doHide(); + return true; + case WHAT_SELECT_TRACK: + doSelectTrack((SubtitleTrack)msg.obj); + return true; + case WHAT_SELECT_DEFAULT_TRACK: + doSelectDefaultTrack(); + return true; + default: + return false; + } + } + }; private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener = new CaptioningManager.CaptioningChangeListener() { @@ -112,7 +143,7 @@ public class SubtitleController { * in-band data from the {@link MediaPlayer}. However, this does * not change the subtitle visibility. * - * Must be called from the UI thread. + * 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}. @@ -122,9 +153,15 @@ public class SubtitleController { if (track != null && !mTracks.contains(track)) { return false; } + + processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track)); + return true; + } + + private void doSelectTrack(SubtitleTrack track) { mTrackIsExplicit = true; if (mSelectedTrack == track) { - return true; + return; } if (mSelectedTrack != null) { @@ -145,7 +182,6 @@ public class SubtitleController { if (mListener != null) { mListener.onSubtitleTrackSelected(track); } - return true; } /** @@ -170,8 +206,6 @@ public class SubtitleController { * * The default values for these flags are DEFAULT=no, AUTOSELECT=yes * and FORCED=no. - * - * Must be called from the UI thread. */ public SubtitleTrack getDefaultTrack() { SubtitleTrack bestTrack = null; @@ -226,8 +260,12 @@ public class SubtitleController { private boolean mTrackIsExplicit = false; private boolean mVisibilityIsExplicit = false; - /** @hide - called from UI thread */ + /** @hide - should be called from anchor thread */ public void selectDefaultTrack() { + processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK)); + } + + private void doSelectDefaultTrack() { if (mTrackIsExplicit) { // If track selection is explicit, but visibility // is not, it falls back to the captioning setting @@ -259,8 +297,9 @@ public class SubtitleController { } } - /** @hide - called from UI thread */ + /** @hide - must be called from anchor thread */ public void reset() { + checkAnchorLooper(); hide(); selectTrack(null); mTracks.clear(); @@ -301,9 +340,13 @@ public class SubtitleController { /** * Show the selected (or default) subtitle track. * - * Must be called from the UI thread. + * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} */ public void show() { + processOnAnchor(mHandler.obtainMessage(WHAT_SHOW)); + } + + private void doShow() { mShowing = true; mVisibilityIsExplicit = true; if (mSelectedTrack != null) { @@ -314,9 +357,13 @@ public class SubtitleController { /** * Hide the selected (or default) subtitle track. * - * Must be called from the UI thread. + * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} */ public void hide() { + processOnAnchor(mHandler.obtainMessage(WHAT_HIDE)); + } + + private void doHide() { mVisibilityIsExplicit = true; if (mSelectedTrack != null) { mSelectedTrack.hide(); @@ -384,25 +431,53 @@ public class SubtitleController { * @hide */ public void setSubtitleWidget(RenderingWidget subtitleWidget); + + /** + * Anchors provide the looper on which all track visibility changes + * (track.show/hide, setSubtitleWidget) will take place. + * @hide + */ + public Looper getSubtitleLooper(); } private Anchor mAnchor; - /** @hide - called from UI thread */ + /** + * @hide - called from anchor's looper (if any, both when unsetting and + * setting) + */ public void setAnchor(Anchor anchor) { 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()); } } + private void checkAnchorLooper() { + assert mHandler != null : "Should have a looper already"; + assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper"; + } + + private void processOnAnchor(Message m) { + assert mHandler != null : "Should have a looper already"; + if (Looper.myLooper() == mHandler.getLooper()) { + mHandler.dispatchMessage(m); + } else { + mHandler.sendMessage(m); + } + } + public interface Listener { /** * Called when a subtitle track has been selected. |