diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llmessage/tests/llcurl_stub.cpp | 1 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
-rw-r--r-- | indra/newview/llmediadataclient.cpp | 135 | ||||
-rw-r--r-- | indra/newview/llmediadataclient.h | 85 | ||||
-rw-r--r-- | indra/newview/llvovolume.cpp | 81 | ||||
-rw-r--r-- | indra/newview/llvovolume.h | 1 | ||||
-rw-r--r-- | indra/newview/tests/llmediadataclient_test.cpp | 481 |
8 files changed, 713 insertions, 83 deletions
diff --git a/indra/llmessage/tests/llcurl_stub.cpp b/indra/llmessage/tests/llcurl_stub.cpp index 5dc5932fde..e6a5ad9946 100644 --- a/indra/llmessage/tests/llcurl_stub.cpp +++ b/indra/llmessage/tests/llcurl_stub.cpp @@ -22,6 +22,7 @@ #include "linden_common.h" LLCurl::Responder::Responder() + : mReferenceCount(0) { } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9f0a0111f5..e0ca0a760f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1540,6 +1540,7 @@ include(LLAddBuildTest) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp lldateutil.cpp + llmediadataclient.cpp llviewerhelputil.cpp ) set_source_files_properties( diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 806f96a654..dc0e5ffa1d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5294,6 +5294,17 @@ <key>Value</key> <integer>13</integer> </map> + <key>PrimMediaMaxRetries</key> + <map> + <key>Comment</key> + <string>Maximum number of retries for media queries.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>4</integer> + </map> <key>PrimMediaRequestQueueDelay</key> <map> <key>Comment</key> diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index 4dde381e97..e69c85f245 100644 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -41,7 +41,6 @@ #include "llmediaentry.h" #include "lltextureentry.h" #include "llviewerregion.h" -#include "llvovolume.h" // // When making a request @@ -54,7 +53,9 @@ // - Any request that gets a 503 still goes through the retry logic // -// Some helpful logging macros +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 4; ////////////////////////////////////////////////////////////////////////////////////// // @@ -65,7 +66,7 @@ LLMediaDataClient::Request::Request(const std::string &cap_name, const LLSD& sd_payload, - LLVOVolume *obj, + LLMediaDataClientObject *obj, LLMediaDataClient *mdc) : mCapName(cap_name), mPayload(sd_payload), @@ -78,6 +79,7 @@ LLMediaDataClient::Request::Request(const std::string &cap_name, LLMediaDataClient::Request::~Request() { + LL_DEBUGS("LLMediaDataClient") << "~Request" << (*this) << LL_ENDL; mMDC = NULL; mObject = NULL; } @@ -85,7 +87,7 @@ LLMediaDataClient::Request::~Request() std::string LLMediaDataClient::Request::getCapability() const { - return getObject()->getRegion()->getCapability(getCapName()); + return getObject()->getCapabilityUrl(getCapName()); } // Helper function to get the "type" of request, which just pokes around to @@ -137,6 +139,17 @@ void LLMediaDataClient::Request::reEnqueue() const mMDC->enqueue(this); } +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ + return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : + mMDC->mRetryTimerDelay; +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ + return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; +} + std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) { s << "<request>" @@ -163,6 +176,7 @@ LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) // virtual LLMediaDataClient::Responder::RetryTimer::~RetryTimer() { + LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; mResponder = NULL; } @@ -190,28 +204,31 @@ LLMediaDataClient::Responder::Responder(const request_ptr_t &request) LLMediaDataClient::Responder::~Responder() { + LL_DEBUGS("LLMediaDataClient") << "~Responder" << *(getRequest()) << LL_ENDL; mRequest = NULL; } /*virtual*/ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) { - extern LLControlGroup gSavedSettings; - if (status == HTTP_SERVICE_UNAVAILABLE) { - F32 retry_timeout = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); - if (retry_timeout <= 0.0) - { - retry_timeout = (F32)UNAVAILABLE_RETRY_TIMER_DELAY; - } - LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + F32 retry_timeout = mRequest->getRetryTimerDelay(); mRequest->incRetryCount(); - // Start timer (instances are automagically tracked by - // InstanceTracker<> and LLEventTimer) - new RetryTimer(F32(retry_timeout/*secs*/), this); + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), this); + } + else { + LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retry count " << + mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } } else { std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason; @@ -252,29 +269,25 @@ bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const r // Calculate the scores for each. F64 o1_score = Comparator::getObjectScore(o1->getObject()); F64 o2_score = Comparator::getObjectScore(o2->getObject()); - - return ( o1_score > o2_score ); + + // XXX Weird: a higher score should go earlier, but by observation I notice + // that this causes further-away objects load first. This is counterintuitive + // to the priority_queue Comparator, which states that this function should + // return 'true' if o1 should be *before* o2. + // In other words, I'd have expected that the following should return + // ( o1_score > o2_score). + return ( o1_score < o2_score ); } // static -F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) +F64 LLMediaDataClient::Comparator::getObjectScore(const LLMediaDataClientObject::ptr_t &obj) { // *TODO: make this less expensive? - F32 dist = obj->getRenderPosition().length() + 0.1; // avoids div by 0 + F64 dist = obj->getDistanceFromAvatar() + 0.1; // avoids div by 0 // square the distance so that they are in the same "unit magnitude" as // the interest (which is an area) dist *= dist; - F64 interest = (F64)1; - int i = 0; - int end = obj->getNumTEs(); - for ( ; i < end; ++i) - { - const viewer_media_t &impl = obj->getMediaImpl(i); - if (!impl.isNull()) - { - interest += impl->getInterest(); - } - } + F64 interest = obj->getTotalMediaInterest() + 1.0; return interest/dist; } @@ -282,7 +295,7 @@ F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) ////////////////////////////////////////////////////////////////////////////////////// // // LLMediaDataClient::PriorityQueue -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for. // ////////////////////////////////////////////////////////////////////////////////////// @@ -304,7 +317,7 @@ std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue ////////////////////////////////////////////////////////////////////////////////////// // // LLMediaDataClient::QueueTimer -// Queue of LLVOVolume smart pointers to request media for. +// Queue of LLMediaDataClientObject smart pointers to request media for. // ////////////////////////////////////////////////////////////////////////////////////// @@ -316,6 +329,7 @@ LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) LLMediaDataClient::QueueTimer::~QueueTimer() { + LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; mMDC->setIsRunning(false); mMDC = NULL; } @@ -343,10 +357,10 @@ BOOL LLMediaDataClient::QueueTimer::tick() // Peel one off of the items from the queue, and execute request request_ptr_t request = queue.top(); llassert(!request.isNull()); - const ll_vo_volume_ptr_t &object = request->getObject(); + const LLMediaDataClientObject *object = request->getObject(); bool performed_request = false; - llassert(!object.isNull()); - if (!object.isNull() && object->hasMedia()) + llassert(NULL != object); + if (NULL != object && object->hasMedia()) { std::string url = request->getCapability(); if (!url.empty()) @@ -368,12 +382,13 @@ BOOL LLMediaDataClient::QueueTimer::tick() LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL; } } - bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES; + bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries; if (performed_request || exceeded_retries) // Try N times before giving up { if (exceeded_retries) { - LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << LLMediaDataClient::MAX_RETRIES << " tries...popping object id " << object->getID() << LL_ENDL; + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " + << mMDC->mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; // XXX Should we bring up a warning dialog?? } queue.pop(); @@ -390,15 +405,9 @@ void LLMediaDataClient::startQueueTimer() { if (! mQueueTimerIsRunning) { - extern LLControlGroup gSavedSettings; - F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); - if (queue_timer_delay <= 0.0f) - { - queue_timer_delay = (F32)LLMediaDataClient::QUEUE_TIMER_DELAY; - } - LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << queue_timer_delay << " seconds)" << LL_ENDL; + LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; // LLEventTimer automagically takes care of the lifetime of this object - new QueueTimer(queue_timer_delay, this); + new QueueTimer(mQueueTimerDelay, this); } } @@ -407,9 +416,9 @@ void LLMediaDataClient::stopQueueTimer() mQueueTimerIsRunning = false; } -void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload) +void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) { - if (NULL == object || ! object->hasMedia()) return; + if (object.isNull() || ! object->hasMedia()) return; // Push the object on the priority queue enqueue(new Request(getCapabilityName(), payload, object, this)); @@ -432,7 +441,13 @@ void LLMediaDataClient::enqueue(const Request *request) // ////////////////////////////////////////////////////////////////////////////////////// -LLMediaDataClient::LLMediaDataClient() +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mQueueTimerIsRunning(false) { pRequestQueue = new PriorityQueue(); } @@ -442,12 +457,17 @@ LLMediaDataClient::~LLMediaDataClient() stopQueueTimer(); // This should clear the queue, and hopefully call all the destructors. - LL_DEBUGS("LLMediaDataClient") << "destructor: queue: " << + LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << (pRequestQueue->empty() ? "<empty> " : "<not empty> ") << (*pRequestQueue) << LL_ENDL; delete pRequestQueue; pRequestQueue = NULL; } +bool LLMediaDataClient::isEmpty() const +{ + return (NULL == pRequestQueue) ? true : pRequestQueue->empty(); +} + ////////////////////////////////////////////////////////////////////////////////////// // // LLObjectMediaDataClient @@ -465,7 +485,7 @@ const char *LLObjectMediaDataClient::getCapabilityName() const return "ObjectMedia"; } -void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object) +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) { LLSD sd_payload; sd_payload["verb"] = "GET"; @@ -473,18 +493,17 @@ void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object) request(object, sd_payload); } -void LLObjectMediaDataClient::updateMedia(LLVOVolume *object) +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) { LLSD sd_payload; sd_payload["verb"] = "UPDATE"; sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); LLSD object_media_data; - for (int i=0; i < object->getNumTEs(); i++) { - LLTextureEntry *texture_entry = object->getTE(i); - llassert((texture_entry->getMediaData() != NULL) == texture_entry->hasMedia()); - const LLSD &media_data = - (texture_entry->getMediaData() == NULL) ? LLSD() : texture_entry->getMediaData()->asLLSD(); - object_media_data.append(media_data); + int i = 0; + int end = object->getMediaDataCount(); + for ( ; i < end ; ++i) + { + object_media_data.append(object->getMediaDataLLSD(i)); } sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; @@ -548,7 +567,7 @@ const char *LLObjectMediaNavigateClient::getCapabilityName() const return "ObjectMediaNavigate"; } -void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url) +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) { LLSD sd_payload; sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h index 949e7239be..9d0aa0981e 100644 --- a/indra/newview/llmediadataclient.h +++ b/indra/newview/llmediadataclient.h @@ -36,12 +36,36 @@ #include "llhttpclient.h" #include <queue> #include "llrefcount.h" +#include "llpointer.h" #include "lltimer.h" -// Forward decls -class LLVOVolume; -typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t; +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: + // Get the number of media data items + virtual U8 getMediaDataCount() const = 0; + // Get the media data at index, as an LLSD + virtual LLSD getMediaDataLLSD(U8 index) const = 0; + // Get this object's UUID + virtual LLUUID getID() const = 0; + // Navigate back to previous URL + virtual void mediaNavigateBounceBack(U8 index) = 0; + // Does this object have media? + virtual bool hasMedia() const = 0; + // Update the object's media data to the given array + virtual void updateObjectMediaData(LLSD const &media_data_array) = 0; + // Return the distance from the object to the avatar + virtual F64 getDistanceFromAvatar() const = 0; + // Return the total "interest" of the media (on-screen area) + virtual F64 getTotalMediaInterest() const = 0; + // Return the given cap url + virtual std::string getCapabilityUrl(const std::string &name) const = 0; + + // smart pointer + typedef LLPointer<LLMediaDataClientObject> ptr_t; +}; // This object creates a priority queue for requests. // Abstracts the Cap URL, the request, and the responder @@ -50,15 +74,23 @@ class LLMediaDataClient : public LLRefCount public: LOG_CLASS(LLMediaDataClient); - const static int QUEUE_TIMER_DELAY = 1; // seconds(s) - const static int MAX_RETRIES = 4; + const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) + const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs + const static U32 MAX_RETRIES;// = 4; // Constructor - LLMediaDataClient(); + LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES); // Make the request - void request(LLVOVolume *object, const LLSD &payload); - + void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload); + + F32 getRetryTimerDelay() const { return mRetryTimerDelay; } + + // Returns true iff the queue is empty + bool isEmpty() const; + protected: // Destructor virtual ~LLMediaDataClient(); // use unref @@ -73,10 +105,10 @@ protected: NAVIGATE }; - Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc); + Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc); const std::string &getCapName() const { return mCapName; } const LLSD &getPayload() const { return mPayload; } - LLVOVolume *getObject() const { return mObject; } + LLMediaDataClientObject *getObject() const { return mObject; } U32 getNum() const { return mNum; } @@ -92,6 +124,9 @@ protected: // Re-enqueue thyself void reEnqueue() const; + F32 getRetryTimerDelay() const; + U32 getMaxNumRetries() const; + public: friend std::ostream& operator<<(std::ostream &s, const Request &q); @@ -101,7 +136,7 @@ protected: private: std::string mCapName; LLSD mPayload; - ll_vo_volume_ptr_t mObject; + LLMediaDataClientObject::ptr_t mObject; // Simple tracking const U32 mNum; static U32 sNum; @@ -115,8 +150,6 @@ protected: // Responder class Responder : public LLHTTPClient::Responder { - static const int UNAVAILABLE_RETRY_TIMER_DELAY = 5; // secs - public: Responder(const request_ptr_t &request); //If we get back an error (not found, etc...), handle it here @@ -163,7 +196,7 @@ private: public: bool operator() (const request_ptr_t &o1, const request_ptr_t &o2) const; private: - static F64 getObjectScore(const ll_vo_volume_ptr_t &obj); + static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); }; // PriorityQueue @@ -194,6 +227,10 @@ private: void startQueueTimer(); void stopQueueTimer(); void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + const F32 mQueueTimerDelay; + const F32 mRetryTimerDelay; + const U32 mMaxNumRetries; bool mQueueTimerIsRunning; @@ -205,11 +242,15 @@ private: class LLObjectMediaDataClient : public LLMediaDataClient { public: - LLObjectMediaDataClient() {} + LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} ~LLObjectMediaDataClient() {} - void fetchMedia(LLVOVolume *object); - void updateMedia(LLVOVolume *object); + void fetchMedia(LLMediaDataClientObject *object); + void updateMedia(LLMediaDataClientObject *object); protected: // Subclasses must override this factory method to return a new responder @@ -231,14 +272,18 @@ protected: // MediaDataResponder specific for the ObjectMediaNavigate cap class LLObjectMediaNavigateClient : public LLMediaDataClient { +public: // NOTE: from llmediaservice.h static const int ERROR_PERMISSION_DENIED_CODE = 8002; -public: - LLObjectMediaNavigateClient() {} + LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} ~LLObjectMediaNavigateClient() {} - void navigate(LLVOVolume *object, U8 texture_index, const std::string &url); + void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); protected: // Subclasses must override this factory method to return a new responder diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 83e65af054..1704f63376 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -91,6 +91,57 @@ LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); +// Implementation class of LLMediaDataClientObject. See llmediadataclient.h +class LLMediaDataClientObjectImpl : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {} + LLMediaDataClientObjectImpl() { mObject = NULL; } + + virtual U8 getMediaDataCount() const + { return mObject->getNumTEs(); } + + virtual LLSD getMediaDataLLSD(U8 index) const + { + LLSD result; + LLTextureEntry *te = mObject->getTE(index); + if (NULL != te) + { + llassert((te->getMediaData() != NULL) == te->hasMedia()); + if (te->getMediaData() != NULL) + { + result = te->getMediaData()->asLLSD(); + } + } + return result; + } + + virtual LLUUID getID() const + { return mObject->getID(); } + + virtual void mediaNavigateBounceBack(U8 index) + { mObject->mediaNavigateBounceBack(index); } + + virtual bool hasMedia() const + { return mObject->hasMedia(); } + + virtual void updateObjectMediaData(LLSD const &data) + { mObject->updateObjectMediaData(data); } + + virtual F64 getDistanceFromAvatar() const + { return mObject->getRenderPosition().length(); } + + virtual F64 getTotalMediaInterest() const + { return mObject->getTotalMediaInterest(); } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mObject->getRegion()->getCapability(name); } + +private: + LLPointer<LLVOVolume> mObject; +}; + + LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mVolumeImpl(NULL) @@ -134,8 +185,12 @@ LLVOVolume::~LLVOVolume() // static void LLVOVolume::initClass() { - sObjectMediaClient = new LLObjectMediaDataClient(); - sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(); + // gSavedSettings better be around + const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); + const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); + const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries"); + sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, max_retries); } // static @@ -1634,7 +1689,7 @@ bool LLVOVolume::hasMedia() const void LLVOVolume::requestMediaDataUpdate() { - sObjectMediaClient->fetchMedia(this); + sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this)); } void LLVOVolume::cleanUpMediaImpls() @@ -1834,7 +1889,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, llinfos << "broadcasting navigate with URI " << new_location << llendl; - sObjectMediaNavigateClient->navigate(this, face_index, new_location); + sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location); } } break; @@ -1860,7 +1915,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, void LLVOVolume::sendMediaDataUpdate() { - sObjectMediaClient->updateMedia(this); + sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this)); } void LLVOVolume::removeMediaImpl(S32 texture_index) @@ -1953,6 +2008,22 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const return NULL; } +F64 LLVOVolume::getTotalMediaInterest() const +{ + F64 interest = (F64)0.0; + int i = 0; + const int end = getNumTEs(); + for ( ; i < end; ++i) + { + const viewer_media_t &impl = getMediaImpl(i); + if (!impl.isNull()) + { + interest += impl->getInterest(); + } + } + return interest; +} + S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id) { S32 end = (S32)mMediaImplList.size() ; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index bb2b890000..90dfa2204b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -255,6 +255,7 @@ public: viewer_media_t getMediaImpl(U8 face_id) const; S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); + F64 getTotalMediaInterest() const; bool hasMedia() const; diff --git a/indra/newview/tests/llmediadataclient_test.cpp b/indra/newview/tests/llmediadataclient_test.cpp new file mode 100644 index 0000000000..135c5ab501 --- /dev/null +++ b/indra/newview/tests/llmediadataclient_test.cpp @@ -0,0 +1,481 @@ +/** + * @file llmediadataclient_test.cpp + * @brief LLMediaDatClient tests + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include <iostream> +#include "../test/lltut.h" + +#include "llsdserialize.h" +#include "llerrorcontrol.h" +#include "llhttpstatuscodes.h" + +#include "../llmediadataclient.h" +#include "../llvovolume.h" + +#include "../../llprimitive/llmediaentry.cpp" +#include "../../llprimitive/lltextureentry.cpp" +#include "../../llmessage/tests/llcurl_stub.cpp" + +#include <boost/lexical_cast.hpp> + +#define VALID_OBJECT_ID "3607d5c4-644b-4a8a-871a-8b78471af2a2" +#define VALID_OBJECT_ID_1 "11111111-1111-1111-1111-111111111111" +#define VALID_OBJECT_ID_2 "22222222-2222-2222-2222-222222222222" +#define VALID_OBJECT_ID_3 "33333333-3333-3333-3333-333333333333" +#define VALID_OBJECT_ID_4 "44444444-4444-4444-4444-444444444444" + +#define FAKE_OBJECT_MEDIA_CAP_URL "foo_ObjectMedia" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL "foo_ObjectMediaNavigate" +#define FAKE_OBJECT_MEDIA_CAP_URL_503 "foo_ObjectMedia_503" +#define FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR "foo_ObjectMediaNavigate_ERROR" + +#define MEDIA_DATA "\ +<array> \ +<string>foo</string> \ +<string>bar</string> \ +<string>baz</string> \ +</array>" + +#define _DATA_URLS(ID,DIST,INT,URL1,URL2) " \ +<llsd> \ + <map> \ + <key>uuid</key> \ + <string>" ID "</string> \ + <key>distance</key> \ + <real>" DIST "</real> \ + <key>interest</key> \ + <real>" INT "</real> \ + <key>cap_urls</key> \ + <map> \ + <key>ObjectMedia</key> \ + <string>" URL1 "</string> \ + <key>ObjectMediaNavigate</key> \ + <string>" URL2 "</string> \ + </map> \ + <key>media_data</key> \ + " MEDIA_DATA " \ + </map> \ +</llsd>" + +#define _DATA(ID,DIST,INT) _DATA_URLS(ID,DIST,INT,FAKE_OBJECT_MEDIA_CAP_URL,FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL) + +const char *DATA = _DATA(VALID_OBJECT_ID,"1.0","1.0"); + +#define STR(I) boost::lexical_cast<std::string>(I) + +#define LOG_TEST(N) LL_DEBUGS("LLMediaDataClient") << "\n" << \ +"================================================================================\n" << \ +"===================================== TEST " #N " ===================================\n" << \ +"================================================================================\n" << LL_ENDL; + +LLSD *gPostRecords = NULL; + +// stubs: +void LLHTTPClient::post( + const std::string& url, + const LLSD& body, + LLHTTPClient::ResponderPtr responder, + const LLSD& headers, + const F32 timeout) +{ + LLSD record; + record["url"] = url; + record["body"] = body; + record["headers"] = headers; + record["timeout"] = timeout; + gPostRecords->append(record); + + // Magic URL that triggers a 503: + if ( url == FAKE_OBJECT_MEDIA_CAP_URL_503 ) + { + responder->error(HTTP_SERVICE_UNAVAILABLE, "fake reason"); + } + else if (url == FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR) + { + LLSD result; + LLSD error; + error["code"] = LLObjectMediaNavigateClient::ERROR_PERMISSION_DENIED_CODE; + result["error"] = error; + responder->result(result); + } + else { + responder->result(LLSD()); + } +} + +const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; + +class LLMediaDataClientObjectTest : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectTest(const char *data) + { + std::istringstream d(data); + LLSDSerialize::fromXML(mRep, d); + mNumBounceBacks = 0; + + // std::cout << ll_pretty_print_sd(mRep) << std::endl; + // std::cout << "ID: " << getID() << std::endl; + } + LLMediaDataClientObjectTest(const LLSD &rep) + : mRep(rep), mNumBounceBacks(0) {} + ~LLMediaDataClientObjectTest() + { LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClientObjectTest" << LL_ENDL; } + + virtual U8 getMediaDataCount() const + { return mRep["media_data"].size(); } + virtual LLSD getMediaDataLLSD(U8 index) const + { return mRep["media_data"][(LLSD::Integer)index]; } + virtual LLUUID getID() const + { return mRep["uuid"]; } + virtual void mediaNavigateBounceBack(U8 index) + { + mNumBounceBacks++; + } + + virtual bool hasMedia() const + { return mRep.has("media_data"); } + + virtual void updateObjectMediaData(LLSD const &media_data_array) + { mRep["media_data"] = media_data_array; } + + virtual F64 getDistanceFromAvatar() const + { return (LLSD::Real)mRep["distance"]; } + + virtual F64 getTotalMediaInterest() const + { return (LLSD::Real)mRep["interest"]; } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mRep["cap_urls"][name]; } + + int getNumBounceBacks() const + { return mNumBounceBacks; } + +private: + LLSD mRep; + int mNumBounceBacks; +}; + + +namespace tut +{ + struct mediadataclient + { + mediadataclient() { + gPostRecords = &mLLSD; + +// LLError::setDefaultLevel(LLError::LEVEL_DEBUG); +// LLError::setClassLevel("LLMediaDataClient", LLError::LEVEL_DEBUG); +// LLError::setTagLevel("MediaOnAPrim", LLError::LEVEL_DEBUG); + } + LLSD mLLSD; + }; + + typedef test_group<mediadataclient> mediadataclient_t; + typedef mediadataclient_t::object mediadataclient_object_t; + tut::mediadataclient_t tut_mediadataclient("mediadataclient"); + + void ensure(const std::string &msg, int value, int expected) + { + std::string m = msg; + m += " value: " + STR(value); + m += ", expected: " + STR(expected); + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const std::string & value, const std::string & expected) + { + std::string m = msg; + m += " value: " + value; + m += ", expected: " + expected; + ensure(m, value == expected); + } + + void ensure(const std::string &msg, const LLUUID & value, const LLUUID & expected) + { + std::string m = msg; + m += " value: " + value.asString(); + m += ", expected: " + expected.asString(); + ensure(m, value == expected); + } + + void ensure_llsd(const std::string &msg, const LLSD & value, const char *expected) + { + LLSD expected_llsd; + std::istringstream e(expected); + LLSDSerialize::fromXML(expected_llsd, e); + + std::string value_str = ll_pretty_print_sd(value); + std::string expected_str = ll_pretty_print_sd(expected_llsd); + std::string m = msg; + m += " value: " + value_str; + m += ", expected: " + expected_str; + ensure(m, value_str == expected_str); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<1>() + { + // + // Test fetchMedia() + // + LOG_TEST(1); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + int num_refs_start = o->getNumRefs(); + { + // queue time w/ no delay ensures that LLEventTimer::updateClass() will hit the tick() + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4); + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + LLEventTimer::updateClass(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post GET", (*gPostRecords)[0]["body"]["verb"], "GET"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<2>() + { + // + // Test updateMedia() + // + LOG_TEST(2); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + // queue time w/ no delay ensures that LLEventTimer::updateClass() will hit the tick() + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4); + mdc->updateMedia(o); + ensure("post records", gPostRecords->size(), 0); + LLEventTimer::updateClass(); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("post UPDATE", (*gPostRecords)[0]["body"]["verb"], "UPDATE"); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure_llsd("post data llsd", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + "<llsd>" MEDIA_DATA "</llsd>"); + ensure("queue empty", mdc->isEmpty()); + } + + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<3>() + { + // + // Test navigate() + // + LOG_TEST(3); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest(DATA); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(0,0,4); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + LLEventTimer::updateClass(); + + // ensure no bounce back + ensure("bounce back", dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), 0); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<4>() + { + // + // Test queue ordering + // + LOG_TEST(4); + + LLMediaDataClientObject::ptr_t o1 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_1,"3.0","1.0")); + LLMediaDataClientObject::ptr_t o2 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_2,"1.0","1.0")); + LLMediaDataClientObject::ptr_t o3 = new LLMediaDataClientObjectTest( + _DATA(VALID_OBJECT_ID_3,"2.0","1.0")); + { + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,4); + const char *ORDERED_OBJECT_IDS[] = { VALID_OBJECT_ID_2, VALID_OBJECT_ID_3, VALID_OBJECT_ID_1 }; + mdc->fetchMedia(o1); + mdc->fetchMedia(o2); + mdc->fetchMedia(o3); + + // Make sure no posts happened yet... + ensure("post records", gPostRecords->size(), 0); + + // tick 3 times... + LLEventTimer::updateClass(); + ensure("post records", gPostRecords->size(), 1); + LLEventTimer::updateClass(); + ensure("post records", gPostRecords->size(), 2); + LLEventTimer::updateClass(); + ensure("post records", gPostRecords->size(), 3); + + for( int i=0; i < 3; i++ ) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), + LLUUID(ORDERED_OBJECT_IDS[i])); + } + + ensure("queue empty", mdc->isEmpty()); + } + ensure("refcount of o1", o1->getNumRefs(), 1); + ensure("refcount of o2", o2->getNumRefs(), 1); + ensure("refcount of o3", o3->getNumRefs(), 1); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + + template<> template<> + void mediadataclient_object_t::test<5>() + { + // + // Test fetchMedia() getting a 503 error + // + LOG_TEST(5); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0", + "1.0", + FAKE_OBJECT_MEDIA_CAP_URL_503, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL)); + int num_refs_start = o->getNumRefs(); + { + const int NUM_RETRIES = 5; + LLPointer<LLObjectMediaDataClient> mdc = new LLObjectMediaDataClient(0,0,NUM_RETRIES); + + // This should generate a retry + mdc->fetchMedia(o); + + // Make sure no posts happened yet... + ensure("post records before", gPostRecords->size(), 0); + + // Once, causes retry + // Second, fires retry timer + // Third, fires queue timer again + for (int i=0; i<NUM_RETRIES; ++i) + { + LLEventTimer::updateClass(); + ensure("post records " + STR(i), gPostRecords->size(), i+1); + LLEventTimer::updateClass(); + } + + // Do some extre pumps to make sure no other timer work occurs. + LLEventTimer::updateClass(); + LLEventTimer::updateClass(); + LLEventTimer::updateClass(); + + // Make sure there were 2 posts + ensure("post records after", gPostRecords->size(), NUM_RETRIES); + for (int i=0; i<NUM_RETRIES; ++i) + { + ensure("[" + STR(i) + "] post url", (*gPostRecords)[i]["url"], FAKE_OBJECT_MEDIA_CAP_URL_503); + ensure("[" + STR(i) + "] post GET", (*gPostRecords)[i]["body"]["verb"], "GET"); + ensure("[" + STR(i) + "] post object id", (*gPostRecords)[i]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + } + ensure("queue empty", mdc->isEmpty()); + } + + // Make sure everyone's destroyed properly + ensure("REF COUNT", o->getNumRefs(), num_refs_start); + } + + template<> template<> + void mediadataclient_object_t::test<6>() + { + // + // Test navigate() with a bounce back + // + LOG_TEST(6); + + LLMediaDataClientObject::ptr_t o = new LLMediaDataClientObjectTest( + _DATA_URLS(VALID_OBJECT_ID, + "1.0", + "1.0", + FAKE_OBJECT_MEDIA_CAP_URL, + FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR)); + { + LLPointer<LLObjectMediaNavigateClient> mdc = new LLObjectMediaNavigateClient(0,0,4); + const char *TEST_URL = "http://example.com"; + mdc->navigate(o, 0, TEST_URL); + ensure("post records", gPostRecords->size(), 0); + LLEventTimer::updateClass(); + + // ensure bounce back + ensure("bounce back", + dynamic_cast<LLMediaDataClientObjectTest*>(static_cast<LLMediaDataClientObject*>(o))->getNumBounceBacks(), + 1); + + ensure("post records", gPostRecords->size(), 1); + ensure("post url", (*gPostRecords)[0]["url"], FAKE_OBJECT_MEDIA_NAVIGATE_CAP_URL_ERROR); + ensure("post object id", (*gPostRecords)[0]["body"][LLTextureEntry::OBJECT_ID_KEY].asUUID(), LLUUID(VALID_OBJECT_ID)); + ensure("post data", (*gPostRecords)[0]["body"][LLTextureEntry::TEXTURE_INDEX_KEY], 0); + ensure("post data", (*gPostRecords)[0]["body"][LLMediaEntry::CURRENT_URL_KEY], TEST_URL); + ensure("queue empty", mdc->isEmpty()); + } + ensure("REF COUNT", o->getNumRefs(), 1); + } + + +} |