Fix parsing of language code in mdhd box

Language codes in the `mdhd` box are stored as three 5-bit values, each representing a letter ('a' to 'z') using an offset of `0x60`. If the decoded characters are not in this range, the language should be treated as undefined.

PiperOrigin-RevId: 738470544
(cherry picked from commit 2a4cbc3be47b54a59aef384b288ea802e34fb2f0)
This commit is contained in:
rohks 2025-03-19 11:15:19 -07:00 committed by tonihei
parent 963bae9dd8
commit d920cf87a6
19 changed files with 39 additions and 34 deletions

View File

@ -169,12 +169,15 @@ public final class Ac3Util {
* *
* @param data The AC3SpecificBox to parse. * @param data The AC3SpecificBox to parse.
* @param trackId The track identifier to set on the format. * @param trackId The track identifier to set on the format.
* @param language The language to set on the format. * @param language The language to set on the format, or {@code null} if unset.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format.
* @return The AC-3 format parsed from data in the header. * @return The AC-3 format parsed from data in the header.
*/ */
public static Format parseAc3AnnexFFormat( public static Format parseAc3AnnexFFormat(
ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) { ParsableByteArray data,
String trackId,
@Nullable String language,
@Nullable DrmInitData drmInitData) {
ParsableBitArray dataBitArray = new ParsableBitArray(); ParsableBitArray dataBitArray = new ParsableBitArray();
dataBitArray.reset(data); dataBitArray.reset(data);
@ -208,12 +211,15 @@ public final class Ac3Util {
* *
* @param data The EC3SpecificBox to parse. * @param data The EC3SpecificBox to parse.
* @param trackId The track identifier to set on the format. * @param trackId The track identifier to set on the format.
* @param language The language to set on the format. * @param language The language to set on the format, or {@code null} if unset.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format.
* @return The E-AC-3 format parsed from data in the header. * @return The E-AC-3 format parsed from data in the header.
*/ */
public static Format parseEAc3AnnexFFormat( public static Format parseEAc3AnnexFFormat(
ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) { ParsableByteArray data,
String trackId,
@Nullable String language,
@Nullable DrmInitData drmInitData) {
ParsableBitArray dataBitArray = new ParsableBitArray(); ParsableBitArray dataBitArray = new ParsableBitArray();
dataBitArray.reset(data); dataBitArray.reset(data);

View File

@ -163,14 +163,17 @@ public final class Ac4Util {
* *
* @param data The AC4SpecificBox to parse. * @param data The AC4SpecificBox to parse.
* @param trackId The track identifier to set on the format. * @param trackId The track identifier to set on the format.
* @param language The language to set on the format. * @param language The language to set on the format, or {@code null} if unset.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format.
* @return The AC-4 format parsed from data in the header. * @return The AC-4 format parsed from data in the header.
* @throws ParserException If an unsupported container feature is encountered while parsing AC-4 * @throws ParserException If an unsupported container feature is encountered while parsing AC-4
* Annex E. * Annex E.
*/ */
public static Format parseAc4AnnexEFormat( public static Format parseAc4AnnexEFormat(
ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) ParsableByteArray data,
String trackId,
@Nullable String language,
@Nullable DrmInitData drmInitData)
throws ParserException { throws ParserException {
ParsableBitArray dataBitArray = new ParsableBitArray(); ParsableBitArray dataBitArray = new ParsableBitArray();
dataBitArray.reset(data); dataBitArray.reset(data);

View File

@ -997,22 +997,34 @@ public final class BoxParser {
mediaDurationUs = Util.scaleLargeTimestamp(mediaDuration, C.MICROS_PER_SECOND, timescale); mediaDurationUs = Util.scaleLargeTimestamp(mediaDuration, C.MICROS_PER_SECOND, timescale);
} }
} }
int languageCode = mdhd.readUnsignedShort();
String language = String language = getLanguageFromCode(/* languageCode= */ mdhd.readUnsignedShort());
""
+ (char) (((languageCode >> 10) & 0x1F) + 0x60)
+ (char) (((languageCode >> 5) & 0x1F) + 0x60)
+ (char) ((languageCode & 0x1F) + 0x60);
return new MdhdData(timescale, mediaDurationUs, language); return new MdhdData(timescale, mediaDurationUs, language);
} }
@Nullable
private static String getLanguageFromCode(int languageCode) {
char[] chars = {
(char) (((languageCode >> 10) & 0x1F) + 0x60),
(char) (((languageCode >> 5) & 0x1F) + 0x60),
(char) ((languageCode & 0x1F) + 0x60)
};
for (char c : chars) {
if (c < 'a' || c > 'z') {
return null;
}
}
return new String(chars);
}
/** /**
* Parses a stsd atom (defined in ISO/IEC 14496-12). * Parses a stsd atom (defined in ISO/IEC 14496-12).
* *
* @param stsd The stsd atom to decode. * @param stsd The stsd atom to decode.
* @param trackId The track's identifier in its container. * @param trackId The track's identifier in its container.
* @param rotationDegrees The rotation of the track in degrees. * @param rotationDegrees The rotation of the track in degrees.
* @param language The language of the track. * @param language The language of the track, or {@code null} if unset.
* @param drmInitData {@link DrmInitData} to be included in the format, or {@code null}. * @param drmInitData {@link DrmInitData} to be included in the format, or {@code null}.
* @param isQuickTime True for QuickTime media. False otherwise. * @param isQuickTime True for QuickTime media. False otherwise.
* @return An object containing the parsed data. * @return An object containing the parsed data.
@ -1021,7 +1033,7 @@ public final class BoxParser {
ParsableByteArray stsd, ParsableByteArray stsd,
int trackId, int trackId,
int rotationDegrees, int rotationDegrees,
String language, @Nullable String language,
@Nullable DrmInitData drmInitData, @Nullable DrmInitData drmInitData,
boolean isQuickTime) boolean isQuickTime)
throws ParserException { throws ParserException {
@ -1125,7 +1137,7 @@ public final class BoxParser {
int position, int position,
int atomSize, int atomSize,
int trackId, int trackId,
String language, @Nullable String language,
StsdData out) { StsdData out) {
parent.setPosition(position + Mp4Box.HEADER_SIZE + StsdData.STSD_HEADER_SIZE); parent.setPosition(position + Mp4Box.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
@ -1820,7 +1832,7 @@ public final class BoxParser {
int position, int position,
int size, int size,
int trackId, int trackId,
String language, @Nullable String language,
boolean isQuickTime, boolean isQuickTime,
@Nullable DrmInitData drmInitData, @Nullable DrmInitData drmInitData,
StsdData out, StsdData out,
@ -2593,9 +2605,9 @@ public final class BoxParser {
private static final class MdhdData { private static final class MdhdData {
private final long timescale; private final long timescale;
private final long mediaDurationUs; private final long mediaDurationUs;
private final String language; @Nullable private final String language;
public MdhdData(long timescale, long mediaDurationUs, String language) { public MdhdData(long timescale, long mediaDurationUs, @Nullable String language) {
this.timescale = timescale; this.timescale = timescale;
this.mediaDurationUs = mediaDurationUs; this.mediaDurationUs = mediaDurationUs;
this.language = language; this.language = language;

View File

@ -12,7 +12,6 @@ track 0:
sampleMimeType = audio/opus sampleMimeType = audio/opus
channelCount = 2 channelCount = 2
sampleRate = 16000 sampleRate = 16000
language = 
initializationData: initializationData:
data = length 19, hash 4034F23B data = length 19, hash 4034F23B
data = length 8, hash 94446F01 data = length 8, hash 94446F01

View File

@ -12,7 +12,6 @@ track 0:
sampleMimeType = audio/opus sampleMimeType = audio/opus
channelCount = 2 channelCount = 2
sampleRate = 16000 sampleRate = 16000
language = 
initializationData: initializationData:
data = length 19, hash 4034F23B data = length 19, hash 4034F23B
data = length 8, hash 94446F01 data = length 8, hash 94446F01

View File

@ -12,7 +12,6 @@ track 0:
sampleMimeType = audio/opus sampleMimeType = audio/opus
channelCount = 2 channelCount = 2
sampleRate = 16000 sampleRate = 16000
language = 
initializationData: initializationData:
data = length 19, hash 4034F23B data = length 19, hash 4034F23B
data = length 8, hash 94446F01 data = length 8, hash 94446F01

View File

@ -12,7 +12,6 @@ track 0:
sampleMimeType = audio/opus sampleMimeType = audio/opus
channelCount = 2 channelCount = 2
sampleRate = 16000 sampleRate = 16000
language = 
initializationData: initializationData:
data = length 19, hash 4034F23B data = length 19, hash 4034F23B
data = length 8, hash 94446F01 data = length 8, hash 94446F01

View File

@ -18,7 +18,6 @@ track 0:
maxInputSize = 295 maxInputSize = 295
channelCount = 1 channelCount = 1
sampleRate = 16000 sampleRate = 16000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 30, hash C22462B1 data = length 30, hash C22462B1

View File

@ -18,7 +18,6 @@ track 0:
channelCount = 2 channelCount = 2
sampleRate = 44100 sampleRate = 44100
pcmEncoding = 2 pcmEncoding = 2
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
sample 0: sample 0:
time = 0 time = 0

View File

@ -17,7 +17,6 @@ track 0:
maxInputSize = 1185 maxInputSize = 1185
channelCount = 6 channelCount = 6
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 27, hash 9EE6F879 data = length 27, hash 9EE6F879

View File

@ -42,7 +42,6 @@ track 1:
maxInputSize = 627 maxInputSize = 627
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560

View File

@ -552,7 +552,6 @@ track 1:
maxInputSize = 877 maxInputSize = 877
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560

View File

@ -543,7 +543,6 @@ track 1:
codecs = mp4a.40.2 codecs = mp4a.40.2
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560
sample 0: sample 0:

View File

@ -416,7 +416,6 @@ track 1:
maxInputSize = 877 maxInputSize = 877
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560

View File

@ -552,7 +552,6 @@ track 1:
maxInputSize = 877 maxInputSize = 877
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560

View File

@ -552,7 +552,6 @@ track 1:
maxInputSize = 877 maxInputSize = 877
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560

View File

@ -18,7 +18,6 @@ track 0:
maxInputSize = 295 maxInputSize = 295
channelCount = 1 channelCount = 1
sampleRate = 16000 sampleRate = 16000
language = ```
metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData: initializationData:
data = length 30, hash C22462B1 data = length 30, hash C22462B1

View File

@ -17,7 +17,6 @@ track 0:
maxInputSize = 1185 maxInputSize = 1185
channelCount = 6 channelCount = 6
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData: initializationData:
data = length 27, hash 9EE6F879 data = length 27, hash 9EE6F879

View File

@ -57,7 +57,6 @@ track 1:
maxInputSize = 627 maxInputSize = 627
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ```
metadata = entries=[xyz: latitude=51.5932, longitude=-0.2431, Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000] metadata = entries=[xyz: latitude=51.5932, longitude=-0.2431, Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560