Merge pull request #1265 from DolbyLaboratories:dlb/ac4-level4/dev_new2

PiperOrigin-RevId: 696157037
This commit is contained in:
Copybara-Service 2024-11-13 09:08:22 -08:00
commit 74611bbdc0
16 changed files with 1402 additions and 11 deletions

View File

@ -29,6 +29,8 @@
* Add support for transmuxing into alternative backwards compatible
formats.
* Extractors:
* Add AC-4 Level-4 ISO base media file format support
([#1265](https://github.com/androidx/media/pull/1265)).
* DataSource:
* `DataSourceContractTest`: Assert that `DataSource.getUri()` and
`getResponseHeaders()` return their 'open' value after a failed call to

View File

@ -2251,6 +2251,24 @@ public final class Util {
}
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;
}

View File

@ -4293,18 +4293,22 @@ public class DefaultTrackSelector extends MappingTrackSelector
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;
}

View File

@ -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 @@ public final class Ac4Util {
}
}
/**
* 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 @@ public final class Ac4Util {
/**
* 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 @@ public final class Ac4Util {
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() {}
}

View File

@ -107,6 +107,11 @@ public final class Mp4ExtractorParameterizedTest {
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");

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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

View File

@ -0,0 +1,61 @@
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
track duration = 853333
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

View File

@ -0,0 +1,61 @@
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
track duration = 853333
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

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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

View File

@ -0,0 +1,61 @@
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
track duration = 853333
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

View File

@ -0,0 +1,61 @@
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
track duration = 853333
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

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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

View File

@ -0,0 +1,101 @@
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
track duration = 853333
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