summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/stagefright/AACWriter.h75
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp27
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h3
-rw-r--r--media/libstagefright/AACWriter.cpp382
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/codecs/aacenc/AACEncoder.cpp2
6 files changed, 483 insertions, 7 deletions
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
new file mode 100644
index 0000000..fa3ab8a
--- /dev/null
+++ b/include/media/stagefright/AACWriter.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef AAC_WRITER_H_
+#define AAC_WRITER_H_
+
+#include <media/stagefright/MediaWriter.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct MediaSource;
+struct MetaData;
+
+struct AACWriter : public MediaWriter {
+ AACWriter(const char *filename);
+ AACWriter(int fd);
+
+ status_t initCheck() const;
+
+ virtual status_t addSource(const sp<MediaSource> &source);
+ virtual bool reachedEOS();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
+
+protected:
+ virtual ~AACWriter();
+
+private:
+ enum {
+ kAdtsHeaderLength = 7, // # of bytes for the adts header
+ kSamplesPerFrame = 1024, // # of samples in a frame
+ };
+
+ int mFd;
+ status_t mInitCheck;
+ sp<MediaSource> mSource;
+ bool mStarted;
+ volatile bool mPaused;
+ volatile bool mResumed;
+ volatile bool mDone;
+ volatile bool mReachedEOS;
+ pthread_t mThread;
+ int64_t mEstimatedSizeBytes;
+ int64_t mEstimatedDurationUs;
+ int32_t mChannelCount;
+ int32_t mSampleRate;
+ int32_t mFrameDurationUs;
+
+ static void *ThreadWrapper(void *);
+ status_t threadFunc();
+ bool exceedsFileSizeLimit();
+ bool exceedsFileDurationLimit();
+ status_t writeAdtsHeader(uint32_t frameLength);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AACWriter);
+};
+
+} // namespace android
+
+#endif // AAC_WRITER_H_
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 01fbea1..c1687c4 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -26,6 +26,7 @@
#include <media/IMediaPlayerService.h>
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
+#include <media/stagefright/AACWriter.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/VideoSourceDownSampler.h>
#include <media/stagefright/CameraSourceTimeLapse.h>
@@ -872,15 +873,21 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {
}
status_t StagefrightRecorder::startAACRecording() {
- CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADIF ||
- mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
+ // FIXME:
+ // Add support for OUTPUT_FORMAT_AAC_ADIF
+ CHECK(mOutputFormat == OUTPUT_FORMAT_AAC_ADTS);
CHECK(mAudioEncoder == AUDIO_ENCODER_AAC);
CHECK(mAudioSource != AUDIO_SOURCE_CNT);
- CHECK(0 == "AACWriter is not implemented yet");
+ mWriter = new AACWriter(mOutputFd);
+ status_t status = startRawAudioRecording();
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
- return OK;
+ return status;
}
status_t StagefrightRecorder::startAMRRecording() {
@@ -902,6 +909,16 @@ status_t StagefrightRecorder::startAMRRecording() {
}
}
+ mWriter = new AMRWriter(mOutputFd);
+ status_t status = startRawAudioRecording();
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
+ return status;
+}
+
+status_t StagefrightRecorder::startRawAudioRecording() {
if (mAudioSource >= AUDIO_SOURCE_CNT) {
LOGE("Invalid audio source: %d", mAudioSource);
return BAD_VALUE;
@@ -917,7 +934,7 @@ status_t StagefrightRecorder::startAMRRecording() {
return UNKNOWN_ERROR;
}
- mWriter = new AMRWriter(mOutputFd);
+ CHECK(mWriter != 0);
mWriter->addSource(audioEncoder);
if (mMaxFileDurationUs != 0) {
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 3d463ea..c3e6d65 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -69,6 +69,7 @@ private:
sp<Surface> mPreviewSurface;
sp<IMediaRecorderClient> mListener;
sp<MediaWriter> mWriter, mWriterAux;
+ int mOutputFd, mOutputFdAux;
sp<AudioSource> mAudioSourceNode;
audio_source_t mAudioSource;
@@ -104,7 +105,6 @@ private:
sp<CameraSourceTimeLapse> mCameraSourceTimeLapse;
String8 mParams;
- int mOutputFd, mOutputFdAux;
bool mIsMetaDataStoredInVideoBuffers;
MediaProfiles *mEncoderProfiles;
@@ -123,6 +123,7 @@ private:
status_t startMPEG4Recording();
status_t startAMRRecording();
status_t startAACRecording();
+ status_t startRawAudioRecording();
status_t startRTPRecording();
status_t startMPEG2TSRecording();
sp<MediaSource> createAudioSource();
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
new file mode 100644
index 0000000..8413208
--- /dev/null
+++ b/media/libstagefright/AACWriter.cpp
@@ -0,0 +1,382 @@
+/*
+ * 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 "AACWriter"
+#include <utils/Log.h>
+
+#include <media/stagefright/AACWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+
+namespace android {
+
+AACWriter::AACWriter(const char *filename)
+ : mFd(-1),
+ mInitCheck(NO_INIT),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mChannelCount(-1),
+ mSampleRate(-1) {
+
+ LOGV("AACWriter Constructor");
+
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
+ if (mFd >= 0) {
+ mInitCheck = OK;
+ }
+}
+
+AACWriter::AACWriter(int fd)
+ : mFd(dup(fd)),
+ mInitCheck(mFd < 0? NO_INIT: OK),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mChannelCount(-1),
+ mSampleRate(-1) {
+}
+
+AACWriter::~AACWriter() {
+ if (mStarted) {
+ stop();
+ }
+
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t AACWriter::initCheck() const {
+ return mInitCheck;
+}
+
+static int writeInt8(int fd, uint8_t x) {
+ return ::write(fd, &x, 1);
+}
+
+
+status_t AACWriter::addSource(const sp<MediaSource> &source) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mSource != NULL) {
+ LOGE("AAC files only support a single track of audio.");
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC));
+ CHECK(meta->findInt32(kKeyChannelCount, &mChannelCount));
+ CHECK(meta->findInt32(kKeySampleRate, &mSampleRate));
+ CHECK(mChannelCount >= 1 && mChannelCount <= 2);
+
+ mSource = source;
+ return OK;
+}
+
+status_t AACWriter::start(MetaData *params) {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ if (mSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mStarted && mPaused) {
+ mPaused = false;
+ mResumed = true;
+ return OK;
+ } else if (mStarted) {
+ // Already started, does nothing
+ return OK;
+ }
+
+ mFrameDurationUs = (kSamplesPerFrame * 1000000LL + (mSampleRate >> 1))
+ / mSampleRate;
+
+ 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);
+
+ mReachedEOS = false;
+ mDone = false;
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t AACWriter::pause() {
+ if (!mStarted) {
+ return OK;
+ }
+ mPaused = true;
+ return OK;
+}
+
+status_t AACWriter::stop() {
+ if (!mStarted) {
+ return OK;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
+
+ mStarted = false;
+ return err;
+}
+
+bool AACWriter::exceedsFileSizeLimit() {
+ if (mMaxFileSizeLimitBytes == 0) {
+ return false;
+ }
+ return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
+}
+
+bool AACWriter::exceedsFileDurationLimit() {
+ if (mMaxFileDurationLimitUs == 0) {
+ return false;
+ }
+ return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
+}
+
+// static
+void *AACWriter::ThreadWrapper(void *me) {
+ return (void *) static_cast<AACWriter *>(me)->threadFunc();
+}
+
+/*
+* Returns an index into the sample rate table if the
+* given sample rate is found; otherwise, returns -1.
+*/
+static bool getSampleRateTableIndex(int sampleRate, uint8_t* tableIndex) {
+ static const int kSampleRateTable[] = {
+ 96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000
+ };
+ const int tableSize =
+ sizeof(kSampleRateTable) / sizeof(kSampleRateTable[0]);
+
+ *tableIndex = 0;
+ for (int index = 0; index < tableSize; ++index) {
+ if (sampleRate == kSampleRateTable[index]) {
+ LOGV("Sample rate: %d and index: %d",
+ sampleRate, index);
+ *tableIndex = index;
+ return true;
+ }
+ }
+
+ LOGE("Sampling rate %d bps is not supported", sampleRate);
+ return false;
+}
+
+/*
+ * ADTS (Audio data transport stream) header structure.
+ * It consists of 7 or 9 bytes (with or without CRC):
+ * 12 bits of syncword 0xFFF, all bits must be 1
+ * 1 bit of field ID. 0 for MPEG-4, and 1 for MPEG-2
+ * 2 bits of MPEG layer. If in MPEG-TS, set to 0
+ * 1 bit of protection absense. Set to 1 if no CRC.
+ * 2 bits of profile code. Set to 1 (The MPEG-4 Audio
+ * object type minus 1. We are using AAC-LC = 2)
+ * 4 bits of sampling frequency index code (15 is not allowed)
+ * 1 bit of private stream. Set to 0.
+ * 3 bits of channel configuration code. 0 resevered for inband PCM
+ * 1 bit of originality. Set to 0.
+ * 1 bit of home. Set to 0.
+ * 1 bit of copyrighted steam. Set to 0.
+ * 1 bit of copyright start. Set to 0.
+ * 13 bits of frame length. It included 7 ot 9 bytes header length.
+ * it is set to (protection absense? 7: 9) + size(AAC frame)
+ * 11 bits of buffer fullness. 0x7FF for VBR.
+ * 2 bits of frames count in one packet. Set to 0.
+ */
+status_t AACWriter::writeAdtsHeader(uint32_t frameLength) {
+ uint8_t data = 0xFF;
+ write(mFd, &data, 1);
+
+ const uint8_t kFieldId = 0;
+ const uint8_t kMpegLayer = 0;
+ const uint8_t kProtectionAbsense = 1; // 1: kAdtsHeaderLength = 7
+ data = 0xF0;
+ data |= (kFieldId << 3);
+ data |= (kMpegLayer << 1);
+ data |= kProtectionAbsense;
+ write(mFd, &data, 1);
+
+ const uint8_t kProfileCode = 1; // AAC-LC
+ uint8_t kSampleFreqIndex;
+ CHECK(getSampleRateTableIndex(mSampleRate, &kSampleFreqIndex));
+ const uint8_t kPrivateStream = 0;
+ const uint8_t kChannelConfigCode = mChannelCount;
+ data = (kProfileCode << 6);
+ data |= (kSampleFreqIndex << 2);
+ data |= (kPrivateStream << 1);
+ data |= (kChannelConfigCode >> 2);
+ write(mFd, &data, 1);
+
+ // 4 bits from originality to copyright start
+ const uint8_t kCopyright = 0;
+ const uint32_t kFrameLength = frameLength;
+ data = ((kChannelConfigCode & 3) << 6);
+ data |= (kCopyright << 2);
+ data |= ((kFrameLength & 0x1800) >> 11);
+ write(mFd, &data, 1);
+
+ data = ((kFrameLength & 0x07F8) >> 3);
+ write(mFd, &data, 1);
+
+ const uint32_t kBufferFullness = 0x7FF; // VBR
+ data = ((kFrameLength & 0x07) << 5);
+ data |= ((kBufferFullness & 0x07C0) >> 6);
+ write(mFd, &data, 1);
+
+ const uint8_t kFrameCount = 0;
+ data = ((kBufferFullness & 0x03F) << 2);
+ data |= kFrameCount;
+ write(mFd, &data, 1);
+
+ return OK;
+}
+
+status_t AACWriter::threadFunc() {
+ mEstimatedDurationUs = 0;
+ mEstimatedSizeBytes = 0;
+ int64_t previousPausedDurationUs = 0;
+ int64_t maxTimestampUs = 0;
+ status_t err = OK;
+
+ prctl(PR_SET_NAME, (unsigned long)"AACWriterThread", 0, 0, 0);
+
+ while (!mDone && err == OK) {
+ MediaBuffer *buffer;
+ err = mSource->read(&buffer);
+
+ if (err != OK) {
+ break;
+ }
+
+ if (mPaused) {
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ mEstimatedSizeBytes += kAdtsHeaderLength + buffer->range_length();
+ if (exceedsFileSizeLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+ break;
+ }
+
+ int32_t isCodecSpecific = 0;
+ if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecSpecific) && isCodecSpecific) {
+ LOGV("Drop codec specific info buffer");
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
+ if (timestampUs > mEstimatedDurationUs) {
+ mEstimatedDurationUs = timestampUs;
+ }
+ if (mResumed) {
+ previousPausedDurationUs += (timestampUs - maxTimestampUs - mFrameDurationUs);
+ mResumed = false;
+ }
+ timestampUs -= previousPausedDurationUs;
+ LOGV("time stamp: %lld, previous paused duration: %lld",
+ timestampUs, previousPausedDurationUs);
+ if (timestampUs > maxTimestampUs) {
+ maxTimestampUs = timestampUs;
+ }
+
+ if (exceedsFileDurationLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+ break;
+ }
+
+ // Each output AAC audio frame to the file contains
+ // 1. an ADTS header, followed by
+ // 2. the compressed audio data.
+ ssize_t dataLength = buffer->range_length();
+ uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
+ if (writeAdtsHeader(kAdtsHeaderLength + dataLength) != OK ||
+ dataLength != write(mFd, data, dataLength)) {
+ err = ERROR_IO;
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ close(mFd);
+ mFd = -1;
+ mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
+}
+
+bool AACWriter::reachedEOS() {
+ return mReachedEOS;
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 6c7176c..10ce00c 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -6,6 +6,7 @@ include frameworks/base/media/libstagefright/codecs/common/Config.mk
LOCAL_SRC_FILES:= \
ACodec.cpp \
AACExtractor.cpp \
+ AACWriter.cpp \
AMRExtractor.cpp \
AMRWriter.cpp \
AVIExtractor.cpp \
diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
index e4ff128..0bff52d 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -84,7 +84,7 @@ status_t AACEncoder::initCheck() {
params.sampleRate = mSampleRate;
params.bitRate = mBitRate;
params.nChannels = mChannels;
- params.adtsUsed = 0; // For MP4 file, don't use adts format$
+ 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)) {
LOGE("Failed to set AAC encoder parameters");
return UNKNOWN_ERROR;