/*
 * Copyright (C) 2010 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 "AACEncoder"
#include <utils/Log.h>

#include "AACEncoder.h"
#include "voAAC.h"
#include "cmnMemory.h"

#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>

namespace android {

AACEncoder::AACEncoder(const sp<IMediaSource> &source, const sp<MetaData> &meta)
    : mSource(source),
      mMeta(meta),
      mStarted(false),
      mBufferGroup(NULL),
      mInputBuffer(NULL),
      mInputFrame(NULL),
      mEncoderHandle(NULL),
      mApiHandle(NULL),
      mMemOperator(NULL) {
}

status_t AACEncoder::initCheck() {
    CHECK(mApiHandle == NULL && mEncoderHandle == NULL);
    CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
    CHECK(mMeta->findInt32(kKeyChannelCount, &mChannels));
    CHECK(mChannels <= 2 && mChannels >= 1);
    CHECK(mMeta->findInt32(kKeyBitRate, &mBitRate));

    mApiHandle = new VO_AUDIO_CODECAPI;
    CHECK(mApiHandle);

    if (VO_ERR_NONE != voGetAACEncAPI(mApiHandle)) {
        ALOGE("Failed to get api handle");
        return UNKNOWN_ERROR;
    }

    mMemOperator = new VO_MEM_OPERATOR;
    CHECK(mMemOperator != NULL);
    mMemOperator->Alloc = cmnMemAlloc;
    mMemOperator->Copy = cmnMemCopy;
    mMemOperator->Free = cmnMemFree;
    mMemOperator->Set = cmnMemSet;
    mMemOperator->Check = cmnMemCheck;

    VO_CODEC_INIT_USERDATA userData;
    memset(&userData, 0, sizeof(userData));
    userData.memflag = VO_IMF_USERMEMOPERATOR;
    userData.memData = (VO_PTR) mMemOperator;
    if (VO_ERR_NONE != mApiHandle->Init(&mEncoderHandle, VO_AUDIO_CodingAAC, &userData)) {
        ALOGE("Failed to init AAC encoder");
        return UNKNOWN_ERROR;
    }
    if (OK != setAudioSpecificConfigData()) {
        ALOGE("Failed to configure AAC encoder");
        return UNKNOWN_ERROR;
    }

    // Configure AAC encoder$
    AACENC_PARAM params;
    memset(&params, 0, sizeof(params));
    params.sampleRate = mSampleRate;
    params.bitRate = mBitRate;
    params.nChannels = mChannels;
    params.adtsUsed = 0;  // We add adts header in the file writer if needed.
    if (VO_ERR_NONE != mApiHandle->SetParam(mEncoderHandle, VO_PID_AAC_ENCPARAM,  &params)) {
        ALOGE("Failed to set AAC encoder parameters");
        return UNKNOWN_ERROR;
    }

    return OK;
}

static status_t getSampleRateTableIndex(int32_t sampleRate, int32_t &index) {
    static const int32_t kSampleRateTable[] = {
        96000, 88200, 64000, 48000, 44100, 32000,
        24000, 22050, 16000, 12000, 11025, 8000
    };
    const int32_t tableSize = sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]);
    for (int32_t i = 0; i < tableSize; ++i) {
        if (sampleRate == kSampleRateTable[i]) {
            index = i;
            return OK;
        }
    }

    ALOGE("Sampling rate %d bps is not supported", sampleRate);
    return UNKNOWN_ERROR;
}

status_t AACEncoder::setAudioSpecificConfigData() {
    ALOGV("setAudioSpecificConfigData: %d hz, %d bps, and %d channels",
         mSampleRate, mBitRate, mChannels);

    int32_t index = 0;
    CHECK_EQ((status_t)OK, getSampleRateTableIndex(mSampleRate, index));
    if (mChannels > 2 || mChannels <= 0) {
        ALOGE("Unsupported number of channels(%d)", mChannels);
        return UNKNOWN_ERROR;
    }

    // OMX_AUDIO_AACObjectLC
    mAudioSpecificConfigData[0] = ((0x02 << 3) | (index >> 1));
    mAudioSpecificConfigData[1] = ((index & 0x01) << 7) | (mChannels << 3);
    return OK;
}

AACEncoder::~AACEncoder() {
    if (mStarted) {
        stop();
    }
}

status_t AACEncoder::start(MetaData *params) {
    if (mStarted) {
        ALOGW("Call start() when encoder already started");
        return OK;
    }

    mBufferGroup = new MediaBufferGroup;
    mBufferGroup->add_buffer(new MediaBuffer(2048));

    CHECK_EQ((status_t)OK, initCheck());

    mNumInputSamples = 0;
    mAnchorTimeUs = 0;
    mFrameCount = 0;

    mInputFrame = new int16_t[mChannels * kNumSamplesPerFrame];
    CHECK(mInputFrame != NULL);

    status_t err = mSource->start(params);
    if (err != OK) {
         ALOGE("AudioSource is not available");
        return err;
    }

    mStarted = true;

    return OK;
}

status_t AACEncoder::stop() {
    if (mInputBuffer) {
        mInputBuffer->release();
        mInputBuffer = NULL;
    }

    delete mBufferGroup;
    mBufferGroup = NULL;

    if (mInputFrame) {
        delete[] mInputFrame;
        mInputFrame = NULL;
    }

    if (!mStarted) {
        ALOGW("Call stop() when encoder has not started");
        return ERROR_END_OF_STREAM;
    }

    mSource->stop();
    if (mEncoderHandle) {
        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
        mEncoderHandle = NULL;
    }
    delete mApiHandle;
    mApiHandle = NULL;

    delete mMemOperator;
    mMemOperator = NULL;

    mStarted = false;

    return OK;
}

sp<MetaData> AACEncoder::getFormat() {
    sp<MetaData> srcFormat = mSource->getFormat();

    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);

    int64_t durationUs;
    if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
        mMeta->setInt64(kKeyDuration, durationUs);
    }

    mMeta->setCString(kKeyDecoderComponent, "AACEncoder");

    return mMeta;
}

status_t AACEncoder::read(
        MediaBuffer **out, const ReadOptions *options) {
    *out = NULL;

    int64_t seekTimeUs;
    ReadOptions::SeekMode mode;
    CHECK(options == NULL || !options->getSeekTo(&seekTimeUs, &mode));

    MediaBuffer *buffer;
    CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), (status_t)OK);
    uint8_t *outPtr = (uint8_t *)buffer->data();
    bool readFromSource = false;
    int64_t wallClockTimeUs = -1;

    if (mFrameCount == 0) {
        memcpy(outPtr, mAudioSpecificConfigData, 2);
        buffer->set_range(0, 2);
        buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
        *out = buffer;
        ++mFrameCount;
        return OK;
    } else if (mFrameCount == 1) {
        buffer->meta_data()->setInt32(kKeyIsCodecConfig, false);
    }

    const int32_t nSamples = mChannels * kNumSamplesPerFrame;
    while (mNumInputSamples < nSamples) {
        if (mInputBuffer == NULL) {
            if (mSource->read(&mInputBuffer, options) != OK) {
                if (mNumInputSamples == 0) {
                    buffer->release();
                    return ERROR_END_OF_STREAM;
                }
                memset(&mInputFrame[mNumInputSamples],
                       0,
                       sizeof(int16_t) * (nSamples - mNumInputSamples));
                mNumInputSamples = 0;
                break;
            }

            size_t align = mInputBuffer->range_length() % sizeof(int16_t);
            CHECK_EQ(align, (size_t)0);

            int64_t timeUs;
            if (mInputBuffer->meta_data()->findInt64(kKeyDriftTime, &timeUs)) {
                wallClockTimeUs = timeUs;
            }
            if (mInputBuffer->meta_data()->findInt64(kKeyAnchorTime, &timeUs)) {
                mAnchorTimeUs = timeUs;
            }
            readFromSource = true;
        } else {
            readFromSource = false;
        }
        size_t copy = (nSamples - mNumInputSamples) * sizeof(int16_t);

        if (copy > mInputBuffer->range_length()) {
            copy = mInputBuffer->range_length();
        }

        memcpy(&mInputFrame[mNumInputSamples],
               (const uint8_t *) mInputBuffer->data()
                    + mInputBuffer->range_offset(),
               copy);

        mInputBuffer->set_range(
               mInputBuffer->range_offset() + copy,
               mInputBuffer->range_length() - copy);

        if (mInputBuffer->range_length() == 0) {
            mInputBuffer->release();
            mInputBuffer = NULL;
        }
        mNumInputSamples += copy / sizeof(int16_t);
        if (mNumInputSamples >= nSamples) {
            mNumInputSamples %= nSamples;
            break;
        }
    }

    VO_CODECBUFFER inputData;
    memset(&inputData, 0, sizeof(inputData));
    inputData.Buffer = (unsigned char*) mInputFrame;
    inputData.Length = nSamples * sizeof(int16_t);
    CHECK(VO_ERR_NONE == mApiHandle->SetInputData(mEncoderHandle,&inputData));

    VO_CODECBUFFER outputData;
    memset(&outputData, 0, sizeof(outputData));
    VO_AUDIO_OUTPUTINFO outputInfo;
    memset(&outputInfo, 0, sizeof(outputInfo));

    VO_U32 ret = VO_ERR_NONE;
    size_t nOutputBytes = 0;
    do {
        outputData.Buffer = outPtr;
        outputData.Length = buffer->size() - nOutputBytes;
        ret = mApiHandle->GetOutputData(mEncoderHandle, &outputData, &outputInfo);
        if (ret == VO_ERR_NONE) {
            outPtr += outputData.Length;
            nOutputBytes += outputData.Length;
        }
    } while (ret != VO_ERR_INPUT_BUFFER_SMALL);
    buffer->set_range(0, nOutputBytes);

    int64_t mediaTimeUs =
        ((mFrameCount - 1) * 1000000LL * kNumSamplesPerFrame) / mSampleRate;

    buffer->meta_data()->setInt64(kKeyTime, mAnchorTimeUs + mediaTimeUs);
    if (readFromSource && wallClockTimeUs != -1) {
        buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs);
    }
    ++mFrameCount;

    *out = buffer;
    return OK;
}

}  // namespace android
