blob: 6436071d1120220fad4ef5975a5cbbf9f6e9b5b5 [file] [log] [blame]
Andreas Huber07bf09d2010-01-25 14:27:12 -08001/*
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>
Andreas Huber07bf09d2010-01-25 14:27:12 -080018#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>
James Dong18244862010-05-11 14:57:02 -070024#include <media/mediarecorder.h>
James Dongc67acb22010-10-07 20:20:59 -070025#include <sys/prctl.h>
James Dong2747e0e2010-11-18 20:59:13 -080026#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
Andreas Huber07bf09d2010-01-25 14:27:12 -080029
30namespace android {
31
32AMRWriter::AMRWriter(const char *filename)
James Dong2747e0e2010-11-18 20:59:13 -080033 : mFd(-1),
34 mInitCheck(NO_INIT),
James Dong08c74732010-06-10 12:28:15 -070035 mStarted(false),
36 mPaused(false),
37 mResumed(false) {
James Dong2747e0e2010-11-18 20:59:13 -080038
James Dongf84433f2011-03-21 14:26:18 -070039 mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR);
James Dong2747e0e2010-11-18 20:59:13 -080040 if (mFd >= 0) {
41 mInitCheck = OK;
42 }
Andreas Huber07bf09d2010-01-25 14:27:12 -080043}
44
45AMRWriter::AMRWriter(int fd)
James Dong2747e0e2010-11-18 20:59:13 -080046 : mFd(dup(fd)),
47 mInitCheck(mFd < 0? NO_INIT: OK),
James Dong08c74732010-06-10 12:28:15 -070048 mStarted(false),
49 mPaused(false),
50 mResumed(false) {
Andreas Huber07bf09d2010-01-25 14:27:12 -080051}
52
53AMRWriter::~AMRWriter() {
54 if (mStarted) {
55 stop();
56 }
57
James Dong2747e0e2010-11-18 20:59:13 -080058 if (mFd != -1) {
59 close(mFd);
60 mFd = -1;
Andreas Huber07bf09d2010-01-25 14:27:12 -080061 }
62}
63
64status_t AMRWriter::initCheck() const {
65 return mInitCheck;
66}
67
68status_t AMRWriter::addSource(const sp<MediaSource> &source) {
Andreas Huber07bf09d2010-01-25 14:27:12 -080069 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";
James Dong2747e0e2010-11-18 20:59:13 -0800100 ssize_t n = strlen(kHeader);
James Dongb1262a82010-11-16 14:04:54 -0800101 if (write(mFd, kHeader, n) != n) {
Andreas Huber07bf09d2010-01-25 14:27:12 -0800102 return ERROR_IO;
103 }
104
105 return OK;
106}
107
James Dong6feaa462010-06-20 08:20:54 -0700108status_t AMRWriter::start(MetaData *params) {
Andreas Huber07bf09d2010-01-25 14:27:12 -0800109 if (mInitCheck != OK) {
110 return mInitCheck;
111 }
112
James Dong08c74732010-06-10 12:28:15 -0700113 if (mSource == NULL) {
Andreas Huber07bf09d2010-01-25 14:27:12 -0800114 return UNKNOWN_ERROR;
115 }
116
James Dong08c74732010-06-10 12:28:15 -0700117 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
Andreas Huber07bf09d2010-01-25 14:27:12 -0800126 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
Andreas Huber996dddf2010-01-25 15:30:31 -0800136 mReachedEOS = false;
Andreas Huber07bf09d2010-01-25 14:27:12 -0800137 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
James Dongd0366622010-08-18 19:10:39 -0700147status_t AMRWriter::pause() {
James Dong08c74732010-06-10 12:28:15 -0700148 if (!mStarted) {
James Dongd0366622010-08-18 19:10:39 -0700149 return OK;
James Dong08c74732010-06-10 12:28:15 -0700150 }
151 mPaused = true;
James Dongd0366622010-08-18 19:10:39 -0700152 return OK;
James Dong08c74732010-06-10 12:28:15 -0700153}
154
James Dongd0366622010-08-18 19:10:39 -0700155status_t AMRWriter::stop() {
Andreas Hubere2018ca2010-03-23 14:33:02 -0700156 if (!mStarted) {
James Dongd0366622010-08-18 19:10:39 -0700157 return OK;
Andreas Huber07bf09d2010-01-25 14:27:12 -0800158 }
159
Andreas Hubere2018ca2010-03-23 14:33:02 -0700160 mDone = true;
161
Andreas Huber07bf09d2010-01-25 14:27:12 -0800162 void *dummy;
163 pthread_join(mThread, &dummy);
164
James Dongd0366622010-08-18 19:10:39 -0700165 status_t err = (status_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 }
Andreas Huber07bf09d2010-01-25 14:27:12 -0800173
174 mStarted = false;
James Dongd0366622010-08-18 19:10:39 -0700175 return err;
Andreas Huber07bf09d2010-01-25 14:27:12 -0800176}
177
James Dong18244862010-05-11 14:57:02 -0700178bool 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
Andreas Huber07bf09d2010-01-25 14:27:12 -0800192// static
193void *AMRWriter::ThreadWrapper(void *me) {
James Dongd0366622010-08-18 19:10:39 -0700194 return (void *) static_cast<AMRWriter *>(me)->threadFunc();
Andreas Huber07bf09d2010-01-25 14:27:12 -0800195}
196
James Dongd0366622010-08-18 19:10:39 -0700197status_t AMRWriter::threadFunc() {
James Dong18244862010-05-11 14:57:02 -0700198 mEstimatedDurationUs = 0;
199 mEstimatedSizeBytes = 0;
James Dong68510e62010-05-14 11:48:00 -0700200 bool stoppedPrematurely = true;
James Dong08c74732010-06-10 12:28:15 -0700201 int64_t previousPausedDurationUs = 0;
202 int64_t maxTimestampUs = 0;
James Dongd0366622010-08-18 19:10:39 -0700203 status_t err = OK;
James Dong08c74732010-06-10 12:28:15 -0700204
James Dongc67acb22010-10-07 20:20:59 -0700205 prctl(PR_SET_NAME, (unsigned long)"AMRWriter", 0, 0, 0);
Andreas Hubere2018ca2010-03-23 14:33:02 -0700206 while (!mDone) {
Andreas Huber07bf09d2010-01-25 14:27:12 -0800207 MediaBuffer *buffer;
James Dongd0366622010-08-18 19:10:39 -0700208 err = mSource->read(&buffer);
Andreas Huber07bf09d2010-01-25 14:27:12 -0800209
210 if (err != OK) {
211 break;
212 }
213
James Dong08c74732010-06-10 12:28:15 -0700214 if (mPaused) {
215 buffer->release();
216 buffer = NULL;
217 continue;
218 }
219
James Dong18244862010-05-11 14:57:02 -0700220 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, &timestampUs));
230 if (timestampUs > mEstimatedDurationUs) {
231 mEstimatedDurationUs = timestampUs;
232 }
James Dong08c74732010-06-10 12:28:15 -0700233 if (mResumed) {
234 previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000);
235 mResumed = false;
236 }
237 timestampUs -= previousPausedDurationUs;
238 LOGV("time stamp: %lld, previous paused duration: %lld",
239 timestampUs, previousPausedDurationUs);
240 if (timestampUs > maxTimestampUs) {
241 maxTimestampUs = timestampUs;
242 }
243
James Dong18244862010-05-11 14:57:02 -0700244 if (exceedsFileDurationLimit()) {
245 buffer->release();
246 buffer = NULL;
247 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
248 break;
249 }
James Dongb1262a82010-11-16 14:04:54 -0800250 ssize_t n = write(mFd,
251 (const uint8_t *)buffer->data() + buffer->range_offset(),
252 buffer->range_length());
Andreas Huber07bf09d2010-01-25 14:27:12 -0800253
Andreas Huber07bf09d2010-01-25 14:27:12 -0800254 if (n < (ssize_t)buffer->range_length()) {
Andreas Huber259b7c12010-02-10 15:04:31 -0800255 buffer->release();
256 buffer = NULL;
257
Andreas Huber07bf09d2010-01-25 14:27:12 -0800258 break;
259 }
Andreas Huber259b7c12010-02-10 15:04:31 -0800260
James Dong68510e62010-05-14 11:48:00 -0700261 // XXX: How to tell it is stopped prematurely?
262 if (stoppedPrematurely) {
263 stoppedPrematurely = false;
264 }
265
Andreas Huber259b7c12010-02-10 15:04:31 -0800266 buffer->release();
267 buffer = NULL;
Andreas Huber07bf09d2010-01-25 14:27:12 -0800268 }
Andreas Huber996dddf2010-01-25 15:30:31 -0800269
James Dong68510e62010-05-14 11:48:00 -0700270 if (stoppedPrematurely) {
James Dong7fc8b4f2011-03-18 11:25:41 -0700271 notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
James Dong68510e62010-05-14 11:48:00 -0700272 }
273
James Dong2747e0e2010-11-18 20:59:13 -0800274 close(mFd);
275 mFd = -1;
Andreas Huber996dddf2010-01-25 15:30:31 -0800276 mReachedEOS = true;
James Dongd0366622010-08-18 19:10:39 -0700277 if (err == ERROR_END_OF_STREAM) {
278 return OK;
279 }
280 return err;
Andreas Huber996dddf2010-01-25 15:30:31 -0800281}
282
283bool AMRWriter::reachedEOS() {
Andreas Huber996dddf2010-01-25 15:30:31 -0800284 return mReachedEOS;
Andreas Huber07bf09d2010-01-25 14:27:12 -0800285}
286
287} // namespace android