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