ID3: Fix end-of-string detection for UTF-16

The current detection logic checks that the two byte terminator starts
at an even position in the ID3 data, where-as it should check that it
starts at an even position relative to the start of the string.

Issue: #9087
PiperOrigin-RevId: 395274934
This commit is contained in:
olly 2021-09-07 18:14:46 +01:00 committed by Christos Tsilopoulos
parent a1d376fae1
commit 1bd96fbaf7
3 changed files with 57 additions and 2 deletions

View File

@ -12,6 +12,8 @@
* Extractors: * Extractors:
* Support TS packets without PTS flag * Support TS packets without PTS flag
([#9294](https://github.com/google/ExoPlayer/issues/9294)). ([#9294](https://github.com/google/ExoPlayer/issues/9294)).
* ID3: Fix issue decoding ID3 tags containing UTF-16 encoded strings
([#9087](https://github.com/google/ExoPlayer/issues/9087)).
* Video: * Video:
* Request smaller decoder input buffers for Dolby Vision. This fixes an * Request smaller decoder input buffers for Dolby Vision. This fixes an
issue that could cause UHD Dolby Vision playbacks to fail on some issue that could cause UHD Dolby Vision playbacks to fail on some

View File

@ -806,9 +806,9 @@ public final class Id3Decoder extends SimpleMetadataDecoder {
return terminationPos; return terminationPos;
} }
// Otherwise ensure an even index and look for a second zero byte. // Otherwise ensure an even offset from the start, and look for a second zero byte.
while (terminationPos < data.length - 1) { while (terminationPos < data.length - 1) {
if (terminationPos % 2 == 0 && data[terminationPos + 1] == (byte) 0) { if ((terminationPos - fromIndex) % 2 == 0 && data[terminationPos + 1] == (byte) 0) {
return terminationPos; return terminationPos;
} }
terminationPos = indexOfZeroByte(data, terminationPos + 1); terminationPos = indexOfZeroByte(data, terminationPos + 1);

View File

@ -38,6 +38,7 @@ public final class Id3DecoderTest {
@Test @Test
public void decodeTxxxFrame() { public void decodeTxxxFrame() {
// Test UTF-8.
byte[] rawId3 = byte[] rawId3 =
buildSingleFrameTag( buildSingleFrameTag(
"TXXX", "TXXX",
@ -53,6 +54,21 @@ public final class Id3DecoderTest {
assertThat(textInformationFrame.description).isEmpty(); assertThat(textInformationFrame.description).isEmpty();
assertThat(textInformationFrame.value).isEqualTo("mdialog_VINDICO1527664_start"); assertThat(textInformationFrame.value).isEqualTo("mdialog_VINDICO1527664_start");
// Test UTF-16.
rawId3 =
buildSingleFrameTag(
"TXXX",
new byte[] {
1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0,
100, 0, 0
});
metadata = decoder.decode(rawId3, rawId3.length);
assertThat(metadata.length()).isEqualTo(1);
textInformationFrame = (TextInformationFrame) metadata.get(0);
assertThat(textInformationFrame.id).isEqualTo("TXXX");
assertThat(textInformationFrame.description).isEqualTo("Hello World");
assertThat(textInformationFrame.value).isEmpty();
// Test empty. // Test empty.
rawId3 = buildSingleFrameTag("TXXX", new byte[0]); rawId3 = buildSingleFrameTag("TXXX", new byte[0]);
metadata = decoder.decode(rawId3, rawId3.length); metadata = decoder.decode(rawId3, rawId3.length);
@ -220,6 +236,43 @@ public final class Id3DecoderTest {
assertThat(apicFrame.description).isEqualTo("Hello World"); assertThat(apicFrame.description).isEqualTo("Hello World");
assertThat(apicFrame.pictureData).hasLength(10); assertThat(apicFrame.pictureData).hasLength(10);
assertThat(apicFrame.pictureData).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); assertThat(apicFrame.pictureData).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
// Test with UTF-16 description at even offset.
rawId3 =
buildSingleFrameTag(
"APIC",
new byte[] {
1, 105, 109, 97, 103, 101, 47, 106, 112, 101, 103, 0, 16, 0, 72, 0, 101, 0, 108, 0,
108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 0
});
decoder = new Id3Decoder();
metadata = decoder.decode(rawId3, rawId3.length);
assertThat(metadata.length()).isEqualTo(1);
apicFrame = (ApicFrame) metadata.get(0);
assertThat(apicFrame.mimeType).isEqualTo("image/jpeg");
assertThat(apicFrame.pictureType).isEqualTo(16);
assertThat(apicFrame.description).isEqualTo("Hello World");
assertThat(apicFrame.pictureData).hasLength(10);
assertThat(apicFrame.pictureData).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
// Test with UTF-16 description at odd offset.
rawId3 =
buildSingleFrameTag(
"APIC",
new byte[] {
1, 105, 109, 97, 103, 101, 47, 112, 110, 103, 0, 16, 0, 72, 0, 101, 0, 108, 0, 108, 0,
111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
});
decoder = new Id3Decoder();
metadata = decoder.decode(rawId3, rawId3.length);
assertThat(metadata.length()).isEqualTo(1);
apicFrame = (ApicFrame) metadata.get(0);
assertThat(apicFrame.mimeType).isEqualTo("image/png");
assertThat(apicFrame.pictureType).isEqualTo(16);
assertThat(apicFrame.description).isEqualTo("Hello World");
assertThat(apicFrame.pictureData).hasLength(10);
assertThat(apicFrame.pictureData).isEqualTo(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
} }
@Test @Test