47
47
import android .view .SurfaceView ;
48
48
import android .view .TextureView ;
49
49
import androidx .annotation .Nullable ;
50
+ import androidx .collection .ArraySet ;
50
51
import androidx .media3 .common .AdPlaybackState ;
51
52
import androidx .media3 .common .AudioAttributes ;
52
53
import androidx .media3 .common .BundleListRetriever ;
110
111
private final SurfaceCallback surfaceCallback ;
111
112
private final ListenerSet <Listener > listeners ;
112
113
private final FlushCommandQueueHandler flushCommandQueueHandler ;
114
+ private final ArraySet <Integer > pendingMaskingSequencedFutureNumbers ;
113
115
114
116
@ Nullable private SessionToken connectedToken ;
115
117
@ Nullable private SessionServiceConnection serviceConnection ;
127
129
@ Nullable private IMediaSession iSession ;
128
130
private long lastReturnedCurrentPositionMs ;
129
131
private long lastSetPlayWhenReadyCalledTimeMs ;
132
+ @ Nullable private Timeline pendingPlayerInfoUpdateTimeline ;
130
133
131
134
public MediaControllerImplBase (
132
135
Context context ,
@@ -154,6 +157,7 @@ public MediaControllerImplBase(
154
157
this .context = context ;
155
158
sequencedFutureManager = new SequencedFutureManager ();
156
159
controllerStub = new MediaControllerStub (this );
160
+ pendingMaskingSequencedFutureNumbers = new ArraySet <>();
157
161
this .token = token ;
158
162
this .connectionHints = connectionHints ;
159
163
deathRecipient =
@@ -303,7 +307,7 @@ private ListenableFuture dispatchRemoteSessionTaskWithPlayerComma
303
307
if (command != Player .COMMAND_SET_VIDEO_SURFACE ) {
304
308
flushCommandQueueHandler .sendFlushCommandQueueMessage ();
305
309
}
306
- return dispatchRemoteSessionTask (iSession , task );
310
+ return dispatchRemoteSessionTask (iSession , task , /* addToPendingMaskingOperations= */ true );
307
311
}
308
312
309
313
private ListenableFuture <SessionResult > dispatchRemoteSessionTaskWithSessionCommand (
@@ -326,17 +330,22 @@ private ListenableFuture dispatchRemoteSessionTaskWithSessionComm
326
330
sessionCommand != null
327
331
? getSessionInterfaceWithSessionCommandIfAble (sessionCommand )
328
332
: getSessionInterfaceWithSessionCommandIfAble (commandCode ),
329
- task );
333
+ task ,
334
+ /* addToPendingMaskingOperations= */ false );
330
335
}
331
336
332
337
private ListenableFuture <SessionResult > dispatchRemoteSessionTask (
333
- IMediaSession iSession , RemoteSessionTask task ) {
338
+ IMediaSession iSession , RemoteSessionTask task , boolean addToPendingMaskingOperations ) {
334
339
if (iSession != null ) {
335
340
SequencedFutureManager .SequencedFuture <SessionResult > result =
336
341
sequencedFutureManager .createSequencedFuture (
337
342
new SessionResult (SessionResult .RESULT_INFO_SKIPPED ));
338
343
try {
339
- task .run (iSession , result .getSequenceNumber ());
344
+ int sequenceNumber = result .getSequenceNumber ();
345
+ task .run (iSession , sequenceNumber );
346
+ if (addToPendingMaskingOperations ) {
347
+ pendingMaskingSequencedFutureNumbers .add (sequenceNumber );
348
+ }
340
349
} catch (RemoteException e ) {
341
350
Log .w (TAG , "Cannot connect to the service or the session is gone" , e );
342
351
result .set (new SessionResult (SessionResult .RESULT_ERROR_SESSION_DISCONNECTED ));
@@ -2223,7 +2232,12 @@ void notifyPeriodicSessionPositionInfoChanged(SessionPositionInfo sessionPositio
2223
2232
}
2224
2233
2225
2234
<T extends @ NonNull Object > void setFutureResult (int seq , T futureResult ) {
2235
+ // Don't set the future result on the application looper so that the result can be obtained by a
2236
+ // blocking future.get() on the application looper. But post a message to remove the pending
2237
+ // masking operation on the application looper to ensure it's executed in order with other
2238
+ // updates sent to the application looper.
2226
2239
sequencedFutureManager .setFutureResult (seq , futureResult );
2240
+ getInstance ().runOnApplicationLooper (() -> pendingMaskingSequencedFutureNumbers .remove (seq ));
2227
2241
}
2228
2242
2229
2243
void onConnected (ConnectionState result ) {
@@ -2313,11 +2327,23 @@ void onPlayerInfoChanged(
2313
2327
if (!isConnected ()) {
2314
2328
return ;
2315
2329
}
2330
+ if (!pendingMaskingSequencedFutureNumbers .isEmpty ()) {
2331
+ // We are still waiting for all pending masking operations to be handled.
2332
+ if (!isTimelineExcluded ) {
2333
+ pendingPlayerInfoUpdateTimeline = newPlayerInfo .timeline ;
2334
+ }
2335
+ return ;
2336
+ }
2316
2337
PlayerInfo oldPlayerInfo = playerInfo ;
2317
2338
playerInfo = newPlayerInfo ;
2318
2339
if (isTimelineExcluded ) {
2319
- playerInfo = playerInfo .copyWithTimeline (oldPlayerInfo .timeline );
2340
+ playerInfo =
2341
+ playerInfo .copyWithTimeline (
2342
+ pendingPlayerInfoUpdateTimeline != null
2343
+ ? pendingPlayerInfoUpdateTimeline
2344
+ : oldPlayerInfo .timeline );
2320
2345
}
2346
+ pendingPlayerInfoUpdateTimeline = null ;
2321
2347
PlaybackException oldPlayerError = oldPlayerInfo .playerError ;
2322
2348
PlaybackException playerError = playerInfo .playerError ;
2323
2349
boolean errorsMatch =
@@ -2568,7 +2594,8 @@ public void onRenderedFirstFrame() {
2568
2594
}
2569
2595
2570
2596
private void updateSessionPositionInfoIfNeeded (SessionPositionInfo sessionPositionInfo ) {
2571
- if (playerInfo .sessionPositionInfo .eventTimeMs < sessionPositionInfo .eventTimeMs ) {
2597
+ if (pendingMaskingSequencedFutureNumbers .isEmpty ()
2598
+ && playerInfo .sessionPositionInfo .eventTimeMs < sessionPositionInfo .eventTimeMs ) {
2572
2599
playerInfo = playerInfo .copyWithSessionPositionInfo (sessionPositionInfo );
2573
2600
}
2574
2601
}
0 commit comments