Merge "stagefright: Add AC4 support in MediaExtractor for MP4/TS"

gugelfrei
TreeHugger Robot 6 years ago committed by Android (Google) Code Review
commit 7fa505e059

@ -647,7 +647,7 @@ static void dumpCodecProfiles(bool queryDecoders) {
MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
MEDIA_MIMETYPE_VIDEO_DOLBY_VISION
MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4
};
const char *codecType = queryDecoders? "decoder" : "encoder";

@ -0,0 +1,624 @@
/*
* Copyright (C) 2018 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.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "AC4Parser"
#include <inttypes.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include "AC4Parser.h"
#define BOOLSTR(a) ((a)?"true":"false")
#define BYTE_ALIGN mBitReader.skipBits(mBitReader.numBitsLeft() % 8)
#define CHECK_BITS_LEFT(n) if (mBitReader.numBitsLeft() < n) {return false;}
namespace android {
AC4Parser::AC4Parser() {
}
AC4DSIParser::AC4DSIParser(ABitReader &br)
: mBitReader(br){
mDSISize = mBitReader.numBitsLeft();
}
// ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
static const char *ChannelModes[] = {
"mono",
"stereo",
"3.0",
"5.0",
"5.1",
"7.0 (3/4/0)",
"7.1 (3/4/0.1)",
"7.0 (5/2/0)",
"7.1 (5/2/0.1)",
"7.0 (3/2/2)",
"7.1 (3/2/2.1)",
"7.0.4",
"7.1.4",
"9.0.4",
"9.1.4",
"22.2"
};
static const char* ContentClassifier[] = {
"Complete Main",
"Music and Effects",
"Visually Impaired",
"Hearing Impaired",
"Dialog",
"Commentary",
"Emergency",
"Voice Over"
};
bool AC4DSIParser::parseLanguageTag(uint32_t presentationID, uint32_t substreamID){
CHECK_BITS_LEFT(6);
uint32_t n_language_tag_bytes = mBitReader.getBits(6);
if (n_language_tag_bytes < 2 || n_language_tag_bytes >= 42) {
return false;
}
CHECK_BITS_LEFT(n_language_tag_bytes * 8);
char language_tag_bytes[42]; // TS 103 190 part 1 4.3.3.8.7
for (uint32_t i = 0; i < n_language_tag_bytes; i++) {
language_tag_bytes[i] = (char)mBitReader.getBits(8);
}
language_tag_bytes[n_language_tag_bytes] = 0;
ALOGV("%u.%u: language_tag = %s\n", presentationID, substreamID, language_tag_bytes);
std::string language(language_tag_bytes, n_language_tag_bytes);
mPresentations[presentationID].mLanguage = language;
return true;
}
// TS 103 190-1 v1.2.1 E.5 and TS 103 190-2 v1.1.1 E.9
bool AC4DSIParser::parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID){
CHECK_BITS_LEFT(5);
uint32_t channel_mode = mBitReader.getBits(5);
CHECK_BITS_LEFT(2);
uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
CHECK_BITS_LEFT(1);
bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u: channel_mode = %u (%s)\n", presentationID, substreamID, channel_mode,
channel_mode < NELEM(ChannelModes) ? ChannelModes[channel_mode] : "reserved");
ALOGV("%u.%u: dsi_sf_multiplier = %u\n", presentationID,
substreamID, dsi_sf_multiplier);
ALOGV("%u.%u: b_substream_bitrate_indicator = %s\n", presentationID,
substreamID, BOOLSTR(b_substream_bitrate_indicator));
if (b_substream_bitrate_indicator) {
CHECK_BITS_LEFT(5);
uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
ALOGV("%u.%u: substream_bitrate_indicator = %u\n", presentationID, substreamID,
substream_bitrate_indicator);
}
if (channel_mode >= 7 && channel_mode <= 10) {
CHECK_BITS_LEFT(1);
uint32_t add_ch_base = mBitReader.getBits(1);
ALOGV("%u.%u: add_ch_base = %u\n", presentationID, substreamID, add_ch_base);
}
CHECK_BITS_LEFT(1);
bool b_content_type = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u: b_content_type = %s\n", presentationID, substreamID, BOOLSTR(b_content_type));
if (b_content_type) {
CHECK_BITS_LEFT(3);
uint32_t content_classifier = mBitReader.getBits(3);
ALOGV("%u.%u: content_classifier = %u (%s)\n", presentationID, substreamID,
content_classifier, ContentClassifier[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 (mPresentations[presentationID].mChannelMode == -1 &&
(content_classifier == 0 || content_classifier == 1)) {
mPresentations[presentationID].mChannelMode = channel_mode;
}
mPresentations[presentationID].mContentClassifier = content_classifier;
CHECK_BITS_LEFT(1);
bool b_language_indicator = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, substreamID,
BOOLSTR(b_language_indicator));
if (b_language_indicator) {
if (!parseLanguageTag(presentationID, substreamID)) {
return false;
}
}
}
return true;
}
// ETSI TS 103 190-2 v1.1.1 section E.11
bool AC4DSIParser::parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID)
{
CHECK_BITS_LEFT(1);
bool b_substreams_present = (mBitReader.getBits(1) == 1);
CHECK_BITS_LEFT(1);
bool b_hsf_ext = (mBitReader.getBits(1) == 1);
CHECK_BITS_LEFT(1);
bool b_channel_coded = (mBitReader.getBits(1) == 1);
CHECK_BITS_LEFT(8);
uint32_t n_substreams = mBitReader.getBits(8);
ALOGV("%u.%u: b_substreams_present = %s\n", presentationID, groupID,
BOOLSTR(b_substreams_present));
ALOGV("%u.%u: b_hsf_ext = %s\n", presentationID, groupID, BOOLSTR(b_hsf_ext));
ALOGV("%u.%u: b_channel_coded = %s\n", presentationID, groupID, BOOLSTR(b_channel_coded));
ALOGV("%u.%u: n_substreams = %u\n", presentationID, groupID, n_substreams);
for (uint32_t i = 0; i < n_substreams; i++) {
CHECK_BITS_LEFT(2);
uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
CHECK_BITS_LEFT(1);
bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u.%u: dsi_sf_multiplier = %u\n", presentationID, groupID, i, dsi_sf_multiplier);
ALOGV("%u.%u.%u: b_substream_bitrate_indicator = %s\n", presentationID, groupID, i,
BOOLSTR(b_substream_bitrate_indicator));
if (b_substream_bitrate_indicator) {
CHECK_BITS_LEFT(5);
uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
ALOGV("%u.%u.%u: substream_bitrate_indicator = %u\n", presentationID, groupID, i,
substream_bitrate_indicator);
}
if (b_channel_coded) {
CHECK_BITS_LEFT(24);
uint32_t dsi_substream_channel_mask = mBitReader.getBits(24);
ALOGV("%u.%u.%u: dsi_substream_channel_mask = 0x%06x\n", presentationID, groupID, i,
dsi_substream_channel_mask);
} else {
CHECK_BITS_LEFT(1);
bool b_ajoc = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u.%u: b_ajoc = %s\n", presentationID, groupID, i, BOOLSTR(b_ajoc));
if (b_ajoc) {
CHECK_BITS_LEFT(1);
bool b_static_dmx = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u.%u: b_static_dmx = %s\n", presentationID, groupID, i,
BOOLSTR(b_static_dmx));
if (!b_static_dmx) {
CHECK_BITS_LEFT(4);
uint32_t n_dmx_objects_minus1 = mBitReader.getBits(4);
ALOGV("%u.%u.%u: n_dmx_objects_minus1 = %u\n", presentationID, groupID, i,
n_dmx_objects_minus1);
}
CHECK_BITS_LEFT(6);
uint32_t n_umx_objects_minus1 = mBitReader.getBits(6);
ALOGV("%u.%u.%u: n_umx_objects_minus1 = %u\n", presentationID, groupID, i,
n_umx_objects_minus1);
}
CHECK_BITS_LEFT(4);
mBitReader.skipBits(4); // objects_assignment_mask
}
}
CHECK_BITS_LEFT(1);
bool b_content_type = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u: b_content_type = %s\n", presentationID, groupID, BOOLSTR(b_content_type));
if (b_content_type) {
CHECK_BITS_LEFT(3);
uint32_t content_classifier = mBitReader.getBits(3);
ALOGV("%u.%u: content_classifier = %s (%u)\n", presentationID, groupID,
ContentClassifier[content_classifier], content_classifier);
mPresentations[presentationID].mContentClassifier = content_classifier;
CHECK_BITS_LEFT(1);
bool b_language_indicator = (mBitReader.getBits(1) == 1);
ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, groupID,
BOOLSTR(b_language_indicator));
if (b_language_indicator) {
if (!parseLanguageTag(presentationID, groupID)) {
return false;
}
}
}
return true;
}
bool AC4DSIParser::parseBitrateDsi() {
CHECK_BITS_LEFT(2 + 32 + 32);
mBitReader.skipBits(2); // bit_rate_mode
mBitReader.skipBits(32); // bit_rate
mBitReader.skipBits(32); // bit_rate_precision
return true;
}
// TS 103 190-1 section E.4 (ac4_dsi) and TS 103 190-2 section E.6 (ac4_dsi_v1)
bool AC4DSIParser::parse() {
CHECK_BITS_LEFT(3);
uint32_t ac4_dsi_version = mBitReader.getBits(3);
if (ac4_dsi_version > 1) {
ALOGE("error while parsing ac-4 dsi: only versions 0 and 1 are supported");
return false;
}
CHECK_BITS_LEFT(7 + 1 + 4 + 9);
uint32_t bitstream_version = mBitReader.getBits(7);
mBitReader.skipBits(1); // fs_index
mBitReader.skipBits(4); // frame_rate_index
uint32_t n_presentations = mBitReader.getBits(9);
int32_t short_program_id = -1;
if (bitstream_version > 1) {
if (ac4_dsi_version == 0){
ALOGE("invalid ac4 dsi");
return false;
}
CHECK_BITS_LEFT(1);
bool b_program_id = (mBitReader.getBits(1) == 1);
if (b_program_id) {
CHECK_BITS_LEFT(16 + 1);
short_program_id = mBitReader.getBits(16);
bool b_uuid = (mBitReader.getBits(1) == 1);
if (b_uuid) {
const uint32_t kAC4UUIDSizeInBytes = 16;
char program_uuid[kAC4UUIDSizeInBytes];
CHECK_BITS_LEFT(kAC4UUIDSizeInBytes * 8);
for (uint32_t i = 0; i < kAC4UUIDSizeInBytes; i++) {
program_uuid[i] = (char)(mBitReader.getBits(8));
}
ALOGV("UUID = %s", program_uuid);
}
}
}
if (ac4_dsi_version == 1) {
if (!parseBitrateDsi()) {
return false;
}
BYTE_ALIGN;
}
for (uint32_t presentation = 0; presentation < n_presentations; presentation++) {
mPresentations[presentation].mProgramID = short_program_id;
// known as b_single_substream in ac4_dsi_version 0
bool b_single_substream_group = false;
uint32_t presentation_config = 0, presentation_version = 0;
uint32_t pres_bytes = 0;
if (ac4_dsi_version == 0) {
CHECK_BITS_LEFT(1 + 5 + 5);
b_single_substream_group = (mBitReader.getBits(1) == 1);
presentation_config = mBitReader.getBits(5);
presentation_version = mBitReader.getBits(5);
} else if (ac4_dsi_version == 1) {
CHECK_BITS_LEFT(8 + 8);
presentation_version = mBitReader.getBits(8);
pres_bytes = mBitReader.getBits(8);
if (pres_bytes == 0xff) {
CHECK_BITS_LEFT(16);
pres_bytes += mBitReader.getBits(16);
}
ALOGV("%u: pres_bytes = %u\n", presentation, pres_bytes);
if (presentation_version > 1) {
CHECK_BITS_LEFT(pres_bytes * 8);
mBitReader.skipBits(pres_bytes * 8);
continue;
}
// ac4_presentation_v0_dsi() and ac4_presentation_v1_dsi() both
// start with a presentation_config of 5 bits
CHECK_BITS_LEFT(5);
presentation_config = mBitReader.getBits(5);
b_single_substream_group = (presentation_config == 0x1f);
}
static const char *PresentationConfig[] = {
"Music&Effects + Dialog",
"Main + DE",
"Main + Associate",
"Music&Effects + Dialog + Associate",
"Main + DE + Associate",
"Arbitrary substream groups",
"EMDF only"
};
ALOGV("%u: b_single_substream/group = %s\n", presentation,
BOOLSTR(b_single_substream_group));
ALOGV("%u: presentation_version = %u\n", presentation, presentation_version);
ALOGV("%u: presentation_config = %u (%s)\n", presentation, presentation_config,
(presentation_config >= NELEM(PresentationConfig) ?
"reserved" : PresentationConfig[presentation_config]));
/* record a marker, less the size of the presentation_config */
uint64_t start = (mDSISize - mBitReader.numBitsLeft()) / 8;
bool b_add_emdf_substreams = false;
if (!b_single_substream_group && presentation_config == 6) {
b_add_emdf_substreams = true;
ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, BOOLSTR(b_add_emdf_substreams));
} else {
CHECK_BITS_LEFT(3 + 1);
uint32_t mdcompat = mBitReader.getBits(3);
ALOGV("%u: mdcompat = %d\n", presentation, mdcompat);
bool b_presentation_group_index = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_presentation_group_index = %s\n", presentation,
BOOLSTR(b_presentation_group_index));
if (b_presentation_group_index) {
CHECK_BITS_LEFT(5);
mPresentations[presentation].mGroupIndex = mBitReader.getBits(5);
ALOGV("%u: presentation_group_index = %d\n", presentation,
mPresentations[presentation].mGroupIndex);
}
CHECK_BITS_LEFT(2);
uint32_t dsi_frame_rate_multiply_info = mBitReader.getBits(2);
ALOGV("%u: dsi_frame_rate_multiply_info = %d\n", presentation,
dsi_frame_rate_multiply_info);
if (ac4_dsi_version == 1 && presentation_version == 1) {
CHECK_BITS_LEFT(2);
uint32_t dsi_frame_rate_fraction_info = mBitReader.getBits(2);
ALOGV("%u: dsi_frame_rate_fraction_info = %d\n", presentation,
dsi_frame_rate_fraction_info);
}
CHECK_BITS_LEFT(5 + 10);
uint32_t presentation_emdf_version = mBitReader.getBits(5);
uint32_t presentation_key_id = mBitReader.getBits(10);
ALOGV("%u: presentation_emdf_version = %d\n", presentation, presentation_emdf_version);
ALOGV("%u: presentation_key_id = %d\n", presentation, presentation_key_id);
if (ac4_dsi_version == 1) {
bool b_presentation_channel_coded = false;
if (presentation_version == 0) {
b_presentation_channel_coded = true;
} else {
CHECK_BITS_LEFT(1);
b_presentation_channel_coded = (mBitReader.getBits(1) == 1);
}
ALOGV("%u: b_presentation_channel_coded = %s\n", presentation,
BOOLSTR(b_presentation_channel_coded));
if (b_presentation_channel_coded) {
if (presentation_version == 1) {
CHECK_BITS_LEFT(5);
uint32_t dsi_presentation_ch_mode = mBitReader.getBits(5);
mPresentations[presentation].mChannelMode = dsi_presentation_ch_mode;
ALOGV("%u: dsi_presentation_ch_mode = %d (%s)\n", presentation,
dsi_presentation_ch_mode,
dsi_presentation_ch_mode < NELEM(ChannelModes) ?
ChannelModes[dsi_presentation_ch_mode] : "reserved");
if (dsi_presentation_ch_mode >= 11 && dsi_presentation_ch_mode <= 14) {
CHECK_BITS_LEFT(1 + 2);
uint32_t pres_b_4_back_channels_present = mBitReader.getBits(1);
uint32_t pres_top_channel_pairs = mBitReader.getBits(2);
ALOGV("%u: pres_b_4_back_channels_present = %s\n", presentation,
BOOLSTR(pres_b_4_back_channels_present));
ALOGV("%u: pres_top_channel_pairs = %d\n", presentation,
pres_top_channel_pairs);
}
}
// presentation_channel_mask in ac4_presentation_v0_dsi()
CHECK_BITS_LEFT(24);
uint32_t presentation_channel_mask_v1 = mBitReader.getBits(24);
ALOGV("%u: presentation_channel_mask_v1 = 0x%06x\n", presentation,
presentation_channel_mask_v1);
}
if (presentation_version == 1) {
CHECK_BITS_LEFT(1);
bool b_presentation_core_differs = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_presentation_core_differs = %s\n", presentation,
BOOLSTR(b_presentation_core_differs));
if (b_presentation_core_differs) {
CHECK_BITS_LEFT(1);
bool b_presentation_core_channel_coded = (mBitReader.getBits(1) == 1);
if (b_presentation_core_channel_coded) {
CHECK_BITS_LEFT(2);
mBitReader.skipBits(2); // dsi_presentation_channel_mode_core
}
}
CHECK_BITS_LEFT(1);
bool b_presentation_filter = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_presentation_filter = %s\n", presentation,
BOOLSTR(b_presentation_filter));
if (b_presentation_filter) {
CHECK_BITS_LEFT(1 + 8);
bool b_enable_presentation = (mBitReader.getBits(1) == 1);
if (!b_enable_presentation) {
mPresentations[presentation].mEnabled = false;
}
ALOGV("%u: b_enable_presentation = %s\n", presentation,
BOOLSTR(b_enable_presentation));
uint32_t n_filter_bytes = mBitReader.getBits(8);
CHECK_BITS_LEFT(n_filter_bytes * 8);
for (uint32_t i = 0; i < n_filter_bytes; i++) {
mBitReader.skipBits(8); // filter_data
}
}
}
} /* ac4_dsi_version == 1 */
if (b_single_substream_group) {
if (presentation_version == 0) {
if (!parseSubstreamDSI(presentation, 0)) {
return false;
}
} else {
if (!parseSubstreamGroupDSI(presentation, 0)) {
return false;
}
}
} else {
if (ac4_dsi_version == 1) {
CHECK_BITS_LEFT(1);
bool b_multi_pid = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_multi_pid = %s\n", presentation, BOOLSTR(b_multi_pid));
} else {
CHECK_BITS_LEFT(1);
bool b_hsf_ext = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_hsf_ext = %s\n", presentation, BOOLSTR(b_hsf_ext));
}
switch (presentation_config) {
case 0:
case 1:
case 2:
if (presentation_version == 0) {
if (!parseSubstreamDSI(presentation, 0)) {
return false;
}
if (!parseSubstreamDSI(presentation, 1)) {
return false;
}
} else {
if (!parseSubstreamGroupDSI(presentation, 0)) {
return false;
}
if (!parseSubstreamGroupDSI(presentation, 1)) {
return false;
}
}
break;
case 3:
case 4:
if (presentation_version == 0) {
if (!parseSubstreamDSI(presentation, 0)) {
return false;
}
if (!parseSubstreamDSI(presentation, 1)) {
return false;
}
if (!parseSubstreamDSI(presentation, 2)) {
return false;
}
} else {
if (!parseSubstreamGroupDSI(presentation, 0)) {
return false;
}
if (!parseSubstreamGroupDSI(presentation, 1)) {
return false;
}
if (!parseSubstreamGroupDSI(presentation, 2)) {
return false;
}
}
break;
case 5:
if (presentation_version == 0) {
if (!parseSubstreamDSI(presentation, 0)) {
return false;
}
} else {
CHECK_BITS_LEFT(3);
uint32_t n_substream_groups_minus2 = mBitReader.getBits(3);
ALOGV("%u: n_substream_groups_minus2 = %d\n", presentation,
n_substream_groups_minus2);
for (uint32_t sg = 0; sg < n_substream_groups_minus2 + 2; sg++) {
if (!parseSubstreamGroupDSI(presentation, sg)) {
return false;
}
}
}
break;
default:
CHECK_BITS_LEFT(7);
uint32_t n_skip_bytes = mBitReader.getBits(7);
CHECK_BITS_LEFT(n_skip_bytes * 8)
for (uint32_t j = 0; j < n_skip_bytes; j++) {
mBitReader.getBits(8);
}
break;
}
CHECK_BITS_LEFT(1 + 1);
bool b_pre_virtualized = (mBitReader.getBits(1) == 1);
mPresentations[presentation].mPreVirtualized = b_pre_virtualized;
b_add_emdf_substreams = (mBitReader.getBits(1) == 1);
ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized));
ALOGV("%u: b_add_emdf_substreams = %s\n", presentation,
BOOLSTR(b_add_emdf_substreams));
}
}
if (b_add_emdf_substreams) {
CHECK_BITS_LEFT(7);
uint32_t n_add_emdf_substreams = mBitReader.getBits(7);
for (uint32_t j = 0; j < n_add_emdf_substreams; j++) {
CHECK_BITS_LEFT(5 + 10);
uint32_t substream_emdf_version = mBitReader.getBits(5);
uint32_t substream_key_id = mBitReader.getBits(10);
ALOGV("%u: emdf_substream[%d]: version=%d, key_id=%d\n", presentation, j,
substream_emdf_version, substream_key_id);
}
}
bool b_presentation_bitrate_info = false;
if (presentation_version > 0) {
CHECK_BITS_LEFT(1);
b_presentation_bitrate_info = (mBitReader.getBits(1) == 1);
}
ALOGV("b_presentation_bitrate_info = %s\n", BOOLSTR(b_presentation_bitrate_info));
if (b_presentation_bitrate_info) {
if (!parseBitrateDsi()) {
return false;
}
}
if (presentation_version > 0) {
CHECK_BITS_LEFT(1);
bool b_alternative = (mBitReader.getBits(1) == 1);
ALOGV("b_alternative = %s\n", BOOLSTR(b_alternative));
if (b_alternative) {
BYTE_ALIGN;
CHECK_BITS_LEFT(16);
uint32_t name_len = mBitReader.getBits(16);
char* presentation_name = new char[name_len+1];
CHECK_BITS_LEFT(name_len * 8);
for (uint32_t i = 0; i < name_len; i++) {
presentation_name[i] = (char)(mBitReader.getBits(8));
}
presentation_name[name_len] = '\0';
std::string description(presentation_name, name_len);
mPresentations[presentation].mDescription = description;
CHECK_BITS_LEFT(5);
uint32_t n_targets = mBitReader.getBits(5);
CHECK_BITS_LEFT(n_targets * (3 + 8));
for (uint32_t i = 0; i < n_targets; i++){
mBitReader.skipBits(3); // target_md_compat
mBitReader.skipBits(8); // target_device_category
}
}
}
BYTE_ALIGN;
if (ac4_dsi_version == 1) {
uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8;
if (mBitReader.numBitsLeft() % 8 != 0) {
end += 1;
}
uint64_t presentation_bytes = end - start;
uint64_t skip_bytes = pres_bytes - presentation_bytes;
ALOGV("skipping = %" PRIu64 " bytes", skip_bytes);
CHECK_BITS_LEFT(skip_bytes * 8);
mBitReader.skipBits(skip_bytes * 8);
}
// we should know this or something is probably wrong
// with the bitstream (or we don't support it)
if (mPresentations[presentation].mChannelMode == -1){
ALOGE("could not determing channel mode of presentation %d", presentation);
return false;
}
} /* each presentation */
return true;
}
};

