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, &timestampUs));
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