diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 48e5b6b1e9..2483a1954e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,9 @@ ### Unreleased changes +* Extractors: + * Add AC-4 Level-4 ISO base media file format support + ([#1265](https://github.com/androidx/media/pull/1265)). * Text: * Fix garbled CEA-608 subtitles in content with more than one SEI message per sample. diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index 45a408a24e..bec66b385b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -2250,6 +2250,24 @@ public static int getAudioTrackChannelConfig(int channelCount) { } case 12: return AudioFormat.CHANNEL_OUT_7POINT1POINT4; + case 24: + if (Util.SDK_INT >= 32) { + return AudioFormat.CHANNEL_OUT_7POINT1POINT4 + | AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER + | AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER + | AudioFormat.CHANNEL_OUT_BACK_CENTER + | AudioFormat.CHANNEL_OUT_TOP_CENTER + | AudioFormat.CHANNEL_OUT_TOP_FRONT_CENTER + | AudioFormat.CHANNEL_OUT_TOP_BACK_CENTER + | AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT + | AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT + | AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT + | AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT + | AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER + | AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2; + } else { + return AudioFormat.CHANNEL_INVALID; + } default: return AudioFormat.CHANNEL_INVALID; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 0bd8b7ac81..ec51047be3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -4224,18 +4224,22 @@ public boolean isEnabled() { public boolean canBeSpatialized(AudioAttributes audioAttributes, Format format) { int linearChannelCount; - if (Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_E_AC3_JOC) - && format.channelCount == 16) { + if (Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_E_AC3_JOC)) { // For E-AC3 JOC, the format is object based. When the channel count is 16, this maps to 12 // linear channels and the rest are used for objects. See // https://github.com/google/ExoPlayer/pull/10322#discussion_r895265881 - linearChannelCount = 12; - } else if (Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_IAMF) - && format.channelCount == Format.NO_VALUE) { + linearChannelCount = format.channelCount == 16 ? 12 : format.channelCount; + } else if (Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_IAMF)) { // IAMF with no channel count specified, assume 5.1 channels. This depends on // IamfDecoder.SPATIALIZED_OUTPUT_LAYOUT being set to AudioFormat.CHANNEL_OUT_5POINT1. Any // changes to that constant will require updates to this logic. - linearChannelCount = 6; + linearChannelCount = format.channelCount == Format.NO_VALUE ? 6 : format.channelCount; + } else if (Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_AC4)) { + // For AC-4 level 3 or level 4, the format may be object based. When the channel count is + // 18 (level 3 17.1 OBI) or 21 (level 4 20.1 OBI), it is mapped to 24 linear channels (some + // channels are used for metadata transfer). + linearChannelCount = + (format.channelCount == 18 || format.channelCount == 21) ? 24 : format.channelCount; } else { linearChannelCount = format.channelCount; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/Ac4Util.java b/libraries/extractor/src/main/java/androidx/media3/extractor/Ac4Util.java index 3386f73b41..d895cae984 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/Ac4Util.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/Ac4Util.java @@ -15,14 +15,22 @@ */ package androidx.media3.extractor; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.DrmInitData; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; +import androidx.media3.common.ParserException; import androidx.media3.common.util.ParsableBitArray; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; import java.nio.ByteBuffer; /** Utility methods for parsing AC-4 frames, which are access units in AC-4 bitstreams. */ @@ -57,6 +65,52 @@ private SyncFrameInfo( } } + /** + * Types of channel modes for AC-4 audio as per ETSI TS 103 190-2 V1.1.1 (2015-09), Section + * 6.3.2.7.2, Table 79. + */ + @Documented + @Retention(SOURCE) + @Target(TYPE_USE) + @IntDef({ + CHANNEL_MODE_UNKNOWN, + CHANNEL_MODE_MONO, + CHANNEL_MODE_STEREO, + CHANNEL_MODE_3_0, + CHANNEL_MODE_5_0, + CHANNEL_MODE_5_1, + CHANNEL_MODE_7_0_34, + CHANNEL_MODE_7_1_34, + CHANNEL_MODE_7_0_52, + CHANNEL_MODE_7_1_52, + CHANNEL_MODE_7_0_322, + CHANNEL_MODE_7_1_322, + CHANNEL_MODE_7_0_4, + CHANNEL_MODE_7_1_4, + CHANNEL_MODE_9_0_4, + CHANNEL_MODE_9_1_4, + CHANNEL_MODE_22_2 + }) + private @interface ChannelMode {} + + private static final int CHANNEL_MODE_UNKNOWN = -1; + private static final int CHANNEL_MODE_MONO = 0; + private static final int CHANNEL_MODE_STEREO = 1; + private static final int CHANNEL_MODE_3_0 = 2; + private static final int CHANNEL_MODE_5_0 = 3; + private static final int CHANNEL_MODE_5_1 = 4; + private static final int CHANNEL_MODE_7_0_34 = 5; + private static final int CHANNEL_MODE_7_1_34 = 6; + private static final int CHANNEL_MODE_7_0_52 = 7; + private static final int CHANNEL_MODE_7_1_52 = 8; + private static final int CHANNEL_MODE_7_0_322 = 9; + private static final int CHANNEL_MODE_7_1_322 = 10; + private static final int CHANNEL_MODE_7_0_4 = 11; + private static final int CHANNEL_MODE_7_1_4 = 12; + private static final int CHANNEL_MODE_9_0_4 = 13; + private static final int CHANNEL_MODE_9_1_4 = 14; + private static final int CHANNEL_MODE_22_2 = 15; + public static final int AC40_SYNCWORD = 0xAC40; public static final int AC41_SYNCWORD = 0xAC41; @@ -104,28 +158,467 @@ private SyncFrameInfo( /** * Returns the AC-4 format given {@code data} containing the AC4SpecificBox according to ETSI TS - * 103 190-1 Annex E. The reading position of {@code data} will be modified. + * 103 190-1 Annex E.4 (ac4_dsi) and TS 103 190-2 section E.6 (ac4_dsi_v1). The reading position + * of {@code data} will be modified. * * @param data The AC4SpecificBox to parse. * @param trackId The track identifier to set on the format. * @param language The language to set on the format. * @param drmInitData {@link DrmInitData} to be included in the format. * @return The AC-4 format parsed from data in the header. + * @throws ParserException If an unsupported container feature is encountered while parsing AC-4 + * Annex E. */ public static Format parseAc4AnnexEFormat( - ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) { - data.skipBytes(1); // ac4_dsi_version, bitstream_version[0:5] - int sampleRate = ((data.readUnsignedByte() & 0x20) >> 5 == 1) ? 48000 : 44100; + ParsableByteArray data, String trackId, String language, @Nullable DrmInitData drmInitData) + throws ParserException { + ParsableBitArray dataBitArray = new ParsableBitArray(); + dataBitArray.reset(data); + + int dsiSize = dataBitArray.bitsLeft(); + int ac4DsiVersion = dataBitArray.readBits(3); // ac4_dsi_version + if (ac4DsiVersion > 1) { + throw ParserException.createForUnsupportedContainerFeature( + "Unsupported AC-4 DSI version: " + ac4DsiVersion); + } + + int bitstreamVersion = dataBitArray.readBits(7); // bitstream_version + int sampleRate = dataBitArray.readBit() ? 48000 : 44100; // fs_index + dataBitArray.skipBits(4); // frame_rate_index + int numberOfPresentations = dataBitArray.readBits(9); // n_presentations + + if (bitstreamVersion > 1) { + if (ac4DsiVersion == 0) { + throw ParserException.createForUnsupportedContainerFeature( + "Invalid AC-4 DSI version: " + ac4DsiVersion); + } + if (dataBitArray.readBit()) { // b_program_id + dataBitArray.skipBits(16); // short_program_id + if (dataBitArray.readBit()) { // b_uuid + dataBitArray.skipBits(16 * 8); // program_uuid + } + } + } + + if (ac4DsiVersion == 1) { + if (!skipDsiBitrate(dataBitArray)) { + throw ParserException.createForUnsupportedContainerFeature("Invalid AC-4 DSI bitrate."); + } + dataBitArray.byteAlign(); + } + + Ac4Presentation ac4Presentation = new Ac4Presentation(); + for (int presentationIdx = 0; presentationIdx < numberOfPresentations; presentationIdx++) { + boolean isSingleSubstream = false; + boolean isSingleSubstreamGroup = false; + int presentationConfig; + int presentationVersion; + int presentationBytes = 0; + int start = 0; + + if (ac4DsiVersion == 0) { + isSingleSubstream = dataBitArray.readBit(); // b_single_substream + presentationConfig = dataBitArray.readBits(5); // presentation_config + presentationVersion = dataBitArray.readBits(5); // presentation_version + } else { + presentationVersion = dataBitArray.readBits(8); // presentation_version + presentationBytes = dataBitArray.readBits(8); // pres_bytes + if (presentationBytes == 0xff) { + presentationBytes += dataBitArray.readBits(16); // pres_bytes + } + if (presentationVersion > 2) { + dataBitArray.skipBits(presentationBytes * 8); + continue; + } + // record a marker, less the size of the presentation_config + start = (dsiSize - dataBitArray.bitsLeft()) / 8; + // ac4_presentation_v0_dsi(), ac4_presentation_v1_dsi() and ac4_presentation_v2_dsi() + // all start with a presentation_config of 5 bits + presentationConfig = dataBitArray.readBits(5); // presentation_config + isSingleSubstreamGroup = (presentationConfig == 0x1f); + } + + boolean addEmdfSubstreams; + if (!(isSingleSubstream || isSingleSubstreamGroup) && presentationConfig == 6) { + addEmdfSubstreams = true; + } else { + ac4Presentation.level = dataBitArray.readBits(3); // mdcompat + + if (dataBitArray.readBit()) { // b_presentation_group_index + dataBitArray.skipBits(5); // group_index + } + + dataBitArray.skipBits(2); // dsi_frame_rate_multiply_info + if (ac4DsiVersion == 1 && (presentationVersion == 1 || presentationVersion == 2)) { + dataBitArray.skipBits(2); // dsi_frame_rate_fraction_info + } + dataBitArray.skipBits(5); // presentation_emdf_version + dataBitArray.skipBits(10); // presentation_key_id + + if (ac4DsiVersion == 1) { + if (presentationVersion > 0) { + ac4Presentation.isChannelCoded = dataBitArray.readBit(); // b_presentation_channel_coded + } + + if (ac4Presentation.isChannelCoded) { + if (presentationVersion == 1 || presentationVersion == 2) { + int channelMode = dataBitArray.readBits(5); // dsi_presentation_ch_mode + if (channelMode >= 0 && channelMode <= 15) { + ac4Presentation.channelMode = channelMode; + } + + if (channelMode >= 11 && channelMode <= 14) { + ac4Presentation.hasBackChannels = + dataBitArray.readBit(); // pres_b_4_back_channels_present + ac4Presentation.topChannelPairs = + dataBitArray.readBits(2); // pres_top_channel_pairs + } + } + // presentation_channel_mask in ac4_presentation_v0_dsi() + dataBitArray.skipBits(24); // presentation_channel_mask_v1 + } + + if (presentationVersion == 1 || presentationVersion == 2) { + if (dataBitArray.readBit()) { // b_presentation_core_differs + if (dataBitArray.readBit()) { // b_presentation_core_channel_coded + dataBitArray.skipBits(2); // dsi_presentation_channel_mode_core + } + } + if (dataBitArray.readBit()) { // b_presentation_filter + // Ignore b_enable_presentation field since this flag occurs in AC-4 elementary stream + // TOC and AC-4 decoder doesn't handle it either. + dataBitArray.skipBit(); // b_enable_presentation + int filterBytes = dataBitArray.readBits(8); // n_filter_bytes + for (int i = 0; i < filterBytes; i++) { + dataBitArray.skipBits(8); // filter_data + } + } + } + } + + if (isSingleSubstream || isSingleSubstreamGroup) { + if (presentationVersion == 0) { + parseDsiSubstream(dataBitArray, ac4Presentation); + } else { + parseDsiSubstreamGroup(dataBitArray, ac4Presentation); + } + } else { + // b_hsf_ext for ac4DsiVersion 0 OR b_multi_pid for ac4DsiVersion 1 + dataBitArray.skipBit(); + switch (presentationConfig) { + case 0: + case 1: + case 2: + if (presentationVersion == 0) { + for (int substreamId = 0; substreamId < 2; substreamId++) { + parseDsiSubstream(dataBitArray, ac4Presentation); + } + } else { + for (int substreamGroupId = 0; substreamGroupId < 2; substreamGroupId++) { + parseDsiSubstreamGroup(dataBitArray, ac4Presentation); + } + } + break; + case 3: + case 4: + if (presentationVersion == 0) { + for (int substreamId = 0; substreamId < 3; substreamId++) { + parseDsiSubstream(dataBitArray, ac4Presentation); + } + } else { + for (int substreamGroupId = 0; substreamGroupId < 3; substreamGroupId++) { + parseDsiSubstreamGroup(dataBitArray, ac4Presentation); + } + } + break; + case 5: + if (presentationVersion == 0) { + parseDsiSubstream(dataBitArray, ac4Presentation); + } else { + int nSubstreamGroupsMinus2 = dataBitArray.readBits(3); + for (int substreamGroupId = 0; + substreamGroupId < nSubstreamGroupsMinus2 + 2; + substreamGroupId++) { + parseDsiSubstreamGroup(dataBitArray, ac4Presentation); + } + } + break; + default: + int nSkipBytes = dataBitArray.readBits(7); // n_skip_bytes + for (int j = 0; j < nSkipBytes; j++) { + dataBitArray.skipBits(8); + } + break; + } + } + dataBitArray.skipBit(); // b_pre_virtualized + addEmdfSubstreams = dataBitArray.readBit(); // b_add_emdf_substreams + } + if (addEmdfSubstreams) { + int nAddEmdfSubstreams = dataBitArray.readBits(7); // n_add_emdf_substreams + for (int j = 0; j < nAddEmdfSubstreams; j++) { + dataBitArray.skipBits(5 + 10); // substream_emdf_version and substream_key_id + } + } + + if (presentationVersion > 0) { + if (dataBitArray.readBit()) { // b_presentation_bitrate_info + if (!skipDsiBitrate(dataBitArray)) { + throw ParserException.createForUnsupportedContainerFeature("Can't parse bitrate DSI."); + } + } + + if (dataBitArray.readBit()) { // b_alternative + dataBitArray.byteAlign(); + int nameLen = dataBitArray.readBits(16); // name_len + dataBitArray.skipBytes(nameLen); // presentation_name + + int nTargets = dataBitArray.readBits(5); // n_targets + for (int i = 0; i < nTargets; i++) { + dataBitArray.skipBits(3); // target_md_compat + dataBitArray.skipBits(8); // target_device_category + } + } + } + + dataBitArray.byteAlign(); + + if (ac4DsiVersion == 1) { + int end = (dsiSize - dataBitArray.bitsLeft()) / 8; + int presentationBytesRead = end - start; + if (presentationBytes < presentationBytesRead) { + throw ParserException.createForUnsupportedContainerFeature( + "pres_bytes is smaller than presentation bytes read."); + } + int skipBytes = presentationBytes - presentationBytesRead; + dataBitArray.skipBytes(skipBytes); + } + // We should know this or something is probably wrong with the bitstream (or we don't support + // it) + if (ac4Presentation.isChannelCoded && ac4Presentation.channelMode == CHANNEL_MODE_UNKNOWN) { + throw ParserException.createForUnsupportedContainerFeature( + "Can't determine channel mode of presentation " + presentationIdx); + } + break; // Successfully parsed the first presentation with presentation version 0, 1 or 2. + } + + int channelCount; + if (ac4Presentation.isChannelCoded) { + channelCount = + getAdjustedChannelCount( + ac4Presentation.channelMode, + ac4Presentation.hasBackChannels, + ac4Presentation.topChannelPairs); + } else { + channelCount = ac4Presentation.numOfUmxObjects; + // TODO: There is a bug in ETSI TS 103 190-2 V1.2.1 (2018-02), E.11.11 + // For AC-4 level 4 stream, the intention is to set 19 to n_umx_objects_minus1 but it is + // equal to 15 based on current specification. Dolby has filed a bug report to ETSI. + // The following sentence should be deleted after ETSI specification error is fixed. + if (ac4Presentation.level == 4) { + channelCount = channelCount == 16 ? 21 : channelCount; + } + } + + if (channelCount <= 0) { + throw ParserException.createForUnsupportedContainerFeature( + "Can't determine channel count of presentation."); + } + return new Format.Builder() .setId(trackId) .setSampleMimeType(MimeTypes.AUDIO_AC4) - .setChannelCount(CHANNEL_COUNT_2) + .setChannelCount(channelCount) .setSampleRate(sampleRate) .setDrmInitData(drmInitData) .setLanguage(language) .build(); } + /** + * Parses the AC-4 DSI substream according to TS 103 190-1 v1.2.1 section E.5 and TS 103 190-2 + * v1.1.1 section E.9. Modifies the reading position of {@code data} to be just after the AC-4 DSI + * substream field. + * + * @param data The {@link ParsableBitArray} containing the AC-4 DSI, positioned at the start of + * the substream field. + * @param ac4Presentation A structure to store parsed AC-4 presentation information. + * @throws ParserException If an unsupported container feature is encountered while parsing the + * AC-4 DSI substream. + */ + private static void parseDsiSubstream(ParsableBitArray data, Ac4Presentation ac4Presentation) + throws ParserException { + int channelMode = data.readBits(5); // channel_mode + data.skipBits(2); // dsi_sf_multiplier + + if (data.readBit()) { // b_bitrate_indicator + data.skipBits(5); // b_bitrate_indicator + } + if (channelMode >= 7 && channelMode <= 10) { + data.skipBit(); // add_ch_base + } + + if (data.readBit()) { // b_content_type + int contentClassifier = data.readBits(3); // content_classifier + // For streams based on TS 103 190 part 1 the presentation level channel_mode doesn't exist + // and so we use the channel_mode from either the CM or M&E substream (they are mutually + // exclusive). + if (ac4Presentation.channelMode == CHANNEL_MODE_UNKNOWN + && (channelMode >= 0 && channelMode <= 15) + && (contentClassifier == 0 || contentClassifier == 1)) { + ac4Presentation.channelMode = channelMode; + } + + if (data.readBit()) { // b_language_indicator + skipDsiLanguage(data); + } + } + } + + /** + * Parses the AC-4 DSI substream group information according to ETSI TS 103 190-2 v1.1.1 section + * E.11. Modifies the reading position of {@code data} to be just after the AC-4 DSI substream + * group field. + * + * @param data The {@link ParsableBitArray} containing the AC-4 DSI, positioned at the start of + * the substream group field. + * @param ac4Presentation A structure to store parsed AC-4 presentation information. + * @throws ParserException If an unsupported container feature is encountered while parsing the + * AC-4 DSI substream group. + */ + private static void parseDsiSubstreamGroup(ParsableBitArray data, Ac4Presentation ac4Presentation) + throws ParserException { + data.skipBits(2); // b_substreams_present(1), b_hsf_ext(1) + boolean channelCoded = data.readBit(); // b_channel_coded + int numberOfSubstreams = data.readBits(8); // n_substreams + + for (int i = 0; i < numberOfSubstreams; i++) { + data.skipBits(2); // dsi_sf_multiplier + if (data.readBit()) { // b_substream_bitrate_indicator + data.skipBits(5); // substream_bitrate_indicator + } + if (channelCoded) { + data.skipBits(24); // dsi_substream_channel_mask + } else { + if (data.readBit()) { // b_ajoc + if (!data.readBit()) { // b_static_dmx + data.skipBits(4); // n_dmx_objects_minus1 + } + ac4Presentation.numOfUmxObjects = data.readBits(6) + 1; // n_umx_objects_minus1 + } + data.skipBits(4); // objects_assignment_mask + } + } + + if (data.readBit()) { // b_content_type + data.skipBits(3); // content_classifier + + if (data.readBit()) { // b_language_indicator + skipDsiLanguage(data); + } + } + } + + /** + * Skips the language information fields in an AC-4 DSI bit stream according to TS 103 190-1 + * section 4.3.3.8.7. Modifies the reading position of {@code data} to be just after the language + * fields. + * + * @param data The {@link ParsableBitArray} containing the AC-4 DSI, positioned at the start of + * the language tag field. + * @throws ParserException If the language tag length is invalid. + */ + private static void skipDsiLanguage(ParsableBitArray data) throws ParserException { + int languageTagBytesNumber = data.readBits(6); // n_language_tag_bytes + if (languageTagBytesNumber < 2 || languageTagBytesNumber > 42) { + throw ParserException.createForUnsupportedContainerFeature( + String.format( + "Invalid language tag bytes number: %d. Must be between 2 and 42.", + languageTagBytesNumber)); + } + // Can't use readBytes() since it is not byte-aligned here. + data.skipBits(languageTagBytesNumber * C.BITS_PER_BYTE); + } + + /** + * Skips the bitrate information fields in an AC-4 DSI bit stream. The reading position of {@code + * data} will be modified to be just after the bitrate fields, if sufficient bits remain. + * + * @param data The {@link ParsableBitArray} containing the AC-4 DSI, positioned at the start of + * the bitrate information. + * @return {@code true} if the bitrate fields were successfully skipped or {@code false} if there + * were insufficient bits remaining in {@code data}. + */ + private static boolean skipDsiBitrate(ParsableBitArray data) { + int totalBitsToSkip = 2 + 32 + 32; // bit_rate_mode, bit_rate, bit_rate_precision + if (data.bitsLeft() < totalBitsToSkip) { + return false; + } + data.skipBits(totalBitsToSkip); + return true; + } + + private static int getAdjustedChannelCount( + @ChannelMode int channelMode, boolean hasBackChannels, int topChannelPairs) { + int channelCount = getChannelCountFromChannelMode(channelMode); + if (channelMode == CHANNEL_MODE_7_0_4 + || channelMode == CHANNEL_MODE_7_1_4 + || channelMode == CHANNEL_MODE_9_0_4 + || channelMode == CHANNEL_MODE_9_1_4) { + + if (!hasBackChannels) { + channelCount -= 2; + } + + switch (topChannelPairs) { + case 0: + channelCount -= 4; + break; + case 1: + channelCount -= 2; + break; + default: + break; + } + } + return channelCount; + } + + private static int getChannelCountFromChannelMode(@ChannelMode int channelMode) { + switch (channelMode) { + case CHANNEL_MODE_MONO: + return 1; + case CHANNEL_MODE_STEREO: + return 2; + case CHANNEL_MODE_3_0: + return 3; + case CHANNEL_MODE_5_0: + return 5; + case CHANNEL_MODE_5_1: + return 6; + case CHANNEL_MODE_7_0_34: + case CHANNEL_MODE_7_0_52: + case CHANNEL_MODE_7_0_322: + return 7; + case CHANNEL_MODE_7_1_34: + case CHANNEL_MODE_7_1_52: + case CHANNEL_MODE_7_1_322: + return 8; + case CHANNEL_MODE_7_0_4: + return 11; + case CHANNEL_MODE_7_1_4: + return 12; + case CHANNEL_MODE_9_0_4: + return 13; + case CHANNEL_MODE_9_1_4: + return 14; + case CHANNEL_MODE_22_2: + return 24; + default: + return -1; + } + } + /** * Returns AC-4 format information given {@code data} containing a syncframe. The reading position * of {@code data} will be modified. @@ -255,5 +748,24 @@ private static int readVariableBits(ParsableBitArray data, int bitsPerRead) { return value; } + /** Holds AC-4 presentation information. */ + private static final class Ac4Presentation { + public boolean isChannelCoded; + public @ChannelMode int channelMode; + public int numOfUmxObjects; + public boolean hasBackChannels; + public int topChannelPairs; + public int level; + + private Ac4Presentation() { + isChannelCoded = true; + channelMode = CHANNEL_MODE_UNKNOWN; + numOfUmxObjects = -1; + hasBackChannels = true; + topChannelPairs = 2; + level = 0; + } + } + private Ac4Util() {} } diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/Mp4ExtractorParameterizedTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/Mp4ExtractorParameterizedTest.java index 528a62d337..dbb1e82f81 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/Mp4ExtractorParameterizedTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/Mp4ExtractorParameterizedTest.java @@ -107,6 +107,11 @@ public void mp4SampleWithAc4Track() throws Exception { assertExtractorBehavior("media/mp4/sample_ac4.mp4"); } + @Test + public void mp4SampleWithAc4Level4Track() throws Exception { + assertExtractorBehavior("media/mp4/sample_ac4_level4.mp4"); + } + @Test public void mp4SampleWithEac3Track() throws Exception { assertExtractorBehavior("media/mp4/sample_eac3.mp4"); diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.0.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.0.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.1.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.1.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.2.dump new file mode 100644 index 0000000000..5f9737eed8 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.2.dump @@ -0,0 +1,60 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 81350 + sample count = 10 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 1: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 2: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 3: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 4: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 5: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 6: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 7: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 8: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 9: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.3.dump new file mode 100644 index 0000000000..5f9737eed8 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.3.dump @@ -0,0 +1,60 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 81350 + sample count = 10 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 1: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 2: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 3: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 4: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 5: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 6: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 7: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 8: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 9: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.0.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.0.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.1.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.1.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.2.dump new file mode 100644 index 0000000000..5f9737eed8 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.2.dump @@ -0,0 +1,60 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 81350 + sample count = 10 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 1: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 2: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 3: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 4: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 5: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 6: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 7: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 8: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 9: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.3.dump new file mode 100644 index 0000000000..5f9737eed8 --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.3.dump @@ -0,0 +1,60 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 81350 + sample count = 10 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 1: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 2: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 3: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 4: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 5: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 6: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 7: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 8: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 9: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.unknown_length.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.reading_within_gop_sample_dependencies.unknown_length.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.unknown_length.dump new file mode 100644 index 0000000000..90cd9d402c --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4.mp4.unknown_length.dump @@ -0,0 +1,100 @@ +seekMap: + isSeekable = true + duration = 853333 + getPosition(0) = [[timeUs=0, position=665]] + getPosition(1) = [[timeUs=1, position=665]] + getPosition(426666) = [[timeUs=426666, position=81945]] + getPosition(853333) = [[timeUs=853333, position=81945]] +numberOfTracks = 1 +track 0: + total output bytes = 162700 + sample count = 20 + format 0: + id = 1 + sampleMimeType = audio/ac4 + maxInputSize = 8158 + channelCount = 21 + sampleRate = 48000 + language = und + metadata = entries=[Mp4Timestamp: creation time=3785281997, modification time=3785281997, timescale=600] + sample 0: + time = 0 + flags = 1 + data = length 8135, hash B524F88E + sample 1: + time = 42666 + flags = 0 + data = length 8135, hash FB80C2FB + sample 2: + time = 85333 + flags = 0 + data = length 8135, hash 907C0C31 + sample 3: + time = 128000 + flags = 0 + data = length 8135, hash FDFBD32B + sample 4: + time = 170666 + flags = 0 + data = length 8135, hash 6CAF0549 + sample 5: + time = 213333 + flags = 0 + data = length 8135, hash F5CA1C9A + sample 6: + time = 256000 + flags = 0 + data = length 8135, hash B1B5160D + sample 7: + time = 298666 + flags = 0 + data = length 8135, hash 9E923B3F + sample 8: + time = 341333 + flags = 0 + data = length 8135, hash B1C0BB1F + sample 9: + time = 384000 + flags = 0 + data = length 8135, hash 56F65A03 + sample 10: + time = 426666 + flags = 1 + data = length 8135, hash D07FA9A1 + sample 11: + time = 469333 + flags = 0 + data = length 8135, hash EF26FDDE + sample 12: + time = 512000 + flags = 0 + data = length 8135, hash 8946EEEB + sample 13: + time = 554666 + flags = 0 + data = length 8135, hash AC2E4C99 + sample 14: + time = 597333 + flags = 0 + data = length 8135, hash B63A1D8 + sample 15: + time = 640000 + flags = 0 + data = length 8135, hash 23119F0F + sample 16: + time = 682666 + flags = 0 + data = length 8135, hash 507972CA + sample 17: + time = 725333 + flags = 0 + data = length 8135, hash E574BC00 + sample 18: + time = 768000 + flags = 0 + data = length 8135, hash 52F482FA + sample 19: + time = 810666 + flags = 536870912 + data = length 8135, hash C1A7B518 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4.mp4 b/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4.mp4 new file mode 100644 index 0000000000..7e110d301e Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4.mp4 differ