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