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