diff options
-rw-r--r-- | indra/newview/CMakeLists.txt | 6 | ||||
-rw-r--r-- | indra/newview/app_settings/settings.xml | 2 | ||||
-rwxr-xr-x | indra/newview/llmediadataclient.cpp | 627 | ||||
-rwxr-xr-x | indra/newview/llmediadataclient.h | 263 | ||||
-rw-r--r-- | indra/newview/llviewerobject.cpp | 1 | ||||
-rw-r--r-- | indra/newview/llvovolume.cpp | 53 | ||||
-rw-r--r-- | indra/newview/llvovolume.h | 7 |
7 files changed, 910 insertions, 49 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 14a37981a6..d368dd5497 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -264,8 +264,7 @@ set(viewer_SOURCE_FILES llmaniptranslate.cpp llmapresponders.cpp llmediactrl.cpp - llmediadataresponder.cpp - llmediadatafetcher.cpp + llmediadataclient.cpp llmediaremotectrl.cpp llmemoryview.cpp llmenucommands.cpp @@ -735,8 +734,7 @@ set(viewer_HEADER_FILES llmanipscale.h llmaniptranslate.h llmapresponders.h - llmediadataresponder.h - llmediadatafetcher.h + llmediadataclient.h llmediaremotectrl.h llmemoryview.h llmenucommands.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 94a5f5c52b..7f96e0761e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5294,7 +5294,7 @@ <key>Value</key> <integer>13</integer> </map> - <key>PrimMediaFetchQueueDelay</key> + <key>PrimMediaRequestQueueDelay</key> <map> <key>Comment</key> <string>Timer delay for fetching media from the queue (in seconds).</string> diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp new file mode 100755 index 0000000000..e3d3f1a96b --- /dev/null +++ b/indra/newview/llmediadataclient.cpp @@ -0,0 +1,627 @@ +/** + * @file llmediadataclient.cpp + * @brief class for queueing up requests for media data + * + * $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 "llviewerprecompiledheaders.h" + +#include "llmediadataclient.h" + +#include <boost/lexical_cast.hpp> + +#include "llhttpstatuscodes.h" +#include "llnotifications.h" +#include "llsdutil.h" +#include "llmediaentry.h" +#include "lltextureentry.h" +#include "llviewerregion.h" +#include "llvovolume.h" + +// +// When making a request +// - obtain the "overall interest score" of the object. +// This would be the sum of the impls' interest scores. +// - put the request onto a queue sorted by this score +// (highest score at the front of the queue) +// - On a timer, once a second, pull off the head of the queue and send +// the request. +// - Any request that gets a 503 still goes through the retry logic +// + +// Some helpful logging macros + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Request +// +////////////////////////////////////////////////////////////////////////////////////// +/*static*/U32 LLMediaDataClient::Request::sNum = 0; + +LLMediaDataClient::Request::Request(const std::string &cap_name, + const LLSD& sd_payload, + LLVOVolume *obj, + LLMediaDataClient *mdc) + : mCapName(cap_name), + mPayload(sd_payload), + mObject(obj), + mNum(++sNum), + mRetryCount(0), + mMDC(mdc) +{ +} + +LLMediaDataClient::Request::~Request() +{ + mMDC = NULL; + mObject = NULL; +} + + +std::string LLMediaDataClient::Request::getCapability() const +{ + return getObject()->getRegion()->getCapability(getCapName()); +} + +// Helper function to get the "type" of request, which just pokes around to +// discover it. +LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const +{ + if (mCapName == "ObjectMediaNavigate") + { + return NAVIGATE; + } + else if (mCapName == "ObjectMedia") + { + const std::string &verb = mPayload["verb"]; + if (verb == "GET") + { + return GET; + } + else if (verb == "UPDATE") + { + return UPDATE; + } + } + llassert(false); + return GET; +} + +const char *LLMediaDataClient::Request::getTypeAsString() const +{ + Type t = getType(); + switch (t) + { + case GET: + return "GET"; + break; + case UPDATE: + return "UPDATE"; + break; + case NAVIGATE: + return "NAVIGATE"; + break; + } + return ""; +} + + +void LLMediaDataClient::Request::reEnqueue() const +{ + // I sure hope this doesn't deref a bad pointer: + mMDC->enqueue(this); +} + +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) +{ + s << "<request>" + << "<num>" << r.getNum() << "</num>" + << "<type>" << r.getTypeAsString() << "</type>" + << "<object_id>" << r.getObject()->getID() << "</object_id>" + << "<num_retries>" << r.getRetryCount() << "</num_retries>" + << "</request> "; + return s; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder::RetryTimer +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) + : LLEventTimer(time), mResponder(mdr) +{ +} + +// virtual +LLMediaDataClient::Responder::RetryTimer::~RetryTimer() +{ + mResponder = NULL; +} + +// virtual +BOOL LLMediaDataClient::Responder::RetryTimer::tick() +{ + // Instead of retrying, we just put the request back onto the queue + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *(mResponder->getRequest()) << "retrying" << LL_ENDL; + mResponder->getRequest()->reEnqueue(); + // Don't fire again + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::Responder(const request_ptr_t &request) + : mRequest(request) +{ +} + +LLMediaDataClient::Responder::~Responder() +{ + 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; + + mRequest->incRetryCount(); + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), this); + } + else { + std::string msg = boost::lexical_cast<std::string>(status) + ": " + reason; + LL_INFOS("LLMediaDataClient") << *mRequest << " error(" << msg << ")" << LL_ENDL; + LLSD args; + args["ERROR"] = msg; + LLNotifications::instance().add("ObjectMediaFailure", args); + } +} + + +/*virtual*/ +void LLMediaDataClient::Responder::result(const LLSD& content) +{ + LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_pretty_print_sd(content) << LL_ENDL; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Comparator +// +////////////////////////////////////////////////////////////////////////////////////// + +bool LLMediaDataClient::Comparator::operator() (const request_ptr_t &o1, const request_ptr_t &o2) const +{ + if (o2.isNull()) return true; + if (o1.isNull()) return false; + + // The score is intended to be a measure of how close an object is or + // how much screen real estate (interest) it takes up + // Further away = lower score. + // Lesser interest = lower score + // For instance, here are some cases: + // 1: Two items with no impl, closest one wins + // 2: Two items with an impl: interest should rule, but distance is + // still taken into account (i.e. something really close might take + // precedence over a large item far away) + // 3: One item with an impl, another without: item with impl wins + // (XXX is that what we want?) + // Calculate the scores for each. + F64 o1_score = Comparator::getObjectScore(o1->getObject()); + F64 o2_score = Comparator::getObjectScore(o2->getObject()); + + return ( o1_score > o2_score ); +} + +// static +F64 LLMediaDataClient::Comparator::getObjectScore(const ll_vo_volume_ptr_t &obj) +{ + // *TODO: make this less expensive? + F32 dist = obj->getRenderPosition().length() + 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(); + } + } + + return interest/dist; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::PriorityQueue +// Queue of LLVOVolume smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +// dump the queue +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::PriorityQueue &q) +{ + int i = 0; + std::vector<LLMediaDataClient::request_ptr_t>::const_iterator iter = q.c.begin(); + std::vector<LLMediaDataClient::request_ptr_t>::const_iterator end = q.c.end(); + while (iter < end) + { + s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString(); + iter++; + i++; + } + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::QueueTimer +// Queue of LLVOVolume smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) + : LLEventTimer(time), mMDC(mdc) +{ + mMDC->setIsRunning(true); +} + +LLMediaDataClient::QueueTimer::~QueueTimer() +{ + mMDC->setIsRunning(false); + mMDC = NULL; +} + +// virtual +BOOL LLMediaDataClient::QueueTimer::tick() +{ + if (NULL == mMDC->pRequestQueue) + { + // Shutting down? stop. + LL_DEBUGS("LLMediaDataClient") << "queue gone" << LL_ENDL; + return TRUE; + } + + LLMediaDataClient::PriorityQueue &queue = *(mMDC->pRequestQueue); + + if (queue.empty()) + { + LL_DEBUGS("LLMediaDataClient") << "queue empty: " << queue << LL_ENDL; + return TRUE; + } + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL; + + // 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(); + bool performed_request = false; + llassert(!object.isNull()); + if (!object.isNull() && object->hasMedia()) + { + std::string url = request->getCapability(); + if (!url.empty()) + { + const LLSD &sd_payload = request->getPayload(); + LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; + + // Call the subclass for creating the responder + LLHTTPClient::post(url, sd_payload, mMDC->createResponder(request)); + performed_request = true; + } + else { + LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL; + } + } + else { + if (!object->hasMedia()) + { + LL_INFOS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL; + } + } + bool exceeded_retries = request->getRetryCount() > LLMediaDataClient::MAX_RETRIES; + 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; + // XXX Should we bring up a warning dialog?? + } + queue.pop(); + } + else { + request->incRetryCount(); + } + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue is now: " << (*(mMDC->pRequestQueue)) << LL_ENDL; + + return queue.empty(); +} + +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; + // LLEventTimer automagically takes care of the lifetime of this object + new QueueTimer(queue_timer_delay, this); + } +} + +void LLMediaDataClient::stopQueueTimer() +{ + mQueueTimerIsRunning = false; +} + +void LLMediaDataClient::request(LLVOVolume *object, const LLSD &payload) +{ + if (NULL == object || ! object->hasMedia()) return; + + // Push the object on the priority queue + enqueue(new Request(getCapabilityName(), payload, object, this)); +} + +void LLMediaDataClient::enqueue(const Request *request) +{ + LL_INFOS("LLMediaDataClient") << "Queuing request for " << *request << LL_ENDL; + // Push the request on the priority queue + // Sadly, we have to const-cast because items put into the queue are not const + pRequestQueue->push(const_cast<LLMediaDataClient::Request*>(request)); + LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL; + // Start the timer if not already running + startQueueTimer(); +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::LLMediaDataClient() +{ + pRequestQueue = new PriorityQueue(); +} + + +LLMediaDataClient::~LLMediaDataClient() +{ + stopQueueTimer(); + + // This should clear the queue, and hopefully call all the destructors. + while (! pRequestQueue->empty()) pRequestQueue->pop(); + + delete pRequestQueue; + pRequestQueue = NULL; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaDataClient +// Subclass of LLMediaDataClient for the ObjectMedia cap +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder *LLObjectMediaDataClient::createResponder(const request_ptr_t &request) const +{ + return new LLObjectMediaDataClient::Responder(request); +} + +const char *LLObjectMediaDataClient::getCapabilityName() const +{ + return "ObjectMedia"; +} + +void LLObjectMediaDataClient::fetchMedia(LLVOVolume *object) +{ + LLSD sd_payload; + sd_payload["verb"] = "GET"; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + request(object, sd_payload); +} + +void LLObjectMediaDataClient::updateMedia(LLVOVolume *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); + } + sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; + + LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_pretty_print_sd(sd_payload) << LL_ENDL; + + request(object, sd_payload); +} + +/*virtual*/ +void LLObjectMediaDataClient::Responder::result(const LLSD& content) +{ + const LLMediaDataClient::Request::Type type = getRequest()->getType(); + llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) + if (type == LLMediaDataClient::Request::GET) + { + LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_pretty_print_sd(content) << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + + // XXX Warn user? + } + else { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << "DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY]); + } + } + else if (type == LLMediaDataClient::Request::UPDATE) + { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaNavigateClient +// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap +// +////////////////////////////////////////////////////////////////////////////////////// +LLMediaDataClient::Responder *LLObjectMediaNavigateClient::createResponder(const request_ptr_t &request) const +{ + return new LLObjectMediaNavigateClient::Responder(request); +} + +const char *LLObjectMediaNavigateClient::getCapabilityName() const +{ + return "ObjectMediaNavigate"; +} + +void LLObjectMediaNavigateClient::navigate(LLVOVolume *object, U8 texture_index, const std::string &url) +{ + LLSD sd_payload; + sd_payload[LLTextureEntry::OBJECT_ID_KEY] = object->getID(); + sd_payload[LLMediaEntry::CURRENT_URL_KEY] = url; + sd_payload[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)texture_index; + request(object, sd_payload); +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason) +{ + // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base + // class + if (status == HTTP_SERVICE_UNAVAILABLE) + { + LLMediaDataClient::Responder::error(status, reason); + } + else { + // bounce the face back + bounceBack(); + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; + } +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) +{ + LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_pretty_print_sd(content) << LL_ENDL; + + if (content.has("error")) + { + const LLSD &error = content["error"]; + int error_code = error["code"]; + + if (ERROR_PERMISSION_DENIED_CODE == error_code) + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + // bounce the face back + bounceBack(); + } + else { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + } + // XXX Warn user? + } + else { + // just do what our superclass does + LLMediaDataClient::Responder::result(content); + } +} + + +void LLObjectMediaNavigateClient::Responder::bounceBack() +{ + const LLSD &payload = getRequest()->getPayload(); + U8 texture_index = (U8)(LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]; + viewer_media_t impl = getRequest()->getObject()->getMediaImpl(texture_index); + // Find the media entry for this navigate + LLMediaEntry* mep = NULL; + LLTextureEntry *te = getRequest()->getObject()->getTE(texture_index); + if(te) + { + mep = te->getMediaData(); + } + + if (mep && impl) + { + impl->navigateTo(mep->getCurrentURL()); + } +} diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h new file mode 100755 index 0000000000..59c4334251 --- /dev/null +++ b/indra/newview/llmediadataclient.h @@ -0,0 +1,263 @@ +/** + * @file llmediadataclient.h + * @brief class for queueing up requests to the media service + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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$ + */ + +#ifndef LL_LLMEDIADATACLIENT_H +#define LL_LLMEDIADATACLIENT_H + +#include "llhttpclient.h" +#include <queue> +#include "llrefcount.h" +#include "lltimer.h" + +// Forward decls +class LLVOVolume; + +typedef LLPointer<LLVOVolume> ll_vo_volume_ptr_t; + +// This object creates a priority queue for requests. +// Abstracts the Cap URL, the request, and the responder +class LLMediaDataClient : public LLRefCount +{ +public: + LOG_CLASS(LLMediaDataClient); + + const static int QUEUE_TIMER_DELAY = 1; // seconds(s) + const static int MAX_RETRIES = 4; + + // Constructor + LLMediaDataClient(); + + // Make the request + void request(LLVOVolume *object, const LLSD &payload); + +protected: + // Destructor + virtual ~LLMediaDataClient(); // use unref + + // Request + class Request : public LLRefCount + { + public: + enum Type { + GET, + UPDATE, + NAVIGATE + }; + + Request(const std::string &cap_name, const LLSD& sd_payload, LLVOVolume *obj, LLMediaDataClient *mdc); + const std::string &getCapName() const { return mCapName; } + const LLSD &getPayload() const { return mPayload; } + LLVOVolume *getObject() const { return mObject; } + + U32 getNum() const { return mNum; } + + U32 getRetryCount() const { return mRetryCount; } + void incRetryCount() { mRetryCount++; } + + // Note: may return empty string! + std::string getCapability() const; + + Type getType() const; + const char *getTypeAsString() const; + + // Re-enqueue thyself + void reEnqueue() const; + + public: + friend std::ostream& operator<<(std::ostream &s, const Request &q); + + protected: + virtual ~Request(); // use unref(); + + private: + std::string mCapName; + LLSD mPayload; + ll_vo_volume_ptr_t mObject; + // Simple tracking + const U32 mNum; + static U32 sNum; + U32 mRetryCount; + + // Back pointer to the MDC...not a ref! + LLMediaDataClient *mMDC; + }; + typedef LLPointer<Request> request_ptr_t; + + // 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 + virtual void error(U32 status, const std::string& reason); + //If we get back a normal response, handle it here. Default just logs it. + virtual void result(const LLSD& content); + + const request_ptr_t &getRequest() const { return mRequest; } + + protected: + virtual ~Responder(); + + private: + + class RetryTimer : public LLEventTimer + { + public: + RetryTimer(F32 time, Responder *); + virtual ~RetryTimer(); + virtual BOOL tick(); + private: + // back-pointer + boost::intrusive_ptr<Responder> mResponder; + }; + + request_ptr_t mRequest; + }; + +protected: + + void enqueue(const Request*); + + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const = 0; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const = 0; + +private: + + // Comparator for PriorityQueue + class Comparator + { + 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); + }; + + class PriorityQueue : public std::priority_queue< + request_ptr_t, + std::vector<request_ptr_t>, + Comparator > + { + public: + friend std::ostream& operator<<(std::ostream &s, const PriorityQueue &q); + }; + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + friend std::ostream& operator<<(std::ostream &s, const PriorityQueue &q); + + class QueueTimer : public LLEventTimer + { + public: + QueueTimer(F32 time, LLMediaDataClient *mdc); + virtual BOOL tick(); + protected: + virtual ~QueueTimer(); + private: + // back-pointer + LLPointer<LLMediaDataClient> mMDC; + }; + + void startQueueTimer(); + void stopQueueTimer(); + void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + bool mQueueTimerIsRunning; + + PriorityQueue *pRequestQueue; +}; + + +// MediaDataResponder specific for the ObjectMedia cap +class LLObjectMediaDataClient : public LLMediaDataClient +{ +public: + LLObjectMediaDataClient() {} + ~LLObjectMediaDataClient() {} + + void fetchMedia(LLVOVolume *object); + void updateMedia(LLVOVolume *object); + +protected: + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void result(const LLSD &content); + }; +}; + + +// MediaDataResponder specific for the ObjectMediaNavigate cap +class LLObjectMediaNavigateClient : public LLMediaDataClient +{ + // NOTE: from llmediaservice.h + static const int ERROR_PERMISSION_DENIED_CODE = 8002; + +public: + LLObjectMediaNavigateClient() {} + ~LLObjectMediaNavigateClient() {} + + void navigate(LLVOVolume *object, U8 texture_index, const std::string &url); + +protected: + // Subclasses must override this factory method to return a new responder + virtual Responder *createResponder(const request_ptr_t &request) const; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void error(U32 status, const std::string& reason); + virtual void result(const LLSD &content); + private: + void bounceBack(); + }; + +}; + + +#endif // LL_LLMEDIADATACLIENT_H diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 05011a1568..1f4f1322fd 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -47,7 +47,6 @@ #include "llframetimer.h" #include "llinventory.h" #include "llmaterialtable.h" -#include "llmediadataresponder.h" #include "llmutelist.h" #include "llnamevalue.h" #include "llprimitive.h" diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e69779b2dc..583246c23e 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -46,7 +46,6 @@ #include "llvolumemessage.h" #include "material_codes.h" #include "message.h" -#include "llmediadataresponder.h" #include "llpluginclassmedia.h" // for code in the mediaEvent handler #include "object_flags.h" #include "llagentconstants.h" @@ -69,7 +68,7 @@ #include "pipeline.h" #include "llsdutil.h" #include "llmediaentry.h" -#include "llmediadatafetcher.h" +#include "llmediadataclient.h" #include "llagent.h" const S32 MIN_QUIET_FRAMES_COALESCE = 30; @@ -86,6 +85,8 @@ F32 LLVOVolume::sLODFactor = 1.f; F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; +LLPointer<LLObjectMediaDataClient> LLVOVolume::sObjectMediaClient = NULL; +LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = NULL; static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); @@ -133,13 +134,15 @@ LLVOVolume::~LLVOVolume() // static void LLVOVolume::initClass() { - LLMediaDataFetcher::initClass(); + sObjectMediaClient = new LLObjectMediaDataClient(); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(); } // static void LLVOVolume::cleanupClass() { - LLMediaDataFetcher::cleanupClass(); + sObjectMediaClient = NULL; + sObjectMediaNavigateClient = NULL; } U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, @@ -1621,7 +1624,7 @@ bool LLVOVolume::hasMedia() const void LLVOVolume::requestMediaDataUpdate() { - LLMediaDataFetcher::fetchMedia(this); + sObjectMediaClient->fetchMedia(this); } void LLVOVolume::cleanUpMediaImpls() @@ -1752,21 +1755,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, llinfos << "broadcasting navigate with URI " << new_location << llendl; - // Post the navigate to the cap - std::string cap = getRegion()->getCapability("ObjectMediaNavigate"); - if(cap.empty()) - { - // XXX *TODO: deal with no cap! It may happen! (retry?) - LL_WARNS("Media") << "Can't broadcast navigate event -- ObjectMediaNavigate cap is not available" << LL_ENDL; - return; - } - - // If we got here, the cap is available. Index through all faces that have this media and send the navigate message. - LLSD sd; - sd["object_id"] = mID; - sd["current_url"] = new_location; - sd["texture_index"] = face_index; - LLHTTPClient::post(cap, sd, new LLMediaDataResponder("ObjectMediaNavigate", sd, this)); + sObjectMediaNavigateClient->navigate(this, face_index, new_location); } } break; @@ -1790,29 +1779,9 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, } -void LLVOVolume::sendMediaDataUpdate() const +void LLVOVolume::sendMediaDataUpdate() { - std::string url = getRegion()->getCapability("ObjectMedia"); - if (!url.empty()) - { - LLSD sd_payload; - sd_payload["verb"] = "UPDATE"; - sd_payload[LLTextureEntry::OBJECT_ID_KEY] = mID; - LLSD object_media_data; - for (int i=0; i < getNumTEs(); i++) { - LLTextureEntry *texture_entry = 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); - } - sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; - - llinfos << "Sending media data: " << getID() << " " << ll_pretty_print_sd(sd_payload) << llendl; - - LLHTTPClient::post(url, sd_payload, new LLMediaDataResponder("ObjectMedia", sd_payload, this)); - } - // XXX *TODO: deal with no cap! It may happen! (retry?) + sObjectMediaClient->updateMedia(this); } void LLVOVolume::removeMediaImpl(S32 texture_index) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 9a79b620d5..250c3ed917 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -45,6 +45,8 @@ class LLViewerTextureAnim; class LLDrawPool; class LLSelectNode; +class LLObjectMediaDataClient; +class LLObjectMediaNavigateClient; typedef std::vector<viewer_media_t> media_list_t; @@ -238,7 +240,7 @@ public: void syncMediaData(S32 te, const LLSD &media_data, bool merge, bool ignore_agent); // Send media data update to the simulator. - void sendMediaDataUpdate() const; + void sendMediaDataUpdate(); viewer_media_t getMediaImpl(U8 face_id) const; S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); @@ -283,6 +285,9 @@ public: static F32 sLODFactor; // LOD scale factor static F32 sDistanceFactor; // LOD distance factor + static LLPointer<LLObjectMediaDataClient> sObjectMediaClient; + static LLPointer<LLObjectMediaNavigateClient> sObjectMediaNavigateClient; + protected: static S32 sNumLODChanges; |