| /* |
| * 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 |