diff options
-rw-r--r-- | cmds/stagefright/audioloop.cpp | 24 | ||||
-rw-r--r-- | include/media/stagefright/AMRWriter.h | 63 | ||||
-rw-r--r-- | include/media/stagefright/AudioSource.h | 19 | ||||
-rw-r--r-- | media/libstagefright/AMRWriter.cpp | 184 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 2 | ||||
-rw-r--r-- | media/libstagefright/AudioSource.cpp | 122 |
6 files changed, 408 insertions, 6 deletions
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index 3788e73..8733662 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -1,7 +1,10 @@ #include "SineSource.h" #include <binder/ProcessState.h> +#include <media/mediarecorder.h> +#include <media/stagefright/AMRWriter.h> #include <media/stagefright/AudioPlayer.h> +#include <media/stagefright/AudioSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> @@ -21,7 +24,16 @@ int main() { OMXClient client; CHECK_EQ(client.connect(), OK); +#if 0 sp<MediaSource> source = new SineSource(kSampleRate, kNumChannels); +#else + sp<MediaSource> source = new AudioSource( + AUDIO_SOURCE_DEFAULT, + kSampleRate, + kNumChannels == 1 + ? AudioSystem::CHANNEL_IN_MONO + : AudioSystem::CHANNEL_IN_STEREO); +#endif sp<MetaData> meta = new MetaData; @@ -43,12 +55,19 @@ int main() { meta, true /* createEncoder */, source); +#if 1 + sp<AMRWriter> writer = new AMRWriter("/sdcard/out.amr"); + writer->addSource(encoder); + writer->start(); + sleep(10); + writer->stop(); +#else sp<MediaSource> decoder = OMXCodec::Create( client.interface(), meta, false /* createEncoder */, encoder); -#if 1 +#if 0 AudioPlayer *player = new AudioPlayer(NULL); player->setSource(decoder); @@ -60,7 +79,7 @@ int main() { delete player; player = NULL; -#else +#elif 0 CHECK_EQ(decoder->start(), OK); MediaBuffer *buffer; @@ -76,6 +95,7 @@ int main() { CHECK_EQ(decoder->stop(), OK); #endif +#endif return 0; } diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h new file mode 100644 index 0000000..6ee9869 --- /dev/null +++ b/include/media/stagefright/AMRWriter.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef AMR_WRITER_H_ + +#define AMR_WRITER_H_ + +#include <stdio.h> + +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +struct MediaSource; + +struct AMRWriter : public RefBase { + AMRWriter(const char *filename); + AMRWriter(int fd); + + status_t initCheck() const; + + status_t addSource(const sp<MediaSource> &source); + + status_t start(); + void stop(); + +protected: + virtual ~AMRWriter(); + +private: + Mutex mLock; + + FILE *mFile; + status_t mInitCheck; + sp<MediaSource> mSource; + bool mStarted; + volatile bool mDone; + pthread_t mThread; + + static void *ThreadWrapper(void *); + void threadFunc(); + + AMRWriter(const AMRWriter &); + AMRWriter &operator=(const AMRWriter &); +}; + +} // namespace android + +#endif // AMR_WRITER_H_ diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h index e129958..eb00140 100644 --- a/include/media/stagefright/AudioSource.h +++ b/include/media/stagefright/AudioSource.h @@ -18,16 +18,20 @@ #define AUDIO_SOURCE_H_ +#include <media/AudioSystem.h> #include <media/stagefright/MediaSource.h> namespace android { class AudioRecord; +struct MediaBufferGroup; -class AudioSource { -public: - AudioSource(int inputSource); - virtual ~AudioSource(); +struct AudioSource : public MediaSource { + // Note that the "channels" parameter is _not_ the number of channels, + // but a bitmask of AudioSystem::audio_channels constants. + AudioSource( + int inputSource, uint32_t sampleRate, + uint32_t channels = AudioSystem::CHANNEL_IN_MONO); status_t initCheck() const; @@ -38,9 +42,16 @@ public: virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); +protected: + virtual ~AudioSource(); + private: + enum { kMaxBufferSize = 8192 }; + AudioRecord *mRecord; status_t mInitCheck; + bool mStarted; + MediaBufferGroup *mGroup; AudioSource(const AudioSource &); AudioSource &operator=(const AudioSource &); diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp new file mode 100644 index 0000000..7b681f1 --- /dev/null +++ b/media/libstagefright/AMRWriter.cpp @@ -0,0 +1,184 @@ +/* + * 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. + */ + +#include <media/stagefright/AMRWriter.h> + +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +AMRWriter::AMRWriter(const char *filename) + : mFile(fopen(filename, "wb")), + mInitCheck(mFile != NULL ? OK : NO_INIT), + mStarted(false) { +} + +AMRWriter::AMRWriter(int fd) + : mFile(fdopen(fd, "wb")), + mInitCheck(mFile != NULL ? OK : NO_INIT), + mStarted(false) { +} + +AMRWriter::~AMRWriter() { + if (mStarted) { + stop(); + } + + if (mFile != NULL) { + fclose(mFile); + mFile = NULL; + } +} + +status_t AMRWriter::initCheck() const { + return mInitCheck; +} + +status_t AMRWriter::addSource(const sp<MediaSource> &source) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mSource != NULL) { + // AMR files only support a single track of audio. + return UNKNOWN_ERROR; + } + + sp<MetaData> meta = source->getFormat(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + bool isWide = false; + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { + isWide = true; + } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { + return ERROR_UNSUPPORTED; + } + + int32_t channelCount; + int32_t sampleRate; + CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); + CHECK_EQ(channelCount, 1); + CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + CHECK_EQ(sampleRate, (isWide ? 16000 : 8000)); + + mSource = source; + + const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n"; + size_t n = strlen(kHeader); + if (fwrite(kHeader, 1, n, mFile) != n) { + return ERROR_IO; + } + + return OK; +} + +status_t AMRWriter::start() { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mStarted || mSource == NULL) { + return UNKNOWN_ERROR; + } + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + mDone = false; + + pthread_create(&mThread, &attr, ThreadWrapper, this); + pthread_attr_destroy(&attr); + + mStarted = true; + + return OK; +} + +void AMRWriter::stop() { + { + Mutex::Autolock autoLock(mLock); + + if (!mStarted) { + return; + } + + mDone = true; + } + + void *dummy; + pthread_join(mThread, &dummy); + + mSource->stop(); + + mStarted = false; +} + +// static +void *AMRWriter::ThreadWrapper(void *me) { + static_cast<AMRWriter *>(me)->threadFunc(); + + return NULL; +} + +void AMRWriter::threadFunc() { + for (;;) { + Mutex::Autolock autoLock(mLock); + + if (mDone) { + break; + } + + MediaBuffer *buffer; + status_t err = mSource->read(&buffer); + + if (err != OK) { + break; + } + + ssize_t n = fwrite( + (const uint8_t *)buffer->data() + buffer->range_offset(), + 1, + buffer->range_length(), + mFile); + + buffer->release(); + buffer = NULL; + + if (n < (ssize_t)buffer->range_length()) { + break; + } + } +} + +} // namespace android diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 26b9357..3813907 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -16,7 +16,9 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) LOCAL_SRC_FILES += \ AMRExtractor.cpp \ + AMRWriter.cpp \ AudioPlayer.cpp \ + AudioSource.cpp \ AwesomePlayer.cpp \ CachingDataSource.cpp \ CameraSource.cpp \ diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp new file mode 100644 index 0000000..edabaf9 --- /dev/null +++ b/media/libstagefright/AudioSource.cpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#include <media/stagefright/AudioSource.h> + +#include <media/AudioRecord.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> + +namespace android { + +AudioSource::AudioSource( + int inputSource, uint32_t sampleRate, uint32_t channels) + : mRecord(new AudioRecord( + inputSource, sampleRate, AudioSystem::PCM_16_BIT, channels)), + mInitCheck(mRecord->initCheck()), + mStarted(false), + mGroup(NULL) { +} + +AudioSource::~AudioSource() { + if (mStarted) { + stop(); + } + + delete mRecord; + mRecord = NULL; +} + +status_t AudioSource::initCheck() const { + return mInitCheck; +} + +status_t AudioSource::start(MetaData *params) { + if (mStarted) { + return UNKNOWN_ERROR; + } + + status_t err = mRecord->start(); + + if (err == OK) { + mGroup = new MediaBufferGroup; + mGroup->add_buffer(new MediaBuffer(kMaxBufferSize)); + + mStarted = true; + } + + return err; +} + +status_t AudioSource::stop() { + if (!mStarted) { + return UNKNOWN_ERROR; + } + + mRecord->stop(); + + delete mGroup; + mGroup = NULL; + + mStarted = false; + + return OK; +} + +sp<MetaData> AudioSource::getFormat() { + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeySampleRate, mRecord->getSampleRate()); + meta->setInt32(kKeyChannelCount, mRecord->channelCount()); + meta->setInt32(kKeyMaxInputSize, kMaxBufferSize); + + return meta; +} + +status_t AudioSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + MediaBuffer *buffer; + CHECK_EQ(mGroup->acquire_buffer(&buffer), OK); + + uint32_t numFramesRecorded; + mRecord->getPosition(&numFramesRecorded); + + buffer->meta_data()->setInt64( + kKeyTime, + (1000000ll * numFramesRecorded) / mRecord->getSampleRate() + - mRecord->latency() * 1000); + + ssize_t n = mRecord->read(buffer->data(), buffer->size()); + + if (n < 0) { + buffer->release(); + buffer = NULL; + + return (status_t)n; + } + + buffer->set_range(0, n); + + *out = buffer; + + return OK; +} + +} // namespace android |