Skip to content

Enable multiple audio presentations parsing for AC-4 in TS #2546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
Expand Down Expand Up @@ -259,6 +260,9 @@ public final class ExoPlayerTest {
private static final int TIMEOUT_MS = 10_000;

private static final String SAMPLE_URI = "asset://android_asset/media/mp4/sample.mp4";
private static final String SAMPLE_AC4_TS_URI =
"asset://android_asset/media/ts/"
+ "MultiLangPerso_1PID_PC0_Select_AC4_H265_DVB_50fps_Audio_Only.ts";

@Parameters(name = "preload={0}")
public static ImmutableList<Object[]> params() {
Expand Down Expand Up @@ -14414,6 +14418,72 @@ public void playingMedia_withNoMetadata_doesNotUpdateMediaMetadata() throws Exce
player.release();
}

@SuppressWarnings("UseSdkSuppress") // https://issuetracker.google.com/382253664
@RequiresApi(api = Build.VERSION_CODES.P)
@Test
public void playingAC4_TS_withMultipleAudioPresentations() throws Exception {
ExoPlayer player = parameterizeTestExoPlayerBuilder(new TestExoPlayerBuilder(context)).build();
player.setMediaItem(MediaItem.fromUri(SAMPLE_AC4_TS_URI));
List<AudioPresentation> refPresentations = new ArrayList<>();
HashMap<ULocale, CharSequence> labelsFirst = new HashMap<>();
labelsFirst.put(new ULocale("en"), "Standard");
HashMap<ULocale, CharSequence> labelsSecond = new HashMap<>();
labelsSecond.put(new ULocale("en"), "Kids' choice");
HashMap<ULocale, CharSequence> labelsThird = new HashMap<>();
labelsThird.put(new ULocale("en"), "Artists' commentary");
refPresentations.add(new AudioPresentation.Builder(10)
.setLocale(ULocale.ENGLISH)
.setMasteringIndication(AudioPresentation.MASTERED_FOR_SURROUND)
.setHasSpokenSubtitles(false)
.setLabels(labelsFirst)
.setHasDialogueEnhancement(true)
.build());
refPresentations.add(new AudioPresentation.Builder(11)
.setLocale(ULocale.ENGLISH)
.setMasteringIndication(AudioPresentation.MASTERED_FOR_SURROUND)
.setHasSpokenSubtitles(false)
.setLabels(labelsSecond)
.setHasAudioDescription(true)
.setHasDialogueEnhancement(true)
.build());
refPresentations.add(new AudioPresentation.Builder(12)
.setLocale(ULocale.FRENCH)
.setMasteringIndication(AudioPresentation.MASTERED_FOR_SURROUND)
.setHasSpokenSubtitles(false)
.setLabels(labelsThird)
.setHasAudioDescription(false)
.setHasDialogueEnhancement(true)
.build());
final boolean[] audioPresentationsVerfied = {false};
player.addListener(
new Player.Listener() {
@Override
public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations) {

assertThat(audioPresentations).isNotNull();
assertThat(audioPresentations.size()).isEqualTo(refPresentations.size());
for (int i = 0; i < audioPresentations.size(); i++) {
assertThat(audioPresentations.get(i)).isEqualTo((refPresentations.get(i)));
}
audioPresentationsVerfied[0] = true;
}
});

player.prepare();
player.play();
try {
runUntilPlaybackState(player, Player.STATE_ENDED);
} catch (IllegalStateException ignored) {
// Not expected to play the input.
}
assertThat(audioPresentationsVerfied[0]).isTrue();
player.stop();

shadowOf(Looper.getMainLooper()).idle();

player.release();
}

@Test
@Config(sdk = ALL_SDKS)
public void builder_inBackgroundThreadWithAllowedAnyThreadMethods_doesNotThrow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static java.lang.Math.min;
import static java.lang.annotation.ElementType.TYPE_USE;

import android.media.AudioPresentation;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
Expand All @@ -33,10 +34,12 @@
import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.TrackOutput;
import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;

Expand All @@ -59,6 +62,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
@Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private final String containerMimeType;
private List<AudioPresentation> audioPresentations;

private @MonotonicNonNull String formatId;
private @MonotonicNonNull TrackOutput output;
Expand Down Expand Up @@ -106,6 +110,11 @@ public Ac4Reader(
this.language = language;
this.roleFlags = roleFlags;
this.containerMimeType = containerMimeType;
audioPresentations = ImmutableList.of();
}

public void setAudioPresentations(List<AudioPresentation> audioPresentations) {
this.audioPresentations = ImmutableList.copyOf(audioPresentations);
}

@Override
Expand Down Expand Up @@ -220,7 +229,8 @@ private void parseHeader() {
if (format == null
|| frameInfo.channelCount != format.channelCount
|| frameInfo.sampleRate != format.sampleRate
|| !MimeTypes.AUDIO_AC4.equals(format.sampleMimeType)) {
|| !MimeTypes.AUDIO_AC4.equals(format.sampleMimeType)
|| !format.audioPresentations.equals(audioPresentations)) {
format =
new Format.Builder()
.setId(formatId)
Expand All @@ -230,6 +240,7 @@ private void parseHeader() {
.setSampleRate(frameInfo.sampleRate)
.setLanguage(language)
.setRoleFlags(roleFlags)
.setAudioPresentations(audioPresentations)
.build();
output.format(format);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static java.lang.Math.min;

import android.media.AudioPresentation;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ParserException;
Expand All @@ -27,6 +28,7 @@
import androidx.media3.common.util.TimestampAdjuster;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.extractor.ExtractorOutput;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;

Expand Down Expand Up @@ -181,6 +183,17 @@ public boolean canConsumeSynthesizedEmptyPusi(boolean isModeHls) {
&& headerParsed;
}

/**
* Sets the audio presentation available for current AC-4 audio track.
*
* @param audioPresentations This is a collection of {@link AudioPresentation}.
*/
public void setAudioPresentations(List<AudioPresentation> audioPresentations) {
if (reader instanceof Ac4Reader) {
((Ac4Reader) reader).setAudioPresentations(audioPresentations);
}
}

private void setState(int state) {
this.state = state;
bytesRead = 0;
Expand Down
Loading