C++程序  |  520行  |  17.87 KB

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "LibAAH_RTP"
//#define LOG_NDEBUG 0
#include <utils/Log.h>

#include <poll.h>
#include <pthread.h>

#include <common_time/cc_helper.h>
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/Utils.h>
#include <utils/Timers.h>
#include <utils/threads.h>

#include "aah_decoder_pump.h"

namespace android {

static const long long kLongDecodeErrorThreshold = 1000000ll;
static const uint32_t kMaxLongErrorsBeforeFatal = 3;
static const uint32_t kMaxErrorsBeforeFatal = 60;

AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx)
    : omx_(omx)
    , thread_status_(OK)
    , renderer_(NULL)
    , last_queued_pts_valid_(false)
    , last_queued_pts_(0)
    , last_ts_transform_valid_(false)
    , last_volume_(0xFF) {
    thread_ = new ThreadWrapper(this);
}

AAH_DecoderPump::~AAH_DecoderPump() {
    shutdown();
}

status_t AAH_DecoderPump::initCheck() {
    if (thread_ == NULL) {
        ALOGE("Failed to allocate thread");
        return NO_MEMORY;
    }

    return OK;
}

status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) {
    if (NULL == buf) {
        return BAD_VALUE;
    }

    if (OK != thread_status_) {
        return thread_status_;
    }

    {   // Explicit scope for AutoMutex pattern.
        AutoMutex lock(&thread_lock_);
        in_queue_.push_back(buf);
    }

    thread_cond_.signal();

    return OK;
}

void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) {
    Mutex::Autolock lock(&render_lock_);
    sp<MetaData> meta;
    int64_t ts;
    status_t res;

    // Fetch the metadata and make sure the sample has a timestamp.  We
    // cannot render samples which are missing PTSs.
    meta = decoded_sample->meta_data();
    if ((meta == NULL) || (!meta->findInt64(kKeyTime, &ts))) {
        ALOGV("Decoded sample missing timestamp, cannot render.");
        CHECK(false);
    } else {
        // If we currently are not holding on to a renderer, go ahead and
        // make one now.
        if (NULL == renderer_) {
            renderer_ = new TimedAudioTrack();
            if (NULL != renderer_) {
                int frameCount;
                AudioTrack::getMinFrameCount(&frameCount,
                        AUDIO_STREAM_DEFAULT,
                        static_cast<int>(format_sample_rate_));
                audio_channel_mask_t ch_format =
                        audio_channel_out_mask_from_count(format_channels_);

                res = renderer_->set(AUDIO_STREAM_DEFAULT,
                        format_sample_rate_,
                        AUDIO_FORMAT_PCM_16_BIT,
                        ch_format,
                        frameCount);
                if (res != OK) {
                    ALOGE("Failed to setup audio renderer. (res = %d)", res);
                    delete renderer_;
                    renderer_ = NULL;
                } else {
                    CHECK(last_ts_transform_valid_);

                    res = renderer_->setMediaTimeTransform(
                            last_ts_transform_, TimedAudioTrack::COMMON_TIME);
                    if (res != NO_ERROR) {
                        ALOGE("Failed to set media time transform on AudioTrack"
                              " (res = %d)", res);
                        delete renderer_;
                        renderer_ = NULL;
                    } else {
                        float volume = static_cast<float>(last_volume_)
                                     / 255.0f;
                        if (renderer_->setVolume(volume, volume) != OK) {
                            ALOGW("%s: setVolume failed", __FUNCTION__);
                        }

                        renderer_->start();
                    }
                }
            } else {
                ALOGE("Failed to allocate AudioTrack to use as a renderer.");
            }
        }

        if (NULL != renderer_) {
            uint8_t* decoded_data =
                reinterpret_cast<uint8_t*>(decoded_sample->data());
            uint32_t decoded_amt  = decoded_sample->range_length();
            decoded_data += decoded_sample->range_offset();

            sp<IMemory> pcm_payload;
            res = renderer_->allocateTimedBuffer(decoded_amt, &pcm_payload);
            if (res != OK) {
                ALOGE("Failed to allocate %d byte audio track buffer."
                      " (res = %d)", decoded_amt, res);
            } else {
                memcpy(pcm_payload->pointer(), decoded_data, decoded_amt);

                res = renderer_->queueTimedBuffer(pcm_payload, ts);
                if (res != OK) {
                    ALOGE("Failed to queue %d byte audio track buffer with"
                          " media PTS %lld. (res = %d)", decoded_amt, ts, res);
                } else {
                    last_queued_pts_valid_ = true;
                    last_queued_pts_ = ts;
                }
            }

        } else {
            ALOGE("No renderer, dropping audio payload.");
        }
    }
}

