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.

#minor-release
#exofixit
Issue: #9087
PiperOrigin-RevId: 395274934
This commit is contained in:
olly 2021-09-07 18:14:46 +01:00 committed by Oliver Woodman
parent 66335ab6f5
commit e53e59388f
3 changed files with 58 additions and 2 deletions

View file

@ -71,6 +71,9 @@
* RTSP:
* Handle when additional spaces are in SDP's RTPMAP atrribute
([#9379](https://github.com/google/ExoPlayer/issues/9379)).
* Extractors:
* ID3: Fix issue decoding ID3 tags containing UTF-16 encoded strings
([#9087](https://github.com/google/ExoPlayer/issues/9087)).
### 2.15.0 (2021-08-10)

View file

@ -806,9 +806,9 @@ public final class Id3Decoder extends SimpleMetadataDecoder {
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) {
if (terminationPos % 2 == 0 && data[terminationPos + 1] == (byte) 0) {
if ((terminationPos - fromIndex) % 2 == 0 && data[terminationPos + 1] == (byte) 0) {
return terminationPos;
}
terminationPos = indexOfZeroByte(data, terminationPos + 1);

View file

@ -38,6 +38,7 @@ public final class Id3DecoderTest {
@Test
public void decodeTxxxFrame() {
// Test UTF-8.
byte[] rawId3 =
buildSingleFrameTag(
"TXXX",
@ -53,6 +54,21 @@ public final class Id3DecoderTest {
assertThat(textInformationFrame.description).isEmpty();
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.
rawId3 = buildSingleFrameTag("TXXX", new byte[0]);
metadata = decoder.decode(rawId3, rawId3.length);
@ -220,6 +236,43 @@ public final class Id3DecoderTest {
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 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