@ -0,0 +1,109 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef AC4_PARSER_H_
#define AC4_PARSER_H_
#include <cstdint>
#include <map>
#include <string>
#include <media/stagefright/foundation/ABitReader.h>
namespace android {
class AC4Parser {
public:
AC4Parser();
virtual ~AC4Parser() { }
virtual bool parse() = 0;
struct AC4Presentation {
int32_t mChannelMode = -1;
int32_t mProgramID = -1;
int32_t mGroupIndex = -1;
// TS 103 190-1 v1.2.1 4.3.3.8.1
enum ContentClassifiers {
kCompleteMain,
kMusicAndEffects,
kVisuallyImpaired,
kHearingImpaired,
kDialog,
kCommentary,
kEmergency,
kVoiceOver
};
uint32_t mContentClassifier = kCompleteMain;
// ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
enum InputChannelMode {
kChannelMode_Mono,
kChannelMode_Stereo,
kChannelMode_3_0,
kChannelMode_5_0,
kChannelMode_5_1,
kChannelMode_7_0_34,
kChannelMode_7_1_34,
kChannelMode_7_0_52,
kChannelMode_7_1_52,
kChannelMode_7_0_322,
kChannelMode_7_1_322,
kChannelMode_7_0_4,
kChannelMode_7_1_4,
kChannelMode_9_0_4,
kChannelMode_9_1_4,
kChannelMode_22_2,
kChannelMode_Reserved,
};
bool mHasDialogEnhancements = false;
bool mPreVirtualized = false;
bool mEnabled = true;
std::string mLanguage;
std::string mDescription;
};
typedef std::map<uint32_t, AC4Presentation> AC4Presentations;
const AC4Presentations& getPresentations() const { return mPresentations; }
protected:
AC4Presentations mPresentations;
};
class AC4DSIParser: public AC4Parser {
public:
explicit AC4DSIParser(ABitReader &br);
virtual ~AC4DSIParser() { }
bool parse();
private:
bool parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID);
bool parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID);
bool parseLanguageTag(uint32_t presentationID, uint32_t substreamID);
bool parseBitrateDsi();
uint64_t mDSISize;
ABitReader& mBitReader;
};
};
#endif // AC4_PARSER_H_

