diff options
author | John Grossman <johngro@google.com> | 2012-08-13 09:52:14 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-08-13 09:52:14 -0700 |
commit | d58a68a4d6915d43d8b9d02ebfe04be62efe2eb1 (patch) | |
tree | b2bae0d6b37fff0a0aff3b3456c2831425d7a0f0 /media | |
parent | 75767ab7cc1aee315d655538322a8349faa61a64 (diff) | |
parent | 8ec1f2a4f3c72fe7472f8b8fa227f6b7bbc9279b (diff) |
Merge changes Ibe7e6d4b,Icf8904db into jb-mr1-dev
* changes:
Move the AAH RTP code out of framework and into vendor.
Add the ability to dynamicaly register MediaPlayer types.
Diffstat (limited to 'media')
25 files changed, 513 insertions, 6343 deletions
diff --git a/media/libaah_rtp/Android.mk b/media/libaah_rtp/Android.mk deleted file mode 100644 index df533ecd..00000000 --- a/media/libaah_rtp/Android.mk +++ /dev/null @@ -1,40 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -# -# libaah_rtp -# - -include $(CLEAR_VARS) - -LOCAL_MODULE := libaah_rtp -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := \ - aah_decoder_pump.cpp \ - aah_rx_player.cpp \ - aah_rx_player_core.cpp \ - aah_rx_player_ring_buffer.cpp \ - aah_rx_player_substream.cpp \ - aah_tx_packet.cpp \ - aah_tx_player.cpp \ - aah_tx_sender.cpp \ - pipe_event.cpp - -LOCAL_C_INCLUDES := \ - frameworks/av/include \ - frameworks/av/media \ - frameworks/av/media/libstagefright \ - frameworks/native/include/media/openmax - -LOCAL_SHARED_LIBRARIES := \ - libcommon_time_client \ - libbinder \ - libmedia \ - libmedia_native \ - libstagefright \ - libstagefright_foundation \ - libutils - -LOCAL_LDLIBS := \ - -lpthread - -include $(BUILD_SHARED_LIBRARY) diff --git a/media/libaah_rtp/MODULE_LICENSE_APACHE2 b/media/libaah_rtp/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29b..00000000 --- a/media/libaah_rtp/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/media/libaah_rtp/NOTICE b/media/libaah_rtp/NOTICE deleted file mode 100644 index c5b1efa7..00000000 --- a/media/libaah_rtp/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp deleted file mode 100644 index bebba54e..00000000 --- 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 diff --git a/media/libaah_rtp/aah_decoder_pump.h b/media/libaah_rtp/aah_decoder_pump.h deleted file mode 100644 index 4d57e49f..00000000 --- a/media/libaah_rtp/aah_decoder_pump.h +++ /dev/null @@ -1,107 +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. - */ - -#ifndef __DECODER_PUMP_H__ -#define __DECODER_PUMP_H__ - -#include <pthread.h> - -#include <common_time/cc_helper.h> -#include <media/stagefright/MediaSource.h> -#include <utils/LinearTransform.h> -#include <utils/List.h> -#include <utils/threads.h> - -namespace android { - -class MetaData; -class OMXClient; -class TimedAudioTrack; - -class AAH_DecoderPump : public MediaSource { - public: - explicit AAH_DecoderPump(OMXClient& omx); - status_t initCheck(); - - status_t queueForDecode(MediaBuffer* buf); - - status_t init(const sp<MetaData>& params); - status_t shutdown(); - - void setRenderTSTransform(const LinearTransform& trans); - void setRenderVolume(uint8_t volume); - bool isAboutToUnderflow(int64_t threshold); - bool getStatus() const { return thread_status_; } - - // MediaSource methods - virtual status_t start(MetaData *params) { return OK; } - virtual sp<MetaData> getFormat() { return format_; } - virtual status_t stop() { return OK; } - virtual status_t read(MediaBuffer **buffer, - const ReadOptions *options); - - protected: - virtual ~AAH_DecoderPump(); - - private: - class ThreadWrapper : public Thread { - public: - friend class AAH_DecoderPump; - explicit ThreadWrapper(AAH_DecoderPump* owner); - - private: - virtual bool threadLoop(); - AAH_DecoderPump* owner_; - - DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper); - }; - - void* workThread(); - virtual status_t shutdown_l(); - void queueToRenderer(MediaBuffer* decoded_sample); - void stopAndCleanupRenderer(); - - sp<MetaData> format_; - int32_t format_channels_; // channel count, not channel mask - int32_t format_sample_rate_; - - sp<MediaSource> decoder_; - OMXClient& omx_; - Mutex init_lock_; - - sp<ThreadWrapper> thread_; - Condition thread_cond_; - Mutex thread_lock_; - status_t thread_status_; - - Mutex render_lock_; - TimedAudioTrack* renderer_; - bool last_queued_pts_valid_; - int64_t last_queued_pts_; - bool last_ts_transform_valid_; - LinearTransform last_ts_transform_; - uint8_t last_volume_; - CCHelper cc_helper_; - - // protected by the thread_lock_ - typedef List<MediaBuffer*> MBQueue; - MBQueue in_queue_; - - DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump); -}; - -} // namespace android -#endif // __DECODER_PUMP_H__ diff --git a/media/libaah_rtp/aah_rx_player.cpp b/media/libaah_rtp/aah_rx_player.cpp deleted file mode 100644 index 9dd79fd2..00000000 --- a/media/libaah_rtp/aah_rx_player.cpp +++ /dev/null @@ -1,288 +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 <binder/IServiceManager.h> -#include <media/MediaPlayerInterface.h> -#include <utils/Log.h> - -#include "aah_rx_player.h" - -namespace android { - -const uint32_t AAH_RXPlayer::kRTPRingBufferSize = 1 << 10; - -sp<MediaPlayerBase> createAAH_RXPlayer() { - sp<MediaPlayerBase> ret = new AAH_RXPlayer(); - return ret; -} - -AAH_RXPlayer::AAH_RXPlayer() - : ring_buffer_(kRTPRingBufferSize) - , substreams_(NULL) { - thread_wrapper_ = new ThreadWrapper(*this); - - is_playing_ = false; - multicast_joined_ = false; - transmitter_known_ = false; - current_epoch_known_ = false; - data_source_set_ = false; - sock_fd_ = -1; - - substreams_.setCapacity(4); - - memset(&listen_addr_, 0, sizeof(listen_addr_)); - memset(&transmitter_addr_, 0, sizeof(transmitter_addr_)); - - fetchAudioFlinger(); -} - -AAH_RXPlayer::~AAH_RXPlayer() { - reset_l(); - CHECK(substreams_.size() == 0); - omx_.disconnect(); -} - -status_t AAH_RXPlayer::initCheck() { - if (thread_wrapper_ == NULL) { - ALOGE("Failed to allocate thread wrapper!"); - return NO_MEMORY; - } - - if (!ring_buffer_.initCheck()) { - ALOGE("Failed to allocate reassembly ring buffer!"); - return NO_MEMORY; - } - - // Check for the presense of the common time service by attempting to query - // for CommonTime's frequency. If we get an error back, we cannot talk to - // the service at all and should abort now. - status_t res; - uint64_t freq; - res = cc_helper_.getCommonFreq(&freq); - if (OK != res) { - ALOGE("Failed to connect to common time service!"); - return res; - } - - return omx_.connect(); -} - -status_t AAH_RXPlayer::setDataSource( - const char *url, - const KeyedVector<String8, String8> *headers) { - AutoMutex api_lock(&api_lock_); - uint32_t a, b, c, d; - uint16_t port; - - if (data_source_set_) { - return INVALID_OPERATION; - } - - if (NULL == url) { - return BAD_VALUE; - } - - if (5 != sscanf(url, "%*[^:/]://%u.%u.%u.%u:%hu", &a, &b, &c, &d, &port)) { - ALOGE("Failed to parse URL \"%s\"", url); - return BAD_VALUE; - } - - if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (port == 0)) { - ALOGE("Bad multicast address \"%s\"", url); - return BAD_VALUE; - } - - ALOGI("setDataSource :: %u.%u.%u.%u:%hu", a, b, c, d, port); - - a = (a << 24) | (b << 16) | (c << 8) | d; - - memset(&listen_addr_, 0, sizeof(listen_addr_)); - listen_addr_.sin_family = AF_INET; - listen_addr_.sin_port = htons(port); - listen_addr_.sin_addr.s_addr = htonl(a); - data_source_set_ = true; - - return OK; -} - -status_t AAH_RXPlayer::setDataSource(int fd, int64_t offset, int64_t length) { - return INVALID_OPERATION; -} - -status_t AAH_RXPlayer::setVideoSurface(const sp<Surface>& surface) { - return OK; -} - -status_t AAH_RXPlayer::setVideoSurfaceTexture( - const sp<ISurfaceTexture>& surfaceTexture) { - return OK; -} - -status_t AAH_RXPlayer::prepare() { - return OK; -} - -status_t AAH_RXPlayer::prepareAsync() { - sendEvent(MEDIA_PREPARED); - return OK; -} - -status_t AAH_RXPlayer::start() { - AutoMutex api_lock(&api_lock_); - - if (is_playing_) { - return OK; - } - - status_t res = startWorkThread(); - is_playing_ = (res == OK); - return res; -} - -status_t AAH_RXPlayer::stop() { - return pause(); -} - -status_t AAH_RXPlayer::pause() { - AutoMutex api_lock(&api_lock_); - stopWorkThread(); - CHECK(sock_fd_ < 0); - is_playing_ = false; - return OK; -} - -bool AAH_RXPlayer::isPlaying() { - AutoMutex api_lock(&api_lock_); - return is_playing_; -} - -status_t AAH_RXPlayer::seekTo(int msec) { - sendEvent(MEDIA_SEEK_COMPLETE); - return OK; -} - -status_t AAH_RXPlayer::getCurrentPosition(int *msec) { - if (NULL != msec) { - *msec = 0; - } - return OK; -} - -status_t AAH_RXPlayer::getDuration(int *msec) { - if (NULL != msec) { - *msec = 1; - } - return OK; -} - -status_t AAH_RXPlayer::reset() { - AutoMutex api_lock(&api_lock_); - reset_l(); - return OK; -} - -void AAH_RXPlayer::reset_l() { - stopWorkThread(); - CHECK(sock_fd_ < 0); - CHECK(!multicast_joined_); - is_playing_ = false; - data_source_set_ = false; - transmitter_known_ = false; - memset(&listen_addr_, 0, sizeof(listen_addr_)); -} - -status_t AAH_RXPlayer::setLooping(int loop) { - return OK; -} - -player_type AAH_RXPlayer::playerType() { - return AAH_RX_PLAYER; -} - -status_t AAH_RXPlayer::setParameter(int key, const Parcel &request) { - return ERROR_UNSUPPORTED; -} - -status_t AAH_RXPlayer::getParameter(int key, Parcel *reply) { - return ERROR_UNSUPPORTED; -} - -status_t AAH_RXPlayer::invoke(const Parcel& request, Parcel *reply) { - if (!reply) { - return BAD_VALUE; - } - - int32_t magic; - status_t err = request.readInt32(&magic); - if (err != OK) { - reply->writeInt32(err); - return OK; - } - - if (magic != 0x12345) { - reply->writeInt32(BAD_VALUE); - return OK; - } - - int32_t methodID; - err = request.readInt32(&methodID); - if (err != OK) { - reply->writeInt32(err); - return OK; - } - - switch (methodID) { - // Get Volume - case INVOKE_GET_MASTER_VOLUME: { - if (audio_flinger_ != NULL) { - reply->writeInt32(OK); - reply->writeFloat(audio_flinger_->masterVolume()); - } else { - reply->writeInt32(UNKNOWN_ERROR); - } - } break; - - // Set Volume - case INVOKE_SET_MASTER_VOLUME: { - float targetVol = request.readFloat(); - reply->writeInt32(audio_flinger_->setMasterVolume(targetVol)); - } break; - - default: return BAD_VALUE; - } - - return OK; -} - -void AAH_RXPlayer::fetchAudioFlinger() { - if (audio_flinger_ == NULL) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder; - binder = sm->getService(String16("media.audio_flinger")); - - if (binder == NULL) { - ALOGW("AAH_RXPlayer failed to fetch handle to audio flinger." - " Master volume control will not be possible."); - } - - audio_flinger_ = interface_cast<IAudioFlinger>(binder); - } -} - -} // namespace android diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h deleted file mode 100644 index ba5617eb..00000000 --- a/media/libaah_rtp/aah_rx_player.h +++ /dev/null @@ -1,318 +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. - */ - -#ifndef __AAH_RX_PLAYER_H__ -#define __AAH_RX_PLAYER_H__ - -#include <common_time/cc_helper.h> -#include <media/MediaPlayerInterface.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaSource.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/OMXClient.h> -#include <netinet/in.h> -#include <utils/KeyedVector.h> -#include <utils/LinearTransform.h> -#include <utils/threads.h> - -#include "aah_decoder_pump.h" -#include "pipe_event.h" - -namespace android { - -class AAH_RXPlayer : public MediaPlayerInterface { - public: - AAH_RXPlayer(); - - virtual status_t initCheck(); - virtual status_t setDataSource(const char *url, - const KeyedVector<String8, String8>* - headers); - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoSurface(const sp<Surface>& surface); - virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& - surfaceTexture); - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t seekTo(int msec); - virtual status_t getCurrentPosition(int *msec); - virtual status_t getDuration(int *msec); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType(); - virtual status_t setParameter(int key, const Parcel &request); - virtual status_t getParameter(int key, Parcel *reply); - virtual status_t invoke(const Parcel& request, Parcel *reply); - - protected: - virtual ~AAH_RXPlayer(); - - private: - class ThreadWrapper : public Thread { - public: - friend class AAH_RXPlayer; - explicit ThreadWrapper(AAH_RXPlayer& player) - : Thread(false /* canCallJava */ ) - , player_(player) { } - - virtual bool threadLoop() { return player_.threadLoop(); } - - private: - AAH_RXPlayer& player_; - - DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper); - }; - -#pragma pack(push, 1) - // PacketBuffers are structures used by the RX ring buffer. The ring buffer - // is a ring of pointers to PacketBuffer structures which act as variable - // length byte arrays and hold the contents of received UDP packets. Rather - // than make this a structure which hold a length and a pointer to another - // allocated structure (which would require two allocations), this struct - // uses a structure overlay pattern where allocation for the byte array - // consists of allocating (arrayLen + sizeof(ssize_t)) bytes of data from - // whatever pool/heap the packet buffer pulls from, and then overlaying the - // packed PacketBuffer structure on top of the allocation. The one-byte - // array at the end of the structure serves as an offset to the the data - // portion of the allocation; packet buffers are never allocated on the - // stack or using the new operator. Instead, the static allocate-byte-array - // and destroy methods handle the allocate and overlay pattern. They also - // allow for a potential future optimization where instead of just - // allocating blocks from the process global heap and overlaying, the - // allocator is replaced with a different implementation (private heap, - // free-list, circular buffer, etc) which reduces potential heap - // fragmentation issues which might arise from the frequent allocation and - // destruction of the received UDP traffic. - struct PacketBuffer { - ssize_t length_; - uint8_t data_[1]; - - // TODO : consider changing this to be some form of ring buffer or free - // pool system instead of just using the heap in order to avoid heap - // fragmentation. - static PacketBuffer* allocate(ssize_t length); - static void destroy(PacketBuffer* pb); - - private: - // Force people to use allocate/destroy instead of new/delete. - PacketBuffer() { } - ~PacketBuffer() { } - }; - - struct RetransRequest { - uint32_t magic_; - uint32_t mcast_ip_; - uint16_t mcast_port_; - uint16_t start_seq_; - uint16_t end_seq_; - }; -#pragma pack(pop) - - enum GapStatus { - kGS_NoGap = 0, - kGS_NormalGap, - kGS_FastStartGap, - }; - - struct SeqNoGap { - uint16_t start_seq_; - uint16_t end_seq_; - }; - - class RXRingBuffer { - public: - explicit RXRingBuffer(uint32_t capacity); - ~RXRingBuffer(); - - bool initCheck() const { return (ring_ != NULL); } - void reset(); - - // Push a packet buffer with a given sequence number into the ring - // buffer. pushBuffer will always consume the buffer pushed to it, - // either destroying it because it was a duplicate or overflow, or - // holding on to it in the ring. Callers should not hold any references - // to PacketBuffers after they have been pushed to the ring. Returns - // false in the case of a serious error (such as ring overflow). - // Callers should consider resetting the pipeline entirely in the event - // of a serious error. - bool pushBuffer(PacketBuffer* buf, uint16_t seq); - - // Fetch the next buffer in the RTP sequence. Returns NULL if there is - // no buffer to fetch. If a non-NULL PacketBuffer is returned, - // is_discon will be set to indicate whether or not this PacketBuffer is - // discontiuous with any previously returned packet buffers. Packet - // buffers returned by fetchBuffer are the caller's responsibility; they - // must be certain to destroy the buffers when they are done. - PacketBuffer* fetchBuffer(bool* is_discon); - - // Returns true and fills out the gap structure if the read pointer of - // the ring buffer is currently pointing to a gap which would stall a - // fetchBuffer operation. Returns false if the read pointer is not - // pointing to a gap in the sequence currently. - GapStatus fetchCurrentGap(SeqNoGap* gap); - - // Causes the read pointer to skip over any portion of a gap indicated - // by nak. If nak is NULL, any gap currently blocking the read pointer - // will be completely skipped. If any portion of a gap is skipped, the - // next successful read from fetch buffer will indicate a discontinuity. - void processNAK(const SeqNoGap* nak = NULL); - - // Compute the number of milliseconds until the inactivity timer for - // this RTP stream. Returns -1 if there is no active timeout, or 0 if - // the system has already timed out. - int computeInactivityTimeout(); - - private: - Mutex lock_; - PacketBuffer** ring_; - uint32_t capacity_; - uint32_t rd_; - uint32_t wr_; - - uint16_t rd_seq_; - bool rd_seq_known_; - bool waiting_for_fast_start_; - bool fetched_first_packet_; - - uint64_t rtp_activity_timeout_; - bool rtp_activity_timeout_valid_; - - DISALLOW_EVIL_CONSTRUCTORS(RXRingBuffer); - }; - - class Substream : public virtual RefBase { - public: - Substream(uint32_t ssrc, OMXClient& omx); - - void cleanupBufferInProgress(); - void shutdown(); - void processPayloadStart(uint8_t* buf, - uint32_t amt, - int32_t ts_lower); - void processPayloadCont (uint8_t* buf, - uint32_t amt); - void processTSTransform(const LinearTransform& trans); - - bool isAboutToUnderflow(); - uint32_t getSSRC() const { return ssrc_; } - uint16_t getProgramID() const { return (ssrc_ >> 5) & 0x1F; } - status_t getStatus() const { return status_; } - - protected: - virtual ~Substream(); - - private: - void cleanupDecoder(); - bool shouldAbort(const char* log_tag); - void processCompletedBuffer(); - bool setupSubstreamMeta(); - bool setupMP3SubstreamMeta(); - bool setupAACSubstreamMeta(); - bool setupSubstreamType(uint8_t substream_type, - uint8_t codec_type); - - uint32_t ssrc_; - bool waiting_for_rap_; - status_t status_; - - bool substream_details_known_; - uint8_t substream_type_; - uint8_t codec_type_; - const char* codec_mime_type_; - sp<MetaData> substream_meta_; - - MediaBuffer* buffer_in_progress_; - uint32_t expected_buffer_size_; - uint32_t buffer_filled_; - - Vector<uint8_t> aux_data_in_progress_; - uint32_t aux_data_expected_size_; - - sp<AAH_DecoderPump> decoder_; - - static int64_t kAboutToUnderflowThreshold; - - DISALLOW_EVIL_CONSTRUCTORS(Substream); - }; - - typedef DefaultKeyedVector< uint32_t, sp<Substream> > SubstreamVec; - - status_t startWorkThread(); - void stopWorkThread(); - virtual bool threadLoop(); - bool setupSocket(); - void cleanupSocket(); - void resetPipeline(); - void reset_l(); - bool processRX(PacketBuffer* pb); - void processRingBuffer(); - void processCommandPacket(PacketBuffer* pb); - bool processGaps(); - int computeNextGapRetransmitTimeout(); - void fetchAudioFlinger(); - - PipeEvent wakeup_work_thread_evt_; - sp<ThreadWrapper> thread_wrapper_; - Mutex api_lock_; - bool is_playing_; - bool data_source_set_; - - struct sockaddr_in listen_addr_; - int sock_fd_; - bool multicast_joined_; - - struct sockaddr_in transmitter_addr_; - bool transmitter_known_; - - uint32_t current_epoch_; - bool current_epoch_known_; - - SeqNoGap current_gap_; - GapStatus current_gap_status_; - uint64_t next_retrans_req_time_; - - RXRingBuffer ring_buffer_; - SubstreamVec substreams_; - OMXClient omx_; - CCHelper cc_helper_; - - // Connection to audio flinger used to hack a path to setMasterVolume. - sp<IAudioFlinger> audio_flinger_; - - static const uint32_t kRTPRingBufferSize; - static const uint32_t kRetransRequestMagic; - static const uint32_t kFastStartRequestMagic; - static const uint32_t kRetransNAKMagic; - static const uint32_t kGapRerequestTimeoutUSec; - static const uint32_t kFastStartTimeoutUSec; - static const uint32_t kRTPActivityTimeoutUSec; - - static const uint32_t INVOKE_GET_MASTER_VOLUME = 3; - static const uint32_t INVOKE_SET_MASTER_VOLUME = 4; - - static uint64_t monotonicUSecNow(); - - DISALLOW_EVIL_CONSTRUCTORS(AAH_RXPlayer); -}; - -} // namespace android - -#endif // __AAH_RX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp deleted file mode 100644 index d6b31fd4..00000000 --- a/media/libaah_rtp/aah_rx_player_core.cpp +++ /dev/null @@ -1,809 +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 <fcntl.h> -#include <poll.h> -#include <sys/socket.h> -#include <time.h> -#include <utils/misc.h> - -#include <media/stagefright/Utils.h> - -#include "aah_rx_player.h" -#include "aah_tx_packet.h" - -namespace android { - -const uint32_t AAH_RXPlayer::kRetransRequestMagic = - FOURCC('T','r','e','q'); -const uint32_t AAH_RXPlayer::kRetransNAKMagic = - FOURCC('T','n','a','k'); -const uint32_t AAH_RXPlayer::kFastStartRequestMagic = - FOURCC('T','f','s','t'); -const uint32_t AAH_RXPlayer::kGapRerequestTimeoutUSec = 75000; -const uint32_t AAH_RXPlayer::kFastStartTimeoutUSec = 800000; -const uint32_t AAH_RXPlayer::kRTPActivityTimeoutUSec = 10000000; - -static inline int16_t fetchInt16(uint8_t* data) { - return static_cast<int16_t>(U16_AT(data)); -} - -static inline int32_t fetchInt32(uint8_t* data) { - return static_cast<int32_t>(U32_AT(data)); -} - -static inline int64_t fetchInt64(uint8_t* data) { - return static_cast<int64_t>(U64_AT(data)); -} - -uint64_t AAH_RXPlayer::monotonicUSecNow() { - struct timespec now; - int res = clock_gettime(CLOCK_MONOTONIC, &now); - CHECK(res >= 0); - - uint64_t ret = static_cast<uint64_t>(now.tv_sec) * 1000000; - ret += now.tv_nsec / 1000; - - return ret; -} - -status_t AAH_RXPlayer::startWorkThread() { - status_t res; - stopWorkThread(); - res = thread_wrapper_->run("TRX_Player", PRIORITY_AUDIO); - - if (res != OK) { - ALOGE("Failed to start work thread (res = %d)", res); - } - - return res; -} - -void AAH_RXPlayer::stopWorkThread() { - thread_wrapper_->requestExit(); // set the exit pending flag - wakeup_work_thread_evt_.setEvent(); - - status_t res; - res = thread_wrapper_->requestExitAndWait(); // block until thread exit. - if (res != OK) { - ALOGE("Failed to stop work thread (res = %d)", res); - } - - wakeup_work_thread_evt_.clearPendingEvents(); -} - -void AAH_RXPlayer::cleanupSocket() { - if (sock_fd_ >= 0) { - if (multicast_joined_) { - int res; - struct ip_mreq mreq; - mreq.imr_multiaddr = listen_addr_.sin_addr; - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - res = setsockopt(sock_fd_, - IPPROTO_IP, - IP_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)); - if (res < 0) { - ALOGW("Failed to leave multicast group. (%d, %d)", res, errno); - } - multicast_joined_ = false; - } - - close(sock_fd_); - sock_fd_ = -1; - } - - resetPipeline(); -} - -void AAH_RXPlayer::resetPipeline() { - ring_buffer_.reset(); - - // Explicitly shudown all of the active substreams, then call clear out the - // collection. Failure to clear out a substream can result in its decoder - // holding a reference to itself and therefor not going away when the - // collection is cleared. - for (size_t i = 0; i < substreams_.size(); ++i) - substreams_.valueAt(i)->shutdown(); - - substreams_.clear(); - - current_gap_status_ = kGS_NoGap; -} - -bool AAH_RXPlayer::setupSocket() { - long flags; - int res, buf_size; - socklen_t opt_size; - - cleanupSocket(); - CHECK(sock_fd_ < 0); - - // Make the socket - sock_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock_fd_ < 0) { - ALOGE("Failed to create listen socket (errno %d)", errno); - goto bailout; - } - - // Set non-blocking operation - flags = fcntl(sock_fd_, F_GETFL); - res = fcntl(sock_fd_, F_SETFL, flags | O_NONBLOCK); - if (res < 0) { - ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)", - sock_fd_, errno); - goto bailout; - } - - // Bind to our port - struct sockaddr_in bind_addr; - memset(&bind_addr, 0, sizeof(bind_addr)); - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr.s_addr = INADDR_ANY; - bind_addr.sin_port = listen_addr_.sin_port; - res = bind(sock_fd_, - reinterpret_cast<const sockaddr*>(&bind_addr), - sizeof(bind_addr)); - if (res < 0) { - uint32_t a = ntohl(bind_addr.sin_addr.s_addr); - uint16_t p = ntohs(bind_addr.sin_port); - ALOGE("Failed to bind socket (%d) to %d.%d.%d.%d:%hd. (errno %d)", - sock_fd_, - (a >> 24) & 0xFF, - (a >> 16) & 0xFF, - (a >> 8) & 0xFF, - (a ) & 0xFF, - p, - errno); - - goto bailout; - } - - buf_size = 1 << 16; // 64k - res = setsockopt(sock_fd_, - SOL_SOCKET, SO_RCVBUF, - &buf_size, sizeof(buf_size)); - if (res < 0) { - ALOGW("Failed to increase socket buffer size to %d. (errno %d)", - buf_size, errno); - } - - buf_size = 0; - opt_size = sizeof(buf_size); - res = getsockopt(sock_fd_, - SOL_SOCKET, SO_RCVBUF, - &buf_size, &opt_size); - if (res < 0) { - ALOGW("Failed to fetch socket buffer size. (errno %d)", errno); - } else { - ALOGI("RX socket buffer size is now %d bytes", buf_size); - } - - if (listen_addr_.sin_addr.s_addr) { - // Join the multicast group and we should be good to go. - struct ip_mreq mreq; - mreq.imr_multiaddr = listen_addr_.sin_addr; - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - res = setsockopt(sock_fd_, - IPPROTO_IP, - IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)); - if (res < 0) { - ALOGE("Failed to join multicast group. (errno %d)", errno); - goto bailout; - } - multicast_joined_ = true; - } - - return true; - -bailout: - cleanupSocket(); - return false; -} - -bool AAH_RXPlayer::threadLoop() { - struct pollfd poll_fds[2]; - bool process_more_right_now = false; - - if (!setupSocket()) { - sendEvent(MEDIA_ERROR); - goto bailout; - } - - while (!thread_wrapper_->exitPending()) { - // Step 1: Wait until there is something to do. - int gap_timeout = computeNextGapRetransmitTimeout(); - int ring_timeout = ring_buffer_.computeInactivityTimeout(); - int timeout = -1; - - if (!ring_timeout) { - ALOGW("RTP inactivity timeout reached, resetting pipeline."); - resetPipeline(); - timeout = gap_timeout; - } else { - if (gap_timeout < 0) { - timeout = ring_timeout; - } else if (ring_timeout < 0) { - timeout = gap_timeout; - } else { - timeout = (gap_timeout < ring_timeout) ? gap_timeout - : ring_timeout; - } - } - - if ((0 != timeout) && (!process_more_right_now)) { - // Set up the events to wait on. Start with the wakeup pipe. - memset(&poll_fds, 0, sizeof(poll_fds)); - poll_fds[0].fd = wakeup_work_thread_evt_.getWakeupHandle(); - poll_fds[0].events = POLLIN; - - // Add the RX socket. - poll_fds[1].fd = sock_fd_; - poll_fds[1].events = POLLIN; - - // Wait for something interesing to happen. - int poll_res = poll(poll_fds, NELEM(poll_fds), timeout); - if (poll_res < 0) { - ALOGE("Fatal error (%d,%d) while waiting on events", - poll_res, errno); - sendEvent(MEDIA_ERROR); - goto bailout; - } - } - - if (thread_wrapper_->exitPending()) { - break; - } - - wakeup_work_thread_evt_.clearPendingEvents(); - process_more_right_now = false; - - // Step 2: Do we have data waiting in the socket? If so, drain the - // socket moving valid RTP information into the ring buffer to be - // processed. - if (poll_fds[1].revents) { - struct sockaddr_in from; - socklen_t from_len; - - ssize_t res = 0; - while (!thread_wrapper_->exitPending()) { - // Check the size of any pending packet. - res = recv(sock_fd_, NULL, 0, MSG_PEEK | MSG_TRUNC); - - // Error? - if (res < 0) { - // If the error is anything other than would block, - // something has gone very wrong. - if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { - ALOGE("Fatal socket error during recvfrom (%d, %d)", - (int)res, errno); - goto bailout; - } - - // Socket is out of data, just break out of processing and - // wait for more. - break; - } - - // Allocate a payload. - PacketBuffer* pb = PacketBuffer::allocate(res); - if (NULL == pb) { - ALOGE("Fatal error, failed to allocate packet buffer of" - " length %u", static_cast<uint32_t>(res)); - goto bailout; - } - - // Fetch the data. - from_len = sizeof(from); - res = recvfrom(sock_fd_, pb->data_, pb->length_, 0, - reinterpret_cast<struct sockaddr*>(&from), - &from_len); - if (res != pb->length_) { - ALOGE("Fatal error, fetched packet length (%d) does not" - " match peeked packet length (%u). This should never" - " happen. (errno = %d)", - static_cast<int>(res), - static_cast<uint32_t>(pb->length_), - errno); - } - - bool drop_packet = false; - if (transmitter_known_) { - if (from.sin_addr.s_addr != - transmitter_addr_.sin_addr.s_addr) { - uint32_t a = ntohl(from.sin_addr.s_addr); - uint16_t p = ntohs(from.sin_port); - ALOGV("Dropping packet from unknown transmitter" - " %u.%u.%u.%u:%hu", - ((a >> 24) & 0xFF), - ((a >> 16) & 0xFF), - ((a >> 8) & 0xFF), - ( a & 0xFF), - p); - - drop_packet = true; - } else { - transmitter_addr_.sin_port = from.sin_port; - } - } else { - memcpy(&transmitter_addr_, &from, sizeof(from)); - transmitter_known_ = true; - } - - if (!drop_packet) { - bool serious_error = !processRX(pb); - - if (serious_error) { - // Something went "seriously wrong". Currently, the - // only trigger for this should be a ring buffer - // overflow. The current failsafe behavior for when - // something goes seriously wrong is to just reset the - // pipeline. The system should behave as if this - // AAH_RXPlayer was just set up for the first time. - ALOGE("Something just went seriously wrong with the" - " pipeline. Resetting."); - resetPipeline(); - } - } else { - PacketBuffer::destroy(pb); - } - } - } - - // Step 3: Process any data we mave have accumulated in the ring buffer - // so far. - if (!thread_wrapper_->exitPending()) { - processRingBuffer(); - } - - // Step 4: At this point in time, the ring buffer should either be - // empty, or stalled in front of a gap caused by some dropped packets. - // Check on the current gap situation and deal with it in an appropriate - // fashion. If processGaps returns true, it means that it has given up - // on a gap and that we should try to process some more data - // immediately. - if (!thread_wrapper_->exitPending()) { - process_more_right_now = processGaps(); - } - - // Step 5: Check for fatal errors. If any of our substreams has - // encountered a fatal, unrecoverable, error, then propagate the error - // up to user level and shut down. - for (size_t i = 0; i < substreams_.size(); ++i) { - status_t status; - CHECK(substreams_.valueAt(i) != NULL); - - status = substreams_.valueAt(i)->getStatus(); - if (OK != status) { - ALOGE("Substream index %d has encountered an unrecoverable" - " error (%d). Signalling application level and shutting" - " down.", i, status); - sendEvent(MEDIA_ERROR); - goto bailout; - } - } - } - -bailout: - cleanupSocket(); - return false; -} - -bool AAH_RXPlayer::processRX(PacketBuffer* pb) { - CHECK(NULL != pb); - - uint8_t* data = pb->data_; - ssize_t amt = pb->length_; - uint32_t nak_magic; - uint16_t seq_no; - uint32_t epoch; - - // Every packet either starts with an RTP header which is at least 12 bytes - // long or is a retry NAK which is 14 bytes long. If there are fewer than - // 12 bytes here, this cannot be a proper RTP packet. - if (amt < 12) { - ALOGV("Dropping packet, too short to contain RTP header (%u bytes)", - static_cast<uint32_t>(amt)); - goto drop_packet; - } - - // Check to see if this is the special case of a NAK packet. - nak_magic = ntohl(*(reinterpret_cast<uint32_t*>(data))); - if (nak_magic == kRetransNAKMagic) { - // Looks like a NAK packet; make sure its long enough. - - if (amt < static_cast<ssize_t>(sizeof(RetransRequest))) { - ALOGV("Dropping packet, too short to contain NAK payload" - " (%u bytes)", static_cast<uint32_t>(amt)); - goto drop_packet; - } - - SeqNoGap gap; - RetransRequest* rtr = reinterpret_cast<RetransRequest*>(data); - gap.start_seq_ = ntohs(rtr->start_seq_); - gap.end_seq_ = ntohs(rtr->end_seq_); - - ALOGV("Process NAK for gap at [%hu, %hu]", - gap.start_seq_, gap.end_seq_); - ring_buffer_.processNAK(&gap); - - return true; - } - - // According to the TRTP spec, version should be 2, padding should be 0, - // extension should be 0 and CSRCCnt should be 0. If any of these tests - // fail, we chuck the packet. - if (data[0] != 0x80) { - ALOGV("Dropping packet, bad V/P/X/CSRCCnt field (0x%02x)", - data[0]); - goto drop_packet; - } - - // Check the payload type. For TRTP, it should always be 100. - if ((data[1] & 0x7F) != 100) { - ALOGV("Dropping packet, bad payload type. (%u)", - data[1] & 0x7F); - goto drop_packet; - } - - // Check whether the transmitter has begun a new epoch. - epoch = (U32_AT(data + 8) >> 10) & 0x3FFFFF; - if (current_epoch_known_) { - if (epoch != current_epoch_) { - ALOGV("%s: new epoch %u", __PRETTY_FUNCTION__, epoch); - current_epoch_ = epoch; - resetPipeline(); - } - } else { - current_epoch_ = epoch; - current_epoch_known_ = true; - } - - // Extract the sequence number and hand the packet off to the ring buffer - // for dropped packet detection and later processing. - seq_no = U16_AT(data + 2); - return ring_buffer_.pushBuffer(pb, seq_no); - -drop_packet: - PacketBuffer::destroy(pb); - return true; -} - -void AAH_RXPlayer::processRingBuffer() { - PacketBuffer* pb; - bool is_discon; - sp<Substream> substream; - LinearTransform trans; - bool foundTrans = false; - - while (NULL != (pb = ring_buffer_.fetchBuffer(&is_discon))) { - if (is_discon) { - // Abort all partially assembled payloads. - for (size_t i = 0; i < substreams_.size(); ++i) { - CHECK(substreams_.valueAt(i) != NULL); - substreams_.valueAt(i)->cleanupBufferInProgress(); - } - } - - uint8_t* data = pb->data_; - ssize_t amt = pb->length_; - - // Should not have any non-RTP packets in the ring buffer. RTP packets - // must be at least 12 bytes long. - CHECK(amt >= 12); - - // Extract the marker bit and the SSRC field. - bool marker = (data[1] & 0x80) != 0; - uint32_t ssrc = U32_AT(data + 8); - - // Is this the start of a new TRTP payload? If so, the marker bit - // should be set and there are some things we should be checking for. - if (marker) { - // TRTP headers need to have at least a byte for version, a byte for - // payload type and flags, and 4 bytes for length. - if (amt < 18) { - ALOGV("Dropping packet, too short to contain TRTP header" - " (%u bytes)", static_cast<uint32_t>(amt)); - goto process_next_packet; - } - - // Check the TRTP version and extract the payload type/flags. - uint8_t trtp_version = data[12]; - uint8_t payload_type = (data[13] >> 4) & 0xF; - uint8_t trtp_flags = data[13] & 0xF; - - if (1 != trtp_version) { - ALOGV("Dropping packet, bad trtp version %hhu", trtp_version); - goto process_next_packet; - } - - // Is there a timestamp transformation present on this packet? If - // so, extract it and pass it to the appropriate substreams. - if (trtp_flags & 0x02) { - ssize_t offset = 18 + ((trtp_flags & 0x01) ? 4 : 0); - if (amt < (offset + 24)) { - ALOGV("Dropping packet, too short to contain TRTP Timestamp" - " Transformation (%u bytes)", - static_cast<uint32_t>(amt)); - goto process_next_packet; - } - - trans.a_zero = fetchInt64(data + offset); - trans.b_zero = fetchInt64(data + offset + 16); - trans.a_to_b_numer = static_cast<int32_t>( - fetchInt32 (data + offset + 8)); - trans.a_to_b_denom = U32_AT(data + offset + 12); - foundTrans = true; - - uint32_t program_id = (ssrc >> 5) & 0x1F; - for (size_t i = 0; i < substreams_.size(); ++i) { - sp<Substream> iter = substreams_.valueAt(i); - CHECK(iter != NULL); - - if (iter->getProgramID() == program_id) { - iter->processTSTransform(trans); - } - } - } - - // Is this a command packet? If so, its not necessarily associate - // with one particular substream. Just give it to the command - // packet handler and then move on. - if (4 == payload_type) { - processCommandPacket(pb); - goto process_next_packet; - } - } - - // If we got to here, then we are a normal packet. Find (or allocate) - // the substream we belong to and send the packet off to be processed. - substream = substreams_.valueFor(ssrc); - if (substream == NULL) { - substream = new Substream(ssrc, omx_); - if (substream == NULL) { - ALOGE("Failed to allocate substream for SSRC 0x%08x", ssrc); - goto process_next_packet; - } - substreams_.add(ssrc, substream); - - if (foundTrans) { - substream->processTSTransform(trans); - } - } - - CHECK(substream != NULL); - - if (marker) { - // Start of a new TRTP payload for this substream. Extract the - // lower 32 bits of the timestamp and hand the buffer to the - // substream for processing. - uint32_t ts_lower = U32_AT(data + 4); - substream->processPayloadStart(data + 12, amt - 12, ts_lower); - } else { - // Continuation of an existing TRTP payload. Just hand it off to - // the substream for processing. - substream->processPayloadCont(data + 12, amt - 12); - } - -process_next_packet: - PacketBuffer::destroy(pb); - } // end of main processing while loop. -} - -void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) { - CHECK(NULL != pb); - - uint8_t* data = pb->data_; - ssize_t amt = pb->length_; - - // verify that this packet meets the minimum length of a command packet - if (amt < 20) { - return; - } - - uint8_t trtp_version = data[12]; - uint8_t trtp_flags = data[13] & 0xF; - - if (1 != trtp_version) { - ALOGV("Dropping packet, bad trtp version %hhu", trtp_version); - return; - } - - // calculate the start of the command payload - ssize_t offset = 18; - if (trtp_flags & 0x01) { - // timestamp is present (4 bytes) - offset += 4; - } - if (trtp_flags & 0x02) { - // transform is present (24 bytes) - offset += 24; - } - - // the packet must contain 2 bytes of command payload beyond the TRTP header - if (amt < offset + 2) { - return; - } - - uint16_t command_id = U16_AT(data + offset); - - switch (command_id) { - case TRTPControlPacket::kCommandNop: - break; - - case TRTPControlPacket::kCommandEOS: - case TRTPControlPacket::kCommandFlush: { - uint16_t program_id = (U32_AT(data + 8) >> 5) & 0x1F; - ALOGI("*** %s flushing program_id=%d", - __PRETTY_FUNCTION__, program_id); - - Vector<uint32_t> substreams_to_remove; - for (size_t i = 0; i < substreams_.size(); ++i) { - sp<Substream> iter = substreams_.valueAt(i); - if (iter->getProgramID() == program_id) { - iter->shutdown(); - substreams_to_remove.add(iter->getSSRC()); - } - } - - for (size_t i = 0; i < substreams_to_remove.size(); ++i) { - substreams_.removeItem(substreams_to_remove[i]); - } - } break; - } -} - -bool AAH_RXPlayer::processGaps() { - // Deal with the current gap situation. Specifically... - // - // 1) If a new gap has shown up, send a retransmit request to the - // transmitter. - // 2) If a gap we were working on has had a packet in the middle or at - // the end filled in, send another retransmit request for the begining - // portion of the gap. TRTP was designed for LANs where packet - // re-ordering is very unlikely; so see the middle or end of a gap - // filled in before the begining is an almost certain indication that - // a retransmission packet was also dropped. - // 3) If we have been working on a gap for a while and it still has not - // been filled in, send another retransmit request. - // 4) If the are no more gaps in the ring, clear the current_gap_status_ - // flag to indicate that all is well again. - - // Start by fetching the active gap status. - SeqNoGap gap; - bool send_retransmit_request = false; - bool ret_val = false; - GapStatus gap_status; - if (kGS_NoGap != (gap_status = ring_buffer_.fetchCurrentGap(&gap))) { - // Note: checking for a change in the end sequence number should cover - // moving on to an entirely new gap for case #1 as well as resending the - // begining of a gap range for case #2. - send_retransmit_request = (kGS_NoGap == current_gap_status_) || - (current_gap_.end_seq_ != gap.end_seq_); - - // If this is the same gap we have been working on, and it has timed - // out, then check to see if our substreams are about to underflow. If - // so, instead of sending another retransmit request, just give up on - // this gap and move on. - if (!send_retransmit_request && - (kGS_NoGap != current_gap_status_) && - (0 == computeNextGapRetransmitTimeout())) { - - // If out current gap is the fast-start gap, don't bother to skip it - // because substreams look like the are about to underflow. - if ((kGS_FastStartGap != gap_status) || - (current_gap_.end_seq_ != gap.end_seq_)) { - for (size_t i = 0; i < substreams_.size(); ++i) { - if (substreams_.valueAt(i)->isAboutToUnderflow()) { - ALOGV("About to underflow, giving up on gap [%hu, %hu]", - gap.start_seq_, gap.end_seq_); - ring_buffer_.processNAK(); - current_gap_status_ = kGS_NoGap; - return true; - } - } - } - - // Looks like no one is about to underflow. Just go ahead and send - // the request. - send_retransmit_request = true; - } - } else { - current_gap_status_ = kGS_NoGap; - } - - if (send_retransmit_request) { - // If we have been working on a fast start, and it is still not filled - // in, even after the extended retransmit time out, give up and skip it. - // The system should fall back into its normal slow-start behavior. - if ((kGS_FastStartGap == current_gap_status_) && - (current_gap_.end_seq_ == gap.end_seq_)) { - ALOGV("Fast start is taking forever; giving up."); - ring_buffer_.processNAK(); - current_gap_status_ = kGS_NoGap; - return true; - } - - // Send the request. - RetransRequest req; - uint32_t magic = (kGS_FastStartGap == gap_status) - ? kFastStartRequestMagic - : kRetransRequestMagic; - req.magic_ = htonl(magic); - req.mcast_ip_ = listen_addr_.sin_addr.s_addr; - req.mcast_port_ = listen_addr_.sin_port; - req.start_seq_ = htons(gap.start_seq_); - req.end_seq_ = htons(gap.end_seq_); - - { - uint32_t a = ntohl(transmitter_addr_.sin_addr.s_addr); - uint16_t p = ntohs(transmitter_addr_.sin_port); - ALOGV("Sending to transmitter %u.%u.%u.%u:%hu", - ((a >> 24) & 0xFF), - ((a >> 16) & 0xFF), - ((a >> 8) & 0xFF), - ( a & 0xFF), - p); - } - - int res = sendto(sock_fd_, &req, sizeof(req), 0, - reinterpret_cast<struct sockaddr*>(&transmitter_addr_), - sizeof(transmitter_addr_)); - if (res < 0) { - ALOGE("Error when sending retransmit request (%d)", errno); - } else { - ALOGV("%s request for range [%hu, %hu] sent", - (kGS_FastStartGap == gap_status) ? "Fast Start" - : "Retransmit", - gap.start_seq_, gap.end_seq_); - } - - // Update the current gap info. - current_gap_ = gap; - current_gap_status_ = gap_status; - next_retrans_req_time_ = monotonicUSecNow() + - ((kGS_FastStartGap == current_gap_status_) - ? kFastStartTimeoutUSec - : kGapRerequestTimeoutUSec); - } - - return false; -} - -// Compute when its time to send the next gap retransmission in milliseconds. -// Returns < 0 for an infinite timeout (no gap) and 0 if its time to retransmit -// right now. -int AAH_RXPlayer::computeNextGapRetransmitTimeout() { - if (kGS_NoGap == current_gap_status_) { - return -1; - } - - int64_t timeout_delta = next_retrans_req_time_ - monotonicUSecNow(); - - timeout_delta /= 1000; - if (timeout_delta <= 0) { - return 0; - } - - return static_cast<uint32_t>(timeout_delta); -} - -} // namespace android diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp deleted file mode 100644 index 779405e0..00000000 --- a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp +++ /dev/null @@ -1,366 +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 "aah_rx_player.h" - -namespace android { - -AAH_RXPlayer::RXRingBuffer::RXRingBuffer(uint32_t capacity) { - capacity_ = capacity; - rd_ = wr_ = 0; - ring_ = new PacketBuffer*[capacity]; - memset(ring_, 0, sizeof(PacketBuffer*) * capacity); - reset(); -} - -AAH_RXPlayer::RXRingBuffer::~RXRingBuffer() { - reset(); - delete[] ring_; -} - -void AAH_RXPlayer::RXRingBuffer::reset() { - AutoMutex lock(&lock_); - - if (NULL != ring_) { - while (rd_ != wr_) { - CHECK(rd_ < capacity_); - if (NULL != ring_[rd_]) { - PacketBuffer::destroy(ring_[rd_]); - ring_[rd_] = NULL; - } - rd_ = (rd_ + 1) % capacity_; - } - } - - rd_ = wr_ = 0; - rd_seq_known_ = false; - waiting_for_fast_start_ = true; - fetched_first_packet_ = false; - rtp_activity_timeout_valid_ = false; -} - -bool AAH_RXPlayer::RXRingBuffer::pushBuffer(PacketBuffer* buf, - uint16_t seq) { - AutoMutex lock(&lock_); - CHECK(NULL != ring_); - CHECK(NULL != buf); - - rtp_activity_timeout_valid_ = true; - rtp_activity_timeout_ = monotonicUSecNow() + kRTPActivityTimeoutUSec; - - // If the ring buffer is totally reset (we have never received a single - // payload) then we don't know the rd sequence number and this should be - // simple. We just store the payload, advance the wr pointer and record the - // initial sequence number. - if (!rd_seq_known_) { - CHECK(rd_ == wr_); - CHECK(NULL == ring_[wr_]); - CHECK(wr_ < capacity_); - - ring_[wr_] = buf; - wr_ = (wr_ + 1) % capacity_; - rd_seq_ = seq; - rd_seq_known_ = true; - return true; - } - - // Compute the seqence number of this payload and of the write pointer, - // normalized around the read pointer. IOW - transform the payload seq no - // and the wr pointer seq no into a space where the rd pointer seq no is - // zero. This will define 4 cases we can consider... - // - // 1) norm_seq == norm_wr_seq - // This payload is contiguous with the last. All is good. - // - // 2) ((norm_seq < norm_wr_seq) && (norm_seq >= norm_rd_seq) - // aka ((norm_seq < norm_wr_seq) && (norm_seq >= 0) - // This payload is in the past, in the unprocessed region of the ring - // buffer. It is probably a retransmit intended to fill in a dropped - // payload; it may be a duplicate. - // - // 3) ((norm_seq - norm_wr_seq) & 0x8000) != 0 - // This payload is in the past compared to the write pointer (or so very - // far in the future that it has wrapped the seq no space), but not in - // the unprocessed region of the ring buffer. This could be a duplicate - // retransmit; we just drop these payloads unless we are waiting for our - // first fast start packet. If we are waiting for fast start, than this - // packet is probably the first packet of the fast start retransmission. - // If it will fit in the buffer, back up the read pointer to its position - // and clear the fast start flag, otherwise just drop it. - // - // 4) ((norm_seq - norm_wr_seq) & 0x8000) == 0 - // This payload which is ahead of the next write pointer. This indicates - // that we have missed some payloads and need to request a retransmit. - // If norm_seq >= (capacity - 1), then the gap is so large that it would - // overflow the ring buffer and we should probably start to panic. - - uint16_t norm_wr_seq = ((wr_ + capacity_ - rd_) % capacity_); - uint16_t norm_seq = seq - rd_seq_; - - // Check for overflow first. - if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) { - ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu]," - " seq = %hu", capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq); - PacketBuffer::destroy(buf); - return false; - } - - // Check for case #1 - if (norm_seq == norm_wr_seq) { - CHECK(wr_ < capacity_); - CHECK(NULL == ring_[wr_]); - - ring_[wr_] = buf; - wr_ = (wr_ + 1) % capacity_; - - CHECK(wr_ != rd_); - return true; - } - - // Check case #2 - uint32_t ring_pos = (rd_ + norm_seq) % capacity_; - if ((norm_seq < norm_wr_seq) && (!(norm_seq & 0x8000))) { - // Do we already have a payload for this slot? If so, then this looks - // like a duplicate retransmit. Just ignore it. - if (NULL != ring_[ring_pos]) { - ALOGD("RXed duplicate retransmit, seq = %hu", seq); - PacketBuffer::destroy(buf); - } else { - // Looks like we were missing this payload. Go ahead and store it. - ring_[ring_pos] = buf; - } - - return true; - } - - // Check case #3 - if ((norm_seq - norm_wr_seq) & 0x8000) { - if (!waiting_for_fast_start_) { - ALOGD("RXed duplicate retransmit from before rd pointer, seq = %hu", - seq); - PacketBuffer::destroy(buf); - } else { - // Looks like a fast start fill-in. Go ahead and store it, assuming - // that we can fit it in the buffer. - uint32_t implied_ring_size = static_cast<uint32_t>(norm_wr_seq) - + (rd_seq_ - seq); - - if (implied_ring_size >= (capacity_ - 1)) { - ALOGD("RXed what looks like a fast start packet (seq = %hu)," - " but packet is too far in the past to fit into the ring" - " buffer. Dropping.", seq); - PacketBuffer::destroy(buf); - } else { - ring_pos = (rd_ + capacity_ + seq - rd_seq_) % capacity_; - rd_seq_ = seq; - rd_ = ring_pos; - waiting_for_fast_start_ = false; - - CHECK(ring_pos < capacity_); - CHECK(NULL == ring_[ring_pos]); - ring_[ring_pos] = buf; - } - - } - return true; - } - - // Must be in case #4 with no overflow. This packet fits in the current - // ring buffer, but is discontiuguous. Advance the write pointer leaving a - // gap behind. - uint32_t gap_len = (ring_pos + capacity_ - wr_) % capacity_; - ALOGD("Drop detected; %u packets, seq_range [%hu, %hu]", - gap_len, - rd_seq_ + norm_wr_seq, - rd_seq_ + norm_wr_seq + gap_len - 1); - - CHECK(NULL == ring_[ring_pos]); - ring_[ring_pos] = buf; - wr_ = (ring_pos + 1) % capacity_; - CHECK(wr_ != rd_); - - return true; -} - -AAH_RXPlayer::PacketBuffer* -AAH_RXPlayer::RXRingBuffer::fetchBuffer(bool* is_discon) { - AutoMutex lock(&lock_); - CHECK(NULL != ring_); - CHECK(NULL != is_discon); - - // If the read seqence number is not known, then this ring buffer has not - // received a packet since being reset and there cannot be any packets to - // return. If we are still waiting for the first fast start packet to show - // up, we don't want to let any buffer be consumed yet because we expect to - // see a packet before the initial read sequence number show up shortly. - if (!rd_seq_known_ || waiting_for_fast_start_) { - *is_discon = false; - return NULL; - } - - PacketBuffer* ret = NULL; - *is_discon = !fetched_first_packet_; - - while ((rd_ != wr_) && (NULL == ret)) { - CHECK(rd_ < capacity_); - - // If we hit a gap, stall and do not advance the read pointer. Let the - // higher level code deal with requesting retries and/or deciding to - // skip the current gap. - ret = ring_[rd_]; - if (NULL == ret) { - break; - } - - ring_[rd_] = NULL; - rd_ = (rd_ + 1) % capacity_; - ++rd_seq_; - } - - if (NULL != ret) { - fetched_first_packet_ = true; - } - - return ret; -} - -AAH_RXPlayer::GapStatus -AAH_RXPlayer::RXRingBuffer::fetchCurrentGap(SeqNoGap* gap) { - AutoMutex lock(&lock_); - CHECK(NULL != ring_); - CHECK(NULL != gap); - - // If the read seqence number is not known, then this ring buffer has not - // received a packet since being reset and there cannot be any gaps. - if (!rd_seq_known_) { - return kGS_NoGap; - } - - // If we are waiting for fast start, then the current gap is a fast start - // gap and it includes all packets before the read sequence number. - if (waiting_for_fast_start_) { - gap->start_seq_ = - gap->end_seq_ = rd_seq_ - 1; - return kGS_FastStartGap; - } - - // If rd == wr, then the buffer is empty and there cannot be any gaps. - if (rd_ == wr_) { - return kGS_NoGap; - } - - // If rd_ is currently pointing at an unprocessed packet, then there is no - // current gap. - CHECK(rd_ < capacity_); - if (NULL != ring_[rd_]) { - return kGS_NoGap; - } - - // Looks like there must be a gap here. The start of the gap is the current - // rd sequence number, all we need to do now is determine its length in - // order to compute the end sequence number. - gap->start_seq_ = rd_seq_; - uint16_t end = rd_seq_; - uint32_t tmp = (rd_ + 1) % capacity_; - while ((tmp != wr_) && (NULL == ring_[tmp])) { - ++end; - tmp = (tmp + 1) % capacity_; - } - gap->end_seq_ = end; - - return kGS_NormalGap; -} - -void AAH_RXPlayer::RXRingBuffer::processNAK(const SeqNoGap* nak) { - AutoMutex lock(&lock_); - CHECK(NULL != ring_); - - // If we were waiting for our first fast start fill-in packet, and we - // received a NAK, then apparantly we are not getting our fast start. Just - // clear the waiting flag and go back to normal behavior. - if (waiting_for_fast_start_) { - waiting_for_fast_start_ = false; - } - - // If we have not received a packet since last reset, or there is no data in - // the ring, then there is nothing to skip. - if ((!rd_seq_known_) || (rd_ == wr_)) { - return; - } - - // If rd_ is currently pointing at an unprocessed packet, then there is no - // gap to skip. - CHECK(rd_ < capacity_); - if (NULL != ring_[rd_]) { - return; - } - - // Looks like there must be a gap here. Advance rd until we have passed - // over the portion of it indicated by nak (or all of the gap if nak is - // NULL). Then reset fetched_first_packet_ so that the next read will show - // up as being discontiguous. - uint16_t seq_after_gap = (NULL == nak) ? 0 : nak->end_seq_ + 1; - while ((rd_ != wr_) && - (NULL == ring_[rd_]) && - ((NULL == nak) || (seq_after_gap != rd_seq_))) { - rd_ = (rd_ + 1) % capacity_; - ++rd_seq_; - } - fetched_first_packet_ = false; -} - -int AAH_RXPlayer::RXRingBuffer::computeInactivityTimeout() { - AutoMutex lock(&lock_); - - if (!rtp_activity_timeout_valid_) { - return -1; - } - - uint64_t now = monotonicUSecNow(); - if (rtp_activity_timeout_ <= now) { - return 0; - } - - return (rtp_activity_timeout_ - now) / 1000; -} - -AAH_RXPlayer::PacketBuffer* -AAH_RXPlayer::PacketBuffer::allocate(ssize_t length) { - if (length <= 0) { - return NULL; - } - - uint32_t alloc_len = sizeof(PacketBuffer) + length; - PacketBuffer* ret = reinterpret_cast<PacketBuffer*>( - new uint8_t[alloc_len]); - - if (NULL != ret) { - ret->length_ = length; - } - - return ret; -} - -void AAH_RXPlayer::PacketBuffer::destroy(PacketBuffer* pb) { - uint8_t* kill_me = reinterpret_cast<uint8_t*>(pb); - delete[] kill_me; -} - -} // namespace android diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp deleted file mode 100644 index 18b0e2b2..00000000 --- a/media/libaah_rtp/aah_rx_player_substream.cpp +++ /dev/null @@ -1,677 +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 <include/avc_utils.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/OMXCodec.h> -#include <media/stagefright/Utils.h> - -#include "aah_rx_player.h" -#include "aah_tx_packet.h" - -inline uint32_t min(uint32_t a, uint32_t b) { - return (a < b ? a : b); -} - -namespace android { - -int64_t AAH_RXPlayer::Substream::kAboutToUnderflowThreshold = - 50ull * 1000; - -AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) { - ssrc_ = ssrc; - substream_details_known_ = false; - buffer_in_progress_ = NULL; - status_ = OK; - codec_mime_type_ = ""; - - decoder_ = new AAH_DecoderPump(omx); - if (decoder_ == NULL) { - ALOGE("%s failed to allocate decoder pump!", __PRETTY_FUNCTION__); - } - if (OK != decoder_->initCheck()) { - ALOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__); - } - - // cleanupBufferInProgress will reset most of the internal state variables. - // Just need to make sure that buffer_in_progress_ is NULL before calling. - cleanupBufferInProgress(); -} - -AAH_RXPlayer::Substream::~Substream() { - shutdown(); -} - -void AAH_RXPlayer::Substream::shutdown() { - substream_meta_ = NULL; - status_ = OK; - cleanupBufferInProgress(); - cleanupDecoder(); -} - -void AAH_RXPlayer::Substream::cleanupBufferInProgress() { - if (NULL != buffer_in_progress_) { - buffer_in_progress_->release(); - buffer_in_progress_ = NULL; - } - - expected_buffer_size_ = 0; - buffer_filled_ = 0; - waiting_for_rap_ = true; - - aux_data_in_progress_.clear(); - aux_data_expected_size_ = 0; -} - -void AAH_RXPlayer::Substream::cleanupDecoder() { - if (decoder_ != NULL) { - decoder_->shutdown(); - } -} - -bool AAH_RXPlayer::Substream::shouldAbort(const char* log_tag) { - // If we have already encountered a fatal error, do nothing. We are just - // waiting for our owner to shut us down now. - if (OK != status_) { - ALOGV("Skipping %s, substream has encountered fatal error (%d).", - log_tag, status_); - return true; - } - - return false; -} - -void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, - uint32_t amt, - int32_t ts_lower) { - uint32_t min_length = 6; - - if (shouldAbort(__PRETTY_FUNCTION__)) { - return; - } - - // Do we have a buffer in progress already? If so, abort the buffer. In - // theory, this should never happen. If there were a discontinutity in the - // stream, the discon in the seq_nos at the RTP level should have already - // triggered a cleanup of the buffer in progress. To see a problem at this - // level is an indication either of a bug in the transmitter, or some form - // of terrible corruption/tampering on the wire. - if (NULL != buffer_in_progress_) { - ALOGE("processPayloadStart is aborting payload already in progress."); - cleanupBufferInProgress(); - } - - // Parse enough of the header to know where we stand. Since this is a - // payload start, it should begin with a TRTP header which has to be at - // least 6 bytes long. - if (amt < min_length) { - ALOGV("Discarding payload too short to contain TRTP header (len = %u)", - amt); - return; - } - - // Check the TRTP version number. - if (0x01 != buf[0]) { - ALOGV("Unexpected TRTP version (%u) in header. Expected %u.", - buf[0], 1); - return; - } - - // Extract the substream type field and make sure its one we understand (and - // one that does not conflict with any previously received substream type. - uint8_t header_type = (buf[1] >> 4) & 0xF; - switch (header_type) { - case TRTPPacket::kHeaderTypeAudio: - // Audio, yay! Just break. We understand audio payloads. - break; - case TRTPPacket::kHeaderTypeVideo: - ALOGV("RXed packet with unhandled TRTP header type (Video)."); - return; - case TRTPPacket::kHeaderTypeSubpicture: - ALOGV("RXed packet with unhandled TRTP header type (Subpicture)."); - return; - case TRTPPacket::kHeaderTypeControl: - ALOGV("RXed packet with unhandled TRTP header type (Control)."); - return; - default: - ALOGV("RXed packet with unhandled TRTP header type (%u).", - header_type); - return; - } - - if (substream_details_known_ && (header_type != substream_type_)) { - ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does" - " not match previously received header type (%u)", - ssrc_, header_type, substream_type_); - return; - } - - // Check the flags to see if there is another 32 bits of timestamp present. - uint32_t trtp_header_len = 6; - bool ts_valid = buf[1] & TRTPPacket::kFlag_TSValid; - if (ts_valid) { - min_length += 4; - trtp_header_len += 4; - if (amt < min_length) { - ALOGV("Discarding payload too short to contain TRTP timestamp" - " (len = %u)", amt); - return; - } - } - - // Extract the TRTP length field and sanity check it. - uint32_t trtp_len = U32_AT(buf + 2); - if (trtp_len < min_length) { - ALOGV("TRTP length (%u) is too short to be valid. Must be at least %u" - " bytes.", trtp_len, min_length); - return; - } - - // Extract the rest of the timestamp field if valid. - int64_t ts = 0; - uint32_t parse_offset = 6; - if (ts_valid) { - uint32_t ts_upper = U32_AT(buf + parse_offset); - parse_offset += 4; - ts = (static_cast<int64_t>(ts_upper) << 32) | ts_lower; - } - - // Check the flags to see if there is another 24 bytes of timestamp - // transformation present. - if (buf[1] & TRTPPacket::kFlag_TSTransformPresent) { - min_length += 24; - parse_offset += 24; - trtp_header_len += 24; - if (amt < min_length) { - ALOGV("Discarding payload too short to contain TRTP timestamp" - " transformation (len = %u)", amt); - return; - } - } - - // TODO : break the parsing into individual parsers for the different - // payload types (audio, video, etc). - // - // At this point in time, we know that this is audio. Go ahead and parse - // the basic header, check the codec type, and find the payload portion of - // the packet. - min_length += 3; - if (trtp_len < min_length) { - ALOGV("TRTP length (%u) is too short to be a valid audio payload. Must" - " be at least %u bytes.", trtp_len, min_length); - return; - } - - if (amt < min_length) { - ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain" - " entire TRTP header. TRTP does not currently support" - " fragmenting TRTP headers across RTP payloads", amt); - return; - } - - uint8_t codec_type = buf[parse_offset ]; - uint8_t flags = buf[parse_offset + 1]; - uint8_t volume = buf[parse_offset + 2]; - parse_offset += 3; - trtp_header_len += 3; - - if (!setupSubstreamType(header_type, codec_type)) { - return; - } - - if (decoder_ != NULL) { - decoder_->setRenderVolume(volume); - } - - if (waiting_for_rap_ && !(flags & TRTPAudioPacket::kFlag_RandomAccessPoint)) { - ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP."); - return; - } - - // Check for the presence of codec aux data. - if (flags & TRTPAudioPacket::kFlag_AuxLengthPresent) { - min_length += 4; - trtp_header_len += 4; - - if (trtp_len < min_length) { - ALOGV("TRTP length (%u) is too short to be a valid audio payload. " - "Must be at least %u bytes.", trtp_len, min_length); - return; - } - - if (amt < min_length) { - ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain" - " entire TRTP header. TRTP does not currently support" - " fragmenting TRTP headers across RTP payloads", amt); - return; - } - - aux_data_expected_size_ = U32_AT(buf + parse_offset); - aux_data_in_progress_.clear(); - if (aux_data_in_progress_.capacity() < aux_data_expected_size_) { - aux_data_in_progress_.setCapacity(aux_data_expected_size_); - } - } else { - aux_data_expected_size_ = 0; - } - - if ((aux_data_expected_size_ + trtp_header_len) > trtp_len) { - ALOGV("Expected codec aux data length (%u) and TRTP header overhead" - " (%u) too large for total TRTP payload length (%u).", - aux_data_expected_size_, trtp_header_len, trtp_len); - return; - } - - // OK - everything left is just payload. Compute the payload size, start - // the buffer in progress and pack as much payload as we can into it. If - // the payload is finished once we are done, go ahead and send the payload - // to the decoder. - expected_buffer_size_ = trtp_len - - trtp_header_len - - aux_data_expected_size_; - if (!expected_buffer_size_) { - ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length"); - return; - } - - CHECK(amt >= trtp_header_len); - uint32_t todo = amt - trtp_header_len; - if ((expected_buffer_size_ + aux_data_expected_size_) < todo) { - ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;" - " dropping payload.", todo, - expected_buffer_size_ + aux_data_expected_size_); - return; - } - - buffer_filled_ = 0; - buffer_in_progress_ = new MediaBuffer(expected_buffer_size_); - if ((NULL == buffer_in_progress_) || - (NULL == buffer_in_progress_->data())) { - ALOGV("Failed to allocate MediaBuffer of length %u", - expected_buffer_size_); - cleanupBufferInProgress(); - return; - } - - sp<MetaData> meta = buffer_in_progress_->meta_data(); - if (meta == NULL) { - ALOGV("Missing metadata structure in allocated MediaBuffer; dropping" - " payload"); - cleanupBufferInProgress(); - return; - } - - meta->setCString(kKeyMIMEType, codec_mime_type_); - if (ts_valid) { - meta->setInt64(kKeyTime, ts); - } - - // Skip over the header we have already extracted. - amt -= trtp_header_len; - buf += trtp_header_len; - - // Extract as much of the expected aux data as we can. - todo = min(aux_data_expected_size_, amt); - if (todo) { - aux_data_in_progress_.appendArray(buf, todo); - buf += todo; - amt -= todo; - } - - // Extract as much of the expected payload as we can. - todo = min(expected_buffer_size_, amt); - if (todo > 0) { - uint8_t* tgt = - reinterpret_cast<uint8_t*>(buffer_in_progress_->data()); - memcpy(tgt, buf, todo); - buffer_filled_ = amt; - buf += todo; - amt -= todo; - } - - if (buffer_filled_ >= expected_buffer_size_) { - processCompletedBuffer(); - } -} - -void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf, - uint32_t amt) { - if (shouldAbort(__PRETTY_FUNCTION__)) { - return; - } - - if (NULL == buffer_in_progress_) { - ALOGV("TRTP Receiver skipping payload continuation; no buffer currently" - " in progress."); - return; - } - - CHECK(aux_data_in_progress_.size() <= aux_data_expected_size_); - uint32_t aux_left = aux_data_expected_size_ - aux_data_in_progress_.size(); - if (aux_left) { - uint32_t todo = min(aux_left, amt); - aux_data_in_progress_.appendArray(buf, todo); - amt -= todo; - buf += todo; - - if (!amt) - return; - } - - CHECK(buffer_filled_ < expected_buffer_size_); - uint32_t buffer_left = expected_buffer_size_ - buffer_filled_; - if (amt > buffer_left) { - ALOGV("Extra data (%u > %u) present in continued TRTP Audio Payload;" - " dropping payload.", amt, buffer_left); - cleanupBufferInProgress(); - return; - } - - if (amt > 0) { - uint8_t* tgt = - reinterpret_cast<uint8_t*>(buffer_in_progress_->data()); - memcpy(tgt + buffer_filled_, buf, amt); - buffer_filled_ += amt; - } - - if (buffer_filled_ >= expected_buffer_size_) { - processCompletedBuffer(); - } -} - -void AAH_RXPlayer::Substream::processCompletedBuffer() { - status_t res; - - CHECK(NULL != buffer_in_progress_); - - if (decoder_ == NULL) { - ALOGV("Dropping complete buffer, no decoder pump allocated"); - goto bailout; - } - - // Make sure our metadata used to initialize the decoder has been properly - // set up. - if (!setupSubstreamMeta()) - goto bailout; - - // If our decoder has not be set up, do so now. - res = decoder_->init(substream_meta_); - if (OK != res) { - ALOGE("Failed to init decoder (res = %d)", res); - cleanupDecoder(); - substream_meta_ = NULL; - goto bailout; - } - - // Queue the payload for decode. - res = decoder_->queueForDecode(buffer_in_progress_); - - if (res != OK) { - ALOGD("Failed to queue payload for decode, resetting decoder pump!" - " (res = %d)", res); - status_ = res; - cleanupDecoder(); - cleanupBufferInProgress(); - } - - // NULL out buffer_in_progress before calling the cleanup helper. - // - // MediaBuffers use something of a hybrid ref-counting pattern which prevent - // the AAH_DecoderPump's input queue from adding their own reference to the - // MediaBuffer. MediaBuffers start life with a reference count of 0, as - // well as an observer which starts as NULL. Before being given an - // observer, the ref count cannot be allowed to become non-zero as it will - // cause calls to release() to assert. Basically, before a MediaBuffer has - // an observer, they behave like non-ref counted obects where release() - // serves the roll of delete. After a MediaBuffer has an observer, they - // become more like ref counted objects where add ref and release can be - // used, and when the ref count hits zero, the MediaBuffer is handed off to - // the observer. - // - // Given all of this, when we give the buffer to the decoder pump to wait in - // the to-be-processed queue, the decoder cannot add a ref to the buffer as - // it would in a traditional ref counting system. Instead it needs to - // "steal" the non-existent ref. In the case of queue failure, we need to - // make certain to release this non-existent reference so that the buffer is - // cleaned up during the cleanupBufferInProgress helper. In the case of a - // successful queue operation, we need to make certain that the - // cleanupBufferInProgress helper does not release the buffer since it needs - // to remain alive in the queue. We acomplish this by NULLing out the - // buffer pointer before calling the cleanup helper. - buffer_in_progress_ = NULL; - -bailout: - cleanupBufferInProgress(); -} - -bool AAH_RXPlayer::Substream::setupSubstreamMeta() { - switch (codec_type_) { - case TRTPAudioPacket::kCodecMPEG1Audio: - codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_MPEG; - return setupMP3SubstreamMeta(); - - case TRTPAudioPacket::kCodecAACAudio: - codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_AAC; - return setupAACSubstreamMeta(); - - default: - ALOGV("Failed to setup substream metadata for unsupported codec" - " type (%u)", codec_type_); - break; - } - - return false; -} - -bool AAH_RXPlayer::Substream::setupMP3SubstreamMeta() { - const uint8_t* buffer_data = NULL; - int sample_rate; - int channel_count; - size_t frame_size; - status_t res; - - buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data()); - if (buffer_in_progress_->size() < 4) { - ALOGV("MP3 payload too short to contain header, dropping payload."); - return false; - } - - // Extract the channel count and the sample rate from the MP3 header. The - // stagefright MP3 requires that these be delivered before decoing can - // begin. - if (!GetMPEGAudioFrameSize(U32_AT(buffer_data), - &frame_size, - &sample_rate, - &channel_count, - NULL, - NULL)) { - ALOGV("Failed to parse MP3 header in payload, droping payload."); - return false; - } - - - // Make sure that our substream metadata is set up properly. If there has - // been a format change, be sure to reset the underlying decoder. In - // stagefright, it seems like the only way to do this is to destroy and - // recreate the decoder. - if (substream_meta_ == NULL) { - substream_meta_ = new MetaData(); - - if (substream_meta_ == NULL) { - ALOGE("Failed to allocate MetaData structure for MP3 substream"); - return false; - } - - substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); - substream_meta_->setInt32 (kKeyChannelCount, channel_count); - substream_meta_->setInt32 (kKeySampleRate, sample_rate); - } else { - int32_t prev_sample_rate; - int32_t prev_channel_count; - substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate); - substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count); - - if ((prev_channel_count != channel_count) || - (prev_sample_rate != sample_rate)) { - ALOGW("MP3 format change detected, forcing decoder reset."); - cleanupDecoder(); - - substream_meta_->setInt32(kKeyChannelCount, channel_count); - substream_meta_->setInt32(kKeySampleRate, sample_rate); - } - } - - return true; -} - -bool AAH_RXPlayer::Substream::setupAACSubstreamMeta() { - int32_t sample_rate, channel_cnt; - static const size_t overhead = sizeof(sample_rate) - + sizeof(channel_cnt); - - if (aux_data_in_progress_.size() < overhead) { - ALOGE("Not enough aux data (%u) to initialize AAC substream decoder", - aux_data_in_progress_.size()); - return false; - } - - const uint8_t* aux_data = aux_data_in_progress_.array(); - size_t aux_data_size = aux_data_in_progress_.size(); - sample_rate = U32_AT(aux_data); - channel_cnt = U32_AT(aux_data + sizeof(sample_rate)); - - const uint8_t* esds_data = NULL; - size_t esds_data_size = 0; - if (aux_data_size > overhead) { - esds_data = aux_data + overhead; - esds_data_size = aux_data_size - overhead; - } - - // Do we already have metadata? If so, has it changed at all? If not, then - // there should be nothing else to do. Otherwise, release our old stream - // metadata and make new metadata. - if (substream_meta_ != NULL) { - uint32_t type; - const void* data; - size_t size; - int32_t prev_sample_rate; - int32_t prev_channel_count; - bool res; - - res = substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate); - CHECK(res); - res = substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count); - CHECK(res); - - // If nothing has changed about the codec aux data (esds, sample rate, - // channel count), then we can just do nothing and get out. Otherwise, - // we will need to reset the decoder and make a new metadata object to - // deal with the format change. - bool hasData = (esds_data != NULL); - bool hadData = substream_meta_->findData(kKeyESDS, &type, &data, &size); - bool esds_change = (hadData != hasData); - - if (!esds_change && hasData) - esds_change = ((size != esds_data_size) || - memcmp(data, esds_data, size)); - - if (!esds_change && - (prev_sample_rate == sample_rate) && - (prev_channel_count == channel_cnt)) { - return true; // no change, just get out. - } - - ALOGW("AAC format change detected, forcing decoder reset."); - cleanupDecoder(); - substream_meta_ = NULL; - } - - CHECK(substream_meta_ == NULL); - - substream_meta_ = new MetaData(); - if (substream_meta_ == NULL) { - ALOGE("Failed to allocate MetaData structure for AAC substream"); - return false; - } - - substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); - substream_meta_->setInt32 (kKeySampleRate, sample_rate); - substream_meta_->setInt32 (kKeyChannelCount, channel_cnt); - - if (esds_data) { - substream_meta_->setData(kKeyESDS, kTypeESDS, - esds_data, esds_data_size); - } - - return true; -} - -void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) { - if (decoder_ != NULL) { - decoder_->setRenderTSTransform(trans); - } -} - -bool AAH_RXPlayer::Substream::isAboutToUnderflow() { - if (decoder_ == NULL) { - return false; - } - - return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold); -} - -bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type, - uint8_t codec_type) { - // Sanity check the codec type. Right now we only support MP3 and AAC. - // Also check for conflicts with previously delivered codec types. - if (substream_details_known_) { - if (codec_type != codec_type_) { - ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does" - " not match previously received codec type (%u)", - ssrc_, codec_type, codec_type_); - return false; - } - - return true; - } - - switch (codec_type) { - // MP3 and AAC are all we support right now. - case TRTPAudioPacket::kCodecMPEG1Audio: - case TRTPAudioPacket::kCodecAACAudio: - break; - - default: - ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported" - " codec type (%u)", ssrc_, codec_type); - return false; - } - - substream_type_ = substream_type; - codec_type_ = codec_type; - substream_details_known_ = true; - - return true; -} - -} // namespace android diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp deleted file mode 100644 index 4cd6e471..00000000 --- a/media/libaah_rtp/aah_tx_packet.cpp +++ /dev/null @@ -1,344 +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" -#include <utils/Log.h> - -#include <arpa/inet.h> -#include <string.h> - -#include <media/stagefright/foundation/ADebug.h> - -#include "aah_tx_packet.h" - -namespace android { - -const int TRTPPacket::kRTPHeaderLen; -const uint32_t TRTPPacket::kTRTPEpochMask; - -TRTPPacket::~TRTPPacket() { - delete mPacket; -} - -/*** TRTP packet properties ***/ - -void TRTPPacket::setSeqNumber(uint16_t val) { - mSeqNumber = val; - - if (mIsPacked) { - const int kTRTPSeqNumberOffset = 2; - uint16_t* buf = reinterpret_cast<uint16_t*>( - mPacket + kTRTPSeqNumberOffset); - *buf = htons(mSeqNumber); - } -} - -uint16_t TRTPPacket::getSeqNumber() const { - return mSeqNumber; -} - -void TRTPPacket::setPTS(int64_t val) { - CHECK(!mIsPacked); - mPTS = val; - mPTSValid = true; -} - -int64_t TRTPPacket::getPTS() const { - return mPTS; -} - -void TRTPPacket::setEpoch(uint32_t val) { - mEpoch = val; - - if (mIsPacked) { - const int kTRTPEpochOffset = 8; - uint32_t* buf = reinterpret_cast<uint32_t*>( - mPacket + kTRTPEpochOffset); - uint32_t val = ntohl(*buf); - val &= ~(kTRTPEpochMask << kTRTPEpochShift); - val |= (mEpoch & kTRTPEpochMask) << kTRTPEpochShift; - *buf = htonl(val); - } -} - -void TRTPPacket::setProgramID(uint16_t val) { - CHECK(!mIsPacked); - mProgramID = val; -} - -void TRTPPacket::setSubstreamID(uint16_t val) { - CHECK(!mIsPacked); - mSubstreamID = val; -} - - -void TRTPPacket::setClockTransform(const LinearTransform& trans) { - CHECK(!mIsPacked); - mClockTranform = trans; - mClockTranformValid = true; -} - -uint8_t* TRTPPacket::getPacket() const { - CHECK(mIsPacked); - return mPacket; -} - -int TRTPPacket::getPacketLen() const { - CHECK(mIsPacked); - return mPacketLen; -} - -void TRTPPacket::setExpireTime(nsecs_t val) { - CHECK(!mIsPacked); - mExpireTime = val; -} - -nsecs_t TRTPPacket::getExpireTime() const { - return mExpireTime; -} - -/*** TRTP audio packet properties ***/ - -void TRTPAudioPacket::setCodecType(TRTPAudioCodecType val) { - CHECK(!mIsPacked); - mCodecType = val; -} - -void TRTPAudioPacket::setRandomAccessPoint(bool val) { - CHECK(!mIsPacked); - mRandomAccessPoint = val; -} - -void TRTPAudioPacket::setDropable(bool val) { - CHECK(!mIsPacked); - mDropable = val; -} - -void TRTPAudioPacket::setDiscontinuity(bool val) { - CHECK(!mIsPacked); - mDiscontinuity = val; -} - -void TRTPAudioPacket::setEndOfStream(bool val) { - CHECK(!mIsPacked); - mEndOfStream = val; -} - -void TRTPAudioPacket::setVolume(uint8_t val) { - CHECK(!mIsPacked); - mVolume = val; -} - -void TRTPAudioPacket::setAccessUnitData(const void* data, size_t len) { - CHECK(!mIsPacked); - mAccessUnitData = data; - mAccessUnitLen = len; -} - -void TRTPAudioPacket::setAuxData(const void* data, size_t len) { - CHECK(!mIsPacked); - mAuxData = data; - mAuxDataLen = len; -} - -/*** TRTP control packet properties ***/ - -void TRTPControlPacket::setCommandID(TRTPCommandID val) { - CHECK(!mIsPacked); - mCommandID = val; -} - -/*** TRTP packet serializers ***/ - -void TRTPPacket::writeU8(uint8_t*& buf, uint8_t val) { - *buf = val; - buf++; -} - -void TRTPPacket::writeU16(uint8_t*& buf, uint16_t val) { - *reinterpret_cast<uint16_t*>(buf) = htons(val); - buf += 2; -} - -void TRTPPacket::writeU32(uint8_t*& buf, uint32_t val) { - *reinterpret_cast<uint32_t*>(buf) = htonl(val); - buf += 4; -} - -void TRTPPacket::writeU64(uint8_t*& buf, uint64_t val) { - buf[0] = static_cast<uint8_t>(val >> 56); - buf[1] = static_cast<uint8_t>(val >> 48); - buf[2] = static_cast<uint8_t>(val >> 40); - buf[3] = static_cast<uint8_t>(val >> 32); - buf[4] = static_cast<uint8_t>(val >> 24); - buf[5] = static_cast<uint8_t>(val >> 16); - buf[6] = static_cast<uint8_t>(val >> 8); - buf[7] = static_cast<uint8_t>(val); - buf += 8; -} - -void TRTPPacket::writeTRTPHeader(uint8_t*& buf, - bool isFirstFragment, - int totalPacketLen) { - // RTP header - writeU8(buf, - ((mVersion & 0x03) << 6) | - (static_cast<int>(mPadding) << 5) | - (static_cast<int>(mExtension) << 4) | - (mCsrcCount & 0x0F)); - writeU8(buf, - (static_cast<int>(isFirstFragment) << 7) | - (mPayloadType & 0x7F)); - writeU16(buf, mSeqNumber); - if (isFirstFragment && mPTSValid) { - writeU32(buf, mPTS & 0xFFFFFFFF); - } else { - writeU32(buf, 0); - } - writeU32(buf, - ((mEpoch & kTRTPEpochMask) << kTRTPEpochShift) | - ((mProgramID & 0x1F) << 5) | - (mSubstreamID & 0x1F)); - - // TRTP header - writeU8(buf, mTRTPVersion); - writeU8(buf, - ((mTRTPHeaderType & 0x0F) << 4) | - (mClockTranformValid ? 0x02 : 0x00) | - (mPTSValid ? 0x01 : 0x00)); - writeU32(buf, totalPacketLen - kRTPHeaderLen); - if (mPTSValid) { - writeU32(buf, mPTS >> 32); - } - - if (mClockTranformValid) { - writeU64(buf, mClockTranform.a_zero); - writeU32(buf, mClockTranform.a_to_b_numer); - writeU32(buf, mClockTranform.a_to_b_denom); - writeU64(buf, mClockTranform.b_zero); - } -} - -bool TRTPAudioPacket::pack() { - if (mIsPacked) { - return false; - } - - int packetLen = kRTPHeaderLen + - mAuxDataLen + - mAccessUnitLen + - TRTPHeaderLen(); - - // TODO : support multiple fragments - const int kMaxUDPPayloadLen = 65507; - if (packetLen > kMaxUDPPayloadLen) { - return false; - } - - mPacket = new uint8_t[packetLen]; - if (!mPacket) { - return false; - } - - mPacketLen = packetLen; - - uint8_t* cur = mPacket; - bool hasAux = mAuxData && mAuxDataLen; - uint8_t flags = (static_cast<int>(hasAux) << 4) | - (static_cast<int>(mRandomAccessPoint) << 3) | - (static_cast<int>(mDropable) << 2) | - (static_cast<int>(mDiscontinuity) << 1) | - (static_cast<int>(mEndOfStream)); - - writeTRTPHeader(cur, true, packetLen); - writeU8(cur, mCodecType); - writeU8(cur, flags); - writeU8(cur, mVolume); - - if (hasAux) { - writeU32(cur, mAuxDataLen); - memcpy(cur, mAuxData, mAuxDataLen); - cur += mAuxDataLen; - } - - memcpy(cur, mAccessUnitData, mAccessUnitLen); - - mIsPacked = true; - return true; -} - -int TRTPPacket::TRTPHeaderLen() const { - // 6 bytes for version, payload type, flags and length. An additional 4 if - // there are upper timestamp bits present and another 24 if there is a clock - // transformation present. - return 6 + - (mClockTranformValid ? 24 : 0) + - (mPTSValid ? 4 : 0); -} - -int TRTPAudioPacket::TRTPHeaderLen() const { - // TRTPPacket::TRTPHeaderLen() for the base TRTPHeader. 3 bytes for audio's - // codec type, flags and volume field. Another 5 bytes if the codec type is - // PCM and we are sending sample rate/channel count. as well as however long - // the aux data (if present) is. - - int pcmParamLength; - switch(mCodecType) { - case kCodecPCMBigEndian: - case kCodecPCMLittleEndian: - pcmParamLength = 5; - break; - - default: - pcmParamLength = 0; - break; - } - - - int auxDataLenField = (NULL != mAuxData) ? sizeof(uint32_t) : 0; - return TRTPPacket::TRTPHeaderLen() + - 3 + - auxDataLenField + - pcmParamLength; -} - -bool TRTPControlPacket::pack() { - if (mIsPacked) { - return false; - } - - // command packets contain a 2-byte command ID - int packetLen = kRTPHeaderLen + - TRTPHeaderLen() + - 2; - - mPacket = new uint8_t[packetLen]; - if (!mPacket) { - return false; - } - - mPacketLen = packetLen; - - uint8_t* cur = mPacket; - - writeTRTPHeader(cur, true, packetLen); - writeU16(cur, mCommandID); - - mIsPacked = true; - return true; -} - -} // namespace android diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h deleted file mode 100644 index 7f78ea0b..00000000 --- a/media/libaah_rtp/aah_tx_packet.h +++ /dev/null @@ -1,213 +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. - */ - -#ifndef __AAH_TX_PACKET_H__ -#define __AAH_TX_PACKET_H__ - -#include <media/stagefright/foundation/ABase.h> -#include <utils/LinearTransform.h> -#include <utils/RefBase.h> -#include <utils/Timers.h> - -namespace android { - -class TRTPPacket : public RefBase { - public: - enum TRTPHeaderType { - kHeaderTypeAudio = 1, - kHeaderTypeVideo = 2, - kHeaderTypeSubpicture = 3, - kHeaderTypeControl = 4, - }; - - enum TRTPPayloadFlags { - kFlag_TSTransformPresent = 0x02, - kFlag_TSValid = 0x01, - }; - - protected: - TRTPPacket(TRTPHeaderType headerType) - : mIsPacked(false) - , mVersion(2) - , mPadding(false) - , mExtension(false) - , mCsrcCount(0) - , mPayloadType(100) - , mSeqNumber(0) - , mPTSValid(false) - , mPTS(0) - , mEpoch(0) - , mProgramID(0) - , mSubstreamID(0) - , mClockTranformValid(false) - , mTRTPVersion(1) - , mTRTPLength(0) - , mTRTPHeaderType(headerType) - , mPacket(NULL) - , mPacketLen(0) { } - - public: - virtual ~TRTPPacket(); - - void setSeqNumber(uint16_t val); - uint16_t getSeqNumber() const; - - void setPTS(int64_t val); - int64_t getPTS() const; - - void setEpoch(uint32_t val); - void setProgramID(uint16_t val); - void setSubstreamID(uint16_t val); - void setClockTransform(const LinearTransform& trans); - - uint8_t* getPacket() const; - int getPacketLen() const; - - void setExpireTime(nsecs_t val); - nsecs_t getExpireTime() const; - - virtual bool pack() = 0; - - // mask for the number of bits in a TRTP epoch - static const uint32_t kTRTPEpochMask = (1 << 22) - 1; - static const int kTRTPEpochShift = 10; - - protected: - static const int kRTPHeaderLen = 12; - virtual int TRTPHeaderLen() const; - - void writeTRTPHeader(uint8_t*& buf, - bool isFirstFragment, - int totalPacketLen); - - void writeU8(uint8_t*& buf, uint8_t val); - void writeU16(uint8_t*& buf, uint16_t val); - void writeU32(uint8_t*& buf, uint32_t val); - void writeU64(uint8_t*& buf, uint64_t val); - - bool mIsPacked; - - uint8_t mVersion; - bool mPadding; - bool mExtension; - uint8_t mCsrcCount; - uint8_t mPayloadType; - uint16_t mSeqNumber; - bool mPTSValid; - int64_t mPTS; - uint32_t mEpoch; - uint16_t mProgramID; - uint16_t mSubstreamID; - LinearTransform mClockTranform; - bool mClockTranformValid; - uint8_t mTRTPVersion; - uint32_t mTRTPLength; - TRTPHeaderType mTRTPHeaderType; - - uint8_t* mPacket; - int mPacketLen; - - nsecs_t mExpireTime; - - DISALLOW_EVIL_CONSTRUCTORS(TRTPPacket); -}; - -class TRTPAudioPacket : public TRTPPacket { - public: - enum AudioPayloadFlags { - kFlag_AuxLengthPresent = 0x10, - kFlag_RandomAccessPoint = 0x08, - kFlag_Dropable = 0x04, - kFlag_Discontinuity = 0x02, - kFlag_EndOfStream = 0x01, - }; - - TRTPAudioPacket() - : TRTPPacket(kHeaderTypeAudio) - , mCodecType(kCodecInvalid) - , mRandomAccessPoint(false) - , mDropable(false) - , mDiscontinuity(false) - , mEndOfStream(false) - , mVolume(0) - , mAccessUnitData(NULL) - , mAccessUnitLen(0) - , mAuxData(NULL) - , mAuxDataLen(0) { } - - enum TRTPAudioCodecType { - kCodecInvalid = 0, - kCodecPCMBigEndian = 1, - kCodecPCMLittleEndian = 2, - kCodecMPEG1Audio = 3, - kCodecAACAudio = 4, - }; - - void setCodecType(TRTPAudioCodecType val); - void setRandomAccessPoint(bool val); - void setDropable(bool val); - void setDiscontinuity(bool val); - void setEndOfStream(bool val); - void setVolume(uint8_t val); - void setAccessUnitData(const void* data, size_t len); - void setAuxData(const void* data, size_t len); - - virtual bool pack(); - - protected: - virtual int TRTPHeaderLen() const; - - private: - TRTPAudioCodecType mCodecType; - bool mRandomAccessPoint; - bool mDropable; - bool mDiscontinuity; - bool mEndOfStream; - uint8_t mVolume; - - const void* mAccessUnitData; - size_t mAccessUnitLen; - const void* mAuxData; - size_t mAuxDataLen; - - DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket); -}; - -class TRTPControlPacket : public TRTPPacket { - public: - TRTPControlPacket() - : TRTPPacket(kHeaderTypeControl) - , mCommandID(kCommandNop) {} - - enum TRTPCommandID { - kCommandNop = 1, - kCommandFlush = 2, - kCommandEOS = 3, - }; - - void setCommandID(TRTPCommandID val); - - virtual bool pack(); - - private: - TRTPCommandID mCommandID; - - DISALLOW_EVIL_CONSTRUCTORS(TRTPControlPacket); -}; - -} // namespace android - -#endif // __AAH_TX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp deleted file mode 100644 index 974805b3..00000000 --- a/media/libaah_rtp/aah_tx_player.cpp +++ /dev/null @@ -1,1177 +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" -#include <utils/Log.h> - -#define __STDC_FORMAT_MACROS -#include <inttypes.h> -#include <netdb.h> -#include <netinet/ip.h> - -#include <common_time/cc_helper.h> -#include <media/IMediaPlayer.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/FileSource.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaDefs.h> -#include <media/stagefright/MetaData.h> -#include <utils/Timers.h> - -#include "aah_tx_packet.h" -#include "aah_tx_player.h" - -namespace android { - -static int64_t kLowWaterMarkUs = 2000000ll; // 2secs -static int64_t kHighWaterMarkUs = 10000000ll; // 10secs -static const size_t kLowWaterMarkBytes = 40000; -static const size_t kHighWaterMarkBytes = 200000; - -// When we start up, how much lead time should we put on the first access unit? -static const int64_t kAAHStartupLeadTimeUs = 300000LL; - -// How much time do we attempt to lead the clock by in steady state? -static const int64_t kAAHBufferTimeUs = 1000000LL; - -// how long do we keep data in our retransmit buffer after sending it. -const int64_t AAH_TXPlayer::kAAHRetryKeepAroundTimeNs = - kAAHBufferTimeUs * 1100; - -sp<MediaPlayerBase> createAAH_TXPlayer() { - sp<MediaPlayerBase> ret = new AAH_TXPlayer(); - return ret; -} - -template <typename T> static T clamp(T val, T min, T max) { - if (val < min) { - return min; - } else if (val > max) { - return max; - } else { - return val; - } -} - -struct AAH_TXEvent : public TimedEventQueue::Event { - AAH_TXEvent(AAH_TXPlayer *player, - void (AAH_TXPlayer::*method)()) : mPlayer(player) - , mMethod(method) {} - - protected: - virtual ~AAH_TXEvent() {} - - virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { - (mPlayer->*mMethod)(); - } - - private: - AAH_TXPlayer *mPlayer; - void (AAH_TXPlayer::*mMethod)(); - - AAH_TXEvent(const AAH_TXEvent &); - AAH_TXEvent& operator=(const AAH_TXEvent &); -}; - -AAH_TXPlayer::AAH_TXPlayer() - : mQueueStarted(false) - , mFlags(0) - , mExtractorFlags(0) { - DataSource::RegisterDefaultSniffers(); - - mBufferingEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onBufferingUpdate); - mBufferingEventPending = false; - - mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio); - mPumpAudioEventPending = false; - - mAudioCodecData = NULL; - - reset_l(); -} - -AAH_TXPlayer::~AAH_TXPlayer() { - if (mQueueStarted) { - mQueue.stop(); - } - - reset_l(); -} - -void AAH_TXPlayer::cancelPlayerEvents(bool keepBufferingGoing) { - if (!keepBufferingGoing) { - mQueue.cancelEvent(mBufferingEvent->eventID()); - mBufferingEventPending = false; - - mQueue.cancelEvent(mPumpAudioEvent->eventID()); - mPumpAudioEventPending = false; - } -} - -status_t AAH_TXPlayer::initCheck() { - // Check for the presense of the common time service by attempting to query - // for CommonTime's frequency. If we get an error back, we cannot talk to - // the service at all and should abort now. - status_t res; - uint64_t freq; - res = mCCHelper.getCommonFreq(&freq); - if (OK != res) { - ALOGE("Failed to connect to common time service! (res %d)", res); - return res; - } - - return OK; -} - -status_t AAH_TXPlayer::setDataSource( - const char *url, - const KeyedVector<String8, String8> *headers) { - Mutex::Autolock autoLock(mLock); - return setDataSource_l(url, headers); -} - -status_t AAH_TXPlayer::setDataSource_l( - const char *url, - const KeyedVector<String8, String8> *headers) { - reset_l(); - - mUri.setTo(url); - - if (headers) { - mUriHeaders = *headers; - - ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log")); - if (index >= 0) { - // Browser is in "incognito" mode, suppress logging URLs. - - // This isn't something that should be passed to the server. - mUriHeaders.removeItemsAt(index); - - mFlags |= INCOGNITO; - } - } - - // The URL may optionally contain a "#" character followed by a Skyjam - // cookie. Ideally the cookie header should just be passed in the headers - // argument, but the Java API for supplying headers is apparently not yet - // exposed in the SDK used by application developers. - const char kSkyjamCookieDelimiter = '#'; - char* skyjamCookie = strrchr(mUri.string(), kSkyjamCookieDelimiter); - if (skyjamCookie) { - skyjamCookie++; - mUriHeaders.add(String8("Cookie"), String8(skyjamCookie)); - mUri = String8(mUri.string(), skyjamCookie - mUri.string()); - } - - return OK; -} - -status_t AAH_TXPlayer::setDataSource(int fd, int64_t offset, int64_t length) { - Mutex::Autolock autoLock(mLock); - - reset_l(); - - sp<DataSource> dataSource = new FileSource(dup(fd), offset, length); - - status_t err = dataSource->initCheck(); - - if (err != OK) { - return err; - } - - mFileSource = dataSource; - - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); - - if (extractor == NULL) { - return UNKNOWN_ERROR; - } - - return setDataSource_l(extractor); -} - -status_t AAH_TXPlayer::setVideoSurface(const sp<Surface>& surface) { - return OK; -} - -status_t AAH_TXPlayer::setVideoSurfaceTexture( - const sp<ISurfaceTexture>& surfaceTexture) { - return OK; -} - -status_t AAH_TXPlayer::prepare() { - return INVALID_OPERATION; -} - -status_t AAH_TXPlayer::prepareAsync() { - Mutex::Autolock autoLock(mLock); - - return prepareAsync_l(); -} - -status_t AAH_TXPlayer::prepareAsync_l() { - if (mFlags & PREPARING) { - return UNKNOWN_ERROR; // async prepare already pending - } - - mAAH_Sender = AAH_TXSender::GetInstance(); - if (mAAH_Sender == NULL) { - return NO_MEMORY; - } - - if (!mQueueStarted) { - mQueue.start(); - mQueueStarted = true; - } - - mFlags |= PREPARING; - mAsyncPrepareEvent = new AAH_TXEvent( - this, &AAH_TXPlayer::onPrepareAsyncEvent); - - mQueue.postEvent(mAsyncPrepareEvent); - - return OK; -} - -status_t AAH_TXPlayer::finishSetDataSource_l() { - sp<DataSource> dataSource; - - if (!strncasecmp("http://", mUri.string(), 7) || - !strncasecmp("https://", mUri.string(), 8)) { - - mConnectingDataSource = HTTPBase::Create( - (mFlags & INCOGNITO) - ? HTTPBase::kFlagIncognito - : 0); - - mLock.unlock(); - status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders); - mLock.lock(); - - if (err != OK) { - mConnectingDataSource.clear(); - - ALOGI("mConnectingDataSource->connect() returned %d", err); - return err; - } - - mCachedSource = new NuCachedSource2(mConnectingDataSource); - mConnectingDataSource.clear(); - - dataSource = mCachedSource; - - // We're going to prefill the cache before trying to instantiate - // the extractor below, as the latter is an operation that otherwise - // could block on the datasource for a significant amount of time. - // During that time we'd be unable to abort the preparation phase - // without this prefill. - - mLock.unlock(); - - for (;;) { - status_t finalStatus; - size_t cachedDataRemaining = - mCachedSource->approxDataRemaining(&finalStatus); - - if (finalStatus != OK || - cachedDataRemaining >= kHighWaterMarkBytes || - (mFlags & PREPARE_CANCELLED)) { - break; - } - - usleep(200000); - } - - mLock.lock(); - - if (mFlags & PREPARE_CANCELLED) { - ALOGI("Prepare cancelled while waiting for initial cache fill."); - return UNKNOWN_ERROR; - } - } else { - dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders); - } - - if (dataSource == NULL) { - return UNKNOWN_ERROR; - } - - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); - - if (extractor == NULL) { - return UNKNOWN_ERROR; - } - - return setDataSource_l(extractor); -} - -status_t AAH_TXPlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { - // Attempt to approximate overall stream bitrate by summing all - // tracks' individual bitrates, if not all of them advertise bitrate, - // we have to fail. - - int64_t totalBitRate = 0; - - for (size_t i = 0; i < extractor->countTracks(); ++i) { - sp<MetaData> meta = extractor->getTrackMetaData(i); - - int32_t bitrate; - if (!meta->findInt32(kKeyBitRate, &bitrate)) { - totalBitRate = -1; - break; - } - - totalBitRate += bitrate; - } - - mBitrate = totalBitRate; - - ALOGV("mBitrate = %lld bits/sec", mBitrate); - - bool haveAudio = false; - for (size_t i = 0; i < extractor->countTracks(); ++i) { - sp<MetaData> meta = extractor->getTrackMetaData(i); - - const char *mime; - CHECK(meta->findCString(kKeyMIMEType, &mime)); - - if (!strncasecmp(mime, "audio/", 6)) { - mAudioSource = extractor->getTrack(i); - CHECK(mAudioSource != NULL); - haveAudio = true; - break; - } - } - - if (!haveAudio) { - return UNKNOWN_ERROR; - } - - mExtractorFlags = extractor->flags(); - - return OK; -} - -void AAH_TXPlayer::abortPrepare(status_t err) { - CHECK(err != OK); - - notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); - - mPrepareResult = err; - mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED); - mPreparedCondition.broadcast(); -} - -void AAH_TXPlayer::onPrepareAsyncEvent() { - Mutex::Autolock autoLock(mLock); - - if (mFlags & PREPARE_CANCELLED) { - ALOGI("prepare was cancelled before doing anything"); - abortPrepare(UNKNOWN_ERROR); - return; - } - - if (mUri.size() > 0) { - status_t err = finishSetDataSource_l(); - - if (err != OK) { - abortPrepare(err); - return; - } - } - - mAudioFormat = mAudioSource->getFormat(); - if (!mAudioFormat->findInt64(kKeyDuration, &mDurationUs)) - mDurationUs = 1; - - const char* mime_type = NULL; - if (!mAudioFormat->findCString(kKeyMIMEType, &mime_type)) { - ALOGE("Failed to find audio substream MIME type during prepare."); - abortPrepare(BAD_VALUE); - return; - } - - if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_MPEG)) { - mAudioCodec = TRTPAudioPacket::kCodecMPEG1Audio; - } else - if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_AAC)) { - mAudioCodec = TRTPAudioPacket::kCodecAACAudio; - - uint32_t type; - int32_t sample_rate; - int32_t channel_count; - const void* esds_data; - size_t esds_len; - - if (!mAudioFormat->findInt32(kKeySampleRate, &sample_rate)) { - ALOGE("Failed to find sample rate for AAC substream."); - abortPrepare(BAD_VALUE); - return; - } - - if (!mAudioFormat->findInt32(kKeyChannelCount, &channel_count)) { - ALOGE("Failed to find channel count for AAC substream."); - abortPrepare(BAD_VALUE); - return; - } - - if (!mAudioFormat->findData(kKeyESDS, &type, &esds_data, &esds_len)) { - ALOGE("Failed to find codec init data for AAC substream."); - abortPrepare(BAD_VALUE); - return; - } - - CHECK(NULL == mAudioCodecData); - mAudioCodecDataSize = esds_len - + sizeof(sample_rate) - + sizeof(channel_count); - mAudioCodecData = new uint8_t[mAudioCodecDataSize]; - if (NULL == mAudioCodecData) { - ALOGE("Failed to allocate %u bytes for AAC substream codec aux" - " data.", mAudioCodecDataSize); - mAudioCodecDataSize = 0; - abortPrepare(BAD_VALUE); - return; - } - - uint8_t* tmp = mAudioCodecData; - tmp[0] = static_cast<uint8_t>((sample_rate >> 24) & 0xFF); - tmp[1] = static_cast<uint8_t>((sample_rate >> 16) & 0xFF); - tmp[2] = static_cast<uint8_t>((sample_rate >> 8) & 0xFF); - tmp[3] = static_cast<uint8_t>((sample_rate ) & 0xFF); - tmp[4] = static_cast<uint8_t>((channel_count >> 24) & 0xFF); - tmp[5] = static_cast<uint8_t>((channel_count >> 16) & 0xFF); - tmp[6] = static_cast<uint8_t>((channel_count >> 8) & 0xFF); - tmp[7] = static_cast<uint8_t>((channel_count ) & 0xFF); - - memcpy(tmp + 8, esds_data, esds_len); - } else { - ALOGE("Unsupported MIME type \"%s\" in audio substream", mime_type); - abortPrepare(BAD_VALUE); - return; - } - - status_t err = mAudioSource->start(); - if (err != OK) { - ALOGI("failed to start audio source, err=%d", err); - abortPrepare(err); - return; - } - - mFlags |= PREPARING_CONNECTED; - - if (mCachedSource != NULL) { - postBufferingEvent_l(); - } else { - finishAsyncPrepare_l(); - } -} - -void AAH_TXPlayer::finishAsyncPrepare_l() { - notifyListener_l(MEDIA_PREPARED); - - mPrepareResult = OK; - mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED); - mFlags |= PREPARED; - mPreparedCondition.broadcast(); -} - -status_t AAH_TXPlayer::start() { - Mutex::Autolock autoLock(mLock); - - mFlags &= ~CACHE_UNDERRUN; - - return play_l(); -} - -status_t AAH_TXPlayer::play_l() { - if (mFlags & PLAYING) { - return OK; - } - - if (!(mFlags & PREPARED)) { - return INVALID_OPERATION; - } - - { - Mutex::Autolock lock(mEndpointLock); - if (!mEndpointValid) { - return INVALID_OPERATION; - } - if (!mEndpointRegistered) { - mProgramID = mAAH_Sender->registerEndpoint(mEndpoint); - mEndpointRegistered = true; - } - } - - mFlags |= PLAYING; - - updateClockTransform_l(false); - - postPumpAudioEvent_l(-1); - - return OK; -} - -status_t AAH_TXPlayer::stop() { - status_t ret = pause(); - sendEOS_l(); - return ret; -} - -status_t AAH_TXPlayer::pause() { - Mutex::Autolock autoLock(mLock); - - mFlags &= ~CACHE_UNDERRUN; - - return pause_l(); -} - -status_t AAH_TXPlayer::pause_l(bool doClockUpdate) { - if (!(mFlags & PLAYING)) { - return OK; - } - - cancelPlayerEvents(true /* keepBufferingGoing */); - - mFlags &= ~PLAYING; - - if (doClockUpdate) { - updateClockTransform_l(true); - } - - return OK; -} - -void AAH_TXPlayer::updateClockTransform_l(bool pause) { - // record the new pause status so that onPumpAudio knows what rate to apply - // when it initializes the transform - mPlayRateIsPaused = pause; - - // if we haven't yet established a valid clock transform, then we can't - // do anything here - if (!mCurrentClockTransformValid) { - return; - } - - // sample the current common time - int64_t commonTimeNow; - if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { - ALOGE("updateClockTransform_l get common time failed"); - mCurrentClockTransformValid = false; - return; - } - - // convert the current common time to media time using the old - // transform - int64_t mediaTimeNow; - if (!mCurrentClockTransform.doReverseTransform( - commonTimeNow, &mediaTimeNow)) { - ALOGE("updateClockTransform_l reverse transform failed"); - mCurrentClockTransformValid = false; - return; - } - - // calculate a new transform that preserves the old transform's - // result for the current time - mCurrentClockTransform.a_zero = mediaTimeNow; - mCurrentClockTransform.b_zero = commonTimeNow; - mCurrentClockTransform.a_to_b_numer = 1; - mCurrentClockTransform.a_to_b_denom = pause ? 0 : 1; - - // send a packet announcing the new transform - sp<TRTPControlPacket> packet = new TRTPControlPacket(); - packet->setClockTransform(mCurrentClockTransform); - packet->setCommandID(TRTPControlPacket::kCommandNop); - queuePacketToSender_l(packet); -} - -void AAH_TXPlayer::sendEOS_l() { - sp<TRTPControlPacket> packet = new TRTPControlPacket(); - packet->setCommandID(TRTPControlPacket::kCommandEOS); - queuePacketToSender_l(packet); -} - -bool AAH_TXPlayer::isPlaying() { - return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN); -} - -status_t AAH_TXPlayer::seekTo(int msec) { - if (mExtractorFlags & MediaExtractor::CAN_SEEK) { - Mutex::Autolock autoLock(mLock); - return seekTo_l(static_cast<int64_t>(msec) * 1000); - } - - notifyListener_l(MEDIA_SEEK_COMPLETE); - return OK; -} - -status_t AAH_TXPlayer::seekTo_l(int64_t timeUs) { - mIsSeeking = true; - mSeekTimeUs = timeUs; - - mCurrentClockTransformValid = false; - mLastQueuedMediaTimePTSValid = false; - - // send a flush command packet - sp<TRTPControlPacket> packet = new TRTPControlPacket(); - packet->setCommandID(TRTPControlPacket::kCommandFlush); - queuePacketToSender_l(packet); - - return OK; -} - -status_t AAH_TXPlayer::getCurrentPosition(int *msec) { - if (!msec) { - return BAD_VALUE; - } - - Mutex::Autolock lock(mLock); - - int position; - - if (mIsSeeking) { - position = mSeekTimeUs / 1000; - } else if (mCurrentClockTransformValid) { - // sample the current common time - int64_t commonTimeNow; - if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { - ALOGE("getCurrentPosition get common time failed"); - return INVALID_OPERATION; - } - - int64_t mediaTimeNow; - if (!mCurrentClockTransform.doReverseTransform(commonTimeNow, - &mediaTimeNow)) { - ALOGE("getCurrentPosition reverse transform failed"); - return INVALID_OPERATION; - } - - position = static_cast<int>(mediaTimeNow / 1000); - } else { - position = 0; - } - - int duration; - if (getDuration_l(&duration) == OK) { - *msec = clamp(position, 0, duration); - } else { - *msec = (position >= 0) ? position : 0; - } - - return OK; -} - -status_t AAH_TXPlayer::getDuration(int* msec) { - if (!msec) { - return BAD_VALUE; - } - - Mutex::Autolock lock(mLock); - - return getDuration_l(msec); -} - -status_t AAH_TXPlayer::getDuration_l(int* msec) { - if (mDurationUs < 0) { - return UNKNOWN_ERROR; - } - - *msec = (mDurationUs + 500) / 1000; - - return OK; -} - -status_t AAH_TXPlayer::reset() { - Mutex::Autolock autoLock(mLock); - reset_l(); - return OK; -} - -void AAH_TXPlayer::reset_l() { - if (mFlags & PREPARING) { - mFlags |= PREPARE_CANCELLED; - if (mConnectingDataSource != NULL) { - ALOGI("interrupting the connection process"); - mConnectingDataSource->disconnect(); - } - - if (mFlags & PREPARING_CONNECTED) { - // We are basically done preparing, we're just buffering - // enough data to start playback, we can safely interrupt that. - finishAsyncPrepare_l(); - } - } - - while (mFlags & PREPARING) { - mPreparedCondition.wait(mLock); - } - - cancelPlayerEvents(); - - sendEOS_l(); - - mCachedSource.clear(); - - if (mAudioSource != NULL) { - mAudioSource->stop(); - } - mAudioSource.clear(); - mAudioCodec = TRTPAudioPacket::kCodecInvalid; - mAudioFormat = NULL; - delete[] mAudioCodecData; - mAudioCodecData = NULL; - mAudioCodecDataSize = 0; - - mFlags = 0; - mExtractorFlags = 0; - - mDurationUs = -1; - mIsSeeking = false; - mSeekTimeUs = 0; - - mUri.setTo(""); - mUriHeaders.clear(); - - mFileSource.clear(); - - mBitrate = -1; - - { - Mutex::Autolock lock(mEndpointLock); - if (mAAH_Sender != NULL && mEndpointRegistered) { - mAAH_Sender->unregisterEndpoint(mEndpoint); - } - mEndpointRegistered = false; - mEndpointValid = false; - } - - mProgramID = 0; - - mAAH_Sender.clear(); - mLastQueuedMediaTimePTSValid = false; - mCurrentClockTransformValid = false; - mPlayRateIsPaused = false; - - mTRTPVolume = 255; -} - -status_t AAH_TXPlayer::setLooping(int loop) { - return OK; -} - -player_type AAH_TXPlayer::playerType() { - return AAH_TX_PLAYER; -} - -status_t AAH_TXPlayer::setParameter(int key, const Parcel &request) { - return ERROR_UNSUPPORTED; -} - -status_t AAH_TXPlayer::getParameter(int key, Parcel *reply) { - return ERROR_UNSUPPORTED; -} - -status_t AAH_TXPlayer::invoke(const Parcel& request, Parcel *reply) { - return INVALID_OPERATION; -} - -status_t AAH_TXPlayer::getMetadata(const media::Metadata::Filter& ids, - Parcel* records) { - using media::Metadata; - - Metadata metadata(records); - - metadata.appendBool(Metadata::kPauseAvailable, true); - metadata.appendBool(Metadata::kSeekBackwardAvailable, false); - metadata.appendBool(Metadata::kSeekForwardAvailable, false); - metadata.appendBool(Metadata::kSeekAvailable, false); - - return OK; -} - -status_t AAH_TXPlayer::setVolume(float leftVolume, float rightVolume) { - if (leftVolume != rightVolume) { - ALOGE("%s does not support per channel volume: %f, %f", - __PRETTY_FUNCTION__, leftVolume, rightVolume); - } - - float volume = clamp(leftVolume, 0.0f, 1.0f); - - Mutex::Autolock lock(mLock); - mTRTPVolume = static_cast<uint8_t>((leftVolume * 255.0) + 0.5); - - return OK; -} - -status_t AAH_TXPlayer::setAudioStreamType(audio_stream_type_t streamType) { - return OK; -} - -status_t AAH_TXPlayer::setRetransmitEndpoint( - const struct sockaddr_in* endpoint) { - Mutex::Autolock lock(mLock); - - if (NULL == endpoint) - return BAD_VALUE; - - // Once the endpoint has been registered, it may not be changed. - if (mEndpointRegistered) - return INVALID_OPERATION; - - mEndpoint.addr = endpoint->sin_addr.s_addr; - mEndpoint.port = endpoint->sin_port; - mEndpointValid = true; - - return OK; -} - -void AAH_TXPlayer::notifyListener_l(int msg, int ext1, int ext2) { - sendEvent(msg, ext1, ext2); -} - -bool AAH_TXPlayer::getBitrate_l(int64_t *bitrate) { - off64_t size; - if (mDurationUs >= 0 && - mCachedSource != NULL && - mCachedSource->getSize(&size) == OK) { - *bitrate = size * 8000000ll / mDurationUs; // in bits/sec - return true; - } - - if (mBitrate >= 0) { - *bitrate = mBitrate; - return true; - } - - *bitrate = 0; - - return false; -} - -// Returns true iff cached duration is available/applicable. -bool AAH_TXPlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) { - int64_t bitrate; - - if (mCachedSource != NULL && getBitrate_l(&bitrate)) { - status_t finalStatus; - size_t cachedDataRemaining = mCachedSource->approxDataRemaining( - &finalStatus); - *durationUs = cachedDataRemaining * 8000000ll / bitrate; - *eos = (finalStatus != OK); - return true; - } - - return false; -} - -void AAH_TXPlayer::ensureCacheIsFetching_l() { - if (mCachedSource != NULL) { - mCachedSource->resumeFetchingIfNecessary(); - } -} - -void AAH_TXPlayer::postBufferingEvent_l() { - if (mBufferingEventPending) { - return; - } - mBufferingEventPending = true; - mQueue.postEventWithDelay(mBufferingEvent, 1000000ll); -} - -void AAH_TXPlayer::postPumpAudioEvent_l(int64_t delayUs) { - if (mPumpAudioEventPending) { - return; - } - mPumpAudioEventPending = true; - mQueue.postEventWithDelay(mPumpAudioEvent, delayUs < 0 ? 10000 : delayUs); -} - -void AAH_TXPlayer::onBufferingUpdate() { - Mutex::Autolock autoLock(mLock); - if (!mBufferingEventPending) { - return; - } - mBufferingEventPending = false; - - if (mCachedSource != NULL) { - status_t finalStatus; - size_t cachedDataRemaining = mCachedSource->approxDataRemaining( - &finalStatus); - bool eos = (finalStatus != OK); - - if (eos) { - if (finalStatus == ERROR_END_OF_STREAM) { - notifyListener_l(MEDIA_BUFFERING_UPDATE, 100); - } - if (mFlags & PREPARING) { - ALOGV("cache has reached EOS, prepare is done."); - finishAsyncPrepare_l(); - } - } else { - int64_t bitrate; - if (getBitrate_l(&bitrate)) { - size_t cachedSize = mCachedSource->cachedSize(); - int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate; - - int percentage = (100.0 * (double) cachedDurationUs) - / mDurationUs; - if (percentage > 100) { - percentage = 100; - } - - notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage); - } else { - // We don't know the bitrate of the stream, use absolute size - // limits to maintain the cache. - - if ((mFlags & PLAYING) && - !eos && - (cachedDataRemaining < kLowWaterMarkBytes)) { - ALOGI("cache is running low (< %d) , pausing.", - kLowWaterMarkBytes); - mFlags |= CACHE_UNDERRUN; - pause_l(); - ensureCacheIsFetching_l(); - notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START); - } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) { - if (mFlags & CACHE_UNDERRUN) { - ALOGI("cache has filled up (> %d), resuming.", - kHighWaterMarkBytes); - mFlags &= ~CACHE_UNDERRUN; - play_l(); - notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END); - } else if (mFlags & PREPARING) { - ALOGV("cache has filled up (> %d), prepare is done", - kHighWaterMarkBytes); - finishAsyncPrepare_l(); - } - } - } - } - } - - int64_t cachedDurationUs; - bool eos; - if (getCachedDuration_l(&cachedDurationUs, &eos)) { - ALOGV("cachedDurationUs = %.2f secs, eos=%d", - cachedDurationUs / 1E6, eos); - - if ((mFlags & PLAYING) && - !eos && - (cachedDurationUs < kLowWaterMarkUs)) { - ALOGI("cache is running low (%.2f secs) , pausing.", - cachedDurationUs / 1E6); - mFlags |= CACHE_UNDERRUN; - pause_l(); - ensureCacheIsFetching_l(); - notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START); - } else if (eos || cachedDurationUs > kHighWaterMarkUs) { - if (mFlags & CACHE_UNDERRUN) { - ALOGI("cache has filled up (%.2f secs), resuming.", - cachedDurationUs / 1E6); - mFlags &= ~CACHE_UNDERRUN; - play_l(); - notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END); - } else if (mFlags & PREPARING) { - ALOGV("cache has filled up (%.2f secs), prepare is done", - cachedDurationUs / 1E6); - finishAsyncPrepare_l(); - } - } - } - - postBufferingEvent_l(); -} - -void AAH_TXPlayer::onPumpAudio() { - while (true) { - Mutex::Autolock autoLock(mLock); - // If this flag is clear, its because someone has externally canceled - // this pump operation (probably because we a resetting/shutting down). - // Get out immediately, do not reschedule ourselves. - if (!mPumpAudioEventPending) { - return; - } - - // Start by checking if there is still work to be doing. If we have - // never queued a payload (so we don't know what the last queued PTS is) - // or we have never established a MediaTime->CommonTime transformation, - // then we have work to do (one time through this loop should establish - // both). Otherwise, we want to keep a fixed amt of presentation time - // worth of data buffered. If we cannot get common time (service is - // unavailable, or common time is undefined)) then we don't have a lot - // of good options here. For now, signal an error up to the app level - // and shut down the transmission pump. - int64_t commonTimeNow; - if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { - // Failed to get common time; either the service is down or common - // time is not synced. Raise an error and shutdown the player. - ALOGE("*** Cannot pump audio, unable to fetch common time." - " Shutting down."); - notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, UNKNOWN_ERROR); - mPumpAudioEventPending = false; - break; - } - - if (mCurrentClockTransformValid && mLastQueuedMediaTimePTSValid) { - int64_t mediaTimeNow; - bool conversionResult = mCurrentClockTransform.doReverseTransform( - commonTimeNow, - &mediaTimeNow); - CHECK(conversionResult); - - if ((mediaTimeNow + - kAAHBufferTimeUs - - mLastQueuedMediaTimePTS) <= 0) { - break; - } - } - - MediaSource::ReadOptions options; - if (mIsSeeking) { - options.setSeekTo(mSeekTimeUs); - } - - MediaBuffer* mediaBuffer; - status_t err = mAudioSource->read(&mediaBuffer, &options); - if (err != NO_ERROR) { - if (err == ERROR_END_OF_STREAM) { - ALOGI("*** %s reached end of stream", __PRETTY_FUNCTION__); - notifyListener_l(MEDIA_BUFFERING_UPDATE, 100); - notifyListener_l(MEDIA_PLAYBACK_COMPLETE); - pause_l(false); - sendEOS_l(); - } else { - ALOGE("*** %s read failed err=%d", __PRETTY_FUNCTION__, err); - } - return; - } - - if (mIsSeeking) { - mIsSeeking = false; - notifyListener_l(MEDIA_SEEK_COMPLETE); - } - - uint8_t* data = (static_cast<uint8_t*>(mediaBuffer->data()) + - mediaBuffer->range_offset()); - ALOGV("*** %s got media buffer data=[%02hhx %02hhx %02hhx %02hhx]" - " offset=%d length=%d", __PRETTY_FUNCTION__, - data[0], data[1], data[2], data[3], - mediaBuffer->range_offset(), mediaBuffer->range_length()); - - int64_t mediaTimeUs; - CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &mediaTimeUs)); - ALOGV("*** timeUs=%lld", mediaTimeUs); - - if (!mCurrentClockTransformValid) { - if (OK == mCCHelper.getCommonTime(&commonTimeNow)) { - mCurrentClockTransform.a_zero = mediaTimeUs; - mCurrentClockTransform.b_zero = commonTimeNow + - kAAHStartupLeadTimeUs; - mCurrentClockTransform.a_to_b_numer = 1; - mCurrentClockTransform.a_to_b_denom = mPlayRateIsPaused ? 0 : 1; - mCurrentClockTransformValid = true; - } else { - // Failed to get common time; either the service is down or - // common time is not synced. Raise an error and shutdown the - // player. - ALOGE("*** Cannot begin transmission, unable to fetch common" - " time. Dropping sample with pts=%lld", mediaTimeUs); - notifyListener_l(MEDIA_ERROR, - MEDIA_ERROR_UNKNOWN, - UNKNOWN_ERROR); - mPumpAudioEventPending = false; - break; - } - } - - ALOGV("*** transmitting packet with pts=%lld", mediaTimeUs); - - sp<TRTPAudioPacket> packet = new TRTPAudioPacket(); - packet->setPTS(mediaTimeUs); - packet->setSubstreamID(1); - - packet->setCodecType(mAudioCodec); - packet->setVolume(mTRTPVolume); - // TODO : introduce a throttle for this so we can control the - // frequency with which transforms get sent. - packet->setClockTransform(mCurrentClockTransform); - packet->setAccessUnitData(data, mediaBuffer->range_length()); - - // TODO : while its pretty much universally true that audio ES payloads - // are all RAPs across all codecs, it might be a good idea to throttle - // the frequency with which we send codec out of band data to the RXers. - // If/when we do, we need to flag only those payloads which have - // required out of band data attached to them as RAPs. - packet->setRandomAccessPoint(true); - - if (mAudioCodecData && mAudioCodecDataSize) { - packet->setAuxData(mAudioCodecData, mAudioCodecDataSize); - } - - queuePacketToSender_l(packet); - mediaBuffer->release(); - - mLastQueuedMediaTimePTSValid = true; - mLastQueuedMediaTimePTS = mediaTimeUs; - } - - { // Explicit scope for the autolock pattern. - Mutex::Autolock autoLock(mLock); - - // If someone externally has cleared this flag, its because we should be - // shutting down. Do not reschedule ourselves. - if (!mPumpAudioEventPending) { - return; - } - - // Looks like no one canceled us explicitly. Clear our flag and post a - // new event to ourselves. - mPumpAudioEventPending = false; - postPumpAudioEvent_l(10000); - } -} - -void AAH_TXPlayer::queuePacketToSender_l(const sp<TRTPPacket>& packet) { - if (mAAH_Sender == NULL) { - return; - } - - sp<AMessage> message = new AMessage(AAH_TXSender::kWhatSendPacket, - mAAH_Sender->handlerID()); - - { - Mutex::Autolock lock(mEndpointLock); - if (!mEndpointValid) { - return; - } - - message->setInt32(AAH_TXSender::kSendPacketIPAddr, mEndpoint.addr); - message->setInt32(AAH_TXSender::kSendPacketPort, mEndpoint.port); - } - - packet->setProgramID(mProgramID); - packet->setExpireTime(systemTime() + kAAHRetryKeepAroundTimeNs); - packet->pack(); - - message->setObject(AAH_TXSender::kSendPacketTRTPPacket, packet); - - message->post(); -} - -} // namespace android diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h deleted file mode 100644 index 2e4b1f7e..00000000 --- a/media/libaah_rtp/aah_tx_player.h +++ /dev/null @@ -1,176 +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. - */ - -#ifndef __AAH_TX_PLAYER_H__ -#define __AAH_TX_PLAYER_H__ - -#include <common_time/cc_helper.h> -#include <libstagefright/include/HTTPBase.h> -#include <libstagefright/include/NuCachedSource2.h> -#include <libstagefright/include/TimedEventQueue.h> -#include <media/MediaPlayerInterface.h> -#include <media/stagefright/MediaExtractor.h> -#include <media/stagefright/MediaSource.h> -#include <utils/LinearTransform.h> -#include <utils/String8.h> -#include <utils/threads.h> - -#include "aah_tx_sender.h" - -namespace android { - -class AAH_TXPlayer : public MediaPlayerHWInterface { - public: - AAH_TXPlayer(); - - virtual status_t initCheck(); - virtual status_t setDataSource(const char *url, - const KeyedVector<String8, String8>* - headers); - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoSurface(const sp<Surface>& surface); - virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& - surfaceTexture); - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t seekTo(int msec); - virtual status_t getCurrentPosition(int *msec); - virtual status_t getDuration(int *msec); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType(); - virtual status_t setParameter(int key, const Parcel &request); - virtual status_t getParameter(int key, Parcel *reply); - virtual status_t invoke(const Parcel& request, Parcel *reply); - virtual status_t getMetadata(const media::Metadata::Filter& ids, - Parcel* records); - virtual status_t setVolume(float leftVolume, float rightVolume); - virtual status_t setAudioStreamType(audio_stream_type_t streamType); - virtual status_t setRetransmitEndpoint( - const struct sockaddr_in* endpoint); - - static const int64_t kAAHRetryKeepAroundTimeNs; - - protected: - virtual ~AAH_TXPlayer(); - - private: - friend struct AwesomeEvent; - - enum { - PLAYING = 1, - PREPARING = 8, - PREPARED = 16, - PREPARE_CANCELLED = 64, - CACHE_UNDERRUN = 128, - - // We are basically done preparing but are currently buffering - // sufficient data to begin playback and finish the preparation - // phase for good. - PREPARING_CONNECTED = 2048, - - INCOGNITO = 32768, - }; - - status_t setDataSource_l(const char *url, - const KeyedVector<String8, String8> *headers); - status_t setDataSource_l(const sp<MediaExtractor>& extractor); - status_t finishSetDataSource_l(); - status_t prepareAsync_l(); - void onPrepareAsyncEvent(); - void finishAsyncPrepare_l(); - void abortPrepare(status_t err); - status_t play_l(); - status_t pause_l(bool doClockUpdate = true); - status_t seekTo_l(int64_t timeUs); - void updateClockTransform_l(bool pause); - void sendEOS_l(); - void cancelPlayerEvents(bool keepBufferingGoing = false); - void reset_l(); - void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0); - bool getBitrate_l(int64_t* bitrate); - status_t getDuration_l(int* msec); - bool getCachedDuration_l(int64_t* durationUs, bool* eos); - void ensureCacheIsFetching_l(); - void postBufferingEvent_l(); - void postPumpAudioEvent_l(int64_t delayUs); - void onBufferingUpdate(); - void onPumpAudio(); - void queuePacketToSender_l(const sp<TRTPPacket>& packet); - - Mutex mLock; - - TimedEventQueue mQueue; - bool mQueueStarted; - - sp<TimedEventQueue::Event> mBufferingEvent; - bool mBufferingEventPending; - - uint32_t mFlags; - uint32_t mExtractorFlags; - - String8 mUri; - KeyedVector<String8, String8> mUriHeaders; - - sp<DataSource> mFileSource; - - sp<TimedEventQueue::Event> mAsyncPrepareEvent; - Condition mPreparedCondition; - status_t mPrepareResult; - - bool mIsSeeking; - int64_t mSeekTimeUs; - - sp<TimedEventQueue::Event> mPumpAudioEvent; - bool mPumpAudioEventPending; - - sp<HTTPBase> mConnectingDataSource; - sp<NuCachedSource2> mCachedSource; - - sp<MediaSource> mAudioSource; - TRTPAudioPacket::TRTPAudioCodecType mAudioCodec; - sp<MetaData> mAudioFormat; - uint8_t* mAudioCodecData; - size_t mAudioCodecDataSize; - - int64_t mDurationUs; - int64_t mBitrate; - - sp<AAH_TXSender> mAAH_Sender; - LinearTransform mCurrentClockTransform; - bool mCurrentClockTransformValid; - int64_t mLastQueuedMediaTimePTS; - bool mLastQueuedMediaTimePTSValid; - bool mPlayRateIsPaused; - CCHelper mCCHelper; - - Mutex mEndpointLock; - AAH_TXSender::Endpoint mEndpoint; - bool mEndpointValid; - bool mEndpointRegistered; - uint16_t mProgramID; - uint8_t mTRTPVolume; - - DISALLOW_EVIL_CONSTRUCTORS(AAH_TXPlayer); -}; - -} // namespace android - -#endif // __AAH_TX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp deleted file mode 100644 index 08e32d26..00000000 --- a/media/libaah_rtp/aah_tx_sender.cpp +++ /dev/null @@ -1,603 +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" -#include <media/stagefright/foundation/ADebug.h> - -#include <netinet/in.h> -#include <poll.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <media/stagefright/foundation/AMessage.h> -#include <utils/misc.h> - -#include "aah_tx_player.h" -#include "aah_tx_sender.h" - -namespace android { - -const char* AAH_TXSender::kSendPacketIPAddr = "ipaddr"; -const char* AAH_TXSender::kSendPacketPort = "port"; -const char* AAH_TXSender::kSendPacketTRTPPacket = "trtp"; - -const int AAH_TXSender::kRetryTrimIntervalUs = 100000; -const int AAH_TXSender::kHeartbeatIntervalUs = 1000000; -const int AAH_TXSender::kRetryBufferCapacity = 100; -const nsecs_t AAH_TXSender::kHeartbeatTimeout = 600ull * 1000000000ull; - -Mutex AAH_TXSender::sLock; -wp<AAH_TXSender> AAH_TXSender::sInstance; -uint32_t AAH_TXSender::sNextEpoch; -bool AAH_TXSender::sNextEpochValid = false; - -AAH_TXSender::AAH_TXSender() : mSocket(-1) { - mLastSentPacketTime = systemTime(); -} - -sp<AAH_TXSender> AAH_TXSender::GetInstance() { - Mutex::Autolock autoLock(sLock); - - sp<AAH_TXSender> sender = sInstance.promote(); - - if (sender == NULL) { - sender = new AAH_TXSender(); - if (sender == NULL) { - return NULL; - } - - sender->mLooper = new ALooper(); - if (sender->mLooper == NULL) { - return NULL; - } - - sender->mReflector = new AHandlerReflector<AAH_TXSender>(sender.get()); - if (sender->mReflector == NULL) { - return NULL; - } - - sender->mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sender->mSocket == -1) { - ALOGW("%s unable to create socket", __PRETTY_FUNCTION__); - return NULL; - } - - struct sockaddr_in bind_addr; - memset(&bind_addr, 0, sizeof(bind_addr)); - bind_addr.sin_family = AF_INET; - if (bind(sender->mSocket, - reinterpret_cast<const sockaddr*>(&bind_addr), - sizeof(bind_addr)) < 0) { - ALOGW("%s unable to bind socket (errno %d)", - __PRETTY_FUNCTION__, errno); - return NULL; - } - - sender->mRetryReceiver = new RetryReceiver(sender.get()); - if (sender->mRetryReceiver == NULL) { - return NULL; - } - - sender->mLooper->setName("AAH_TXSender"); - sender->mLooper->registerHandler(sender->mReflector); - sender->mLooper->start(false, false, PRIORITY_AUDIO); - - if (sender->mRetryReceiver->run("AAH_TXSenderRetry", PRIORITY_AUDIO) - != OK) { - ALOGW("%s unable to start retry thread", __PRETTY_FUNCTION__); - return NULL; - } - - sInstance = sender; - } - - return sender; -} - -AAH_TXSender::~AAH_TXSender() { - mLooper->stop(); - mLooper->unregisterHandler(mReflector->id()); - - if (mRetryReceiver != NULL) { - mRetryReceiver->requestExit(); - mRetryReceiver->mWakeupEvent.setEvent(); - if (mRetryReceiver->requestExitAndWait() != OK) { - ALOGW("%s shutdown of retry receiver failed", __PRETTY_FUNCTION__); - } - mRetryReceiver->mSender = NULL; - mRetryReceiver.clear(); - } - - if (mSocket != -1) { - close(mSocket); - } -} - -// Return the next epoch number usable for a newly instantiated endpoint. -uint32_t AAH_TXSender::getNextEpoch() { - Mutex::Autolock autoLock(sLock); - - if (sNextEpochValid) { - sNextEpoch = (sNextEpoch + 1) & TRTPPacket::kTRTPEpochMask; - } else { - sNextEpoch = ns2ms(systemTime()) & TRTPPacket::kTRTPEpochMask; - sNextEpochValid = true; - } - - return sNextEpoch; -} - -// Notify the sender that a player has started sending to this endpoint. -// Returns a program ID for use by the calling player. -uint16_t AAH_TXSender::registerEndpoint(const Endpoint& endpoint) { - Mutex::Autolock lock(mEndpointLock); - - EndpointState* eps = mEndpointMap.valueFor(endpoint); - if (eps) { - eps->playerRefCount++; - } else { - eps = new EndpointState(getNextEpoch()); - mEndpointMap.add(endpoint, eps); - } - - // if this is the first registered endpoint, then send a message to start - // trimming retry buffers and a message to start sending heartbeats. - if (mEndpointMap.size() == 1) { - sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers, - handlerID()); - trimMessage->post(kRetryTrimIntervalUs); - - sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats, - handlerID()); - heartbeatMessage->post(kHeartbeatIntervalUs); - } - - eps->nextProgramID++; - return eps->nextProgramID; -} - -// Notify the sender that a player has ceased sending to this endpoint. -// An endpoint's state can not be deleted until all of the endpoint's -// registered players have called unregisterEndpoint. -void AAH_TXSender::unregisterEndpoint(const Endpoint& endpoint) { - Mutex::Autolock lock(mEndpointLock); - - EndpointState* eps = mEndpointMap.valueFor(endpoint); - if (eps) { - eps->playerRefCount--; - CHECK(eps->playerRefCount >= 0); - } -} - -void AAH_TXSender::onMessageReceived(const sp<AMessage>& msg) { - switch (msg->what()) { - case kWhatSendPacket: - onSendPacket(msg); - break; - - case kWhatTrimRetryBuffers: - trimRetryBuffers(); - break; - - case kWhatSendHeartbeats: - sendHeartbeats(); - break; - - default: - TRESPASS(); - break; - } -} - -void AAH_TXSender::onSendPacket(const sp<AMessage>& msg) { - sp<RefBase> obj; - CHECK(msg->findObject(kSendPacketTRTPPacket, &obj)); - sp<TRTPPacket> packet = static_cast<TRTPPacket*>(obj.get()); - - uint32_t ipAddr; - CHECK(msg->findInt32(kSendPacketIPAddr, - reinterpret_cast<int32_t*>(&ipAddr))); - - int32_t port32; - CHECK(msg->findInt32(kSendPacketPort, &port32)); - uint16_t port = port32; - - Mutex::Autolock lock(mEndpointLock); - doSendPacket_l(packet, Endpoint(ipAddr, port)); - mLastSentPacketTime = systemTime(); -} - -void AAH_TXSender::doSendPacket_l(const sp<TRTPPacket>& packet, - const Endpoint& endpoint) { - EndpointState* eps = mEndpointMap.valueFor(endpoint); - if (!eps) { - // the endpoint state has disappeared, so the player that sent this - // packet must be dead. - return; - } - - // assign the packet's sequence number - packet->setEpoch(eps->epoch); - packet->setSeqNumber(eps->trtpSeqNumber++); - - // add the packet to the retry buffer - RetryBuffer& retry = eps->retry; - retry.push_back(packet); - - // send the packet - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = endpoint.addr; - addr.sin_port = endpoint.port; - - ssize_t result = sendto(mSocket, - packet->getPacket(), - packet->getPacketLen(), - 0, - (const struct sockaddr *) &addr, - sizeof(addr)); - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } -} - -void AAH_TXSender::trimRetryBuffers() { - Mutex::Autolock lock(mEndpointLock); - - nsecs_t localTimeNow = systemTime(); - - Vector<Endpoint> endpointsToRemove; - - for (size_t i = 0; i < mEndpointMap.size(); i++) { - EndpointState* eps = mEndpointMap.editValueAt(i); - RetryBuffer& retry = eps->retry; - - while (!retry.isEmpty()) { - if (retry[0]->getExpireTime() < localTimeNow) { - retry.pop_front(); - } else { - break; - } - } - - if (retry.isEmpty() && eps->playerRefCount == 0) { - endpointsToRemove.add(mEndpointMap.keyAt(i)); - } - } - - // remove the state for any endpoints that are no longer in use - for (size_t i = 0; i < endpointsToRemove.size(); i++) { - Endpoint& e = endpointsToRemove.editItemAt(i); - ALOGD("*** %s removing endpoint addr=%08x", - __PRETTY_FUNCTION__, e.addr); - size_t index = mEndpointMap.indexOfKey(e); - delete mEndpointMap.valueAt(index); - mEndpointMap.removeItemsAt(index); - } - - // schedule the next trim - if (mEndpointMap.size()) { - sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers, - handlerID()); - trimMessage->post(kRetryTrimIntervalUs); - } -} - -void AAH_TXSender::sendHeartbeats() { - Mutex::Autolock lock(mEndpointLock); - - if (shouldSendHeartbeats_l()) { - for (size_t i = 0; i < mEndpointMap.size(); i++) { - EndpointState* eps = mEndpointMap.editValueAt(i); - const Endpoint& ep = mEndpointMap.keyAt(i); - - sp<TRTPControlPacket> packet = new TRTPControlPacket(); - packet->setCommandID(TRTPControlPacket::kCommandNop); - - packet->setExpireTime(systemTime() + - AAH_TXPlayer::kAAHRetryKeepAroundTimeNs); - packet->pack(); - - doSendPacket_l(packet, ep); - } - } - - // schedule the next heartbeat - if (mEndpointMap.size()) { - sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats, - handlerID()); - heartbeatMessage->post(kHeartbeatIntervalUs); - } -} - -bool AAH_TXSender::shouldSendHeartbeats_l() { - // assert(holding endpoint lock) - return (systemTime() < (mLastSentPacketTime + kHeartbeatTimeout)); -} - -// Receiver - -// initial 4-byte ID of a retry request packet -const uint32_t AAH_TXSender::RetryReceiver::kRetryRequestID = 'Treq'; - -// initial 4-byte ID of a retry NAK packet -const uint32_t AAH_TXSender::RetryReceiver::kRetryNakID = 'Tnak'; - -// initial 4-byte ID of a fast start request packet -const uint32_t AAH_TXSender::RetryReceiver::kFastStartRequestID = 'Tfst'; - -AAH_TXSender::RetryReceiver::RetryReceiver(AAH_TXSender* sender) - : Thread(false), - mSender(sender) {} - - AAH_TXSender::RetryReceiver::~RetryReceiver() { - mWakeupEvent.clearPendingEvents(); - } - -// Returns true if val is within the interval bounded inclusively by -// start and end. Also handles the case where there is a rollover of the -// range between start and end. -template <typename T> -static inline bool withinIntervalWithRollover(T val, T start, T end) { - return ((start <= end && val >= start && val <= end) || - (start > end && (val >= start || val <= end))); -} - -bool AAH_TXSender::RetryReceiver::threadLoop() { - struct pollfd pollFds[2]; - pollFds[0].fd = mSender->mSocket; - pollFds[0].events = POLLIN; - pollFds[0].revents = 0; - pollFds[1].fd = mWakeupEvent.getWakeupHandle(); - pollFds[1].events = POLLIN; - pollFds[1].revents = 0; - - int pollResult = poll(pollFds, NELEM(pollFds), -1); - if (pollResult == -1) { - ALOGE("%s poll failed", __PRETTY_FUNCTION__); - return false; - } - - if (exitPending()) { - ALOGI("*** %s exiting", __PRETTY_FUNCTION__); - return false; - } - - if (pollFds[0].revents) { - handleRetryRequest(); - } - - return true; -} - -void AAH_TXSender::RetryReceiver::handleRetryRequest() { - ALOGV("*** RX %s start", __PRETTY_FUNCTION__); - - RetryPacket request; - struct sockaddr requestSrcAddr; - socklen_t requestSrcAddrLen = sizeof(requestSrcAddr); - - ssize_t result = recvfrom(mSender->mSocket, &request, sizeof(request), 0, - &requestSrcAddr, &requestSrcAddrLen); - if (result == -1) { - ALOGE("%s recvfrom failed, errno=%d", __PRETTY_FUNCTION__, errno); - return; - } - - if (static_cast<size_t>(result) < sizeof(RetryPacket)) { - ALOGW("%s short packet received", __PRETTY_FUNCTION__); - return; - } - - uint32_t host_request_id = ntohl(request.id); - if ((host_request_id != kRetryRequestID) && - (host_request_id != kFastStartRequestID)) { - ALOGW("%s received retry request with bogus ID (%08x)", - __PRETTY_FUNCTION__, host_request_id); - return; - } - - Endpoint endpoint(request.endpointIP, request.endpointPort); - - Mutex::Autolock lock(mSender->mEndpointLock); - - EndpointState* eps = mSender->mEndpointMap.valueFor(endpoint); - - if (eps == NULL || eps->retry.isEmpty()) { - // we have no retry buffer or an empty retry buffer for this endpoint, - // so NAK the entire request - RetryPacket nak = request; - nak.id = htonl(kRetryNakID); - result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, - &requestSrcAddr, requestSrcAddrLen); - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } - return; - } - - RetryBuffer& retry = eps->retry; - - uint16_t startSeq = ntohs(request.seqStart); - uint16_t endSeq = ntohs(request.seqEnd); - - uint16_t retryFirstSeq = retry[0]->getSeqNumber(); - uint16_t retryLastSeq = retry[retry.size() - 1]->getSeqNumber(); - - // If this is a fast start, then force the start of the retry to match the - // start of the retransmit ring buffer (unless the end of the retransmit - // ring buffer is already past the point of fast start) - if ((host_request_id == kFastStartRequestID) && - !((startSeq - retryFirstSeq) & 0x8000)) { - startSeq = retryFirstSeq; - } - - int startIndex; - if (withinIntervalWithRollover(startSeq, retryFirstSeq, retryLastSeq)) { - startIndex = static_cast<uint16_t>(startSeq - retryFirstSeq); - } else { - startIndex = -1; - } - - int endIndex; - if (withinIntervalWithRollover(endSeq, retryFirstSeq, retryLastSeq)) { - endIndex = static_cast<uint16_t>(endSeq - retryFirstSeq); - } else { - endIndex = -1; - } - - if (startIndex == -1 && endIndex == -1) { - // no part of the request range is found in the retry buffer - RetryPacket nak = request; - nak.id = htonl(kRetryNakID); - result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, - &requestSrcAddr, requestSrcAddrLen); - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } - return; - } - - if (startIndex == -1) { - // NAK a subrange at the front of the request range - RetryPacket nak = request; - nak.id = htonl(kRetryNakID); - nak.seqEnd = htons(retryFirstSeq - 1); - result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, - &requestSrcAddr, requestSrcAddrLen); - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } - - startIndex = 0; - } else if (endIndex == -1) { - // NAK a subrange at the back of the request range - RetryPacket nak = request; - nak.id = htonl(kRetryNakID); - nak.seqStart = htons(retryLastSeq + 1); - result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, - &requestSrcAddr, requestSrcAddrLen); - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } - - endIndex = retry.size() - 1; - } - - // send the retry packets - for (int i = startIndex; i <= endIndex; i++) { - const sp<TRTPPacket>& replyPacket = retry[i]; - - result = sendto(mSender->mSocket, - replyPacket->getPacket(), - replyPacket->getPacketLen(), - 0, - &requestSrcAddr, - requestSrcAddrLen); - - if (result == -1) { - ALOGW("%s sendto failed", __PRETTY_FUNCTION__); - } - } -} - -// Endpoint - -AAH_TXSender::Endpoint::Endpoint() - : addr(0) - , port(0) { } - -AAH_TXSender::Endpoint::Endpoint(uint32_t a, uint16_t p) - : addr(a) - , port(p) {} - -bool AAH_TXSender::Endpoint::operator<(const Endpoint& other) const { - return ((addr < other.addr) || - (addr == other.addr && port < other.port)); -} - -// EndpointState - -AAH_TXSender::EndpointState::EndpointState(uint32_t _epoch) - : retry(kRetryBufferCapacity) - , playerRefCount(1) - , trtpSeqNumber(0) - , nextProgramID(0) - , epoch(_epoch) { } - -// CircularBuffer - -template <typename T> -CircularBuffer<T>::CircularBuffer(size_t capacity) - : mCapacity(capacity) - , mHead(0) - , mTail(0) - , mFillCount(0) { - mBuffer = new T[capacity]; -} - -template <typename T> -CircularBuffer<T>::~CircularBuffer() { - delete [] mBuffer; -} - -template <typename T> -void CircularBuffer<T>::push_back(const T& item) { - if (this->isFull()) { - this->pop_front(); - } - mBuffer[mHead] = item; - mHead = (mHead + 1) % mCapacity; - mFillCount++; -} - -template <typename T> -void CircularBuffer<T>::pop_front() { - CHECK(!isEmpty()); - mBuffer[mTail] = T(); - mTail = (mTail + 1) % mCapacity; - mFillCount--; -} - -template <typename T> -size_t CircularBuffer<T>::size() const { - return mFillCount; -} - -template <typename T> -bool CircularBuffer<T>::isFull() const { - return (mFillCount == mCapacity); -} - -template <typename T> -bool CircularBuffer<T>::isEmpty() const { - return (mFillCount == 0); -} - -template <typename T> -const T& CircularBuffer<T>::itemAt(size_t index) const { - CHECK(index < mFillCount); - return mBuffer[(mTail + index) % mCapacity]; -} - -template <typename T> -const T& CircularBuffer<T>::operator[](size_t index) const { - return itemAt(index); -} - -} // namespace android diff --git a/media/libaah_rtp/aah_tx_sender.h b/media/libaah_rtp/aah_tx_sender.h deleted file mode 100644 index 74206c49..00000000 --- a/media/libaah_rtp/aah_tx_sender.h +++ /dev/null @@ -1,162 +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. - */ - -#ifndef __AAH_TX_SENDER_H__ -#define __AAH_TX_SENDER_H__ - -#include <media/stagefright/foundation/ALooper.h> -#include <media/stagefright/foundation/AHandlerReflector.h> -#include <utils/RefBase.h> -#include <utils/threads.h> - -#include "aah_tx_packet.h" -#include "pipe_event.h" - -namespace android { - -template <typename T> class CircularBuffer { - public: - CircularBuffer(size_t capacity); - ~CircularBuffer(); - void push_back(const T& item);; - void pop_front(); - size_t size() const; - bool isFull() const; - bool isEmpty() const; - const T& itemAt(size_t index) const; - const T& operator[](size_t index) const; - - private: - T* mBuffer; - size_t mCapacity; - size_t mHead; - size_t mTail; - size_t mFillCount; - - DISALLOW_EVIL_CONSTRUCTORS(CircularBuffer); -}; - -class AAH_TXSender : public virtual RefBase { - public: - ~AAH_TXSender(); - - static sp<AAH_TXSender> GetInstance(); - - ALooper::handler_id handlerID() { return mReflector->id(); } - - // an IP address and port - struct Endpoint { - Endpoint(); - Endpoint(uint32_t a, uint16_t p); - bool operator<(const Endpoint& other) const; - - uint32_t addr; - uint16_t port; - }; - - uint16_t registerEndpoint(const Endpoint& endpoint); - void unregisterEndpoint(const Endpoint& endpoint); - - enum { - kWhatSendPacket, - kWhatTrimRetryBuffers, - kWhatSendHeartbeats, - }; - - // fields for SendPacket messages - static const char* kSendPacketIPAddr; - static const char* kSendPacketPort; - static const char* kSendPacketTRTPPacket; - - private: - AAH_TXSender(); - - static Mutex sLock; - static wp<AAH_TXSender> sInstance; - static uint32_t sNextEpoch; - static bool sNextEpochValid; - - static uint32_t getNextEpoch(); - - typedef CircularBuffer<sp<TRTPPacket> > RetryBuffer; - - // state maintained on a per-endpoint basis - struct EndpointState { - EndpointState(uint32_t epoch); - RetryBuffer retry; - int playerRefCount; - uint16_t trtpSeqNumber; - uint16_t nextProgramID; - uint32_t epoch; - }; - - friend class AHandlerReflector<AAH_TXSender>; - void onMessageReceived(const sp<AMessage>& msg); - void onSendPacket(const sp<AMessage>& msg); - void doSendPacket_l(const sp<TRTPPacket>& packet, - const Endpoint& endpoint); - void trimRetryBuffers(); - void sendHeartbeats(); - bool shouldSendHeartbeats_l(); - - sp<ALooper> mLooper; - sp<AHandlerReflector<AAH_TXSender> > mReflector; - - int mSocket; - nsecs_t mLastSentPacketTime; - - DefaultKeyedVector<Endpoint, EndpointState*> mEndpointMap; - Mutex mEndpointLock; - - static const int kRetryTrimIntervalUs; - static const int kHeartbeatIntervalUs; - static const int kRetryBufferCapacity; - static const nsecs_t kHeartbeatTimeout; - - class RetryReceiver : public Thread { - private: - friend class AAH_TXSender; - - RetryReceiver(AAH_TXSender* sender); - virtual ~RetryReceiver(); - virtual bool threadLoop(); - void handleRetryRequest(); - - static const int kMaxReceiverPacketLen; - static const uint32_t kRetryRequestID; - static const uint32_t kFastStartRequestID; - static const uint32_t kRetryNakID; - - AAH_TXSender* mSender; - PipeEvent mWakeupEvent; - }; - - sp<RetryReceiver> mRetryReceiver; - - DISALLOW_EVIL_CONSTRUCTORS(AAH_TXSender); -}; - -struct RetryPacket { - uint32_t id; - uint32_t endpointIP; - uint16_t endpointPort; - uint16_t seqStart; - uint16_t seqEnd; -} __attribute__((packed)); - -} // namespace android - -#endif // __AAH_TX_SENDER_H__ diff --git a/media/libaah_rtp/pipe_event.cpp b/media/libaah_rtp/pipe_event.cpp deleted file mode 100644 index b8e69608..00000000 --- a/media/libaah_rtp/pipe_event.cpp +++ /dev/null @@ -1,86 +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" -#include <utils/Log.h> - -#include <errno.h> -#include <fcntl.h> -#include <poll.h> -#include <unistd.h> - -#include "pipe_event.h" - -namespace android { - -PipeEvent::PipeEvent() { - pipe_[0] = -1; - pipe_[1] = -1; - - // Create the pipe. - if (pipe(pipe_) >= 0) { - // Set non-blocking mode on the read side of the pipe so we can - // easily drain it whenever we wakeup. - fcntl(pipe_[0], F_SETFL, O_NONBLOCK); - } else { - ALOGE("Failed to create pipe event %d %d %d", - pipe_[0], pipe_[1], errno); - pipe_[0] = -1; - pipe_[1] = -1; - } -} - -PipeEvent::~PipeEvent() { - if (pipe_[0] >= 0) { - close(pipe_[0]); - } - - if (pipe_[1] >= 0) { - close(pipe_[1]); - } -} - -void PipeEvent::clearPendingEvents() { - char drain_buffer[16]; - while (read(pipe_[0], drain_buffer, sizeof(drain_buffer)) > 0) { - // No body. - } -} - -bool PipeEvent::wait(int timeout) { - struct pollfd wait_fd; - - wait_fd.fd = getWakeupHandle(); - wait_fd.events = POLLIN; - wait_fd.revents = 0; - - int res = poll(&wait_fd, 1, timeout); - - if (res < 0) { - ALOGE("Wait error in PipeEvent; sleeping to prevent overload!"); - usleep(1000); - } - - return (res > 0); -} - -void PipeEvent::setEvent() { - char foo = 'q'; - write(pipe_[1], &foo, 1); -} - -} // namespace android - diff --git a/media/libaah_rtp/pipe_event.h b/media/libaah_rtp/pipe_event.h deleted file mode 100644 index e53b0fdb..00000000 --- a/media/libaah_rtp/pipe_event.h +++ /dev/null @@ -1,51 +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. - */ - -#ifndef __PIPE_EVENT_H__ -#define __PIPE_EVENT_H__ - -#include <media/stagefright/foundation/ABase.h> - -namespace android { - -class PipeEvent { - public: - PipeEvent(); - ~PipeEvent(); - - bool initCheck() const { - return ((pipe_[0] >= 0) && (pipe_[1] >= 0)); - } - - int getWakeupHandle() const { return pipe_[0]; } - - // block until the event fires; returns true if the event fired and false if - // the wait timed out. Timeout is expressed in milliseconds; negative - // values mean wait forever. - bool wait(int timeout = -1); - - void clearPendingEvents(); - void setEvent(); - - private: - int pipe_[2]; - - DISALLOW_EVIL_CONSTRUCTORS(PipeEvent); -}; - -} // namespace android - -#endif // __PIPE_EVENT_H__ diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 0bb237db..cb077661 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -55,6 +55,7 @@ enum { SET_PARAMETER, GET_PARAMETER, SET_RETRANSMIT_ENDPOINT, + GET_RETRANSMIT_ENDPOINT, SET_NEXT_PLAYER, }; @@ -292,7 +293,8 @@ public: return remote()->transact(GET_PARAMETER, data, reply); } - status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) { + status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) + { Parcel data, reply; status_t err; @@ -319,6 +321,23 @@ public: remote()->transact(SET_NEXT_PLAYER, data, &reply); return reply.readInt32(); } + + status_t getRetransmitEndpoint(struct sockaddr_in* endpoint) + { + Parcel data, reply; + status_t err; + + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + err = remote()->transact(GET_RETRANSMIT_ENDPOINT, data, &reply); + + if ((OK != err) || (OK != (err = reply.readInt32()))) { + return err; + } + + data.read(endpoint, sizeof(*endpoint)); + + return err; + } }; IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer"); @@ -498,11 +517,24 @@ status_t BnMediaPlayer::onTransact( } else { reply->writeInt32(setRetransmitEndpoint(NULL)); } + + return NO_ERROR; + } break; + case GET_RETRANSMIT_ENDPOINT: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + + struct sockaddr_in endpoint; + status_t res = getRetransmitEndpoint(&endpoint); + + reply->writeInt32(res); + reply->write(&endpoint, sizeof(endpoint)); + return NO_ERROR; } break; case SET_NEXT_PLAYER: { CHECK_INTERFACE(IMediaPlayer, data, reply); reply->writeInt32(setNextPlayer(interface_cast<IMediaPlayer>(data.readStrongBinder()))); + return NO_ERROR; } break; default: diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index f173e2e1..1373d3c2 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \ ActivityManager.cpp \ Crypto.cpp \ MediaRecorderClient.cpp \ + MediaPlayerFactory.cpp \ MediaPlayerService.cpp \ MetadataRetrieverClient.cpp \ TestPlayerStub.cpp \ @@ -31,8 +32,7 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_omx \ libstagefright_foundation \ libgui \ - libdl \ - libaah_rtp + libdl LOCAL_STATIC_LIBRARIES := \ libstagefright_nuplayer \ diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp new file mode 100644 index 00000000..f821cc3e --- /dev/null +++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp @@ -0,0 +1,339 @@ +/* +** +** Copyright 2012, 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 "MediaPlayerFactory" +#include <utils/Log.h> + +#include <cutils/properties.h> +#include <media/IMediaPlayer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <utils/Errors.h> +#include <utils/misc.h> + +#include "MediaPlayerFactory.h" + +#include "MidiFile.h" +#include "TestPlayerStub.h" +#include "StagefrightPlayer.h" +#include "nuplayer/NuPlayerDriver.h" + +namespace android { + +Mutex MediaPlayerFactory::sLock; +MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap; +bool MediaPlayerFactory::sInitComplete = false; + +status_t MediaPlayerFactory::registerFactory_l(IFactory* factory, + player_type type) { + if (NULL == factory) { + ALOGE("Failed to register MediaPlayerFactory of type %d, factory is" + " NULL.", type); + return BAD_VALUE; + } + + if (sFactoryMap.indexOfKey(type) >= 0) { + ALOGE("Failed to register MediaPlayerFactory of type %d, type is" + " already registered.", type); + return ALREADY_EXISTS; + } + + if (sFactoryMap.add(type, factory) < 0) { + ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add" + " to map.", type); + return UNKNOWN_ERROR; + } + + return OK; +} + +player_type MediaPlayerFactory::getDefaultPlayerType() { + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.stagefright.use-nuplayer", value, NULL) + && (!strcmp("1", value) || !strcasecmp("true", value))) { + return NU_PLAYER; + } + + return STAGEFRIGHT_PLAYER; +} + +status_t MediaPlayerFactory::registerFactory(IFactory* factory, + player_type type) { + Mutex::Autolock lock_(&sLock); + return registerFactory_l(factory, type); +} + +void MediaPlayerFactory::unregisterFactory(player_type type) { + Mutex::Autolock lock_(&sLock); + sFactoryMap.removeItem(type); +} + +#define GET_PLAYER_TYPE_IMPL(a...) \ + Mutex::Autolock lock_(&sLock); \ + \ + player_type ret = STAGEFRIGHT_PLAYER; \ + float bestScore = 0.0; \ + \ + for (size_t i = 0; i < sFactoryMap.size(); ++i) { \ + \ + IFactory* v = sFactoryMap.valueAt(i); \ + float thisScore; \ + CHECK(v != NULL); \ + thisScore = v->scoreFactory(a, bestScore); \ + if (thisScore > bestScore) { \ + ret = sFactoryMap.keyAt(i); \ + bestScore = thisScore; \ + } \ + } \ + \ + if (0.0 == bestScore) { \ + bestScore = getDefaultPlayerType(); \ + } \ + \ + return ret; + +player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client, + const char* url) { + GET_PLAYER_TYPE_IMPL(client, url); +} + +player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client, + int fd, + int64_t offset, + int64_t length) { + GET_PLAYER_TYPE_IMPL(client, fd, offset, length); +} + +player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client, + const sp<IStreamSource> &source) { + GET_PLAYER_TYPE_IMPL(client, source); +} + +#undef GET_PLAYER_TYPE_IMPL + +sp<MediaPlayerBase> MediaPlayerFactory::createPlayer( + player_type playerType, + void* cookie, + notify_callback_f notifyFunc) { + sp<MediaPlayerBase> p; + IFactory* factory; + status_t init_result; + Mutex::Autolock lock_(&sLock); + + if (sFactoryMap.indexOfKey(playerType) < 0) { + ALOGE("Failed to create player object of type %d, no registered" + " factory", playerType); + return p; + } + + factory = sFactoryMap.valueFor(playerType); + CHECK(NULL != factory); + p = factory->createPlayer(); + + if (p == NULL) { + ALOGE("Failed to create player object of type %d, create failed", + playerType); + return p; + } + + init_result = p->initCheck(); + if (init_result == NO_ERROR) { + p->setNotifyCallback(cookie, notifyFunc); + } else { + ALOGE("Failed to create player object of type %d, initCheck failed" + " (res = %d)", playerType, init_result); + p.clear(); + } + + return p; +} + +/***************************************************************************** + * * + * Built-In Factory Implementations * + * * + *****************************************************************************/ + +class StagefrightPlayerFactory : + public MediaPlayerFactory::IFactory { + public: + virtual float scoreFactory(const sp<IMediaPlayer>& client, + int fd, + int64_t offset, + int64_t length, + float curScore) { + char buf[20]; + lseek(fd, offset, SEEK_SET); + read(fd, buf, sizeof(buf)); + lseek(fd, offset, SEEK_SET); + + long ident = *((long*)buf); + + // Ogg vorbis? + if (ident == 0x5367674f) // 'OggS' + return 1.0; + + return 0.0; + } + + virtual sp<MediaPlayerBase> createPlayer() { + ALOGV(" create StagefrightPlayer"); + return new StagefrightPlayer(); + } +}; + +class NuPlayerFactory : public MediaPlayerFactory::IFactory { + public: + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const char* url, + float curScore) { + static const float kOurScore = 0.8; + + if (kOurScore <= curScore) + return 0.0; + + if (!strncasecmp("http://", url, 7) + || !strncasecmp("https://", url, 8)) { + size_t len = strlen(url); + if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { + return kOurScore; + } + + if (strstr(url,"m3u8")) { + return kOurScore; + } + } + + if (!strncasecmp("rtsp://", url, 7)) { + return kOurScore; + } + + return 0.0; + } + + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const sp<IStreamSource> &source, + float curScore) { + return 1.0; + } + + virtual sp<MediaPlayerBase> createPlayer() { + ALOGV(" create NuPlayer"); + return new NuPlayerDriver; + } +}; + +class SonivoxPlayerFactory : public MediaPlayerFactory::IFactory { + public: + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const char* url, + float curScore) { + static const float kOurScore = 0.4; + static const char* const FILE_EXTS[] = { ".mid", + ".midi", + ".smf", + ".xmf", + ".imy", + ".rtttl", + ".rtx", + ".ota" }; + if (kOurScore <= curScore) + return 0.0; + + // use MidiFile for MIDI extensions + int lenURL = strlen(url); + for (int i = 0; i < NELEM(FILE_EXTS); ++i) { + int len = strlen(FILE_EXTS[i]); + int start = lenURL - len; + if (start > 0) { + if (!strncasecmp(url + start, FILE_EXTS[i], len)) { + return kOurScore; + } + } + } + + return 0.0; + } + + virtual float scoreFactory(const sp<IMediaPlayer>& client, + int fd, + int64_t offset, + int64_t length, + float curScore) { + static const float kOurScore = 0.8; + + if (kOurScore <= curScore) + return 0.0; + + // Some kind of MIDI? + EAS_DATA_HANDLE easdata; + if (EAS_Init(&easdata) == EAS_SUCCESS) { + EAS_FILE locator; + locator.path = NULL; + locator.fd = fd; + locator.offset = offset; + locator.length = length; + EAS_HANDLE eashandle; + if (EAS_OpenFile(easdata, &locator, &eashandle) == EAS_SUCCESS) { + EAS_CloseFile(easdata, eashandle); + EAS_Shutdown(easdata); + return kOurScore; + } + EAS_Shutdown(easdata); + } + + return 0.0; + } + + virtual sp<MediaPlayerBase> createPlayer() { + ALOGV(" create MidiFile"); + return new MidiFile(); + } +}; + +class TestPlayerFactory : public MediaPlayerFactory::IFactory { + public: + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const char* url, + float curScore) { + if (TestPlayerStub::canBeUsed(url)) { + return 1.0; + } + + return 0.0; + } + + virtual sp<MediaPlayerBase> createPlayer() { + ALOGV("Create Test Player stub"); + return new TestPlayerStub(); + } +}; + +void MediaPlayerFactory::registerBuiltinFactories() { + Mutex::Autolock lock_(&sLock); + + if (sInitComplete) + return; + + registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER); + registerFactory_l(new NuPlayerFactory(), NU_PLAYER); + registerFactory_l(new SonivoxPlayerFactory(), SONIVOX_PLAYER); + registerFactory_l(new TestPlayerFactory(), TEST_PLAYER); + + sInitComplete = true; +} + +} // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerFactory.h b/media/libmediaplayerservice/MediaPlayerFactory.h new file mode 100644 index 00000000..fe8972b8 --- /dev/null +++ b/media/libmediaplayerservice/MediaPlayerFactory.h @@ -0,0 +1,84 @@ +/* +** +** Copyright 2012, 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. +*/ + +#ifndef ANDROID_MEDIAPLAYERFACTORY_H +#define ANDROID_MEDIAPLAYERFACTORY_H + +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/foundation/ABase.h> + +namespace android { + +class MediaPlayerFactory { + public: + class IFactory { + public: + virtual ~IFactory() { } + + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const char* url, + float curScore) { return 0.0; } + + virtual float scoreFactory(const sp<IMediaPlayer>& client, + int fd, + int64_t offset, + int64_t length, + float curScore) { return 0.0; } + + virtual float scoreFactory(const sp<IMediaPlayer>& client, + const sp<IStreamSource> &source, + float curScore) { return 0.0; } + + virtual sp<MediaPlayerBase> createPlayer() = 0; + }; + + static status_t registerFactory(IFactory* factory, + player_type type); + static void unregisterFactory(player_type type); + static player_type getPlayerType(const sp<IMediaPlayer>& client, + const char* url); + static player_type getPlayerType(const sp<IMediaPlayer>& client, + int fd, + int64_t offset, + int64_t length); + static player_type getPlayerType(const sp<IMediaPlayer>& client, + const sp<IStreamSource> &source); + + static sp<MediaPlayerBase> createPlayer(player_type playerType, + void* cookie, + notify_callback_f notifyFunc); + + static void registerBuiltinFactories(); + + private: + typedef KeyedVector<player_type, IFactory*> tFactoryMap; + + MediaPlayerFactory() { } + + static status_t registerFactory_l(IFactory* factory, + player_type type); + static player_type getDefaultPlayerType(); + + static Mutex sLock; + static tFactoryMap sFactoryMap; + static bool sInitComplete; + + DISALLOW_EVIL_CONSTRUCTORS(MediaPlayerFactory); +}; + +} // namespace android +#endif // ANDROID_MEDIAPLAYERFACTORY_H diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c47fbd68..8620856b 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -43,7 +43,6 @@ #include <utils/String8.h> #include <utils/SystemClock.h> #include <utils/Vector.h> -#include <cutils/properties.h> #include <media/MediaPlayerInterface.h> #include <media/mediarecorder.h> @@ -61,6 +60,7 @@ #include "MediaRecorderClient.h" #include "MediaPlayerService.h" #include "MetadataRetrieverClient.h" +#include "MediaPlayerFactory.h" #include "MidiFile.h" #include "TestPlayerStub.h" @@ -71,11 +71,6 @@ #include "Crypto.h" -namespace android { -sp<MediaPlayerBase> createAAH_TXPlayer(); -sp<MediaPlayerBase> createAAH_RXPlayer(); -} - namespace { using android::media::Metadata; using android::status_t; @@ -194,22 +189,6 @@ static bool checkPermission(const char* permissionString) { return ok; } -// TODO: Temp hack until we can register players -typedef struct { - const char *extension; - const player_type playertype; -} extmap; -extmap FILE_EXTS [] = { - {".mid", SONIVOX_PLAYER}, - {".midi", SONIVOX_PLAYER}, - {".smf", SONIVOX_PLAYER}, - {".xmf", SONIVOX_PLAYER}, - {".imy", SONIVOX_PLAYER}, - {".rtttl", SONIVOX_PLAYER}, - {".rtx", SONIVOX_PLAYER}, - {".ota", SONIVOX_PLAYER}, -}; - // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround /* static */ int MediaPlayerService::AudioOutput::mMinBufferCount = 4; /* static */ bool MediaPlayerService::AudioOutput::mIsOnEmulator = false; @@ -232,6 +211,8 @@ MediaPlayerService::MediaPlayerService() } // speaker is on by default mBatteryAudio.deviceOn[SPEAKER] = 1; + + MediaPlayerFactory::registerBuiltinFactories(); } MediaPlayerService::~MediaPlayerService() @@ -545,174 +526,6 @@ void MediaPlayerService::Client::disconnect() IPCThreadState::self()->flushCommands(); } -static player_type getDefaultPlayerType() { - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.use-nuplayer", value, NULL) - && (!strcmp("1", value) || !strcasecmp("true", value))) { - return NU_PLAYER; - } - - return STAGEFRIGHT_PLAYER; -} - -player_type getPlayerType(int fd, int64_t offset, int64_t length) -{ - char buf[20]; - lseek(fd, offset, SEEK_SET); - read(fd, buf, sizeof(buf)); - lseek(fd, offset, SEEK_SET); - - long ident = *((long*)buf); - - // Ogg vorbis? - if (ident == 0x5367674f) // 'OggS' - return STAGEFRIGHT_PLAYER; - - // Some kind of MIDI? - EAS_DATA_HANDLE easdata; - if (EAS_Init(&easdata) == EAS_SUCCESS) { - EAS_FILE locator; - locator.path = NULL; - locator.fd = fd; - locator.offset = offset; - locator.length = length; - EAS_HANDLE eashandle; - if (EAS_OpenFile(easdata, &locator, &eashandle) == EAS_SUCCESS) { - EAS_CloseFile(easdata, eashandle); - EAS_Shutdown(easdata); - return SONIVOX_PLAYER; - } - EAS_Shutdown(easdata); - } - - return getDefaultPlayerType(); -} - -player_type getPlayerType(const char* url) -{ - if (TestPlayerStub::canBeUsed(url)) { - return TEST_PLAYER; - } - - if (!strncasecmp("http://", url, 7) - || !strncasecmp("https://", url, 8)) { - size_t len = strlen(url); - if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) { - return NU_PLAYER; - } - - if (strstr(url,"m3u8")) { - return NU_PLAYER; - } - } - - if (!strncasecmp("rtsp://", url, 7)) { - return NU_PLAYER; - } - - if (!strncasecmp("aahRX://", url, 8)) { - return AAH_RX_PLAYER; - } - - // use MidiFile for MIDI extensions - int lenURL = strlen(url); - for (int i = 0; i < NELEM(FILE_EXTS); ++i) { - int len = strlen(FILE_EXTS[i].extension); - int start = lenURL - len; - if (start > 0) { - if (!strncasecmp(url + start, FILE_EXTS[i].extension, len)) { - return FILE_EXTS[i].playertype; - } - } - } - - return getDefaultPlayerType(); -} - -player_type MediaPlayerService::Client::getPlayerType(int fd, - int64_t offset, - int64_t length) -{ - // Until re-transmit functionality is added to the existing core android - // players, we use the special AAH TX player whenever we were configured - // for retransmission. - if (mRetransmitEndpointValid) { - return AAH_TX_PLAYER; - } - - return android::getPlayerType(fd, offset, length); -} - -player_type MediaPlayerService::Client::getPlayerType(const char* url) -{ - // Until re-transmit functionality is added to the existing core android - // players, we use the special AAH TX player whenever we were configured - // for retransmission. - if (mRetransmitEndpointValid) { - return AAH_TX_PLAYER; - } - - return android::getPlayerType(url); -} - -player_type MediaPlayerService::Client::getPlayerType( - const sp<IStreamSource> &source) { - // Until re-transmit functionality is added to the existing core android - // players, we use the special AAH TX player whenever we were configured - // for retransmission. - if (mRetransmitEndpointValid) { - return AAH_TX_PLAYER; - } - - return NU_PLAYER; -} - -static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, - notify_callback_f notifyFunc) -{ - sp<MediaPlayerBase> p; - switch (playerType) { - case SONIVOX_PLAYER: - ALOGV(" create MidiFile"); - p = new MidiFile(); - break; - case STAGEFRIGHT_PLAYER: - ALOGV(" create StagefrightPlayer"); - p = new StagefrightPlayer; - break; - case NU_PLAYER: - ALOGV(" create NuPlayer"); - p = new NuPlayerDriver; - break; - case TEST_PLAYER: - ALOGV("Create Test Player stub"); - p = new TestPlayerStub(); - break; - case AAH_RX_PLAYER: - ALOGV(" create A@H RX Player"); - p = createAAH_RXPlayer(); - break; - case AAH_TX_PLAYER: - ALOGV(" create A@H TX Player"); - p = createAAH_TXPlayer(); - break; - default: - ALOGE("Unknown player type: %d", playerType); - return NULL; - } - if (p != NULL) { - if (p->initCheck() == NO_ERROR) { - p->setNotifyCallback(cookie, notifyFunc); - } else { - p.clear(); - } - } - if (p == NULL) { - ALOGE("Failed to create player object"); - } - return p; -} - sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType) { // determine if we have the right player type @@ -722,7 +535,7 @@ sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerT p.clear(); } if (p == NULL) { - p = android::createPlayer(playerType, this, notify); + p = MediaPlayerFactory::createPlayer(playerType, this, notify); } if (p != NULL) { @@ -805,7 +618,7 @@ status_t MediaPlayerService::Client::setDataSource( close(fd); return mStatus; } else { - player_type playerType = getPlayerType(url); + player_type playerType = MediaPlayerFactory::getPlayerType(this, url); sp<MediaPlayerBase> p = setDataSource_pre(playerType); if (p == NULL) { return NO_INIT; @@ -842,10 +655,10 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 ALOGV("calculated length = %lld", length); } - // Until re-transmit functionality is added to the existing core android - // players, we use the special AAH TX player whenever we were configured for - // retransmission. - player_type playerType = getPlayerType(fd, offset, length); + player_type playerType = MediaPlayerFactory::getPlayerType(this, + fd, + offset, + length); sp<MediaPlayerBase> p = setDataSource_pre(playerType); if (p == NULL) { return NO_INIT; @@ -859,10 +672,7 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 status_t MediaPlayerService::Client::setDataSource( const sp<IStreamSource> &source) { // create the right type of player - // Until re-transmit functionality is added to the existing core android - // players, we use the special AAH TX player whenever we were configured for - // retransmission. - player_type playerType = getPlayerType(source); + player_type playerType = MediaPlayerFactory::getPlayerType(this, source); sp<MediaPlayerBase> p = setDataSource_pre(playerType); if (p == NULL) { return NO_INIT; @@ -1209,6 +1019,25 @@ status_t MediaPlayerService::Client::setRetransmitEndpoint( return NO_ERROR; } +status_t MediaPlayerService::Client::getRetransmitEndpoint( + struct sockaddr_in* endpoint) +{ + if (NULL == endpoint) + return BAD_VALUE; + + sp<MediaPlayerBase> p = getPlayer(); + + if (p != NULL) + return p->getRetransmitEndpoint(endpoint); + + if (!mRetransmitEndpointValid) + return NO_INIT; + + *endpoint = mRetransmitEndpoint; + + return NO_ERROR; +} + void MediaPlayerService::Client::notify( void* cookie, int msg, int ext1, int ext2, const Parcel *obj) { @@ -1315,12 +1144,13 @@ sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, i return mem; } - player_type playerType = getPlayerType(url); + player_type playerType = + MediaPlayerFactory::getPlayerType(NULL /* client */, url); ALOGV("player type = %d", playerType); // create the right type of player sp<AudioCache> cache = new AudioCache(url); - player = android::createPlayer(playerType, cache.get(), cache->notify); + player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); if (player == NULL) goto Exit; if (player->hardwareOutput()) goto Exit; @@ -1362,12 +1192,15 @@ sp<IMemory> MediaPlayerService::decode(int fd, int64_t offset, int64_t length, u sp<MemoryBase> mem; sp<MediaPlayerBase> player; - player_type playerType = getPlayerType(fd, offset, length); + player_type playerType = MediaPlayerFactory::getPlayerType(NULL /* client */, + fd, + offset, + length); ALOGV("player type = %d", playerType); // create the right type of player sp<AudioCache> cache = new AudioCache("decode_fd"); - player = android::createPlayer(playerType, cache.get(), cache->notify); + player = MediaPlayerFactory::createPlayer(playerType, cache.get(), cache->notify); if (player == NULL) goto Exit; if (player->hardwareOutput()) goto Exit; diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 95b1b052..6ede9a4e 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -299,7 +299,6 @@ public: private: class Client : public BnMediaPlayer { - // IMediaPlayer interface virtual void disconnect(); virtual status_t setVideoSurfaceTexture( @@ -326,6 +325,7 @@ private: virtual status_t setParameter(int key, const Parcel &request); virtual status_t getParameter(int key, Parcel *reply); virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint); + virtual status_t getRetransmitEndpoint(struct sockaddr_in* endpoint); virtual status_t setNextPlayer(const sp<IMediaPlayer>& player); sp<MediaPlayerBase> createPlayer(player_type playerType); @@ -342,10 +342,6 @@ private: void setDataSource_post(const sp<MediaPlayerBase>& p, status_t status); - player_type getPlayerType(int fd, int64_t offset, int64_t length); - player_type getPlayerType(const char* url); - player_type getPlayerType(const sp<IStreamSource> &source); - static void notify(void* cookie, int msg, int ext1, int ext2, const Parcel *obj); diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index e44031e6..348957f7 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -37,12 +37,10 @@ #include "MidiMetadataRetriever.h" #include "MetadataRetrieverClient.h" #include "StagefrightMetadataRetriever.h" +#include "MediaPlayerFactory.h" namespace android { -extern player_type getPlayerType(const char* url); -extern player_type getPlayerType(int fd, int64_t offset, int64_t length); - MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) { ALOGV("MetadataRetrieverClient constructor pid(%d)", pid); @@ -115,7 +113,17 @@ status_t MetadataRetrieverClient::setDataSource( if (url == NULL) { return UNKNOWN_ERROR; } - player_type playerType = getPlayerType(url); + + // When asking the MediaPlayerFactory subsystem to choose a media player for + // a given URL, a pointer to an outer IMediaPlayer can be passed to the + // factory system to be taken into consideration along with the URL. In the + // case of choosing an instance of a MediaPlayerBase for a + // MetadataRetrieverClient, there is no outer IMediaPlayer which will + // eventually encapsulate the result of this selection. In this case, just + // pass NULL to getPlayerType to indicate that there is no outer + // IMediaPlayer to consider during selection. + player_type playerType = + MediaPlayerFactory::getPlayerType(NULL /* client */, url); ALOGV("player type = %d", playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL) return NO_INIT; @@ -150,7 +158,11 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t ALOGV("calculated length = %lld", length); } - player_type playerType = getPlayerType(fd, offset, length); + player_type playerType = + MediaPlayerFactory::getPlayerType(NULL /* client */, + fd, + offset, + length); ALOGV("player type = %d", playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL) { |