AMRWriter.cpp revision a6f61fc8e3d06373c17f0a38ff600e2b71c414fa
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      mInitCheck(mFile != NULL ? OK : NO_INIT),
33      mStarted(false),
34      mPaused(false),
35      mResumed(false) {
36}
37
38AMRWriter::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
46AMRWriter::~AMRWriter() {
47    if (mStarted) {
48        stop();
49    }
50
51    if (mFile != NULL) {
52        fclose(mFile);
53        mFile = NULL;
54    }
55}
56
57status_t AMRWriter::initCheck() const {
58    return mInitCheck;
59}
60
61status_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
101status_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
140status_t AMRWriter::pause() {
141    if (!mStarted) {
142        return OK;
143    }
144    mPaused = true;
145    return OK;
146}
147
148status_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
171bool AMRWriter::exceedsFileSizeLimit() {
172    if (mMaxFileSizeLimitBytes == 0) {
173        return false;
174    }
175    return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
176}
177
178bool AMRWriter::exceedsFileDurationLimit() {
179    if (mMaxFileDurationLimitUs == 0) {
180        return false;
181    }
182    return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
183}
184
185// static
186void *AMRWriter::ThreadWrapper(void *me) {
187    return (void *) static_cast<AMRWriter *>(me)->threadFunc();
188}
189
190status_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, &timestampUs));
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
279bool AMRWriter::reachedEOS() {
280    return mReachedEOS;
281}
282
283}  // namespace android
284