AMRWriter.cpp revision d411b4ca2945cd8974a3a78199fce94646950128
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <media/stagefright/foundation/ADebug.h> 18#include <media/stagefright/AMRWriter.h> 19#include <media/stagefright/MediaBuffer.h> 20#include <media/stagefright/MediaDefs.h> 21#include <media/stagefright/MediaErrors.h> 22#include <media/stagefright/MediaSource.h> 23#include <media/stagefright/MetaData.h> 24#include <media/mediarecorder.h> 25#include <sys/prctl.h> 26#include <sys/types.h> 27#include <sys/stat.h> 28#include <fcntl.h> 29 30namespace android { 31 32AMRWriter::AMRWriter(const char *filename) 33 : mFd(-1), 34 mInitCheck(NO_INIT), 35 mStarted(false), 36 mPaused(false), 37 mResumed(false) { 38 39 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); 40 if (mFd >= 0) { 41 mInitCheck = OK; 42 } 43} 44 45AMRWriter::AMRWriter(int fd) 46 : mFd(dup(fd)), 47 mInitCheck(mFd < 0? NO_INIT: OK), 48 mStarted(false), 49 mPaused(false), 50 mResumed(false) { 51} 52 53AMRWriter::~AMRWriter() { 54 if (mStarted) { 55 reset(); 56 } 57 58 if (mFd != -1) { 59 close(mFd); 60 mFd = -1; 61 } 62} 63 64status_t AMRWriter::initCheck() const { 65 return mInitCheck; 66} 67 68status_t AMRWriter::addSource(const sp<MediaSource> &source) { 69 if (mInitCheck != OK) { 70 return mInitCheck; 71 } 72 73 if (mSource != NULL) { 74 // AMR files only support a single track of audio. 75 return UNKNOWN_ERROR; 76 } 77 78 sp<MetaData> meta = source->getFormat(); 79 80 const char *mime; 81 CHECK(meta->findCString(kKeyMIMEType, &mime)); 82 83 bool isWide = false; 84 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { 85 isWide = true; 86 } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { 87 return ERROR_UNSUPPORTED; 88 } 89 90 int32_t channelCount; 91 int32_t sampleRate; 92 CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); 93 CHECK_EQ(channelCount, 1); 94 CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); 95 CHECK_EQ(sampleRate, (isWide ? 16000 : 8000)); 96 97 mSource = source; 98 99 const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n"; 100 ssize_t n = strlen(kHeader); 101 if (write(mFd, kHeader, n) != n) { 102 return ERROR_IO; 103 } 104 105 return OK; 106} 107 108status_t AMRWriter::start(MetaData * /* params */) { 109 if (mInitCheck != OK) { 110 return mInitCheck; 111 } 112 113 if (mSource == NULL) { 114 return UNKNOWN_ERROR; 115 } 116 117 if (mStarted && mPaused) { 118 mPaused = false; 119 mResumed = true; 120 return OK; 121 } else if (mStarted) { 122 // Already started, does nothing 123 return OK; 124 } 125 126 status_t err = mSource->start(); 127 128 if (err != OK) { 129 return err; 130 } 131 132 pthread_attr_t attr; 133 pthread_attr_init(&attr); 134 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 135 136 mReachedEOS = false; 137 mDone = false; 138 139 pthread_create(&mThread, &attr, ThreadWrapper, this); 140 pthread_attr_destroy(&attr); 141 142 mStarted = true; 143 144 return OK; 145} 146 147status_t AMRWriter::pause() { 148 if (!mStarted) { 149 return OK; 150 } 151 mPaused = true; 152 return OK; 153} 154 155status_t AMRWriter::reset() { 156 if (!mStarted) { 157 return OK; 158 } 159 160 mDone = true; 161 162 void *dummy; 163 pthread_join(mThread, &dummy); 164 165 status_t err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy)); 166 { 167 status_t status = mSource->stop(); 168 if (err == OK && 169 (status != OK && status != ERROR_END_OF_STREAM)) { 170 err = status; 171 } 172 } 173 174 mStarted = false; 175 return err; 176} 177 178bool AMRWriter::exceedsFileSizeLimit() { 179 if (mMaxFileSizeLimitBytes == 0) { 180 return false; 181 } 182 return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes; 183} 184 185bool AMRWriter::exceedsFileDurationLimit() { 186 if (mMaxFileDurationLimitUs == 0) { 187 return false; 188 } 189 return mEstimatedDurationUs >= mMaxFileDurationLimitUs; 190} 191 192// static 193void *AMRWriter::ThreadWrapper(void *me) { 194 return (void *)(uintptr_t) static_cast<AMRWriter *>(me)->threadFunc(); 195} 196 197status_t AMRWriter::threadFunc() { 198 mEstimatedDurationUs = 0; 199 mEstimatedSizeBytes = 0; 200 bool stoppedPrematurely = true; 201 int64_t previousPausedDurationUs = 0; 202 int64_t maxTimestampUs = 0; 203 status_t err = OK; 204 205 prctl(PR_SET_NAME, (unsigned long)"AMRWriter", 0, 0, 0); 206 while (!mDone) { 207 MediaBuffer *buffer; 208 err = mSource->read(&buffer); 209 210 if (err != OK) { 211 break; 212 } 213 214 if (mPaused) { 215 buffer->release(); 216 buffer = NULL; 217 continue; 218 } 219 220 mEstimatedSizeBytes += buffer->range_length(); 221 if (exceedsFileSizeLimit()) { 222 buffer->release(); 223 buffer = NULL; 224 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); 225 break; 226 } 227 228 int64_t timestampUs; 229 CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); 230 if (timestampUs > mEstimatedDurationUs) { 231 mEstimatedDurationUs = timestampUs; 232 } 233 if (mResumed) { 234 previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000); 235 mResumed = false; 236 } 237 timestampUs -= previousPausedDurationUs; 238 ALOGV("time stamp: %lld, previous paused duration: %lld", 239 timestampUs, previousPausedDurationUs); 240 if (timestampUs > maxTimestampUs) { 241 maxTimestampUs = timestampUs; 242 } 243 244 if (exceedsFileDurationLimit()) { 245 buffer->release(); 246 buffer = NULL; 247 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); 248 break; 249 } 250 ssize_t n = write(mFd, 251 (const uint8_t *)buffer->data() + buffer->range_offset(), 252 buffer->range_length()); 253 254 if (n < (ssize_t)buffer->range_length()) { 255 buffer->release(); 256 buffer = NULL; 257 err = ERROR_IO; 258 break; 259 } 260 261 if (err != OK) { 262 break; 263 } 264 265 if (stoppedPrematurely) { 266 stoppedPrematurely = false; 267 } 268 269 buffer->release(); 270 buffer = NULL; 271 } 272 273 if ((err == OK || err == ERROR_END_OF_STREAM) && stoppedPrematurely) { 274 err = ERROR_MALFORMED; 275 } 276 277 close(mFd); 278 mFd = -1; 279 mReachedEOS = true; 280 if (err == ERROR_END_OF_STREAM) { 281 return OK; 282 } 283 return err; 284} 285 286bool AMRWriter::reachedEOS() { 287 return mReachedEOS; 288} 289 290} // namespace android 291