fix: audio stream format selection

This commit is contained in:
ThetaDev 2023-03-19 23:37:52 +01:00
parent dbd6e4d11f
commit fdd3b03fe5
2 changed files with 75 additions and 83 deletions

View File

@ -107,11 +107,8 @@ public final class ListHelper {
public static int getDefaultAudioFormat(final Context context, public static int getDefaultAudioFormat(final Context context,
final List<AudioStream> audioStreams) { final List<AudioStream> audioStreams) {
final MediaFormat defaultFormat = getDefaultFormat(context, return getAudioIndexByHighestRank(audioStreams,
R.string.default_audio_format_key, R.string.default_audio_format_value); getAudioTrackComparator(context).thenComparing(getAudioFormatComparator(context)));
return getAudioIndexByHighestRank(defaultFormat, audioStreams,
getAudioStreamComparator(context));
} }
public static int getAudioFormatIndex(final Context context, public static int getAudioFormatIndex(final Context context,
@ -222,8 +219,7 @@ public final class ListHelper {
final HashMap<String, AudioStream> collectedStreams = new HashMap<>(); final HashMap<String, AudioStream> collectedStreams = new HashMap<>();
final Comparator<AudioStream> cmp = final Comparator<AudioStream> cmp = getAudioFormatComparator(context);
getAudioStreamFormatComparator(isLimitingDataUsage(context));
for (final AudioStream stream : audioStreams) { for (final AudioStream stream : audioStreams) {
if (stream.getDeliveryMethod() == DeliveryMethod.TORRENT) { if (stream.getDeliveryMethod() == DeliveryMethod.TORRENT) {
@ -417,32 +413,18 @@ public final class ListHelper {
* Get the audio-stream from the list with the highest rank, depending on the comparator. * Get the audio-stream from the list with the highest rank, depending on the comparator.
* Format will be ignored if it yields no results. * Format will be ignored if it yields no results.
* *
* @param targetedFormat The target format type or null if it doesn't matter
* @param audioStreams List of audio streams * @param audioStreams List of audio streams
* @param comparator The comparator used for determining the max/best/highest ranked value * @param comparator The comparator used for determining the max/best/highest ranked value
* @return Index of audio stream that produces the highest ranked result or -1 if not found * @return Index of audio stream that produces the highest ranked result or -1 if not found
*/ */
static int getAudioIndexByHighestRank(@Nullable final MediaFormat targetedFormat, static int getAudioIndexByHighestRank(@Nullable final List<AudioStream> audioStreams,
@Nullable final List<AudioStream> audioStreams, final Comparator<AudioStream> comparator) {
final Comparator<AudioStream> comparator) {
if (audioStreams == null || audioStreams.isEmpty()) { if (audioStreams == null || audioStreams.isEmpty()) {
return -1; return -1;
} }
final AudioStream highestRankedAudioStream = audioStreams.stream() final AudioStream highestRankedAudioStream = audioStreams.stream()
.filter(audioStream -> targetedFormat == null .max(comparator).orElse(null);
|| audioStream.getFormat() == targetedFormat)
.max(comparator)
.orElse(null);
if (highestRankedAudioStream == null) {
// Fallback: Ignore targetedFormat if not null
if (targetedFormat != null) {
return getAudioIndexByHighestRank(null, audioStreams, comparator);
}
// targetedFormat is already null -> return -1
return -1;
}
return audioStreams.indexOf(highestRankedAudioStream); return audioStreams.indexOf(highestRankedAudioStream);
} }
@ -631,14 +613,27 @@ public final class ListHelper {
return manager.isActiveNetworkMetered(); return manager.isActiveNetworkMetered();
} }
/**
* Get a {@link Comparator} to compare {@link AudioStream}s by their format and bitrate.
* @param context app context
* @return Comparator
*/
private static Comparator<AudioStream> getAudioFormatComparator(
final @NonNull Context context) {
final MediaFormat defaultFormat = getDefaultFormat(context,
R.string.default_audio_format_key, R.string.default_audio_format_value);
return getAudioFormatComparator(defaultFormat, isLimitingDataUsage(context));
}
/** /**
* Get a {@link Comparator} to compare {@link AudioStream}s by their format and bitrate. * Get a {@link Comparator} to compare {@link AudioStream}s by their format and bitrate.
* *
* @param defaultFormat the default format to look for
* @param limitDataUsage choose low bitrate audio stream * @param limitDataUsage choose low bitrate audio stream
* @return Comparator * @return Comparator
*/ */
private static Comparator<AudioStream> getAudioStreamFormatComparator( static Comparator<AudioStream> getAudioFormatComparator(
final boolean limitDataUsage) { @Nullable final MediaFormat defaultFormat, final boolean limitDataUsage) {
final List<MediaFormat> formatRanking = limitDataUsage final List<MediaFormat> formatRanking = limitDataUsage
? AUDIO_FORMAT_EFFICIENCY_RANKING : AUDIO_FORMAT_QUALITY_RANKING; ? AUDIO_FORMAT_EFFICIENCY_RANKING : AUDIO_FORMAT_QUALITY_RANKING;
@ -648,18 +643,22 @@ public final class ListHelper {
bitrateComparator = bitrateComparator.reversed(); bitrateComparator = bitrateComparator.reversed();
} }
return bitrateComparator.thenComparingInt( return Comparator.comparing(AudioStream::getFormat, (o1, o2) -> {
if (defaultFormat != null) {
return Boolean.compare(o1 == defaultFormat, o2 == defaultFormat);
}
return 0;
}).thenComparing(bitrateComparator).thenComparingInt(
stream -> formatRanking.indexOf(stream.getFormat())); stream -> formatRanking.indexOf(stream.getFormat()));
} }
/** /**
* Get a {@link Comparator} to compare {@link AudioStream}s by their language, format * Get a {@link Comparator} to compare {@link AudioStream}s by their tracks.
* and bitrate.
* *
* @param context App context * @param context App context
* @return Comparator * @return Comparator
*/ */
private static Comparator<AudioStream> getAudioStreamComparator( private static Comparator<AudioStream> getAudioTrackComparator(
@NonNull final Context context) { @NonNull final Context context) {
final SharedPreferences preferences = final SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(context); PreferenceManager.getDefaultSharedPreferences(context);
@ -671,23 +670,21 @@ public final class ListHelper {
preferences.getBoolean(context.getString(R.string.prefer_descriptive_audio_key), preferences.getBoolean(context.getString(R.string.prefer_descriptive_audio_key),
false); false);
return getAudioStreamComparator(preferredLanguage, preferOriginalAudio, return getAudioTrackComparator(preferredLanguage, preferOriginalAudio,
preferDescriptiveAudio, isLimitingDataUsage(context)); preferDescriptiveAudio);
} }
/** /**
* Get a {@link Comparator} to compare {@link AudioStream}s by their language, format * Get a {@link Comparator} to compare {@link AudioStream}s by their tracks.
* and bitrate. *
* @param preferredLanguage Preferred audio stream language * @param preferredLanguage Preferred audio stream language
* @param preferOriginalAudio Get the original audio track regardless of its language * @param preferOriginalAudio Get the original audio track regardless of its language
* @param preferDescriptiveAudio Prefer the descriptive audio track if available * @param preferDescriptiveAudio Prefer the descriptive audio track if available
* @param limitDataUsage choose low bitrate audio stream
* @return Comparator * @return Comparator
*/ */
static Comparator<AudioStream> getAudioStreamComparator(final Locale preferredLanguage, static Comparator<AudioStream> getAudioTrackComparator(
final boolean preferOriginalAudio, final Locale preferredLanguage, final boolean preferOriginalAudio,
final boolean preferDescriptiveAudio, final boolean preferDescriptiveAudio) {
final boolean limitDataUsage) {
final String langCode = preferredLanguage.getISO3Language(); final String langCode = preferredLanguage.getISO3Language();
final List<AudioTrackType> trackTypeRanking = preferDescriptiveAudio final List<AudioTrackType> trackTypeRanking = preferDescriptiveAudio
? AUDIO_TRACK_TYPE_RANKING_DESCRIPTIVE : AUDIO_TRACK_TYPE_RANKING; ? AUDIO_TRACK_TYPE_RANKING_DESCRIPTIVE : AUDIO_TRACK_TYPE_RANKING;
@ -706,7 +703,6 @@ public final class ListHelper {
.thenComparing(AudioStream::getAudioLocale, .thenComparing(AudioStream::getAudioLocale,
Comparator.nullsFirst(Comparator.comparing( Comparator.nullsFirst(Comparator.comparing(
locale -> locale.getISO3Language().equals( locale -> locale.getISO3Language().equals(
Locale.ENGLISH.getISO3Language())))) Locale.ENGLISH.getISO3Language()))));
.thenComparing(getAudioStreamFormatComparator(limitDataUsage));
} }
} }

View File

@ -211,20 +211,21 @@ public class ListHelperTest {
@Test @Test
public void getHighestQualityAudioFormatTest() { public void getHighestQualityAudioFormatTest() {
final Comparator<AudioStream> cmp = Comparator<AudioStream> cmp = ListHelper.getAudioFormatComparator(MediaFormat.M4A, false);
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, false);
AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.M4A, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(320, stream.getAverageBitrate()); assertEquals(320, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat()); assertEquals(MediaFormat.M4A, stream.getFormat());
cmp = ListHelper.getAudioFormatComparator(MediaFormat.WEBMA, false);
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.WEBMA, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(320, stream.getAverageBitrate()); assertEquals(320, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
cmp = ListHelper.getAudioFormatComparator(MediaFormat.MP3, false);
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.MP3, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.MP3, stream.getFormat()); assertEquals(MediaFormat.MP3, stream.getFormat());
} }
@ -232,7 +233,7 @@ public class ListHelperTest {
@Test @Test
public void getHighestQualityAudioFormatPreferredAbsent() { public void getHighestQualityAudioFormatPreferredAbsent() {
final Comparator<AudioStream> cmp = final Comparator<AudioStream> cmp =
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, false); ListHelper.getAudioFormatComparator(MediaFormat.MP3, false);
////////////////////////////////////////// //////////////////////////////////////////
// Doesn't contain the preferred format // // Doesn't contain the preferred format //
@ -243,8 +244,7 @@ public class ListHelperTest {
generateAudioStream("webma-192", MediaFormat.WEBMA, 192)); generateAudioStream("webma-192", MediaFormat.WEBMA, 192));
// List doesn't contains this format // List doesn't contains this format
// It should fallback to the highest bitrate audio no matter what format it is // It should fallback to the highest bitrate audio no matter what format it is
AudioStream stream = testList.get(ListHelper.getAudioIndexByHighestRank( AudioStream stream = testList.get(ListHelper.getAudioIndexByHighestRank(testList, cmp));
MediaFormat.MP3, testList, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
@ -263,7 +263,7 @@ public class ListHelperTest {
// List doesn't contains this format, it should fallback to the highest bitrate audio and // List doesn't contains this format, it should fallback to the highest bitrate audio and
// the highest quality format. // the highest quality format.
stream = stream =
testList.get(ListHelper.getAudioIndexByHighestRank(MediaFormat.MP3, testList, cmp)); testList.get(ListHelper.getAudioIndexByHighestRank(testList, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat()); assertEquals(MediaFormat.M4A, stream.getFormat());
@ -271,44 +271,42 @@ public class ListHelperTest {
// it's not a preferred format. // it's not a preferred format.
testList.add(generateAudioStream("webma-192-5", MediaFormat.WEBMA, 192)); testList.add(generateAudioStream("webma-192-5", MediaFormat.WEBMA, 192));
stream = stream =
testList.get(ListHelper.getAudioIndexByHighestRank(MediaFormat.MP3, testList, cmp)); testList.get(ListHelper.getAudioIndexByHighestRank(testList, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat()); assertEquals(MediaFormat.M4A, stream.getFormat());
} }
@Test @Test
public void getHighestQualityAudioNull() { public void getHighestQualityAudioNull() {
final Comparator<AudioStream> cmp = final Comparator<AudioStream> cmp = ListHelper.getAudioFormatComparator(null, false);
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, false); assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, cmp));
assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, null, cmp)); assertEquals(-1, ListHelper.getAudioIndexByHighestRank(new ArrayList<>(), cmp));
assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, new ArrayList<>(), cmp));
} }
@Test @Test
public void getLowestQualityAudioFormatTest() { public void getLowestQualityAudioFormatTest() {
final Comparator<AudioStream> cmp = Comparator<AudioStream> cmp = ListHelper.getAudioFormatComparator(MediaFormat.M4A, true);
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, true);
AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( AudioStream stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.M4A, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(128, stream.getAverageBitrate()); assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat()); assertEquals(MediaFormat.M4A, stream.getFormat());
cmp = ListHelper.getAudioFormatComparator(MediaFormat.WEBMA, true);
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.WEBMA, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(64, stream.getAverageBitrate()); assertEquals(64, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
cmp = ListHelper.getAudioFormatComparator(MediaFormat.MP3, true);
stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_STREAMS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
MediaFormat.MP3, AUDIO_STREAMS_TEST_LIST, cmp)); AUDIO_STREAMS_TEST_LIST, cmp));
assertEquals(64, stream.getAverageBitrate()); assertEquals(64, stream.getAverageBitrate());
assertEquals(MediaFormat.MP3, stream.getFormat()); assertEquals(MediaFormat.MP3, stream.getFormat());
} }
@Test @Test
public void getLowestQualityAudioFormatPreferredAbsent() { public void getLowestQualityAudioFormatPreferredAbsent() {
final Comparator<AudioStream> cmp = Comparator<AudioStream> cmp = ListHelper.getAudioFormatComparator(MediaFormat.MP3, true);
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, true);
////////////////////////////////////////// //////////////////////////////////////////
// Doesn't contain the preferred format // // Doesn't contain the preferred format //
@ -319,15 +317,13 @@ public class ListHelperTest {
generateAudioStream("webma-192-1", MediaFormat.WEBMA, 192))); generateAudioStream("webma-192-1", MediaFormat.WEBMA, 192)));
// List doesn't contains this format // List doesn't contains this format
// It should fallback to the most compact audio no matter what format it is. // It should fallback to the most compact audio no matter what format it is.
AudioStream stream = testList.get(ListHelper.getAudioIndexByHighestRank( AudioStream stream = testList.get(ListHelper.getAudioIndexByHighestRank(testList, cmp));
MediaFormat.MP3, testList, cmp));
assertEquals(128, stream.getAverageBitrate()); assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.M4A, stream.getFormat()); assertEquals(MediaFormat.M4A, stream.getFormat());
// WEBMA is more compact than M4A // WEBMA is more compact than M4A
testList.add(generateAudioStream("webma-192-2", MediaFormat.WEBMA, 128)); testList.add(generateAudioStream("webma-192-2", MediaFormat.WEBMA, 128));
stream = stream = testList.get(ListHelper.getAudioIndexByHighestRank(testList, cmp));
testList.get(ListHelper.getAudioIndexByHighestRank(MediaFormat.MP3, testList, cmp));
assertEquals(128, stream.getAverageBitrate()); assertEquals(128, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
@ -345,56 +341,56 @@ public class ListHelperTest {
// List doesn't contain this format // List doesn't contain this format
// It should fallback to the most compact audio no matter what format it is. // It should fallback to the most compact audio no matter what format it is.
stream = testList.get( stream = testList.get(
ListHelper.getAudioIndexByHighestRank(MediaFormat.MP3, testList, cmp)); ListHelper.getAudioIndexByHighestRank(testList, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
// Should be same as above // Should be same as above
cmp = ListHelper.getAudioFormatComparator(null, true);
stream = testList.get( stream = testList.get(
ListHelper.getAudioIndexByHighestRank(null, testList, cmp)); ListHelper.getAudioIndexByHighestRank(testList, cmp));
assertEquals(192, stream.getAverageBitrate()); assertEquals(192, stream.getAverageBitrate());
assertEquals(MediaFormat.WEBMA, stream.getFormat()); assertEquals(MediaFormat.WEBMA, stream.getFormat());
} }
@Test @Test
public void getLowestQualityAudioNull() { public void getLowestQualityAudioNull() {
final Comparator<AudioStream> cmp = final Comparator<AudioStream> cmp = ListHelper.getAudioFormatComparator(null, false);
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, false); assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, cmp));
assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, null, cmp)); assertEquals(-1, ListHelper.getAudioIndexByHighestRank(new ArrayList<>(), cmp));
assertEquals(-1, ListHelper.getAudioIndexByHighestRank(null, new ArrayList<>(), cmp));
} }
@Test @Test
public void getAudioTrack() { public void getAudioTrack() {
// English language // English language
Comparator<AudioStream> cmp = Comparator<AudioStream> cmp =
ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, false, false); ListHelper.getAudioTrackComparator(Locale.ENGLISH, false, false);
AudioStream stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( AudioStream stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
null, AUDIO_TRACKS_TEST_LIST, cmp)); AUDIO_TRACKS_TEST_LIST, cmp));
assertEquals("en.or", stream.getId()); assertEquals("en.or", stream.getId());
// German language // German language
cmp = ListHelper.getAudioStreamComparator(Locale.GERMAN, false, false, false); cmp = ListHelper.getAudioTrackComparator(Locale.GERMAN, false, false);
stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
null, AUDIO_TRACKS_TEST_LIST, cmp)); AUDIO_TRACKS_TEST_LIST, cmp));
assertEquals("de.du", stream.getId()); assertEquals("de.du", stream.getId());
// German language, but prefer original // German language, but prefer original
cmp = ListHelper.getAudioStreamComparator(Locale.GERMAN, true, false, false); cmp = ListHelper.getAudioTrackComparator(Locale.GERMAN, true, false);
stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
null, AUDIO_TRACKS_TEST_LIST, cmp)); AUDIO_TRACKS_TEST_LIST, cmp));
assertEquals("en.or", stream.getId()); assertEquals("en.or", stream.getId());
// Prefer descriptive audio // Prefer descriptive audio
cmp = ListHelper.getAudioStreamComparator(Locale.ENGLISH, false, true, false); cmp = ListHelper.getAudioTrackComparator(Locale.ENGLISH, false, true);
stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
null, AUDIO_TRACKS_TEST_LIST, cmp)); AUDIO_TRACKS_TEST_LIST, cmp));
assertEquals("en.ds", stream.getId()); assertEquals("en.ds", stream.getId());
// Japanese language, fall back to original // Japanese language, fall back to original
cmp = ListHelper.getAudioStreamComparator(Locale.JAPANESE, true, false, false); cmp = ListHelper.getAudioTrackComparator(Locale.JAPANESE, true, false);
stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank( stream = AUDIO_TRACKS_TEST_LIST.get(ListHelper.getAudioIndexByHighestRank(
null, AUDIO_TRACKS_TEST_LIST, cmp)); AUDIO_TRACKS_TEST_LIST, cmp));
assertEquals("en.or", stream.getId()); assertEquals("en.or", stream.getId());
} }