RTSPSource.cpp revision 5834181d3f168acb8ff4bf3eff1fd1186afb0bd4
127c174483a8ae9688d5d4897c19074f62c7f1701James Dong/* 227c174483a8ae9688d5d4897c19074f62c7f1701James Dong * Copyright (C) 2010 The Android Open Source Project 327c174483a8ae9688d5d4897c19074f62c7f1701James Dong * 427c174483a8ae9688d5d4897c19074f62c7f1701James Dong * Licensed under the Apache License, Version 2.0 (the "License"); 527c174483a8ae9688d5d4897c19074f62c7f1701James Dong * you may not use this file except in compliance with the License. 627c174483a8ae9688d5d4897c19074f62c7f1701James Dong * You may obtain a copy of the License at 727c174483a8ae9688d5d4897c19074f62c7f1701James Dong * 827c174483a8ae9688d5d4897c19074f62c7f1701James Dong * http://www.apache.org/licenses/LICENSE-2.0 927c174483a8ae9688d5d4897c19074f62c7f1701James Dong * 1027c174483a8ae9688d5d4897c19074f62c7f1701James Dong * Unless required by applicable law or agreed to in writing, software 1127c174483a8ae9688d5d4897c19074f62c7f1701James Dong * distributed under the License is distributed on an "AS IS" BASIS, 1227c174483a8ae9688d5d4897c19074f62c7f1701James Dong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1327c174483a8ae9688d5d4897c19074f62c7f1701James Dong * See the License for the specific language governing permissions and 1427c174483a8ae9688d5d4897c19074f62c7f1701James Dong * limitations under the License. 1527c174483a8ae9688d5d4897c19074f62c7f1701James Dong */ 1627c174483a8ae9688d5d4897c19074f62c7f1701James Dong 17f933441648ef6a71dee783d733aac17b9508b452Andreas Huber//#define LOG_NDEBUG 0 18f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#define LOG_TAG "RTSPSource" 19f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include <utils/Log.h> 20f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 21f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include "RTSPSource.h" 22f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 23f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include "AnotherPacketSource.h" 24f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include "MyHandler.h" 25f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 26f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include <media/stagefright/MediaDefs.h> 27f933441648ef6a71dee783d733aac17b9508b452Andreas Huber#include <media/stagefright/MetaData.h> 28f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 29afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Hubernamespace android { 30f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 311173118eace0e9e347cb007f0da817cee87579edGlenn KastenNuPlayer::RTSPSource::RTSPSource( 32f933441648ef6a71dee783d733aac17b9508b452Andreas Huber const char *url, 331065b3f17d3048948e7d522049d1980b90df3dc1Andreas Huber const KeyedVector<String8, String8> *headers, 34f933441648ef6a71dee783d733aac17b9508b452Andreas Huber bool uidValid, 35f933441648ef6a71dee783d733aac17b9508b452Andreas Huber uid_t uid) 36f933441648ef6a71dee783d733aac17b9508b452Andreas Huber : mURL(url), 37f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mUIDValid(uidValid), 38f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mUID(uid), 39f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mFlags(0), 40f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mState(DISCONNECTED), 41f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mFinalResult(OK), 42f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mDisconnectReplyID(0), 43f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mStartingUp(true), 44f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mSeekGeneration(0) { 45f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (headers) { 46f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mExtraHeaders = *headers; 47f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 48f933441648ef6a71dee783d733aac17b9508b452Andreas Huber ssize_t index = 49f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); 50f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 51f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (index >= 0) { 52f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mFlags |= kFlagIncognito; 53f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 54f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mExtraHeaders.removeItemsAt(index); 55f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 56f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 57f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 58f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 59f933441648ef6a71dee783d733aac17b9508b452Andreas HuberNuPlayer::RTSPSource::~RTSPSource() { 60f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper->stop(); 61f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 62f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 63f933441648ef6a71dee783d733aac17b9508b452Andreas Hubervoid NuPlayer::RTSPSource::start() { 64f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mLooper == NULL) { 65f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper = new ALooper; 66f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper->setName("rtsp"); 67f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper->start(); 68f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 69f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mReflector = new AHandlerReflector<RTSPSource>(this); 70f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper->registerHandler(mReflector); 71f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 72f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 73f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(mHandler == NULL); 74f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 75f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id()); 76f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 77f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID); 78f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mLooper->registerHandler(mHandler); 79f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 80f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_EQ(mState, (int)DISCONNECTED); 81f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mState = CONNECTING; 82f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 83f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mHandler->connect(); 84f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 85f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 86f933441648ef6a71dee783d733aac17b9508b452Andreas Hubervoid NuPlayer::RTSPSource::stop() { 87f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mLooper == NULL) { 88f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return; 89f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 90f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AMessage> msg = new AMessage(kWhatDisconnect, mReflector->id()); 91f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 92f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AMessage> dummy; 93f933441648ef6a71dee783d733aac17b9508b452Andreas Huber msg->postAndAwaitResponse(&dummy); 94f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 95f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 96f933441648ef6a71dee783d733aac17b9508b452Andreas Huberstatus_t NuPlayer::RTSPSource::feedMoreTSData() { 97f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return mFinalResult; 98f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 99f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 100f933441648ef6a71dee783d733aac17b9508b452Andreas Hubersp<MetaData> NuPlayer::RTSPSource::getFormatMeta(bool audio) { 101f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = getSource(audio); 102f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 103f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source == NULL) { 104f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return NULL; 105f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 106f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 107f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return source->getFormat(); 108f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 109f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 110f933441648ef6a71dee783d733aac17b9508b452Andreas Huberbool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() { 111f933441648ef6a71dee783d733aac17b9508b452Andreas Huber // We're going to buffer at least 2 secs worth data on all tracks before 112f933441648ef6a71dee783d733aac17b9508b452Andreas Huber // starting playback (both at startup and after a seek). 113f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 114f933441648ef6a71dee783d733aac17b9508b452Andreas Huber static const int64_t kMinDurationUs = 2000000ll; 115f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 116f933441648ef6a71dee783d733aac17b9508b452Andreas Huber status_t err; 117f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int64_t durationUs; 118f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mAudioTrack != NULL 119f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && (durationUs = mAudioTrack->getBufferedDurationUs(&err)) 120f933441648ef6a71dee783d733aac17b9508b452Andreas Huber < kMinDurationUs 121f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && err == OK) { 122f933441648ef6a71dee783d733aac17b9508b452Andreas Huber ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)", 123f933441648ef6a71dee783d733aac17b9508b452Andreas Huber durationUs / 1E6); 124f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return false; 125f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 126f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 127f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mVideoTrack != NULL 128f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && (durationUs = mVideoTrack->getBufferedDurationUs(&err)) 129f933441648ef6a71dee783d733aac17b9508b452Andreas Huber < kMinDurationUs 130f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && err == OK) { 131f933441648ef6a71dee783d733aac17b9508b452Andreas Huber ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)", 132f933441648ef6a71dee783d733aac17b9508b452Andreas Huber durationUs / 1E6); 133f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return false; 134f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 135f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 136f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return true; 137f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 138f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 139f933441648ef6a71dee783d733aac17b9508b452Andreas Huberstatus_t NuPlayer::RTSPSource::dequeueAccessUnit( 140f933441648ef6a71dee783d733aac17b9508b452Andreas Huber bool audio, sp<ABuffer> *accessUnit) { 141f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mStartingUp) { 142f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (!haveSufficientDataOnAllTracks()) { 143f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return -EWOULDBLOCK; 144f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 145f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 146f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mStartingUp = false; 147f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 148f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 149f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = getSource(audio); 150f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 151f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source == NULL) { 152f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return -EWOULDBLOCK; 153f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 154f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 155f933441648ef6a71dee783d733aac17b9508b452Andreas Huber status_t finalResult; 156f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (!source->hasBufferAvailable(&finalResult)) { 157f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return finalResult == OK ? -EWOULDBLOCK : finalResult; 158f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 159f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 160f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return source->dequeueAccessUnit(accessUnit); 161f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 162f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 163f933441648ef6a71dee783d733aac17b9508b452Andreas Hubersp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) { 164f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mTSParser != NULL) { 165f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<MediaSource> source = mTSParser->getSource( 166f933441648ef6a71dee783d733aac17b9508b452Andreas Huber audio ? ATSParser::AUDIO : ATSParser::VIDEO); 167f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 168f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return static_cast<AnotherPacketSource *>(source.get()); 169c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber } 170f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 171f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return audio ? mAudioTrack : mVideoTrack; 172f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 173c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 174f933441648ef6a71dee783d733aac17b9508b452Andreas Huberstatus_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { 175f933441648ef6a71dee783d733aac17b9508b452Andreas Huber *durationUs = 0ll; 176f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 177f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int64_t audioDurationUs; 178f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mAudioTrack != NULL 179f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && mAudioTrack->getFormat()->findInt64( 180c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber kKeyDuration, &audioDurationUs) 181c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber && audioDurationUs > *durationUs) { 182c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber *durationUs = audioDurationUs; 183c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber } 184c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 185c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber int64_t videoDurationUs; 186c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber if (mVideoTrack != NULL 187c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber && mVideoTrack->getFormat()->findInt64( 188c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber kKeyDuration, &videoDurationUs) 189c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber && videoDurationUs > *durationUs) { 190c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber *durationUs = videoDurationUs; 191c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber } 192c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 193c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber return OK; 194c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber} 195c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 196c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huberstatus_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { 197c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber sp<AMessage> msg = new AMessage(kWhatPerformSeek, mReflector->id()); 198c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber msg->setInt32("generation", ++mSeekGeneration); 199f933441648ef6a71dee783d733aac17b9508b452Andreas Huber msg->setInt64("timeUs", seekTimeUs); 200f933441648ef6a71dee783d733aac17b9508b452Andreas Huber msg->post(200000ll); 201f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 202f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return OK; 203f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 204f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 205f933441648ef6a71dee783d733aac17b9508b452Andreas Hubervoid NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { 206f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mState != CONNECTED) { 207f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return; 208f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 209f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 210f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mState = SEEKING; 211f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mHandler->seek(seekTimeUs); 212f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 213f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 214f933441648ef6a71dee783d733aac17b9508b452Andreas Huberbool NuPlayer::RTSPSource::isSeekable() { 215f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return true; 216f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} 217f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 218f933441648ef6a71dee783d733aac17b9508b452Andreas Hubervoid NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { 219f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (msg->what() == kWhatDisconnect) { 220f933441648ef6a71dee783d733aac17b9508b452Andreas Huber uint32_t replyID; 221f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->senderAwaitsResponse(&replyID)); 222f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 223f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mDisconnectReplyID = replyID; 224f933441648ef6a71dee783d733aac17b9508b452Andreas Huber finishDisconnectIfPossible(); 225f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return; 226f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } else if (msg->what() == kWhatPerformSeek) { 227f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int32_t generation; 228f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findInt32("generation", &generation)); 229f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 230f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (generation != mSeekGeneration) { 231f933441648ef6a71dee783d733aac17b9508b452Andreas Huber // obsolete. 232f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return; 233f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 234f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 235f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int64_t seekTimeUs; 236f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findInt64("timeUs", &seekTimeUs)); 237f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 238349d3fcb4afacf754f7b5b5186d2f258f5bf35e7Andreas Huber performSeek(seekTimeUs); 239349d3fcb4afacf754f7b5b5186d2f258f5bf35e7Andreas Huber return; 240349d3fcb4afacf754f7b5b5186d2f258f5bf35e7Andreas Huber } 241f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 242f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_EQ(msg->what(), (int)kWhatNotify); 243f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 244f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int32_t what; 245f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findInt32("what", &what)); 246f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 247f933441648ef6a71dee783d733aac17b9508b452Andreas Huber switch (what) { 248f933441648ef6a71dee783d733aac17b9508b452Andreas Huber case MyHandler::kWhatConnected: 249349d3fcb4afacf754f7b5b5186d2f258f5bf35e7Andreas Huber onConnected(); 250349d3fcb4afacf754f7b5b5186d2f258f5bf35e7Andreas Huber break; 251f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 252f933441648ef6a71dee783d733aac17b9508b452Andreas Huber case MyHandler::kWhatDisconnected: 253f933441648ef6a71dee783d733aac17b9508b452Andreas Huber onDisconnected(msg); 254f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 255f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 256f933441648ef6a71dee783d733aac17b9508b452Andreas Huber case MyHandler::kWhatSeekDone: 257f933441648ef6a71dee783d733aac17b9508b452Andreas Huber { 258f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mState = CONNECTED; 259f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mStartingUp = true; 260f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 261f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 262f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 263f933441648ef6a71dee783d733aac17b9508b452Andreas Huber case MyHandler::kWhatAccessUnit: 264f933441648ef6a71dee783d733aac17b9508b452Andreas Huber { 265f933441648ef6a71dee783d733aac17b9508b452Andreas Huber size_t trackIndex; 266f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findSize("trackIndex", &trackIndex)); 267f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 268f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mTSParser == NULL) { 269f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_LT(trackIndex, mTracks.size()); 270f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } else { 271f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_EQ(trackIndex, 0u); 272f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 273f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 274f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<ABuffer> accessUnit; 275f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findBuffer("accessUnit", &accessUnit)); 276f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 277f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int32_t damaged; 278f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (accessUnit->meta()->findInt32("damaged", &damaged) 279f933441648ef6a71dee783d733aac17b9508b452Andreas Huber && damaged) { 280f933441648ef6a71dee783d733aac17b9508b452Andreas Huber ALOGI("dropping damaged access unit."); 281f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 282f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 283f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 284f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mTSParser != NULL) { 285f933441648ef6a71dee783d733aac17b9508b452Andreas Huber size_t offset = 0; 286f933441648ef6a71dee783d733aac17b9508b452Andreas Huber status_t err = OK; 2875778822d86b0337407514b9372562b86edfa91cdAndreas Huber while (offset + 188 <= accessUnit->size()) { 2885778822d86b0337407514b9372562b86edfa91cdAndreas Huber err = mTSParser->feedTSPacket( 289f933441648ef6a71dee783d733aac17b9508b452Andreas Huber accessUnit->data() + offset, 188); 290f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (err != OK) { 291f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 292f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 293f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 294f933441648ef6a71dee783d733aac17b9508b452Andreas Huber offset += 188; 295f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 296f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 297f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (offset < accessUnit->size()) { 298f933441648ef6a71dee783d733aac17b9508b452Andreas Huber err = ERROR_MALFORMED; 299f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 300f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 301f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (err != OK) { 302f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = getSource(false /* audio */); 303f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source != NULL) { 304f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->signalEOS(err); 305f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 306f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 307f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source = getSource(true /* audio */); 308f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source != NULL) { 309f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->signalEOS(err); 310f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 311f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 312f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 313f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 314f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 315f933441648ef6a71dee783d733aac17b9508b452Andreas Huber TrackInfo *info = &mTracks.editItemAt(trackIndex); 316f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 317f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = info->mSource; 318f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source != NULL) { 319f933441648ef6a71dee783d733aac17b9508b452Andreas Huber uint32_t rtpTime; 320f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); 321f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 322f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (!info->mNPTMappingValid) { 323f933441648ef6a71dee783d733aac17b9508b452Andreas Huber // This is a live stream, we didn't receive any normal 324f933441648ef6a71dee783d733aac17b9508b452Andreas Huber // playtime mapping. We won't map to npt time. 325f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->queueAccessUnit(accessUnit); 326f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 327f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 328f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 329f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int64_t nptUs = 330f933441648ef6a71dee783d733aac17b9508b452Andreas Huber ((double)rtpTime - (double)info->mRTPTime) 331f933441648ef6a71dee783d733aac17b9508b452Andreas Huber / info->mTimeScale 332afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber * 1000000ll 333afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber + info->mNormalPlaytimeUs; 3345778822d86b0337407514b9372562b86edfa91cdAndreas Huber 335c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber accessUnit->meta()->setInt64("timeUs", nptUs); 3369806555d3930be43e11106281dee354820ac1c88Andreas Huber 3379806555d3930be43e11106281dee354820ac1c88Andreas Huber source->queueAccessUnit(accessUnit); 3389806555d3930be43e11106281dee354820ac1c88Andreas Huber } 3399806555d3930be43e11106281dee354820ac1c88Andreas Huber break; 3409806555d3930be43e11106281dee354820ac1c88Andreas Huber } 341f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 342c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber case MyHandler::kWhatEOS: 343f933441648ef6a71dee783d733aac17b9508b452Andreas Huber { 344f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int32_t finalResult; 345f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findInt32("finalResult", &finalResult)); 346f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_NE(finalResult, (status_t)OK); 347f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 348f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (mTSParser != NULL) { 349f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = getSource(false /* audio */); 350f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source != NULL) { 351f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->signalEOS(finalResult); 352f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 353f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 354f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source = getSource(true /* audio */); 355dc9bacd838442a524585887e6ea6696836be2edaAndreas Huber if (source != NULL) { 356f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->signalEOS(finalResult); 357f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 358f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 359f933441648ef6a71dee783d733aac17b9508b452Andreas Huber return; 360f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 361f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 362f933441648ef6a71dee783d733aac17b9508b452Andreas Huber size_t trackIndex; 363f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findSize("trackIndex", &trackIndex)); 364f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_LT(trackIndex, mTracks.size()); 365f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 366f933441648ef6a71dee783d733aac17b9508b452Andreas Huber TrackInfo *info = &mTracks.editItemAt(trackIndex); 367f933441648ef6a71dee783d733aac17b9508b452Andreas Huber sp<AnotherPacketSource> source = info->mSource; 368f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (source != NULL) { 369f933441648ef6a71dee783d733aac17b9508b452Andreas Huber source->signalEOS(finalResult); 370f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 371f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 372f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 3735778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 3745778822d86b0337407514b9372562b86edfa91cdAndreas Huber 3755778822d86b0337407514b9372562b86edfa91cdAndreas Huber case MyHandler::kWhatSeekDiscontinuity: 3765778822d86b0337407514b9372562b86edfa91cdAndreas Huber { 3775778822d86b0337407514b9372562b86edfa91cdAndreas Huber size_t trackIndex; 3785778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK(msg->findSize("trackIndex", &trackIndex)); 3795778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK_LT(trackIndex, mTracks.size()); 3805778822d86b0337407514b9372562b86edfa91cdAndreas Huber 3815778822d86b0337407514b9372562b86edfa91cdAndreas Huber TrackInfo *info = &mTracks.editItemAt(trackIndex); 3825778822d86b0337407514b9372562b86edfa91cdAndreas Huber sp<AnotherPacketSource> source = info->mSource; 3835778822d86b0337407514b9372562b86edfa91cdAndreas Huber if (source != NULL) { 3845778822d86b0337407514b9372562b86edfa91cdAndreas Huber source->queueDiscontinuity(ATSParser::DISCONTINUITY_SEEK, NULL); 3855778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 3865778822d86b0337407514b9372562b86edfa91cdAndreas Huber 3875778822d86b0337407514b9372562b86edfa91cdAndreas Huber break; 3885778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 389f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 3907a3a2b2f9bb9421dcf83fbd47276e57917078aefJames Dong case MyHandler::kWhatNormalPlayTimeMapping: 391f933441648ef6a71dee783d733aac17b9508b452Andreas Huber { 392f933441648ef6a71dee783d733aac17b9508b452Andreas Huber size_t trackIndex; 393f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findSize("trackIndex", &trackIndex)); 394f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK_LT(trackIndex, mTracks.size()); 395f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 396f933441648ef6a71dee783d733aac17b9508b452Andreas Huber uint32_t rtpTime; 397f933441648ef6a71dee783d733aac17b9508b452Andreas Huber CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime)); 398c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 399c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber int64_t nptUs; 400c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber CHECK(msg->findInt64("nptUs", &nptUs)); 401c95c2ddcdfc974f42408a377fbe2de51b94a8c94Andreas Huber 402f933441648ef6a71dee783d733aac17b9508b452Andreas Huber TrackInfo *info = &mTracks.editItemAt(trackIndex); 403f933441648ef6a71dee783d733aac17b9508b452Andreas Huber info->mRTPTime = rtpTime; 404f933441648ef6a71dee783d733aac17b9508b452Andreas Huber info->mNormalPlaytimeUs = nptUs; 405f933441648ef6a71dee783d733aac17b9508b452Andreas Huber info->mNPTMappingValid = true; 406f933441648ef6a71dee783d733aac17b9508b452Andreas Huber break; 407f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 408f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 409f933441648ef6a71dee783d733aac17b9508b452Andreas Huber default: 4105778822d86b0337407514b9372562b86edfa91cdAndreas Huber TRESPASS(); 411f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 4125778822d86b0337407514b9372562b86edfa91cdAndreas Huber} 4135778822d86b0337407514b9372562b86edfa91cdAndreas Huber 4145778822d86b0337407514b9372562b86edfa91cdAndreas Hubervoid NuPlayer::RTSPSource::onConnected() { 4155778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK(mAudioTrack == NULL); 4165778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK(mVideoTrack == NULL); 417f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 4185778822d86b0337407514b9372562b86edfa91cdAndreas Huber size_t numTracks = mHandler->countTracks(); 4195778822d86b0337407514b9372562b86edfa91cdAndreas Huber for (size_t i = 0; i < numTracks; ++i) { 420f933441648ef6a71dee783d733aac17b9508b452Andreas Huber int32_t timeScale; 4215778822d86b0337407514b9372562b86edfa91cdAndreas Huber sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale); 4225778822d86b0337407514b9372562b86edfa91cdAndreas Huber 4235778822d86b0337407514b9372562b86edfa91cdAndreas Huber const char *mime; 4245778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK(format->findCString(kKeyMIMEType, &mime)); 4255778822d86b0337407514b9372562b86edfa91cdAndreas Huber 426f933441648ef6a71dee783d733aac17b9508b452Andreas Huber if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { 4275778822d86b0337407514b9372562b86edfa91cdAndreas Huber // Very special case for MPEG2 Transport Streams. 4285778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK_EQ(numTracks, 1u); 429f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 4305778822d86b0337407514b9372562b86edfa91cdAndreas Huber mTSParser = new ATSParser; 4315778822d86b0337407514b9372562b86edfa91cdAndreas Huber return; 4325778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 433f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 434ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber bool isAudio = !strncasecmp(mime, "audio/", 6); 435ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber bool isVideo = !strncasecmp(mime, "video/", 6); 436f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 437afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber TrackInfo info; 438afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber info.mTimeScale = timeScale; 439afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber info.mRTPTime = 0; 440afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber info.mNormalPlaytimeUs = 0ll; 4411065b3f17d3048948e7d522049d1980b90df3dc1Andreas Huber info.mNPTMappingValid = false; 442ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber 443ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber if ((isAudio && mAudioTrack == NULL) 444ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber || (isVideo && mVideoTrack == NULL)) { 445ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber sp<AnotherPacketSource> source = new AnotherPacketSource(format); 446ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber 447ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber if (isAudio) { 448ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber mAudioTrack = source; 449ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber } else { 450ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber mVideoTrack = source; 451ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber } 4525778822d86b0337407514b9372562b86edfa91cdAndreas Huber 453ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber info.mSource = source; 4545778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 455ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber 456ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber mTracks.push(info); 457ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber } 458ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber 459ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber mState = CONNECTED; 4605778822d86b0337407514b9372562b86edfa91cdAndreas Huber} 4611065b3f17d3048948e7d522049d1980b90df3dc1Andreas Huber 4625778822d86b0337407514b9372562b86edfa91cdAndreas Hubervoid NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) { 4631065b3f17d3048948e7d522049d1980b90df3dc1Andreas Huber status_t err; 4641065b3f17d3048948e7d522049d1980b90df3dc1Andreas Huber CHECK(msg->findInt32("result", &err)); 4655778822d86b0337407514b9372562b86edfa91cdAndreas Huber CHECK_NE(err, (status_t)OK); 466f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 4675778822d86b0337407514b9372562b86edfa91cdAndreas Huber mLooper->unregisterHandler(mHandler->id()); 4685778822d86b0337407514b9372562b86edfa91cdAndreas Huber mHandler.clear(); 4695778822d86b0337407514b9372562b86edfa91cdAndreas Huber 470f933441648ef6a71dee783d733aac17b9508b452Andreas Huber mState = DISCONNECTED; 4715778822d86b0337407514b9372562b86edfa91cdAndreas Huber mFinalResult = err; 4725778822d86b0337407514b9372562b86edfa91cdAndreas Huber 4735778822d86b0337407514b9372562b86edfa91cdAndreas Huber if (mDisconnectReplyID != 0) { 4745778822d86b0337407514b9372562b86edfa91cdAndreas Huber finishDisconnectIfPossible(); 475eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber } 476eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber} 477eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber 4785778822d86b0337407514b9372562b86edfa91cdAndreas Hubervoid NuPlayer::RTSPSource::finishDisconnectIfPossible() { 479eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber if (mState != DISCONNECTED) { 4805778822d86b0337407514b9372562b86edfa91cdAndreas Huber mHandler->disconnect(); 481eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber return; 482f933441648ef6a71dee783d733aac17b9508b452Andreas Huber } 483f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 484eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber (new AMessage)->postReply(mDisconnectReplyID); 4855778822d86b0337407514b9372562b86edfa91cdAndreas Huber mDisconnectReplyID = 0; 4865778822d86b0337407514b9372562b86edfa91cdAndreas Huber} 487f933441648ef6a71dee783d733aac17b9508b452Andreas Huber 488f933441648ef6a71dee783d733aac17b9508b452Andreas Huber} // namespace android 489f933441648ef6a71dee783d733aac17b9508b452Andreas Huber