/*
* Copyright (C) 2011 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 "VideoEditorSRC"
#include <stdlib.h>
#include <utils/Log.h>
#include <audio_utils/primitives.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include "VideoEditorSRC.h"
namespace android {
VideoEditorSRC::VideoEditorSRC(const sp<MediaSource> &source) {
ALOGV("VideoEditorSRC %p(%p)", this, source.get());
static const int32_t kDefaultSamplingFreqencyHz = kFreq32000Hz;
mSource = source;
mResampler = NULL;
mChannelCnt = 0;
mSampleRate = 0;
mOutputSampleRate = kDefaultSamplingFreqencyHz;
mStarted = false;
mInitialTimeStampUs = -1;
mAccuOutBufferSize = 0;
mSeekTimeUs = -1;
mBuffer = NULL;
mLeftover = 0;
mFormatChanged = false;
mStopPending = false;
mSeekMode = ReadOptions::SEEK_PREVIOUS_SYNC;
// Input Source validation
sp<MetaData> format = mSource->getFormat();
const char *mime;
CHECK(format->findCString(kKeyMIMEType, &mime));
CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW));
// Set the metadata of the output after resampling.
mOutputFormat = new MetaData;
mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
mOutputFormat->setInt32(kKeySampleRate, kDefaultSamplingFreqencyHz);
mOutputFormat->setInt32(kKeyChannelCount, 2); // always stereo
}
VideoEditorSRC::~VideoEditorSRC() {
ALOGV("~VideoEditorSRC %p(%p)", this, mSource.get());
stop();
}
status_t VideoEditorSRC::start(MetaData *params) {
ALOGV("start %p(%p)", this, mSource.get());
CHECK(!mStarted);
// Set resampler if required
checkAndSetResampler();
mSeekTimeUs = -1;
mSeekMode = ReadOptions::SEEK_PREVIOUS_SYNC;
mStarted = true;
mSource->start();
return OK;
}
status_t VideoEditorSRC::stop() {
ALOGV("stop %p(%p)", this, mSource.get());
if (!mStarted) {
return OK;
}
if (mBuffer) {
mBuffer->release();
mBuffer = NULL;
}
mSource->stop();
if (mResampler != NULL) {
delete mResampler;
mResampler = NULL;
}
mStarted = false;
mInitialTimeStampUs = -1;
mAccuOutBufferSize = 0;
mLeftover = 0;
return OK;
}
sp<MetaData> VideoEditorSRC::getFormat() {
ALOGV("getFormat");
return mOutputFormat;
}
status_t VideoEditorSRC::read(
MediaBuffer **buffer_out, const ReadOptions *options) {
ALOGV("read %p(%p)", this, mSource.get());
*buffer_out = NULL;
if (!mStarted) {
return ERROR_END_OF_STREAM;
}
if (mResampler) {
// Store the seek parameters
int64_t seekTimeUs;
ReadOptions::SeekMode mode = ReadOptions::SEEK_PREVIOUS_SYNC;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
ALOGV("read Seek %lld", seekTimeUs);
mSeekTimeUs = seekTimeUs;
mSeekMode = mode;
}
// We ask for 1024 frames in output
// resampler output is always 2 channels and 32 bits
const size_t kOutputFrameCount = 1024;
const size_t kBytes = kOutputFrameCount * 2 * sizeof(int32_t);
int32_t *pTmpBuffer = (int32_t *)calloc(1, kBytes);
if (!pTmpBuffer) {
ALOGE("calloc failed to allocate memory: %d bytes", kBytes);
return NO_MEMORY;
}
// Resample to target quality
mResampler->resample(pTmpBuffer, kOutputFrameCount, this);
if (mStopPending) {
stop();
mStopPending = false;
}
// Change resampler and retry if format change happened
if (mFormatChanged) {
mFormatChanged = false;
checkAndSetResampler();
free(pTmpBuffer);
return read(buffer_out, NULL);
}
// Create a new MediaBuffer
int32_t outBufferSize = kOutputFrameCount * 2 * sizeof(int16_t);
MediaBuffer* outBuffer = new MediaBuffer(outBufferSize);
// Convert back to 2 channels and 16 bits
ditherAndClamp(
(int32_t *)((uint8_t*)outBuffer->data() + outBuffer->range_offset()),
pTmpBuffer, kOutputFrameCount);
free(pTmpBuffer);
// Compute and set the new timestamp
sp<MetaData> to = outBuffer->meta_data();
int64_t totalOutDurationUs = (mAccuOutBufferSize * 1000000) / (mOutputSampleRate * 2 * 2);
int64_t timeUs = mInitialTimeStampUs + totalOutDurationUs;
to->setInt64(kKeyTime, timeUs);
// update the accumulate size
mAccuOutBufferSize += outBufferSize;
*buffer_out = outBuffer;
} else {
// Resampling not required. Read and pass-through.
MediaBuffer *aBuffer;
status_t err = mSource->read(&aBuffer, options);
if (err != OK) {
ALOGV("read returns err = %d", err);
}
if (err == INFO_FORMAT_CHANGED) {
checkAndSetResampler();
return read(buffer_out, NULL);
}
// EOS or some other error
if(err != OK) {
stop();
*buffer_out = NULL;
return err;
}
*buffer_out = aBuffer;
}
return OK;
}
status_t VideoEditorSRC::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, int64_t pts) {
ALOGV("getNextBuffer %d, chan = %d", pBuffer->frameCount, mChannelCnt);
uint32_t done = 0;
uint32_t want = pBuffer->frameCount * mChannelCnt * 2;
pBuffer->raw = malloc(want);
while (mStarted && want > 0) {
// If we don't have any data left, read a new buffer.
if (!mBuffer) {
// if we seek, reset the initial time stamp and accumulated time
ReadOptions options;
if (mSeekTimeUs >= 0) {
ALOGV("%p cacheMore_l Seek requested = %lld", this, mSeekTimeUs);
ReadOptions::SeekMode mode = mSeekMode;
options.setSeekTo(mSeekTimeUs, mode);
mSeekTimeUs = -1;
mInitialTimeStampUs = -1;
mAccuOutBufferSize = 0;
}
status_t err = mSource->read(&mBuffer, &options);
if (err != OK) {
free(pBuffer->raw);
pBuffer->raw = NULL;
pBuffer->frameCount = 0;
}
if (err == INFO_FORMAT_CHANGED) {
ALOGV("getNextBuffer: source read returned INFO_FORMAT_CHANGED");
// At this point we cannot switch to a new AudioResampler because
// we are in a callback called by the AudioResampler itself. So
// just remember the fact that the format has changed, and let
// read() handles this.
mFormatChanged = true;
return err;
}
// EOS or some other error
if (err != OK) {
ALOGV("EOS or some err: %d", err);
// We cannot call stop() here because stop() will release the
// AudioResampler, and we are in a callback of the AudioResampler.
// So just remember the fact and let read() call stop().
mStopPending = true;
return err;
}
CHECK(mBuffer);
mLeftover = mBuffer->range_length();
if (mInitialTimeStampUs == -1) {
int64_t curTS;
sp<MetaData> from = mBuffer->meta_data();
from->findInt64(kKeyTime, &curTS);
ALOGV("setting mInitialTimeStampUs to %lld", mInitialTimeStampUs);
mInitialTimeStampUs = curTS;
}
}
// Now copy data to the destination
uint32_t todo = mLeftover;
if (todo > want) {
todo = want;
}
uint8_t* end = (uint8_t*)mBuffer->data() + mBuffer->range_offset()
+ mBuffer->range_length();
memcpy((uint8_t*)pBuffer->raw + done, end - mLeftover, todo);
done += todo;
want -= todo;
mLeftover -= todo;
// Release MediaBuffer as soon as possible.
if (mLeftover == 0) {
mBuffer->release();
mBuffer = NULL;
}
}
pBuffer->frameCount = done / (mChannelCnt * 2);
ALOGV("getNextBuffer done %d", pBuffer->frameCount);
return OK;
}
void VideoEditorSRC::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
ALOGV("releaseBuffer: %p", pBuffers);
free(pBuffer->raw);
pBuffer->raw = NULL;
pBuffer->frameCount = 0;
}
void VideoEditorSRC::checkAndSetResampler() {
ALOGV("checkAndSetResampler");
static const uint16_t kUnityGain = 0x1000;
sp<MetaData> format = mSource->getFormat();
const char *mime;
CHECK(format->findCString(kKeyMIMEType, &mime));
CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW));
CHECK(format->findInt32(kKeySampleRate, &mSampleRate));
CHECK(format->findInt32(kKeyChannelCount, &mChannelCnt));
// If a resampler exists, delete it first
if (mResampler != NULL) {
delete mResampler;
mResampler = NULL;
}
// Clear previous buffer
if (mBuffer) {
mBuffer->release();
mBuffer = NULL;
}
if (mSampleRate != mOutputSampleRate || mChannelCnt != 2) {
ALOGV("Resampling required (%d => %d Hz, # channels = %d)",
mSampleRate, mOutputSampleRate, mChannelCnt);
mResampler = AudioResampler::create(
16 /* bit depth */,
mChannelCnt,
mOutputSampleRate);
CHECK(mResampler);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(kUnityGain, kUnityGain);
} else {
ALOGV("Resampling not required (%d => %d Hz, # channels = %d)",
mSampleRate, mOutputSampleRate, mChannelCnt);
}
}
} //namespce android