Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ac73a5d authored by Sampath Shetty's avatar Sampath Shetty Committed by Mikhail Naganov
Browse files

Extract AudioPresentations from AC4 in MP4 and TS

Translates MPEG-4 AC4 DSI AC4Presentations to AudioPresentation.
Parses MPEG2-TS audio preselection descriptors from MPEG2-TS.
Adds AudioPresentationInfo interface to native MediaExtractor.

Test: Manually test
Bug: 119312182
Change-Id: I61286b38543e114aeaef331aa013bc2d2d7626c3
parent 3df53103
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
@@ -2753,6 +2754,75 @@ status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
    AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC4);
    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);

    AudioPresentationCollection presentations;
    // translate the AC4 presentation information to audio presentations for this track
    AC4DSIParser::AC4Presentations ac4Presentations = parser.getPresentations();
    if (!ac4Presentations.empty()) {
        for (const auto& ac4Presentation : ac4Presentations) {
            auto& presentation = ac4Presentation.second;
            if (!presentation.mEnabled) {
                continue;
            }
            AudioPresentationV1 ap;
            ap.mPresentationId = presentation.mGroupIndex;
            ap.mProgramId = presentation.mProgramID;
            ap.mLanguage = presentation.mLanguage;
            if (presentation.mPreVirtualized) {
                ap.mMasteringIndication = MASTERED_FOR_HEADPHONE;
            } else {
                switch (presentation.mChannelMode) {
                    case AC4Parser::AC4Presentation::kChannelMode_Mono:
                    case AC4Parser::AC4Presentation::kChannelMode_Stereo:
                        ap.mMasteringIndication = MASTERED_FOR_STEREO;
                        break;
                    case AC4Parser::AC4Presentation::kChannelMode_3_0:
                    case AC4Parser::AC4Presentation::kChannelMode_5_0:
                    case AC4Parser::AC4Presentation::kChannelMode_5_1:
                    case AC4Parser::AC4Presentation::kChannelMode_7_0_34:
                    case AC4Parser::AC4Presentation::kChannelMode_7_1_34:
                    case AC4Parser::AC4Presentation::kChannelMode_7_0_52:
                    case AC4Parser::AC4Presentation::kChannelMode_7_1_52:
                        ap.mMasteringIndication = MASTERED_FOR_SURROUND;
                        break;
                    case AC4Parser::AC4Presentation::kChannelMode_7_0_322:
                    case AC4Parser::AC4Presentation::kChannelMode_7_1_322:
                    case AC4Parser::AC4Presentation::kChannelMode_7_0_4:
                    case AC4Parser::AC4Presentation::kChannelMode_7_1_4:
                    case AC4Parser::AC4Presentation::kChannelMode_9_0_4:
                    case AC4Parser::AC4Presentation::kChannelMode_9_1_4:
                    case AC4Parser::AC4Presentation::kChannelMode_22_2:
                        ap.mMasteringIndication = MASTERED_FOR_3D;
                        break;
                    default:
                        ALOGE("Invalid channel mode in AC4 presentation");
                        return ERROR_MALFORMED;
                }
            }

            ap.mAudioDescriptionAvailable = (presentation.mContentClassifier ==
                    AC4Parser::AC4Presentation::kVisuallyImpaired);
            ap.mSpokenSubtitlesAvailable = (presentation.mContentClassifier ==
                    AC4Parser::AC4Presentation::kVoiceOver);
            ap.mDialogueEnhancementAvailable = presentation.mHasDialogEnhancements;
            if (!ap.mLanguage.empty()) {
                ap.mLabels.emplace(ap.mLanguage, presentation.mDescription);
            }
            presentations.push_back(std::move(ap));
        }
    }

    if (presentations.empty()) {
        // Clear audio presentation info in metadata.
        AMediaFormat_setBuffer(
                mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, nullptr, 0);
    } else {
        std::ostringstream outStream(std::ios::out);
        serializeAudioPresentations(presentations, &outStream);
        AMediaFormat_setBuffer(
                mLastTrack->meta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO,
                outStream.str().data(), outStream.str().size());
    }
    return OK;
}

+3 −0
Original line number Diff line number Diff line
@@ -225,6 +225,9 @@ enum {

    // Key for ALAC Magic Cookie
    kKeyAlacMagicCookie  = 'almc', // raw data

    // AC-4 AudioPresentationInfo
    kKeyAudioPresentationInfo = 'audP',  // raw data
};

enum {
+0 −1
Original line number Diff line number Diff line
@@ -96,7 +96,6 @@ cc_library_shared {
        "AHierarchicalStateMachine.cpp",
        "AMRWriter.cpp",
        "AudioPlayer.cpp",
        "AudioPresentationInfo.cpp",
        "AudioSource.cpp",
        "BufferImpl.cpp",
        "CallbackDataSource.cpp",
+0 −45
Original line number Diff line number Diff line
/*
 * 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 "AudioPresentationInfo"

#include <media/AudioPresentationInfo.h>

namespace android {

AudioPresentationInfo::AudioPresentationInfo() {
}

AudioPresentationInfo::~AudioPresentationInfo() {
    mPresentations.clear();
}

void AudioPresentationInfo::addPresentation(sp<AudioPresentation> presentation) {
    mPresentations.push(presentation);
}

size_t AudioPresentationInfo::countPresentations() const {
    return mPresentations.size();
}

// Returns an AudioPresentation for the given valid index
// index must be >=0 and < countPresentations()
const sp<AudioPresentation> AudioPresentationInfo::getPresentation(size_t index) const {
    return mPresentations[index];
}

}  // namespace android
+28 −0
Original line number Diff line number Diff line
@@ -794,4 +794,32 @@ bool NuMediaExtractor::getCachedDuration(
    return false;
}

// Return OK if we have received an audio presentation info.
// Return ERROR_UNSUPPORTED if the track has no audio presentation.
// Return INVALID_OPERATION if audio presentation metadata version does not match.
status_t NuMediaExtractor::getAudioPresentations(
        size_t trackIndex, AudioPresentationCollection *presentations) const {
    Mutex::Autolock autoLock(mLock);

    if (mImpl == NULL) {
        return -EINVAL;
    }

    if (trackIndex >= mImpl->countTracks()) {
        return -ERANGE;
    }

    sp<MetaData> meta = mImpl->getTrackMetaData(trackIndex);

    uint32_t type;
    const void *data;
    size_t size;
    if (meta != NULL && meta->findData(kKeyAudioPresentationInfo, &type, &data, &size)) {
        std::istringstream inStream(std::string(static_cast<const char*>(data), size));
        return deserializeAudioPresentations(&inStream, presentations);
    }
    ALOGE("Source does not contain any audio presentation");
    return ERROR_UNSUPPORTED;
}

}  // namespace android
Loading