void AAH_DecoderPump::stopAndCleanupRenderer() {
    if (NULL == renderer_) {
        return;
    }

    renderer_->stop();
    delete renderer_;
    renderer_ = NULL;
}

void AAH_DecoderPump::setRenderTSTransform(const LinearTransform& trans) {
    Mutex::Autolock lock(&render_lock_);

    if (last_ts_transform_valid_ && !memcmp(&trans,
                                            &last_ts_transform_,
                                            sizeof(trans))) {
        return;
    }

    last_ts_transform_       = trans;
    last_ts_transform_valid_ = true;

    if (NULL != renderer_) {
        status_t res = renderer_->setMediaTimeTransform(
                last_ts_transform_, TimedAudioTrack::COMMON_TIME);
        if (res != NO_ERROR) {
            ALOGE("Failed to set media time transform on AudioTrack"
                  " (res = %d)", res);
        }
    }
}

void AAH_DecoderPump::setRenderVolume(uint8_t volume) {
    Mutex::Autolock lock(&render_lock_);

    if (volume == last_volume_) {
        return;
    }

    last_volume_ = volume;
    if (renderer_ != NULL) {
        float volume = static_cast<float>(last_volume_) / 255.0f;
        if (renderer_->setVolume(volume, volume) != OK) {
            ALOGW("%s: setVolume failed", __FUNCTION__);
        }
    }
}

// isAboutToUnderflow is something of a hack used to figure out when it might be
// time to give up on trying to fill in a gap in the RTP sequence and simply
// move on with a discontinuity.  If we had perfect knowledge of when we were
// going to underflow, it would not be a hack, but unfortunately we do not.
// Right now, we just take the PTS of the last sample queued, and check to see
// if its presentation time is within kAboutToUnderflowThreshold from now.  If
// it is, then we say that we are about to underflow.  This decision is based on
// two (possibly invalid) assumptions.
//
// 1) The transmitter is leading the clock by more than
//    kAboutToUnderflowThreshold.
// 2) The delta between the PTS of the last sample queued and the next sample
//    is less than the transmitter's clock lead amount.
//
// Right now, the default transmitter lead time is 1 second, which is a pretty
// large number and greater than the 50mSec that kAboutToUnderflowThreshold is
// currently set to.  This should satisfy assumption #1 for now, but changes to
// the transmitter clock lead time could effect this.
//
// For non-sparse streams with a homogeneous sample rate (the vast majority of
// streams in the world), the delta between any two adjacent PTSs will always be
// the homogeneous sample period.  It is very uncommon to see a sample period
// greater than the 1 second clock lead we are currently using, and you
// certainly will not see it in an MP3 file which should satisfy assumption #2.
// Sparse audio streams (where no audio is transmitted for long periods of
// silence) and extremely low framerate video stream (like an MPEG-2 slideshow
// or the video stream for a pay TV audio channel) are examples of streams which
// might violate assumption #2.
bool AAH_DecoderPump::isAboutToUnderflow(int64_t threshold) {
    Mutex::Autolock lock(&render_lock_);

    // If we have never queued anything to the decoder, we really don't know if
    // we are going to underflow or not.
    if (!last_queued_pts_valid_ || !last_ts_transform_valid_) {
        return false;
    }

    // Don't have access to Common Time?  If so, then things are Very Bad
    // elsewhere in the system; it pretty much does not matter what we do here.
    // Since we cannot really tell if we are about to underflow or not, its
    // probably best to assume that we are not and proceed accordingly.
    int64_t tt_now;
    if (OK != cc_helper_.getCommonTime(&tt_now)) {
        return false;
    }

    // Transform from media time to common time.
    int64_t last_queued_pts_tt;
    if (!last_ts_transform_.doForwardTransform(last_queued_pts_,
                &last_queued_pts_tt)) {
        return false;
    }

    // Check to see if we are underflowing.
    return ((tt_now + threshold - last_queued_pts_tt) > 0);
}

