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