diff options
author | Andreas Huber <andih@google.com> | 2012-09-20 16:00:17 -0700 |
---|---|---|
committer | Andreas Huber <andih@google.com> | 2012-09-21 09:24:08 -0700 |
commit | 207e18540fefbaf530a4fdf506d266f34ddec84f (patch) | |
tree | d8e7d7db4c874aabfe35c5fdd4a5dcf7bf72047d /media/libstagefright/wifi-display/source | |
parent | 086f958a7e539acffb637b42f5a6998184544179 (diff) | |
download | frameworks_av-207e18540fefbaf530a4fdf506d266f34ddec84f.zip frameworks_av-207e18540fefbaf530a4fdf506d266f34ddec84f.tar.gz frameworks_av-207e18540fefbaf530a4fdf506d266f34ddec84f.tar.bz2 |
Remove legacy code and APIs in wifi display related code.
Change-Id: Ia010e7a00534f9356b3247369d0ffd65591d91aa
Diffstat (limited to 'media/libstagefright/wifi-display/source')
5 files changed, 7 insertions, 625 deletions
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 5f40406..24f33df 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -23,7 +23,6 @@ #include "Converter.h" #include "MediaPuller.h" #include "RepeaterSource.h" -#include "Serializer.h" #include "TSPacketizer.h" #include <binder/IServiceManager.h> @@ -47,9 +46,6 @@ #include <OMX_IVCommon.h> -//#define FAKE_VIDEO 1 -#define USE_SERIALIZER 0 - namespace android { static size_t kMaxRTPPacketSize = 1500; @@ -192,12 +188,10 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify, const in_addr &interfaceAddr, - bool legacyMode, const sp<IHDCP> &hdcp) : mNetSession(netSession), mNotify(notify), mInterfaceAddr(interfaceAddr), - mLegacyMode(legacyMode), mHDCP(hdcp), mLastLifesignUs(), mVideoTrackIndex(-1), @@ -457,10 +451,6 @@ status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() { scheduleSendSR(); } - if (mSerializer != NULL) { - return mSerializer->start(); - } - for (size_t i = 0; i < mTracks.size(); ++i) { status_t err = mTracks.editValueAt(i)->start(); @@ -491,29 +481,8 @@ status_t WifiDisplaySource::PlaybackSession::destroy() { mPacketizer.clear(); - if (mSerializer != NULL) { - mSerializer->stop(); - - looper()->unregisterHandler(mSerializer->id()); - mSerializer.clear(); - } - mTracks.clear(); - if (mSerializerLooper != NULL) { - mSerializerLooper->stop(); - mSerializerLooper.clear(); - } - - if (mLegacyMode) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder = sm->getService(String16("SurfaceFlinger")); - sp<ISurfaceComposer> service = interface_cast<ISurfaceComposer>(binder); - CHECK(service != NULL); - - service->connectDisplay(NULL); - } - #if ENABLE_RETRANSMISSION if (mRTCPRetransmissionSessionID != 0) { mNetSession->destroySession(mRTCPRetransmissionSessionID); @@ -669,26 +638,19 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } - case kWhatSerializerNotify: + case kWhatMediaPullerNotify: { int32_t what; CHECK(msg->findInt32("what", &what)); - if (what == Serializer::kWhatEOS) { + if (what == MediaPuller::kWhatEOS) { ALOGI("input eos"); for (size_t i = 0; i < mTracks.size(); ++i) { -#if FAKE_VIDEO - sp<AMessage> msg = new AMessage(kWhatConverterNotify, id()); - msg->setInt32("what", Converter::kWhatEOS); - msg->setSize("trackIndex", i); - msg->post(); -#else mTracks.valueAt(i)->converter()->signalEOS(); -#endif } } else { - CHECK_EQ(what, Serializer::kWhatAccessUnit); + CHECK_EQ(what, MediaPuller::kWhatAccessUnit); size_t trackIndex; CHECK(msg->findSize("trackIndex", &trackIndex)); @@ -696,25 +658,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); -#if FAKE_VIDEO - int64_t timeUs; - CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); - - void *mbuf; - CHECK(accessUnit->meta()->findPointer("mediaBuffer", &mbuf)); - - ((MediaBuffer *)mbuf)->release(); - mbuf = NULL; - - sp<AMessage> msg = new AMessage(kWhatConverterNotify, id()); - msg->setInt32("what", Converter::kWhatAccessUnit); - msg->setSize("trackIndex", trackIndex); - msg->setBuffer("accessUnit", accessUnit); - msg->post(); -#else mTracks.valueFor(trackIndex)->converter() ->feedAccessUnit(accessUnit); -#endif } break; } @@ -861,11 +806,9 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( ssize_t index = mTracks.indexOfKey(trackIndex); CHECK_GE(index, 0); -#if !FAKE_VIDEO const sp<Converter> &converter = mTracks.valueAt(index)->converter(); looper()->unregisterHandler(converter->id()); -#endif mTracks.removeItemsAt(index); @@ -895,13 +838,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } status_t WifiDisplaySource::PlaybackSession::setupPacketizer() { - sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id()); - mPacketizer = new TSPacketizer; -#if FAKE_VIDEO - return addFakeSources(); -#else status_t err = addVideoSource(); if (err != OK) { @@ -909,85 +847,10 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer() { } return addAudioSource(); -#endif -} - -status_t WifiDisplaySource::PlaybackSession::addFakeSources() { -#if FAKE_VIDEO - mSerializerLooper = new ALooper; - mSerializerLooper->setName("serializer_looper"); - mSerializerLooper->start(); - - sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id()); - mSerializer = new Serializer( - true /* throttled */, msg); - - mSerializerLooper->registerHandler(mSerializer); - - DataSource::RegisterDefaultSniffers(); - - sp<DataSource> dataSource = - DataSource::CreateFromURI( - "/system/etc/inception_1500.mp4"); - - CHECK(dataSource != NULL); - - sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); - CHECK(extractor != NULL); - - bool haveAudio = false; - bool haveVideo = false; - for (size_t i = 0; i < extractor->countTracks(); ++i) { - sp<MetaData> meta = extractor->getTrackMetaData(i); - - const char *mime; - CHECK(meta->findCString(kKeyMIMEType, &mime)); - - bool useTrack = false; - if (!strncasecmp(mime, "audio/", 6) && !haveAudio) { - useTrack = true; - haveAudio = true; - } else if (!strncasecmp(mime, "video/", 6) && !haveVideo) { - useTrack = true; - haveVideo = true; - } - - if (!useTrack) { - continue; - } - - sp<MediaSource> source = extractor->getTrack(i); - - ssize_t index = mSerializer->addSource(source); - CHECK_GE(index, 0); - - sp<AMessage> format; - status_t err = convertMetaDataToMessage(source->getFormat(), &format); - CHECK_EQ(err, (status_t)OK); - - mTracks.add(index, new Track(format)); - } - CHECK(haveAudio || haveVideo); -#endif - - return OK; } status_t WifiDisplaySource::PlaybackSession::addSource( bool isVideo, const sp<MediaSource> &source, size_t *numInputBuffers) { -#if USE_SERIALIZER - if (mSerializer == NULL) { - mSerializerLooper = new ALooper; - mSerializerLooper->setName("serializer_looper"); - mSerializerLooper->start(); - - sp<AMessage> msg = new AMessage(kWhatSerializerNotify, id()); - mSerializer = new Serializer( - false /* throttled */, msg); - - mSerializerLooper->registerHandler(mSerializer); - } -#else sp<ALooper> pullLooper = new ALooper; pullLooper->setName("pull_looper"); @@ -995,7 +858,6 @@ status_t WifiDisplaySource::PlaybackSession::addSource( false /* runOnCallingThread */, false /* canCallJava */, PRIORITY_DEFAULT); -#endif sp<ALooper> codecLooper = new ALooper; codecLooper->setName("codec_looper"); @@ -1009,16 +871,12 @@ status_t WifiDisplaySource::PlaybackSession::addSource( sp<AMessage> notify; -#if USE_SERIALIZER - trackIndex = mSerializer->addSource(source); -#else trackIndex = mTracks.size(); - notify = new AMessage(kWhatSerializerNotify, id()); + notify = new AMessage(kWhatMediaPullerNotify, id()); notify->setSize("trackIndex", trackIndex); sp<MediaPuller> puller = new MediaPuller(source, notify); pullLooper->registerHandler(puller); -#endif sp<AMessage> format; status_t err = convertMetaDataToMessage(source->getFormat(), &format); @@ -1044,11 +902,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( *numInputBuffers = converter->getInputBufferCount(); } -#if USE_SERIALIZER - mTracks.add(trackIndex, new Track(NULL, codecLooper, NULL, converter)); -#else mTracks.add(trackIndex, new Track(pullLooper, codecLooper, puller, converter)); -#endif if (isVideo) { mVideoTrackIndex = trackIndex; @@ -1070,21 +924,11 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource() { return err; } - // Add one reference to account for the serializer. err = source->setMaxAcquiredBufferCount(numInputBuffers); CHECK_EQ(err, (status_t)OK); mBufferQueue = source->getBufferQueue(); - if (mLegacyMode) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder = sm->getService(String16("SurfaceFlinger")); - sp<ISurfaceComposer> service = interface_cast<ISurfaceComposer>(binder); - CHECK(service != NULL); - - service->connectDisplay(mBufferQueue); - } - return OK; } @@ -1111,11 +955,11 @@ sp<ISurfaceTexture> WifiDisplaySource::PlaybackSession::getSurfaceTexture() { } int32_t WifiDisplaySource::PlaybackSession::width() const { - return mLegacyMode ? 720 : 1280; + return 1280; } int32_t WifiDisplaySource::PlaybackSession::height() const { - return mLegacyMode ? 1280 : 720; + return 720; } void WifiDisplaySource::PlaybackSession::scheduleSendSR() { diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 6ab3f84..f2090b4 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -28,7 +28,6 @@ struct IHDCP; struct ISurfaceTexture; struct MediaPuller; struct MediaSource; -struct Serializer; struct TSPacketizer; #define LOG_TRANSPORT_STREAM 0 @@ -41,7 +40,6 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify, const struct in_addr &interfaceAddr, - bool legacyMode, const sp<IHDCP> &hdcp); enum TransportMode { @@ -91,7 +89,7 @@ private: kWhatRTPRetransmissionNotify, kWhatRTCPRetransmissionNotify, #endif - kWhatSerializerNotify, + kWhatMediaPullerNotify, kWhatConverterNotify, kWhatUpdateSurface, kWhatFinishPlay, @@ -108,13 +106,10 @@ private: sp<ANetworkSession> mNetSession; sp<AMessage> mNotify; in_addr mInterfaceAddr; - bool mLegacyMode; sp<IHDCP> mHDCP; int64_t mLastLifesignUs; - sp<ALooper> mSerializerLooper; - sp<Serializer> mSerializer; sp<TSPacketizer> mPacketizer; sp<BufferQueue> mBufferQueue; @@ -178,8 +173,6 @@ private: status_t setupPacketizer(); - status_t addFakeSources(); - status_t addSource( bool isVideo, const sp<MediaSource> &source, diff --git a/media/libstagefright/wifi-display/source/Serializer.cpp b/media/libstagefright/wifi-display/source/Serializer.cpp deleted file mode 100644 index 598fd3e..0000000 --- a/media/libstagefright/wifi-display/source/Serializer.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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_NDEBUG 0 -#define LOG_TAG "Serializer" -#include <utils/Log.h> - -#include "Serializer.h" - -#include <media/stagefright/foundation/ABuffer.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaSource.h> -#include <media/stagefright/MetaData.h> - -namespace android { - -struct Serializer::Track : public RefBase { - Track(const sp<MediaSource> &source); - - status_t start(); - status_t stop(); - - void readBufferIfNecessary(); - - bool reachedEOS() const; - int64_t bufferTimeUs() const; - - sp<ABuffer> drainBuffer(); - -protected: - virtual ~Track(); - -private: - sp<MediaSource> mSource; - AString mMIME; - - bool mStarted; - status_t mFinalResult; - MediaBuffer *mBuffer; - int64_t mBufferTimeUs; - - DISALLOW_EVIL_CONSTRUCTORS(Track); -}; - -Serializer::Track::Track(const sp<MediaSource> &source) - : mSource(source), - mStarted(false), - mFinalResult(OK), - mBuffer(NULL), - mBufferTimeUs(-1ll) { - const char *mime; - sp<MetaData> meta = mSource->getFormat(); - CHECK(meta->findCString(kKeyMIMEType, &mime)); - - mMIME = mime; -} - -Serializer::Track::~Track() { - stop(); -} - -status_t Serializer::Track::start() { - if (mStarted) { - return OK; - } - - status_t err = mSource->start(); - - if (err == OK) { - mStarted = true; - } - - return err; -} - -status_t Serializer::Track::stop() { - if (!mStarted) { - return OK; - } - - if (mBuffer != NULL) { - mBuffer->release(); - mBuffer = NULL; - - mBufferTimeUs = -1ll; - } - - status_t err = mSource->stop(); - - mStarted = false; - - return err; -} - -void Serializer::Track::readBufferIfNecessary() { - if (mBuffer != NULL) { - return; - } - - int64_t nowUs = ALooper::GetNowUs(); - mFinalResult = mSource->read(&mBuffer); - int64_t delayUs = ALooper::GetNowUs() - nowUs; - - ALOGV("read on track %s took %lld us, got %d bytes", - mMIME.c_str(), delayUs, mBuffer->range_length()); - - if (mFinalResult != OK) { - ALOGI("read failed w/ err %d", mFinalResult); - return; - } - - CHECK(mBuffer->meta_data()->findInt64(kKeyTime, &mBufferTimeUs)); -} - -bool Serializer::Track::reachedEOS() const { - return mFinalResult != OK; -} - -int64_t Serializer::Track::bufferTimeUs() const { - return mBufferTimeUs; -} - -sp<ABuffer> Serializer::Track::drainBuffer() { - sp<ABuffer> accessUnit = new ABuffer(mBuffer->range_length()); - - memcpy(accessUnit->data(), - (const uint8_t *)mBuffer->data() + mBuffer->range_offset(), - mBuffer->range_length()); - - accessUnit->meta()->setInt64("timeUs", mBufferTimeUs); - accessUnit->meta()->setPointer("mediaBuffer", mBuffer); - - mBuffer = NULL; - mBufferTimeUs = -1ll; - - return accessUnit; -} - -//////////////////////////////////////////////////////////////////////////////// - -Serializer::Serializer(bool throttle, const sp<AMessage> ¬ify) - : mThrottle(throttle), - mNotify(notify), - mPollGeneration(0), - mStartTimeUs(-1ll) { -} - -Serializer::~Serializer() { -} - -status_t Serializer::postSynchronouslyAndReturnError( - const sp<AMessage> &msg) { - sp<AMessage> response; - status_t err = msg->postAndAwaitResponse(&response); - - if (err != OK) { - return err; - } - - if (!response->findInt32("err", &err)) { - err = OK; - } - - return err; -} - -ssize_t Serializer::addSource(const sp<MediaSource> &source) { - sp<AMessage> msg = new AMessage(kWhatAddSource, id()); - msg->setPointer("source", source.get()); - - sp<AMessage> response; - status_t err = msg->postAndAwaitResponse(&response); - - if (err != OK) { - return err; - } - - if (!response->findInt32("err", &err)) { - size_t index; - CHECK(response->findSize("index", &index)); - - return index; - } - - return err; -} - -status_t Serializer::start() { - return postSynchronouslyAndReturnError(new AMessage(kWhatStart, id())); -} - -status_t Serializer::stop() { - return postSynchronouslyAndReturnError(new AMessage(kWhatStop, id())); -} - -void Serializer::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatAddSource: - { - ssize_t index = onAddSource(msg); - - sp<AMessage> response = new AMessage; - - if (index < 0) { - response->setInt32("err", index); - } else { - response->setSize("index", index); - } - - uint32_t replyID; - CHECK(msg->senderAwaitsResponse(&replyID)); - - response->postReply(replyID); - break; - } - - case kWhatStart: - case kWhatStop: - { - status_t err = (msg->what() == kWhatStart) ? onStart() : onStop(); - - sp<AMessage> response = new AMessage; - response->setInt32("err", err); - - uint32_t replyID; - CHECK(msg->senderAwaitsResponse(&replyID)); - - response->postReply(replyID); - break; - } - - case kWhatPoll: - { - int32_t generation; - CHECK(msg->findInt32("generation", &generation)); - - if (generation != mPollGeneration) { - break; - } - - int64_t delayUs = onPoll(); - if (delayUs >= 0ll) { - schedulePoll(delayUs); - } - break; - } - - default: - TRESPASS(); - } -} - -ssize_t Serializer::onAddSource(const sp<AMessage> &msg) { - void *obj; - CHECK(msg->findPointer("source", &obj)); - - sp<MediaSource> source = static_cast<MediaSource *>(obj); - - sp<Track> track = new Track(source); - return mTracks.add(track); -} - -status_t Serializer::onStart() { - status_t err = OK; - for (size_t i = 0; i < mTracks.size(); ++i) { - err = mTracks.itemAt(i)->start(); - - if (err != OK) { - break; - } - } - - if (err == OK) { - schedulePoll(); - } - - return err; -} - -status_t Serializer::onStop() { - for (size_t i = 0; i < mTracks.size(); ++i) { - mTracks.itemAt(i)->stop(); - } - - cancelPoll(); - - return OK; -} - -int64_t Serializer::onPoll() { - int64_t minTimeUs = -1ll; - ssize_t minTrackIndex = -1; - - for (size_t i = 0; i < mTracks.size(); ++i) { - const sp<Track> &track = mTracks.itemAt(i); - - track->readBufferIfNecessary(); - - if (!track->reachedEOS()) { - int64_t timeUs = track->bufferTimeUs(); - - if (minTrackIndex < 0 || timeUs < minTimeUs) { - minTimeUs = timeUs; - minTrackIndex = i; - } - } - } - - if (minTrackIndex < 0) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatEOS); - notify->post(); - - return -1ll; - } - - if (mThrottle) { - int64_t nowUs = ALooper::GetNowUs(); - - if (mStartTimeUs < 0ll) { - mStartTimeUs = nowUs; - } - - int64_t lateByUs = nowUs - (minTimeUs + mStartTimeUs); - - if (lateByUs < 0ll) { - // Too early - return -lateByUs; - } - } - - sp<AMessage> notify = mNotify->dup(); - - notify->setInt32("what", kWhatAccessUnit); - notify->setSize("trackIndex", minTrackIndex); - - notify->setBuffer( - "accessUnit", mTracks.itemAt(minTrackIndex)->drainBuffer()); - - notify->post(); - - return 0ll; -} - -void Serializer::schedulePoll(int64_t delayUs) { - sp<AMessage> msg = new AMessage(kWhatPoll, id()); - msg->setInt32("generation", mPollGeneration); - msg->post(delayUs); -} - -void Serializer::cancelPoll() { - ++mPollGeneration; -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/source/Serializer.h b/media/libstagefright/wifi-display/source/Serializer.h deleted file mode 100644 index 07950fa..0000000 --- a/media/libstagefright/wifi-display/source/Serializer.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 SERIALIZER_H_ - -#define SERIALIZER_H_ - -#include <media/stagefright/foundation/AHandler.h> -#include <utils/Vector.h> - -namespace android { - -struct AMessage; -struct MediaSource; - -// After adding a number of MediaSource objects and starting the Serializer, -// it'll emit their access units in order of increasing timestamps. -struct Serializer : public AHandler { - enum { - kWhatEOS, - kWhatAccessUnit - }; - - // In throttled operation, data is emitted at a pace corresponding - // to the incoming media timestamps. - Serializer(bool throttle, const sp<AMessage> ¬ify); - - ssize_t addSource(const sp<MediaSource> &source); - - status_t start(); - status_t stop(); - -protected: - virtual void onMessageReceived(const sp<AMessage> &what); - virtual ~Serializer(); - -private: - enum { - kWhatAddSource, - kWhatStart, - kWhatStop, - kWhatPoll - }; - - struct Track; - - bool mThrottle; - sp<AMessage> mNotify; - Vector<sp<Track> > mTracks; - - int32_t mPollGeneration; - - int64_t mStartTimeUs; - - status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg); - - ssize_t onAddSource(const sp<AMessage> &msg); - status_t onStart(); - status_t onStop(); - int64_t onPoll(); - - void schedulePoll(int64_t delayUs = 0ll); - void cancelPoll(); - - DISALLOW_EVIL_CONSTRUCTORS(Serializer); -}; - -} // namespace android - -#endif // SERIALIZER_H_ - diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 1e0b6e2..9ad978f 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -898,7 +898,6 @@ status_t WifiDisplaySource::onSetupRequest( sp<PlaybackSession> playbackSession = new PlaybackSession( mNetSession, notify, mInterfaceAddr, - mClient == NULL, /* legacyMode */ #if REQUIRE_HDCP mHDCP #else |