mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Don't call onAudioPositionAdvancing if AudioTrack is paused
`AudioTrackPositionTracker.pause` "re-arms" the event, to ensure it fires again when playback resumes. However the `AudioTrack` position advances by about 100ms **after** `AudioTrack.pause()`, which consistently causes the event to fire immediately after pausing (and then **not** after a subsequent resumption). This change checks whether the `AudioTrack` is paused before firing the event, to avoid this spurious trigger. PiperOrigin-RevId: 704759929
This commit is contained in:
parent
3e3cd8e6ac
commit
c377a34a5a
3 changed files with 152 additions and 2 deletions
|
|
@ -39,6 +39,8 @@
|
||||||
([#1904](https://github.com/androidx/media/issues/1904)).
|
([#1904](https://github.com/androidx/media/issues/1904)).
|
||||||
* DataSource:
|
* DataSource:
|
||||||
* Audio:
|
* Audio:
|
||||||
|
* Fix `onAudioPositionAdvancing` to be called when playback resumes
|
||||||
|
(previously it was called when playback was paused).
|
||||||
* Video:
|
* Video:
|
||||||
* Rollback of using `MediaCodecAdapter` supplied pixel aspect ratio values
|
* Rollback of using `MediaCodecAdapter` supplied pixel aspect ratio values
|
||||||
when provided while processing `onOutputFormatChanged`
|
when provided while processing `onOutputFormatChanged`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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 androidx.media3.exoplayer;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
|
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for the {@link AnalyticsListener#onAudioPositionAdvancing(AnalyticsListener.EventTime,
|
||||||
|
* long)} callback.
|
||||||
|
*/
|
||||||
|
// Deliberately using System.currentTimeMillis for consistency with onAudioPositionAdvancing.
|
||||||
|
@SuppressWarnings("NowMillisWithoutTimeSource")
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class AudioPositionAdvancingTest {
|
||||||
|
|
||||||
|
private static final int TIMEOUT_MS = 500;
|
||||||
|
|
||||||
|
// Regression test for b/378871275
|
||||||
|
@Test
|
||||||
|
public void calledWhenPlaybackResumesNotPaused() throws Exception {
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp3/bear-id3.mp3");
|
||||||
|
List<Long> playoutStartSystemTimes = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
ConditionVariable onAdvancingCalled = new ConditionVariable();
|
||||||
|
AnalyticsListener analyticsListener =
|
||||||
|
new AnalyticsListener() {
|
||||||
|
@Override
|
||||||
|
public void onAudioPositionAdvancing(EventTime eventTime, long playoutStartSystemTimeMs) {
|
||||||
|
playoutStartSystemTimes.add(playoutStartSystemTimeMs);
|
||||||
|
onAdvancingCalled.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AtomicReference<ExoPlayer> player = new AtomicReference<>();
|
||||||
|
AtomicLong playbackTriggeredSystemTimeMs = new AtomicLong(C.TIME_UNSET);
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() -> {
|
||||||
|
player.set(new ExoPlayer.Builder(getInstrumentation().getContext()).build());
|
||||||
|
player.get().addAnalyticsListener(analyticsListener);
|
||||||
|
player.get().setMediaItem(mediaItem);
|
||||||
|
player.get().prepare();
|
||||||
|
playbackTriggeredSystemTimeMs.set(System.currentTimeMillis());
|
||||||
|
player.get().play();
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(onAdvancingCalled.block(TIMEOUT_MS)).isTrue();
|
||||||
|
long currentTimeMs = System.currentTimeMillis();
|
||||||
|
long playoutStartSystemTimeMs = Iterables.getOnlyElement(playoutStartSystemTimes);
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtLeast(playbackTriggeredSystemTimeMs.get());
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtMost(currentTimeMs);
|
||||||
|
|
||||||
|
onAdvancingCalled.close();
|
||||||
|
getInstrumentation().runOnMainSync(() -> player.get().pause());
|
||||||
|
|
||||||
|
// Expect the callback to *not* be called.
|
||||||
|
assertThat(onAdvancingCalled.block(50)).isFalse();
|
||||||
|
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() -> {
|
||||||
|
playbackTriggeredSystemTimeMs.set(System.currentTimeMillis());
|
||||||
|
player.get().play();
|
||||||
|
});
|
||||||
|
assertThat(onAdvancingCalled.block(TIMEOUT_MS)).isTrue();
|
||||||
|
currentTimeMs = System.currentTimeMillis();
|
||||||
|
playoutStartSystemTimeMs = Iterables.getLast(playoutStartSystemTimes);
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtLeast(playbackTriggeredSystemTimeMs.get());
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtMost(currentTimeMs);
|
||||||
|
|
||||||
|
getInstrumentation().runOnMainSync(() -> player.get().release());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pauseThenPlayInSameLooperIteration() throws Exception {
|
||||||
|
MediaItem mediaItem = MediaItem.fromUri("asset:///media/mp3/bear-id3.mp3");
|
||||||
|
List<Long> playoutStartSystemTimes = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
ConditionVariable onAdvancingCalled = new ConditionVariable();
|
||||||
|
AnalyticsListener analyticsListener =
|
||||||
|
new AnalyticsListener() {
|
||||||
|
@Override
|
||||||
|
public void onAudioPositionAdvancing(EventTime eventTime, long playoutStartSystemTimeMs) {
|
||||||
|
playoutStartSystemTimes.add(playoutStartSystemTimeMs);
|
||||||
|
onAdvancingCalled.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AtomicReference<ExoPlayer> player = new AtomicReference<>();
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() -> {
|
||||||
|
player.set(new ExoPlayer.Builder(getInstrumentation().getContext()).build());
|
||||||
|
player.get().addAnalyticsListener(analyticsListener);
|
||||||
|
player.get().setMediaItem(mediaItem);
|
||||||
|
player.get().prepare();
|
||||||
|
player.get().play();
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(onAdvancingCalled.block(TIMEOUT_MS)).isTrue();
|
||||||
|
onAdvancingCalled.close();
|
||||||
|
|
||||||
|
AtomicLong playbackTriggeredSystemTimeMs = new AtomicLong(C.TIME_UNSET);
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() -> {
|
||||||
|
player.get().pause();
|
||||||
|
playbackTriggeredSystemTimeMs.set(System.currentTimeMillis());
|
||||||
|
player.get().play();
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(onAdvancingCalled.block(TIMEOUT_MS)).isTrue();
|
||||||
|
long currentTimeMs = System.currentTimeMillis();
|
||||||
|
assertThat(playoutStartSystemTimes).hasSize(2);
|
||||||
|
long playoutStartSystemTimeMs = playoutStartSystemTimes.get(1);
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtLeast(playbackTriggeredSystemTimeMs.get());
|
||||||
|
assertThat(playoutStartSystemTimeMs).isAtMost(currentTimeMs);
|
||||||
|
|
||||||
|
getInstrumentation().runOnMainSync(() -> player.get().release());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -285,7 +285,8 @@ import java.lang.reflect.Method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCurrentPositionUs(boolean sourceEnded) {
|
public long getCurrentPositionUs(boolean sourceEnded) {
|
||||||
if (checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) {
|
AudioTrack audioTrack = checkNotNull(this.audioTrack);
|
||||||
|
if (audioTrack.getPlayState() == PLAYSTATE_PLAYING) {
|
||||||
maybeSampleSyncParams();
|
maybeSampleSyncParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +341,9 @@ import java.lang.reflect.Method;
|
||||||
positionUs /= 1000;
|
positionUs /= 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notifiedPositionIncreasing && positionUs > lastPositionUs) {
|
if (!notifiedPositionIncreasing
|
||||||
|
&& positionUs > lastPositionUs
|
||||||
|
&& audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
|
||||||
notifiedPositionIncreasing = true;
|
notifiedPositionIncreasing = true;
|
||||||
long mediaDurationSinceLastPositionUs = Util.usToMs(positionUs - lastPositionUs);
|
long mediaDurationSinceLastPositionUs = Util.usToMs(positionUs - lastPositionUs);
|
||||||
long playoutDurationSinceLastPositionUs =
|
long playoutDurationSinceLastPositionUs =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue