diff options
Diffstat (limited to 'media/libaah_rtp/aah_decoder_pump.cpp')
| -rw-r--r-- | media/libaah_rtp/aah_decoder_pump.cpp | 519 |
1 files changed, 0 insertions, 519 deletions
diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp deleted file mode 100644 index bebba54..0000000 --- a/media/libaah_rtp/aah_decoder_pump.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* - * 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 |