@ -2,6 +2,7 @@ cc_defaults {
name: "libmp4extractor_defaults",
srcs: [
"AC4Parser.cpp",
"ItemTable.cpp",
"MPEG4Extractor.cpp",
"SampleIterator.cpp",

@ -26,6 +26,7 @@
#include <utils/Log.h>
#include "AC4Parser.h"
#include "MPEG4Extractor.h"
#include "SampleTable.h"
#include "ItemTable.h"
@ -125,6 +126,8 @@ private:
bool mIsAVC;
bool mIsHEVC;
bool mIsAC4;
size_t mNALLengthSize;
bool mStarted;
@ -324,6 +327,8 @@ static const char *FourCC2MIME(uint32_t fourcc) {
case FOURCC('h', 'v', 'c', '1'):
case FOURCC('h', 'e', 'v', '1'):
return MEDIA_MIMETYPE_VIDEO_HEVC;
case FOURCC('a', 'c', '-', '4'):
return MEDIA_MIMETYPE_AUDIO_AC4;
default:
ALOGW("Unknown fourcc: %c%c%c%c",
(fourcc >> 24) & 0xff,
@ -2436,6 +2441,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return parseAC3SampleEntry(data_offset);
}
case FOURCC('a', 'c', '-', '4'):
{
*offset += chunk_size;
return parseAC4SampleEntry(data_offset);
}
case FOURCC('f', 't', 'y', 'p'):
{
if (chunk_data_size < 8 || depth != 0) {
@ -2507,6 +2518,84 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return OK;
}
status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) {
// skip 16 bytes:
// + 6-byte reserved,
// + 2-byte data reference index,
// + 8-byte reserved
offset += 16;
uint16_t channelCount;
if (!mDataSource->getUInt16(offset, &channelCount)) {
ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count");
return ERROR_MALFORMED;
}
// skip 8 bytes:
// + 2-byte channelCount,
// + 2-byte sample size,
// + 4-byte reserved
offset += 8;
uint16_t sampleRate;
if (!mDataSource->getUInt16(offset, &sampleRate)) {
ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate");
return ERROR_MALFORMED;
}
// skip 4 bytes:
// + 2-byte sampleRate,
// + 2-byte reserved
offset += 4;
if (mLastTrack == NULL) {
return ERROR_MALFORMED;
}
mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
return parseAC4SpecificBox(offset);
}
status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
uint32_t size;
// + 4-byte size
// + 4-byte type
// + 3-byte payload
const uint32_t kAC4MinimumBoxSize = 4 + 4 + 3;
if (!mDataSource->getUInt32(offset, &size) || size < kAC4MinimumBoxSize) {
ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read specific box size");
return ERROR_MALFORMED;
}
// + 4-byte size
offset += 4;
uint32_t type;
if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '4')) {
ALOGE("MPEG4Extractor: error while reading ac-4 specific block: header not dac4");
return ERROR_MALFORMED;
}
// + 4-byte type
offset += 4;
// at least for AC4 DSI v1 this is big enough
const uint32_t kAC4SpecificBoxPayloadSize = 256;
uint8_t chunk[kAC4SpecificBoxPayloadSize];
ssize_t dsiSize = size - 8; // size of box - size and type fields
if (dsiSize >= (ssize_t)kAC4SpecificBoxPayloadSize ||
mDataSource->readAt(offset, chunk, dsiSize) != dsiSize) {
ALOGE("MPEG4Extractor: error while reading ac-4 specific block: bitstream fields");
return ERROR_MALFORMED;
}
// + size-byte payload
offset += dsiSize;
ABitReader br(chunk, dsiSize);
AC4DSIParser parser(br);
if (!parser.parse()){
ALOGE("MPEG4Extractor: error while parsing ac-4 specific block");
return ERROR_MALFORMED;
}
return OK;
}
status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
// skip 16 bytes:
// + 6-byte reserved,
@ -3857,6 +3946,7 @@ MPEG4Source::MPEG4Source(
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mIsHEVC(false),
mIsAC4(false),
mNALLengthSize(0),
mStarted(false),
mGroup(NULL),
@ -3890,6 +3980,7 @@ MPEG4Source::MPEG4Source(
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
if (mIsAVC) {
uint32_t type;
@ -4830,7 +4921,7 @@ status_t MPEG4Source::read(
}
}
if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) {
if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) {
if (newBuffer) {
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
@ -4862,13 +4953,20 @@ status_t MPEG4Source::read(
++mCurrentSampleIndex;
}
if (!mIsAVC && !mIsHEVC) {
if (!mIsAVC && !mIsHEVC && !mIsAC4) {
*out = mBuffer;
mBuffer = NULL;
return OK;
}
if (mIsAC4) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
}
// Each NAL unit is split up into its constituent fragments and
// each one of them returned in its own buffer.
@ -4906,6 +5004,58 @@ status_t MPEG4Source::read(
*out = clone;
return OK;
} else if (mIsAC4) {
CHECK(mBuffer != NULL);
// Make sure there is enough space to write the sync header and the raw frame
if (mBuffer->range_length() < (7 + size)) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
}
uint8_t *dstData = (uint8_t *)mBuffer->data();
size_t dstOffset = 0;
// Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame
// AC40 sync word, meaning no CRC at the end of the frame
dstData[dstOffset++] = 0xAC;
dstData[dstOffset++] = 0x40;
dstData[dstOffset++] = 0xFF;
dstData[dstOffset++] = 0xFF;
dstData[dstOffset++] = (uint8_t)((size >> 16) & 0xFF);
dstData[dstOffset++] = (uint8_t)((size >> 8) & 0xFF);
dstData[dstOffset++] = (uint8_t)((size >> 0) & 0xFF);
ssize_t numBytesRead = mDataSource->readAt(offset, dstData + dstOffset, size);
if (numBytesRead != (ssize_t)size) {
mBuffer->release();
mBuffer = NULL;
return ERROR_IO;
}
mBuffer->set_range(0, dstOffset + size);
mBuffer->meta_data().clear();
mBuffer->meta_data().setInt64(
kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
mBuffer->meta_data().setInt64(
kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data().setInt64(
kKeyTargetTime, targetSampleTimeUs);
}
if (isSyncSample) {
mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
}
++mCurrentSampleIndex;
*out = mBuffer;
mBuffer = NULL;
return OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
@ -5361,6 +5511,8 @@ status_t MPEG4Source::fragmentedRead(
return OK;
}
return OK;
}
MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(

@ -141,6 +141,8 @@ private:
status_t parseAC3SampleEntry(off64_t offset);
status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate);
status_t parseAC4SampleEntry(off64_t offset);
status_t parseAC4SpecificBox(off64_t offset);
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);

@ -50,6 +50,7 @@ const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts";
const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm";
const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3";
const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3";
const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4";
const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";

@ -52,6 +52,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM;
extern const char *MEDIA_MIMETYPE_AUDIO_AC3;
extern const char *MEDIA_MIMETYPE_AUDIO_EAC3;
extern const char *MEDIA_MIMETYPE_AUDIO_AC4;
extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;

@ -119,6 +119,7 @@ struct ATSParser::Program : public RefBase {
private:
struct StreamInfo {
unsigned mType;
unsigned mTypeExt;
unsigned mPID;
int32_t mCASystemId;
};
@ -145,10 +146,12 @@ struct ATSParser::Stream : public RefBase {
Stream(Program *program,
unsigned elementaryPID,
unsigned streamType,
unsigned streamTypeExt,
unsigned PCR_PID,
int32_t CA_system_ID);
unsigned type() const { return mStreamType; }
unsigned typeExt() const { return mStreamTypeExt; }
unsigned pid() const { return mElementaryPID; }
void setPID(unsigned pid) { mElementaryPID = pid; }
@ -194,6 +197,7 @@ private:
Program *mProgram;
unsigned mElementaryPID;
unsigned mStreamType;
unsigned mStreamTypeExt;
unsigned mPCR_PID;
int32_t mExpectedContinuityCounter;
@ -447,7 +451,7 @@ bool ATSParser::Program::findCADescriptor(
if (descriptor_length > infoLength) {
break;
}
if (descriptor_tag == 9 && descriptor_length >= 4) {
if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) {
found = true;
caDescriptor->mSystemID = br->getBits(16);
caDescriptor->mPID = br->getBits(16) & 0x1fff;
@ -513,37 +517,65 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
// infoBytesRemaining is the number of bytes that make up the
// variable length section of ES_infos. It does not include the
// final CRC.
size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
int32_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
while (infoBytesRemaining >= 5) {
unsigned streamType = br->getBits(8);
ALOGV(" stream_type = 0x%02x", streamType);
StreamInfo info;
info.mType = br->getBits(8);
ALOGV(" stream_type = 0x%02x", info.mType);
MY_LOGV(" reserved = %u", br->getBits(3));
unsigned elementaryPID = br->getBits(13);
ALOGV(" elementary_PID = 0x%04x", elementaryPID);
info.mPID = br->getBits(13);
ALOGV(" elementary_PID = 0x%04x", info.mPID);
MY_LOGV(" reserved = %u", br->getBits(4));
unsigned ES_info_length = br->getBits(12);
ALOGV(" ES_info_length = %u", ES_info_length);
infoBytesRemaining -= 5 + ES_info_length;
CADescriptor streamCA;
bool hasStreamCA = findCADescriptor(br, ES_info_length, &streamCA);
info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX;
bool hasStreamCA = false;
while (ES_info_length > 2 && infoBytesRemaining >= 0) {
unsigned descriptor_tag = br->getBits(8);
ALOGV(" tag = 0x%02x", descriptor_tag);
unsigned descriptor_length = br->getBits(8);
ALOGV(" len = %u", descriptor_length);
ES_info_length -= 2;
if (descriptor_length > ES_info_length) {
return ERROR_MALFORMED;
}
if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) {
hasStreamCA = true;
streamCA.mSystemID = br->getBits(16);
streamCA.mPID = br->getBits(16) & 0x1fff;
ES_info_length -= 4;
streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length - 4);
} else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA &&
descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) {
unsigned descTagExt = br->getBits(8);
ALOGV(" tag_ext = 0x%02x", descTagExt);
if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) {
info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4;
}
ES_info_length -= descriptor_length;
descriptor_length--;
br->skipBits(descriptor_length * 8);
} else {
ES_info_length -= descriptor_length;
br->skipBits(descriptor_length * 8);
}
}
if (hasStreamCA && !mParser->mCasManager->addStream(
mProgramNumber, elementaryPID, streamCA)) {
mProgramNumber, info.mPID, streamCA)) {
return ERROR_MALFORMED;
}
StreamInfo info;
info.mType = streamType;
info.mPID = elementaryPID;
info.mCASystemId = hasProgramCA ? programCA.mSystemID :
hasStreamCA ? streamCA.mSystemID : -1;
infos.push(info);
infoBytesRemaining -= 5 + ES_info_length;
}
if (infoBytesRemaining != 0) {
@ -602,7 +634,7 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
if (index < 0) {
sp<Stream> stream = new Stream(
this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
this, info.mPID, info.mType, info.mTypeExt, PCR_PID, info.mCASystemId);
if (mSampleAesKeyItem != NULL) {
stream->signalNewSampleAesKey(mSampleAesKeyItem);
@ -720,11 +752,13 @@ ATSParser::Stream::Stream(
Program *program,
unsigned elementaryPID,
unsigned streamType,
unsigned streamTypeExt,
unsigned PCR_PID,
int32_t CA_system_ID)
: mProgram(program),
mElementaryPID(elementaryPID),
mStreamType(streamType),
mStreamTypeExt(streamTypeExt),
mPCR_PID(PCR_PID),
mExpectedContinuityCounter(-1),
mPayloadStarted(false),
@ -781,6 +815,12 @@ ATSParser::Stream::Stream(
mode = ElementaryStreamQueue::AC3;
break;
case STREAMTYPE_PES_PRIVATE_DATA:
if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) {
mode = ElementaryStreamQueue::AC4;
}
break;
case STREAMTYPE_METADATA:
mode = ElementaryStreamQueue::METADATA;
break;
@ -989,6 +1029,8 @@ bool ATSParser::Stream::isAudio() const {
case STREAMTYPE_AAC_ENCRYPTED:
case STREAMTYPE_AC3_ENCRYPTED:
return true;
case STREAMTYPE_PES_PRIVATE_DATA:
return mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4;
default:
return false;

@ -142,6 +142,7 @@ struct ATSParser : public RefBase {
STREAMTYPE_MPEG2_VIDEO = 0x02,
STREAMTYPE_MPEG1_AUDIO = 0x03,
STREAMTYPE_MPEG2_AUDIO = 0x04,
STREAMTYPE_PES_PRIVATE_DATA = 0x06,
STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
STREAMTYPE_METADATA = 0x15,
@ -160,6 +161,20 @@ struct ATSParser : public RefBase {
STREAMTYPE_AC3_ENCRYPTED = 0xC1,
};
enum {
// From ISO/IEC 13818-1: 2007 (E), Table 2-29
DESCRIPTOR_CA = 0x09,
// DVB BlueBook A038 Table 12
DESCRIPTOR_DVB_EXTENSION = 0x7F,
};
// DVB BlueBook A038 Table 109
enum {
EXT_DESCRIPTOR_DVB_AC4 = 0x15,
EXT_DESCRIPTOR_DVB_RESERVED_MAX = 0x7F,
};
protected:
virtual ~ATSParser();

@ -86,6 +86,21 @@ void ElementaryStreamQueue::setCasInfo(
mCasSessionId = sessionId;
}
static int32_t readVariableBits(ABitReader &bits, int32_t nbits) {
int32_t value = 0;
int32_t more_bits = 1;
while (more_bits) {
value += bits.getBits(nbits);
more_bits = bits.getBits(1);
if (!more_bits)
break;
value++;
value <<= nbits;
}
return value;
}
// Parse AC3 header assuming the current ptr is start position of syncframe,
// update metadata only applicable, and return the payload size
static unsigned parseAC3SyncFrame(
@ -199,6 +214,78 @@ static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) {
return parseAC3SyncFrame(ptr, size, NULL) > 0;
}
// Parse AC4 header assuming the current ptr is start position of syncframe
// and update frameSize and metadata.
static status_t parseAC4SyncFrame(
const uint8_t *ptr, size_t size, unsigned &frameSize, sp<MetaData> *metaData) {
// ETSI TS 103 190-2 V1.1.1 (2015-09), Annex C
// The sync_word can be either 0xAC40 or 0xAC41.
static const int kSyncWordAC40 = 0xAC40;
static const int kSyncWordAC41 = 0xAC41;
size_t headerSize = 0;
ABitReader bits(ptr, size);
int32_t syncWord = bits.getBits(16);
if ((syncWord != kSyncWordAC40) && (syncWord != kSyncWordAC41)) {
ALOGE("Invalid syncword in AC4 header");
return ERROR_MALFORMED;
}
headerSize += 2;
frameSize = bits.getBits(16);
headerSize += 2;
if (frameSize == 0xFFFF) {
frameSize = bits.getBits(24);
headerSize += 3;
}
if (frameSize == 0) {
ALOGE("Invalid frame size in AC4 header");
return ERROR_MALFORMED;
}
frameSize += headerSize;
// If the sync_word is 0xAC41, a crc_word is also transmitted.
if (syncWord == kSyncWordAC41) {
frameSize += 2; // crc_word
}
ALOGV("AC4 frameSize = %u", frameSize);
// ETSI TS 103 190-2 V1.1.1 6.2.1.1
uint32_t bitstreamVersion = bits.getBits(2);
if (bitstreamVersion == 3) {
bitstreamVersion += readVariableBits(bits, 2);
}
bits.skipBits(10); // Sequence Counter
uint32_t bWaitFrames = bits.getBits(1);
if (bWaitFrames) {
uint32_t waitFrames = bits.getBits(3);
if (waitFrames > 0) {
bits.skipBits(2); // br_code;
}
}
// ETSI TS 103 190 V1.1.1 Table 82
bool fsIndex = bits.getBits(1);
uint32_t samplingRate = fsIndex ? 48000 : 44100;
if (metaData != NULL) {
ALOGV("dequeueAccessUnitAC4 Setting mFormat");
(*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
(*metaData)->setInt32(kKeyIsSyncFrame, 1);
// [FIXME] AC4 channel count is defined per presentation. Provide a default channel count
// as stereo for the entire stream.
(*metaData)->setInt32(kKeyChannelCount, 2);
(*metaData)->setInt32(kKeySampleRate, samplingRate);
}
return OK;
}
static status_t IsSeeminglyValidAC4Header(const uint8_t *ptr, size_t size, unsigned &frameSize) {
return parseAC4SyncFrame(ptr, size, frameSize, NULL);
}
static bool IsSeeminglyValidADTSHeader(
const uint8_t *ptr, size_t size, size_t *frameLength) {
if (size < 7) {
@ -416,6 +503,42 @@ status_t ElementaryStreamQueue::appendData(
break;
}
case AC4:
{
uint8_t *ptr = (uint8_t *)data;
unsigned frameSize = 0;
ssize_t startOffset = -1;
// A valid AC4 stream should have minimum of 7 bytes in its buffer.
// (Sync header 4 bytes + AC4 toc 3 bytes)
if (size < 7) {
return ERROR_MALFORMED;
}
for (size_t i = 0; i < size; ++i) {
if (IsSeeminglyValidAC4Header(&ptr[i], size - i, frameSize) == OK) {
startOffset = i;
break;
}
}
if (startOffset < 0) {
return ERROR_MALFORMED;
}
if (startOffset > 0) {
ALOGI("found something resembling an AC4 syncword at offset %zd",
startOffset);
}
if (frameSize != size - startOffset) {
ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.",
frameSize, size - startOffset);
}
data = &ptr[startOffset];
size -= startOffset;
break;
}
case MPEG_AUDIO:
{
uint8_t *ptr = (uint8_t *)data;
@ -649,6 +772,8 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
return dequeueAccessUnitAAC();
case AC3:
return dequeueAccessUnitAC3();
case AC4:
return dequeueAccessUnitAC4();
case MPEG_VIDEO:
return dequeueAccessUnitMPEGVideo();
case MPEG4_VIDEO:
@ -730,6 +855,69 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() {
return accessUnit;
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC4() {
unsigned syncStartPos = 0;
unsigned payloadSize = 0;
sp<MetaData> format = new MetaData;
ALOGV("dequeueAccessUnit_AC4[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
// A valid AC4 stream should have minimum of 7 bytes in its buffer.
// (Sync header 4 bytes + AC4 toc 3 bytes)
if (mBuffer->size() < 7) {
return NULL;
}
while (true) {
if (syncStartPos + 2 >= mBuffer->size()) {
return NULL;
}
status_t status = parseAC4SyncFrame(
mBuffer->data() + syncStartPos,
mBuffer->size() - syncStartPos,
payloadSize,
&format);
if (status == OK) {
break;
}
ALOGV("dequeueAccessUnit_AC4[%d]: syncStartPos %u payloadSize %u",
mAUIndex, syncStartPos, payloadSize);
++syncStartPos;
}
if (mBuffer->size() < syncStartPos + payloadSize) {
ALOGV("Not enough buffer size for AC4");
return NULL;
}
if (mFormat == NULL) {
mFormat = format;
}
int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
if (timeUs < 0ll) {
ALOGE("negative timeUs");
return NULL;
}
mAUIndex++;
sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
accessUnit->meta()->setInt64("timeUs", timeUs);
accessUnit->meta()->setInt32("isSync", 1);
memmove(
mBuffer->data(),
mBuffer->data() + syncStartPos + payloadSize,
mBuffer->size() - syncStartPos - payloadSize);
mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize);
return accessUnit;
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
if (mBuffer->size() < 4) {
return NULL;

@ -38,6 +38,7 @@ struct ElementaryStreamQueue {
H264,
AAC,
AC3,
AC4,
MPEG_AUDIO,
MPEG_VIDEO,
MPEG4_VIDEO,
@ -116,6 +117,7 @@ private:
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
sp<ABuffer> dequeueAccessUnitAC3();
sp<ABuffer> dequeueAccessUnitAC4();
sp<ABuffer> dequeueAccessUnitMPEGAudio();
sp<ABuffer> dequeueAccessUnitMPEGVideo();
sp<ABuffer> dequeueAccessUnitMPEG4Video();

Loading…
Cancel
Save