Log error for TrackGroups with multiple languages or role flags.
A TrackGroup must contain the same content in all Formats (except for the quality, encoding etc). Verify that the language and role flags are the same and log an error if don't match. Don't throw to avoid breaking existing use cases that just happen to work by chance. PiperOrigin-RevId: 365539240
This commit is contained in:
parent
dd3597c2c1
commit
76700e9d84
@ -21,11 +21,14 @@ import androidx.annotation.Nullable;
|
|||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** Defines an immutable group of tracks identified by their format identity. */
|
/** Defines an immutable group of tracks identified by their format identity. */
|
||||||
public final class TrackGroup implements Parcelable {
|
public final class TrackGroup implements Parcelable {
|
||||||
|
|
||||||
|
private static final String TAG = "TrackGroup";
|
||||||
|
|
||||||
/** The number of tracks in the group. */
|
/** The number of tracks in the group. */
|
||||||
public final int length;
|
public final int length;
|
||||||
|
|
||||||
@ -41,6 +44,7 @@ public final class TrackGroup implements Parcelable {
|
|||||||
Assertions.checkState(formats.length > 0);
|
Assertions.checkState(formats.length > 0);
|
||||||
this.formats = formats;
|
this.formats = formats;
|
||||||
this.length = formats.length;
|
this.length = formats.length;
|
||||||
|
verifyCorrectness();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ TrackGroup(Parcel in) {
|
/* package */ TrackGroup(Parcel in) {
|
||||||
@ -129,4 +133,62 @@ public final class TrackGroup implements Parcelable {
|
|||||||
return new TrackGroup[size];
|
return new TrackGroup[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void verifyCorrectness() {
|
||||||
|
// TrackGroups should only contain tracks with exactly the same content (but in different
|
||||||
|
// qualities). We only log an error instead of throwing to not break backwards-compatibility for
|
||||||
|
// cases where malformed TrackGroups happen to work by chance (e.g. because adaptive selections
|
||||||
|
// are always disabled).
|
||||||
|
String language = normalizeLanguage(formats[0].language);
|
||||||
|
@C.RoleFlags int roleFlags = normalizeRoleFlags(formats[0].roleFlags);
|
||||||
|
for (int i = 1; i < formats.length; i++) {
|
||||||
|
if (!language.equals(normalizeLanguage(formats[i].language))) {
|
||||||
|
logErrorMessage(
|
||||||
|
/* mismatchField= */ "languages",
|
||||||
|
/* valueIndex0= */ formats[0].language,
|
||||||
|
/* otherValue=* */ formats[i].language,
|
||||||
|
/* otherIndex= */ i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (roleFlags != normalizeRoleFlags(formats[i].roleFlags)) {
|
||||||
|
logErrorMessage(
|
||||||
|
/* mismatchField= */ "role flags",
|
||||||
|
/* valueIndex0= */ Integer.toBinaryString(formats[0].roleFlags),
|
||||||
|
/* otherValue=* */ Integer.toBinaryString(formats[i].roleFlags),
|
||||||
|
/* otherIndex= */ i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeLanguage(@Nullable String language) {
|
||||||
|
// Treat all variants of undetermined or unknown languages as compatible.
|
||||||
|
return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language;
|
||||||
|
}
|
||||||
|
|
||||||
|
@C.RoleFlags
|
||||||
|
private static int normalizeRoleFlags(@C.RoleFlags int roleFlags) {
|
||||||
|
// Treat trick-play and non-trick-play formats as compatible.
|
||||||
|
return roleFlags | C.ROLE_FLAG_TRICK_PLAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logErrorMessage(
|
||||||
|
String mismatchField,
|
||||||
|
@Nullable String valueIndex0,
|
||||||
|
@Nullable String otherValue,
|
||||||
|
int otherIndex) {
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"",
|
||||||
|
new IllegalStateException(
|
||||||
|
"Different "
|
||||||
|
+ mismatchField
|
||||||
|
+ " combined in one TrackGroup: '"
|
||||||
|
+ valueIndex0
|
||||||
|
+ "' (track 0) and '"
|
||||||
|
+ otherValue
|
||||||
|
+ "' (track "
|
||||||
|
+ otherIndex
|
||||||
|
+ ")"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,7 +670,7 @@ public final class Util {
|
|||||||
// Locale data (especially for API < 21) may produce tags with '_' instead of the
|
// Locale data (especially for API < 21) may produce tags with '_' instead of the
|
||||||
// standard-conformant '-'.
|
// standard-conformant '-'.
|
||||||
String normalizedTag = language.replace('_', '-');
|
String normalizedTag = language.replace('_', '-');
|
||||||
if (normalizedTag.isEmpty() || "und".equals(normalizedTag)) {
|
if (normalizedTag.isEmpty() || normalizedTag.equals(C.LANGUAGE_UNDETERMINED)) {
|
||||||
// Tag isn't valid, keep using the original.
|
// Tag isn't valid, keep using the original.
|
||||||
normalizedTag = language;
|
normalizedTag = language;
|
||||||
}
|
}
|
||||||
|
@ -279,8 +279,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
"Psybient"
|
"Psybient"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String LANGUAGE_UNDEFINED = "und";
|
|
||||||
|
|
||||||
private static final int TYPE_TOP_BYTE_COPYRIGHT = 0xA9;
|
private static final int TYPE_TOP_BYTE_COPYRIGHT = 0xA9;
|
||||||
private static final int TYPE_TOP_BYTE_REPLACEMENT = 0xFD; // Truncated value of \uFFFD.
|
private static final int TYPE_TOP_BYTE_REPLACEMENT = 0xFD; // Truncated value of \uFFFD.
|
||||||
|
|
||||||
@ -467,7 +465,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
if (atomType == Atom.TYPE_data) {
|
if (atomType == Atom.TYPE_data) {
|
||||||
data.skipBytes(8); // version (1), flags (3), empty (4)
|
data.skipBytes(8); // version (1), flags (3), empty (4)
|
||||||
String value = data.readNullTerminatedString(atomSize - 16);
|
String value = data.readNullTerminatedString(atomSize - 16);
|
||||||
return new CommentFrame(LANGUAGE_UNDEFINED, value, value);
|
return new CommentFrame(C.LANGUAGE_UNDETERMINED, value, value);
|
||||||
}
|
}
|
||||||
Log.w(TAG, "Failed to parse comment attribute: " + Atom.getAtomTypeString(type));
|
Log.w(TAG, "Failed to parse comment attribute: " + Atom.getAtomTypeString(type));
|
||||||
return null;
|
return null;
|
||||||
@ -487,7 +485,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
if (value >= 0) {
|
if (value >= 0) {
|
||||||
return isTextInformationFrame
|
return isTextInformationFrame
|
||||||
? new TextInformationFrame(id, /* description= */ null, Integer.toString(value))
|
? new TextInformationFrame(id, /* description= */ null, Integer.toString(value))
|
||||||
: new CommentFrame(LANGUAGE_UNDEFINED, id, Integer.toString(value));
|
: new CommentFrame(C.LANGUAGE_UNDETERMINED, id, Integer.toString(value));
|
||||||
}
|
}
|
||||||
Log.w(TAG, "Failed to parse uint8 attribute: " + Atom.getAtomTypeString(type));
|
Log.w(TAG, "Failed to parse uint8 attribute: " + Atom.getAtomTypeString(type));
|
||||||
return null;
|
return null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user