void* AAH_DecoderPump::workThread() {
    // No need to lock when accessing decoder_ from the thread.  The
    // implementation of init and shutdown ensure that other threads never touch
    // decoder_ while the work thread is running.
    CHECK(decoder_ != NULL);
    CHECK(format_  != NULL);

    // Start the decoder and note its result code.  If something goes horribly
    // wrong, callers of queueForDecode and getOutput will be able to detect
    // that the thread encountered a fatal error and shut down by examining
    // thread_status_.
    thread_status_ = decoder_->start(format_.get());
    if (OK != thread_status_) {
        ALOGE("AAH_DecoderPump's work thread failed to start decoder"
              " (res = %d)", thread_status_);
        return NULL;
    }

    DurationTimer decode_timer;
    uint32_t consecutive_long_errors = 0;
    uint32_t consecutive_errors = 0;

    while (!thread_->exitPending()) {
        status_t res;
        MediaBuffer* bufOut = NULL;

        decode_timer.start();
        res = decoder_->read(&bufOut);
        decode_timer.stop();

        if (res == INFO_FORMAT_CHANGED) {
            // Format has changed.  Destroy our current renderer so that a new
            // one can be created during queueToRenderer with the proper format.
            //
            // TODO : In order to transition seamlessly, we should change this
            // to put the old renderer in a queue to play out completely before
            // we destroy it.  We can still create a new renderer, the timed
            // nature of the renderer should ensure a seamless splice.
            stopAndCleanupRenderer();
            res = OK;
        }

        // Try to be a little nuanced in our handling of actual decode errors.
        // Errors could happen because of minor stream corruption or because of
        // transient resource limitations.  In these cases, we would rather drop
        // a little bit of output and ride out the unpleasantness then throw up
        // our hands and abort everything.
        //
        // OTOH - When things are really bad (like we have a non-transient
        // resource or bookkeeping issue, or the stream being fed to us is just
        // complete and total garbage) we really want to terminate playback and
        // raise an error condition all the way up to the application level so
        // they can deal with it.
        //
        // Unfortunately, the error codes returned by the decoder can be a
        // little non-specific.  For example, if an OMXCodec times out
        // attempting to obtain an output buffer, the error we get back is a
        // generic -1.  Try to distinguish between this resource timeout error
        // and ES corruption error by timing how long the decode operation
        // takes.  Maintain accounting for both errors and "long errors".  If we
        // get more than a certain number consecutive errors of either type,
        // consider it fatal and shutdown (which will cause the error to
        // propagate all of the way up to the application level).  The threshold
        // for "long errors" is deliberately much lower than that of normal
        // decode errors, both because of how long they take to happen and
        // because they generally indicate resource limitation errors which are
        // unlikely to go away in pathologically bad cases (in contrast to
        // stream corruption errors which might happen 20 times in a row and
        // then be suddenly OK again)
        if (res != OK) {
            consecutive_errors++;
            if (decode_timer.durationUsecs() >= kLongDecodeErrorThreshold)
                consecutive_long_errors++;

            CHECK(NULL == bufOut);

            ALOGW("%s: Failed to decode data (res = %d)",
                    __PRETTY_FUNCTION__, res);

            if ((consecutive_errors      >= kMaxErrorsBeforeFatal) ||
                (consecutive_long_errors >= kMaxLongErrorsBeforeFatal)) {
                ALOGE("%s: Maximum decode error threshold has been reached."
                      " There have been %d consecutive decode errors, and %d"
                      " consecutive decode operations which resulted in errors"
                      " and took more than %lld uSec to process.  The last"
                      " decode operation took %lld uSec.",
                      __PRETTY_FUNCTION__,
                      consecutive_errors, consecutive_long_errors,
                      kLongDecodeErrorThreshold, decode_timer.durationUsecs());
                thread_status_ = res;
                break;
            }

            continue;
        }

        if (NULL == bufOut) {
            ALOGW("%s: Successful decode, but no buffer produced",
                    __PRETTY_FUNCTION__);
            continue;
        }

        // Successful decode (with actual output produced).  Clear the error
        // counters.
        consecutive_errors = 0;
        consecutive_long_errors = 0;

        queueToRenderer(bufOut);
        bufOut->release();
    }

    decoder_->stop();
    stopAndCleanupRenderer();

    return NULL;
}

