summaryrefslogtreecommitdiffstats
path: root/media/libaah_rtp/aah_decoder_pump.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libaah_rtp/aah_decoder_pump.cpp')
-rw-r--r--media/libaah_rtp/aah_decoder_pump.cpp519
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