From 0060ddd0316c614ad1a5847f44e17329c2c90260 Mon Sep 17 00:00:00 2001 From: tolriq Date: Sat, 11 Jul 2026 21:11:06 +0200 Subject: [PATCH] Fix 14-bit DTS frame size calculation ETSI TS 102 114 V1.2.1 section 5.3 defines 14-bit DTS storage as one 14-bit payload word in the least significant bits of each 16-bit word. Convert FSIZE+1 to a whole number of 14-bit words before mapping those words to physical bytes. The previous fsize * 16 / 14 calculation could produce an impossible odd physical size. For common DTS-CD headers with FSIZE+1 equal to 3585 it returned 4097 bytes, although consecutive sync words are 4096 bytes apart. DtsReader then consumed the first byte of the next sync word and failed to recognize subsequent frames. Add direct coverage for little- and big-endian 14-bit headers and for an already-exact conversion. Test: ./gradlew :lib-extractor:testDebugUnitTest --tests androidx.media3.extractor.DtsUtilTest --- .../androidx/media3/extractor/DtsUtil.java | 10 +++- .../media3/extractor/DtsUtilTest.java | 54 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 libraries/extractor/src/test/java/androidx/media3/extractor/DtsUtilTest.java diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java index 1785b496b3b..308395baa66 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java @@ -408,8 +408,14 @@ public static int getDtsFrameSize(byte[] data) { fsize = (((data[5] & 0x03) << 12) | ((data[6] & 0xFF) << 4) | ((data[7] & 0xF0) >> 4)) + 1; } - // If the frame is stored in 14-bit mode, adjust the frame size to reflect the actual byte size. - return uses14BitPerWord ? fsize * 16 / 14 : fsize; + if (!uses14BitPerWord) { + return fsize; + } + // A 14-bit stream stores each 14-bit word in a 16-bit container. Convert the unpacked byte size + // to a whole number of 14-bit words before converting those words to physical bytes. For + // example, DTS-CD frames with FSIZE+1 equal to 3585 have their next sync word after 2048 stored + // words, or 4096 bytes. + return (fsize * C.BITS_PER_BYTE / 14) * 2; } /** diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/DtsUtilTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/DtsUtilTest.java new file mode 100644 index 00000000000..f6d0c26dfa8 --- /dev/null +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/DtsUtilTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.extractor; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.util.Util; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link DtsUtil}. */ +@RunWith(AndroidJUnit4.class) +public final class DtsUtilTest { + + private static final int DTS_CD_FRAME_SIZE = 4_096; + private static final byte[] DTS_CD_14_BIT_LITTLE_ENDIAN_HEADER = + Util.getBytesFromHexString("FF1F00E8F107E0FC980000FAEAF40900DEFB"); + private static final byte[] DTS_CD_14_BIT_BIG_ENDIAN_HEADER = + Util.getBytesFromHexString("1FFFE80007F1FCE0009800FAF4EA0009FBDE"); + private static final byte[] DTS_14_BIT_HEADER_WITH_EXACT_WORD_CONVERSION = + Util.getBytesFromHexString("FF1F00E8F107DFFC983C00FAEAF40900DEFB"); + + @Test + public void getDtsFrameSize_wordAligned14BitLittleEndian_returnsPhysicalFrameSize() { + assertThat(DtsUtil.getDtsFrameSize(DTS_CD_14_BIT_LITTLE_ENDIAN_HEADER)) + .isEqualTo(DTS_CD_FRAME_SIZE); + } + + @Test + public void getDtsFrameSize_wordAligned14BitBigEndian_returnsPhysicalFrameSize() { + assertThat(DtsUtil.getDtsFrameSize(DTS_CD_14_BIT_BIG_ENDIAN_HEADER)) + .isEqualTo(DTS_CD_FRAME_SIZE); + } + + @Test + public void getDtsFrameSize_exact14BitWordConversion_returnsUnchangedFrameSize() { + assertThat(DtsUtil.getDtsFrameSize(DTS_14_BIT_HEADER_WITH_EXACT_WORD_CONVERSION)) + .isEqualTo(DTS_CD_FRAME_SIZE); + } +}