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 <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/MetaData.h>
     29 #include <media/MediaSource.h>
     30 #include <media/mediarecorder.h>
     31 
     32 namespace android {
     33 
     34 AMRWriter::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 
     42 AMRWriter::~AMRWriter() {
     43     if (mStarted) {
     44         reset();
     45     }
     46 
     47     if (mFd != -1) {
     48         close(mFd);
     49         mFd = -1;
     50     }
     51 }
     52 
     53 status_t AMRWriter::initCheck() const {
     54     return mInitCheck;
     55 }
     56 
     57 status_t AMRWriter::addSource(const sp<MediaSource> &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 
     97 status_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 
    136 status_t AMRWriter::pause() {
    137     if (!mStarted) {
    138         return OK;
    139     }
    140     mPaused = true;
    141     return OK;
    142 }
    143 
    144 status_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 
    167 bool AMRWriter::exceedsFileSizeLimit() {
    168     if (mMaxFileSizeLimitBytes == 0) {
    169         return false;
    170     }
    171     return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
    172 }
    173 
    174 bool AMRWriter::exceedsFileDurationLimit() {
    175     if (mMaxFileDurationLimitUs == 0) {
    176         return false;
    177     }
    178     return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
    179 }
    180 
    181 // static
    182 void *AMRWriter::ThreadWrapper(void *me) {
    183     return (void *)(uintptr_t) static_cast<AMRWriter *>(me)->threadFunc();
    184 }
    185 
    186 status_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         MediaBufferBase *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 
    275 bool AMRWriter::reachedEOS() {
    276     return mReachedEOS;
    277 }
    278 
    279 }  // namespace android
    280