AMRWriter.cpp revision bc07bcc65e91c7aea9713631ae67760dcf1b0286
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/AMRWriter.h> 18#include <media/stagefright/MediaBuffer.h> 19#include <media/stagefright/MediaDebug.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/resource.h> 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <fcntl.h> 30 31namespace android { 32 33AMRWriter::AMRWriter(const char *filename) 34 : mFd(-1), 35 mInitCheck(NO_INIT), 36 mStarted(false), 37 mPaused(false), 38 mResumed(false) { 39 40 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC); 41 if (mFd >= 0) { 42 mInitCheck = OK; 43 } 44} 45 46AMRWriter::AMRWriter(int fd) 47 : mFd(dup(fd)), 48 mInitCheck(mFd < 0? NO_INIT: OK), 49 mStarted(false), 50 mPaused(false), 51 mResumed(false) { 52} 53 54AMRWriter::~AMRWriter() { 55 if (mStarted) { 56 stop(); 57 } 58 59 if (mFd != -1) { 60 close(mFd); 61 mFd = -1; 62 } 63} 64 65status_t AMRWriter::initCheck() const { 66 return mInitCheck; 67} 68 69status_t AMRWriter::addSource(const sp<MediaSource> &source) { 70 if (mInitCheck != OK) { 71 return mInitCheck; 72 } 73 74 if (mSource != NULL) { 75 // AMR files only support a single track of audio. 76 return UNKNOWN_ERROR; 77 } 78 79 sp<MetaData> meta = source->getFormat(); 80 81 const char *mime; 82 CHECK(meta->findCString(kKeyMIMEType, &mime)); 83 84 bool isWide = false; 85 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { 86 isWide = true; 87 } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { 88 return ERROR_UNSUPPORTED; 89 } 90 91 int32_t channelCount; 92 int32_t sampleRate; 93 CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); 94 CHECK_EQ(channelCount, 1); 95 CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); 96 CHECK_EQ(sampleRate, (isWide ? 16000 : 8000)); 97 98 mSource = source; 99 100 const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n"; 101 ssize_t n = strlen(kHeader); 102 if (write(mFd, kHeader, n) != n) { 103 return ERROR_IO; 104 } 105 106 return OK; 107} 108 109status_t AMRWriter::start(MetaData *params) { 110 if (mInitCheck != OK) { 111 return mInitCheck; 112 } 113 114 if (mSource == NULL) { 115 return UNKNOWN_ERROR; 116 } 117 118 if (mStarted && mPaused) { 119 mPaused = false; 120 mResumed = true; 121 return OK; 122 } else if (mStarted) { 123 // Already started, does nothing 124 return OK; 125 } 126 127 status_t err = mSource->start(); 128 129 if (err != OK) { 130 return err; 131 } 132 133 pthread_attr_t attr; 134 pthread_attr_init(&attr); 135 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 136 137 mReachedEOS = false; 138 mDone = false; 139 140 pthread_create(&mThread, &attr, ThreadWrapper, this); 141 pthread_attr_destroy(&attr); 142 143 mStarted = true; 144 145 return OK; 146} 147 148status_t AMRWriter::pause() { 149 if (!mStarted) { 150 return OK; 151 } 152 mPaused = true; 153 return OK; 154} 155 156status_t AMRWriter::stop() { 157 if (!mStarted) { 158 return OK; 159 } 160 161 mDone = true; 162 163 void *dummy; 164 pthread_join(mThread, &dummy); 165 166 status_t err = (status_t) dummy; 167 { 168 status_t status = mSource->stop(); 169 if (err == OK && 170 (status != OK && status != ERROR_END_OF_STREAM)) { 171 err = status; 172 } 173 } 174 175 mStarted = false; 176 return err; 177} 178 179bool AMRWriter::exceedsFileSizeLimit() { 180 if (mMaxFileSizeLimitBytes == 0) { 181 return false; 182 } 183 return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes; 184} 185 186bool AMRWriter::exceedsFileDurationLimit() { 187 if (mMaxFileDurationLimitUs == 0) { 188 return false; 189 } 190 return mEstimatedDurationUs >= mMaxFileDurationLimitUs; 191} 192 193// static 194void *AMRWriter::ThreadWrapper(void *me) { 195 return (void *) static_cast<AMRWriter *>(me)->threadFunc(); 196} 197 198status_t AMRWriter::threadFunc() { 199 mEstimatedDurationUs = 0; 200 mEstimatedSizeBytes = 0; 201 bool stoppedPrematurely = true; 202 int64_t previousPausedDurationUs = 0; 203 int64_t maxTimestampUs = 0; 204 status_t err = OK; 205 206 prctl(PR_SET_NAME, (unsigned long)"AMRWriter", 0, 0, 0); 207 while (!mDone) { 208 MediaBuffer *buffer; 209 err = mSource->read(&buffer); 210 211 if (err != OK) { 212 break; 213 } 214 215 if (mPaused) { 216 buffer->release(); 217 buffer = NULL; 218 continue; 219 } 220 221 mEstimatedSizeBytes += buffer->range_length(); 222 if (exceedsFileSizeLimit()) { 223 buffer->release(); 224 buffer = NULL; 225 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); 226 break; 227 } 228 229 int64_t timestampUs; 230 CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); 231 if (timestampUs > mEstimatedDurationUs) { 232 mEstimatedDurationUs = timestampUs; 233 } 234 if (mResumed) { 235 previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000); 236 mResumed = false; 237 } 238 timestampUs -= previousPausedDurationUs; 239 LOGV("time stamp: %lld, previous paused duration: %lld", 240 timestampUs, previousPausedDurationUs); 241 if (timestampUs > maxTimestampUs) { 242 maxTimestampUs = timestampUs; 243 } 244 245 if (exceedsFileDurationLimit()) { 246 buffer->release(); 247 buffer = NULL; 248 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); 249 break; 250 } 251 ssize_t n = write(mFd, 252 (const uint8_t *)buffer->data() + buffer->range_offset(), 253 buffer->range_length()); 254 255 if (n < (ssize_t)buffer->range_length()) { 256 buffer->release(); 257 buffer = NULL; 258 259 break; 260 } 261 262 // XXX: How to tell it is stopped prematurely? 263 if (stoppedPrematurely) { 264 stoppedPrematurely = false; 265 } 266 267 buffer->release(); 268 buffer = NULL; 269 } 270 271 if (stoppedPrematurely) { 272 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); 273 } 274 275 close(mFd); 276 mFd = -1; 277 mReachedEOS = true; 278 if (err == ERROR_END_OF_STREAM) { 279 return OK; 280 } 281 return err; 282} 283 284bool AMRWriter::reachedEOS() { 285 return mReachedEOS; 286} 287 288} // namespace android 289