status_t AAH_DecoderPump::init(const sp<MetaData>& params) {
    Mutex::Autolock lock(&init_lock_);

    if (decoder_ != NULL) {
        // already inited
        return OK;
    }

    if (params == NULL) {
        return BAD_VALUE;
    }

    if (!params->findInt32(kKeyChannelCount, &format_channels_)) {
        return BAD_VALUE;
    }

    if (!params->findInt32(kKeySampleRate, &format_sample_rate_)) {
        return BAD_VALUE;
    }

    CHECK(OK == thread_status_);
    CHECK(decoder_ == NULL);

    status_t ret_val = UNKNOWN_ERROR;

    // Cache the format and attempt to create the decoder.
    format_  = params;
    decoder_ = OMXCodec::Create(
            omx_.interface(),       // IOMX Handle
            format_,                // Metadata for substream (indicates codec)
            false,                  // Make a decoder, not an encoder
            sp<MediaSource>(this)); // We will be the source for this codec.

    if (decoder_ == NULL) {
      ALOGE("Failed to allocate decoder in %s", __PRETTY_FUNCTION__);
      goto bailout;
    }

    // Fire up the pump thread.  It will take care of starting and stopping the
    // decoder.
    ret_val = thread_->run("aah_decode_pump", ANDROID_PRIORITY_AUDIO);
    if (OK != ret_val) {
        ALOGE("Failed to start work thread in %s (res = %d)",
                __PRETTY_FUNCTION__, ret_val);
        goto bailout;
    }

bailout:
    if (OK != ret_val) {
        decoder_ = NULL;
        format_  = NULL;
    }

    return OK;
}

status_t AAH_DecoderPump::shutdown() {
    Mutex::Autolock lock(&init_lock_);
    return shutdown_l();
}

status_t AAH_DecoderPump::shutdown_l() {
    thread_->requestExit();
    thread_cond_.signal();
    thread_->requestExitAndWait();

    for (MBQueue::iterator iter = in_queue_.begin();
         iter != in_queue_.end();
         ++iter) {
        (*iter)->release();
    }
    in_queue_.clear();

    last_queued_pts_valid_   = false;
    last_ts_transform_valid_ = false;
    last_volume_             = 0xFF;
    thread_status_           = OK;

    decoder_ = NULL;
    format_  = NULL;

    return OK;
}

status_t AAH_DecoderPump::read(MediaBuffer **buffer,
                               const ReadOptions *options) {
    if (!buffer) {
        return BAD_VALUE;
    }

    *buffer = NULL;

    // While its not time to shut down, and we have no data to process, wait.
    AutoMutex lock(&thread_lock_);
    while (!thread_->exitPending() && in_queue_.empty())
        thread_cond_.wait(thread_lock_);

    // At this point, if its not time to shutdown then we must have something to
    // process.  Go ahead and pop the front of the queue for processing.
    if (!thread_->exitPending()) {
        CHECK(!in_queue_.empty());

        *buffer = *(in_queue_.begin());
        in_queue_.erase(in_queue_.begin());
    }

    // If we managed to get a buffer, then everything must be OK.  If not, then
    // we must be shutting down.
    return (NULL == *buffer) ? INVALID_OPERATION : OK;
}

AAH_DecoderPump::ThreadWrapper::ThreadWrapper(AAH_DecoderPump* owner)
    : Thread(false /* canCallJava*/ )
    , owner_(owner) {
}

bool AAH_DecoderPump::ThreadWrapper::threadLoop() {
    CHECK(NULL != owner_);
    owner_->workThread();
    return false;
}

}  // namespace android