diff --git a/indra/llplugin/llpluginsharedmemory.cpp b/indra/llplugin/llpluginsharedmemory.cpp index 3c69a69d28..9c18b410c7 100644 --- a/indra/llplugin/llpluginsharedmemory.cpp +++ b/indra/llplugin/llpluginsharedmemory.cpp @@ -1,6 +1,6 @@ /** * @file llpluginsharedmemory.cpp - * @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. + * LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. * * @cond * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -96,6 +96,10 @@ std::string LLPluginSharedMemory::createName(void) return newname.str(); } +/** + * @brief LLPluginSharedMemoryImpl is the platform-dependent implementation of LLPluginSharedMemory. TODO:DOC is this necessary/sufficient? kinda obvious. + * + */ class LLPluginSharedMemoryPlatformImpl { public: @@ -112,6 +116,9 @@ public: }; +/** + * Constructor. Creates a shared memory segment. + */ LLPluginSharedMemory::LLPluginSharedMemory() { mSize = 0; @@ -121,6 +128,9 @@ LLPluginSharedMemory::LLPluginSharedMemory() mImpl = new LLPluginSharedMemoryPlatformImpl; } +/** + * Destructor. Uses destroy() and detach() to ensure shared memory segment is cleaned up. + */ LLPluginSharedMemory::~LLPluginSharedMemory() { if(mNeedsDestroy) diff --git a/indra/llplugin/llpluginsharedmemory.h b/indra/llplugin/llpluginsharedmemory.h index 4014620c52..00c54ef08c 100644 --- a/indra/llplugin/llpluginsharedmemory.h +++ b/indra/llplugin/llpluginsharedmemory.h @@ -1,6 +1,5 @@ /** * @file llpluginsharedmemory.h - * @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. * * @cond * $LicenseInfo:firstyear=2008&license=viewergpl$ @@ -36,6 +35,10 @@ class LLPluginSharedMemoryPlatformImpl; +/** + * @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. + * + */ class LLPluginSharedMemory { LOG_CLASS(LLPluginSharedMemory); @@ -46,16 +49,62 @@ public: // Parent will use create/destroy, child will use attach/detach. // Message transactions will ensure child attaches after parent creates and detaches before parent destroys. - // create() implicitly creates a name for the segment which is guaranteed to be unique on the host at the current time. + /** + * Creates a shared memory segment, with a name which is guaranteed to be unique on the host at the current time. Used by parent. + * Message transactions will (? TODO:DOC - should? must?) ensure child attaches after parent creates and detaches before parent destroys. + * + * @param[in] size Shared memory size in TODO:DOC units = bytes?. + * + * @return False for failure, true for success. + */ bool create(size_t size); + /** + * Destroys a shared memory segment. Used by parent. + * Message transactions will (? TODO:DOC - should? must?) ensure child attaches after parent creates and detaches before parent destroys. + * + * @return True. TODO:DOC - always returns true. Is this the intended behavior? + */ bool destroy(void); + /** + * Creates and attaches a name to a shared memory segment. TODO:DOC what's the difference between attach() and create()? + * + * @param[in] name Name to attach to memory segment + * @param[in] size Size of memory segment TODO:DOC in bytes? + * + * @return False on failure, true otherwise. + */ bool attach(const std::string &name, size_t size); + /** + * Detaches shared memory segment. + * + * @return False on failure, true otherwise. + */ bool detach(void); + /** + * Checks if shared memory is mapped to a non-null address. + * + * @return True if memory address is non-null, false otherwise. + */ bool isMapped(void) const { return (mMappedAddress != NULL); }; + /** + * Get pointer to shared memory. + * + * @return Pointer to shared memory. + */ void *getMappedAddress(void) const { return mMappedAddress; }; + /** + * Get size of shared memory. + * + * @return Size of shared memory in bytes. TODO:DOC are bytes the correct unit? + */ size_t getSize(void) const { return mSize; }; + /** + * Get name of shared memory. + * + * @return Name of shared memory. + */ std::string getName() const { return mName; }; private: diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 289e900eaf..eed84671c1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5514,6 +5514,28 @@ F32 Value 5.0 + + PrimMediaMaxSortedQueueSize + + Comment + Maximum number of objects the viewer will load media for initially + Persist + 1 + Type + U32 + Value + 100000 + + PrimMediaMaxRoundRobinQueueSize + + Comment + Maximum number of objects the viewer will continuously update media for + Persist + 1 + Type + U32 + Value + 100000 ProbeHardwareOnStartup diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9a1b749ba7..3250343b25 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -746,7 +746,15 @@ bool LLAppViewer::init() LLViewerJointMesh::updateVectorize(); // load MIME type -> media impl mappings - LLMIMETypes::parseMIMETypes( std::string("mime_types.xml") ); + std::string mime_types_name; +#if LL_DARWIN + mime_types_name = "mime_types_mac.xml"; +#elif LL_LINUX + mime_types_name = "mime_types_linux.xml"; +#else + mime_types_name = "mime_types.xml"; +#endif + LLMIMETypes::parseMIMETypes( mime_types_name ); // Copy settings to globals. *TODO: Remove or move to appropriage class initializers settings_to_globals(); diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 6ba032c152..317399ad8b 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -1080,7 +1080,7 @@ void LLFloaterTools::getMediaState() { LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); LLViewerObject* first_object = selected_objects->getFirstObject(); - LLLineEditor* media_info = getChild("media_info"); + LLTextBox* media_info = getChild("media_info"); if( !(first_object && first_object->getPCode() == LL_PCODE_VOLUME @@ -1088,12 +1088,6 @@ void LLFloaterTools::getMediaState() )) { childSetEnabled("Add_Media", FALSE); -/* childSetEnabled("media_tex", FALSE); - childSetEnabled("add_media", FALSE); - childSetEnabled("delete_media", FALSE); - childSetEnabled("edit_media", FALSE); - childSetEnabled("media_info", FALSE); - media_info->setEnabled(FALSE);*/ media_info->clear(); clearMediaSettings(); return; @@ -1105,13 +1099,6 @@ void LLFloaterTools::getMediaState() if(!has_media_capability) { childSetEnabled("Add_Media", FALSE); - /* childSetEnabled("media_tex", FALSE); - childSetEnabled("add_media", FALSE); - childSetEnabled("delete_media", FALSE); - childSetEnabled("edit_media", FALSE); - childSetEnabled("media_info", FALSE); - media_info->setEnabled(FALSE); - media_info->clear();*/ LL_WARNS("LLFloaterTools: media") << "Media not enabled (no capability) in this region!" << LL_ENDL; clearMediaSettings(); return; @@ -1233,7 +1220,6 @@ void LLFloaterTools::getMediaState() childSetEnabled( "edit_media", bool_has_media & editable ); childSetEnabled( "delete_media", bool_has_media & editable ); childSetEnabled( "add_media", ( ! bool_has_media ) & editable ); - media_info->setEnabled(false); // TODO: display a list of all media on the face - use 'identical' flag } else // not all face has media but at least one does. @@ -1260,8 +1246,6 @@ void LLFloaterTools::getMediaState() } } - media_info->setEnabled(false); - media_info->setTentative(true); childSetEnabled("media_tex", TRUE); childSetEnabled( "edit_media", TRUE); childSetEnabled( "delete_media", TRUE); @@ -1392,7 +1376,7 @@ void LLFloaterTools::updateMediaTitle() if ( ! media_title.empty() ) { // update the UI widget - LLLineEditor* media_title_field = getChild("media_info"); + LLTextBox* media_title_field = getChild("media_info"); if ( media_title_field ) { media_title_field->setText( media_title ); diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index 3c337961e1..0a20a4a1c9 100755 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -61,6 +61,408 @@ 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; +const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000; +const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries, + U32 max_sorted_queue_size, + U32 max_round_robin_queue_size) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mMaxSortedQueueSize(max_sorted_queue_size), + mMaxRoundRobinQueueSize(max_round_robin_queue_size), + mQueueTimerIsRunning(false), + mCurrentQueueIsTheSortedQueue(true) +{ +} + +LLMediaDataClient::~LLMediaDataClient() +{ + stopQueueTimer(); + + // This should clear the queue, and hopefully call all the destructors. + LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << + (isEmpty() ? " " : " ") << LL_ENDL; + + mSortedQueue.clear(); + mRoundRobinQueue.clear(); +} + +bool LLMediaDataClient::isEmpty() const +{ + return mSortedQueue.empty() && mRoundRobinQueue.empty(); +} + +bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + return (LLMediaDataClient::findOrRemove(mSortedQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull() + || (LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, false/*remove*/, LLMediaDataClient::Request::ANY).notNull())); +} + +bool LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + bool removedFromSortedQueue = LLMediaDataClient::findOrRemove(mSortedQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); + bool removedFromRoundRobinQueue = LLMediaDataClient::findOrRemove(mRoundRobinQueue, object, true/*remove*/, LLMediaDataClient::Request::ANY).notNull(); + return removedFromSortedQueue || removedFromRoundRobinQueue; +} + +//static +LLMediaDataClient::request_ptr_t LLMediaDataClient::findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, LLMediaDataClient::Request::Type type) +{ + request_ptr_t result; + request_queue_t::iterator iter = queue.begin(); + request_queue_t::iterator end = queue.end(); + while (iter != end) + { + if (obj->getID() == (*iter)->getObject()->getID() && (type == LLMediaDataClient::Request::ANY || type == (*iter)->getType())) + { + result = *iter; + if (remove) queue.erase(iter); + break; + } + iter++; + } + return result; +} + +void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) +{ + if (object.isNull() || ! object->hasMedia()) return; + + // Push the object on the queue + enqueue(new Request(getCapabilityName(), payload, object, this)); +} + +void LLMediaDataClient::enqueue(const Request *request) +{ + if (request->isNew()) + { + // Add to sorted queue + if (LLMediaDataClient::findOrRemove(mSortedQueue, request->getObject(), true/*remove*/, request->getType()).notNull()) + { + LL_DEBUGS("LLMediaDataClient") << "REMOVING OLD request for " << *request << " ALREADY THERE!" << LL_ENDL; + } + + LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL; + + // Sadly, we have to const-cast because items put into the queue are not const + mSortedQueue.push_back(const_cast(request)); + + LL_DEBUGS("LLMediaDataClient") << "SORTED queue:" << mSortedQueue << LL_ENDL; + } + else { + if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize) + { + LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL; + return; + } + + // ROUND ROBIN: if it is there, and it is a GET request, leave it. If not, put at front! + request_ptr_t existing_request; + if (request->getType() == Request::GET) + { + existing_request = LLMediaDataClient::findOrRemove(mRoundRobinQueue, request->getObject(), false/*remove*/, request->getType()); + } + if (existing_request.isNull()) + { + LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL; + // Push the request on the pending queue + // Sadly, we have to const-cast because items put into the queue are not const + mRoundRobinQueue.push_front(const_cast(request)); + + LL_DEBUGS("LLMediaDataClient") << "RR queue:" << mRoundRobinQueue << LL_ENDL; + } + else + { + LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; + + existing_request->markSent(false); + } + } + // Start the timer if not already running + startQueueTimer(); +} + +void LLMediaDataClient::startQueueTimer() +{ + if (! mQueueTimerIsRunning) + { + LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; + // LLEventTimer automagically takes care of the lifetime of this object + new QueueTimer(mQueueTimerDelay, this); + } + else { + LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL; + } +} + +void LLMediaDataClient::stopQueueTimer() +{ + mQueueTimerIsRunning = false; +} + +bool LLMediaDataClient::processQueueTimer() +{ + sortQueue(); + + if(!isEmpty()) + { + LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue size is: " << mSortedQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, SORTED queue is: " << mSortedQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, RR queue is: " << mRoundRobinQueue << LL_ENDL; + } + + serviceQueue(); + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue size is: " << mSortedQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, SORTED queue is: " << mSortedQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, RR queue is: " << mRoundRobinQueue << LL_ENDL; + + return isEmpty(); +} + +void LLMediaDataClient::sortQueue() +{ + if(!mSortedQueue.empty()) + { + // Score all items first + request_queue_t::iterator iter = mSortedQueue.begin(); + request_queue_t::iterator end = mSortedQueue.end(); + while (iter != end) + { + (*iter)->updateScore(); + iter++; + } + + // Re-sort the list... + // NOTE: should this be a stable_sort? If so we need to change to using a vector. + mSortedQueue.sort(LLMediaDataClient::compareRequests); + + // ...then cull items over the max + U32 size = mSortedQueue.size(); + if (size > mMaxSortedQueueSize) + { + U32 num_to_cull = (size - mMaxSortedQueueSize); + LL_INFOS("LLMediaDataClient") << "sorted queue MAXED OUT! Culling " + << num_to_cull << " items" << LL_ENDL; + while (num_to_cull-- > 0) + { + mSortedQueue.pop_back(); + } + } + } +} + +// static +bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2) +{ + if (o2.isNull()) return true; + if (o1.isNull()) return false; + return ( o1->getScore() > o2->getScore() ); +} + +void LLMediaDataClient::serviceQueue() +{ + request_queue_t *queue_p = getCurrentQueue(); + + // quick retry loop for cases where we shouldn't wait for the next timer tick + while(true) + { + if (queue_p->empty()) + { + LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; + break; + } + + // Peel one off of the items from the queue, and execute request + request_ptr_t request = queue_p->front(); + llassert(!request.isNull()); + const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject(); + llassert(NULL != object); + + // Check for conditions that would make us just pop and rapidly loop through + // the queue. + if(request.isNull() || + request->isMarkedSent() || + NULL == object || + object->isDead() || + !object->hasMedia()) + { + if (request.isNull()) + { + LL_INFOS("LLMediaDataClient") << "Skipping NULL request" << LL_ENDL; + } + else { + LL_INFOS("LLMediaDataClient") << "Skipping : " << *request << " " + << ((request->isMarkedSent()) ? " request is marked sent" : + ((NULL == object) ? " object is NULL " : + ((object->isDead()) ? "object is dead" : + ((!object->hasMedia()) ? "object has no media!" : "BADNESS!")))) << LL_ENDL; + } + queue_p->pop_front(); + continue; // jump back to the start of the quick retry loop + } + + // Next, ask if this is "interesting enough" to fetch. If not, just stop + // and wait for the next timer go-round. Only do this for the sorted + // queue. + if (mCurrentQueueIsTheSortedQueue && !object->isInterestingEnough()) + { + LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; + break; + } + + // Finally, try to send the HTTP message to the cap url + std::string url = request->getCapability(); + bool maybe_retry = false; + 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, createResponder(request)); + } + else { + LL_INFOS("LLMediaDataClient") << "NOT Sending request for " << *request << ": empty cap url!" << LL_ENDL; + maybe_retry = true; + } + + bool exceeded_retries = request->getRetryCount() > mMaxNumRetries; + if (maybe_retry && ! exceeded_retries) // Try N times before giving up + { + // We got an empty cap, but in that case we will retry again next + // timer fire. + request->incRetryCount(); + } + else { + if (exceeded_retries) + { + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " + << mMaxNumRetries << " tries...popping object id " << object->getID() << LL_ENDL; + // XXX Should we bring up a warning dialog?? + } + + queue_p->pop_front(); + + if (! mCurrentQueueIsTheSortedQueue) { + // Round robin + request->markSent(true); + mRoundRobinQueue.push_back(request); + } + } + + // end of quick loop -- any cases where we want to loop will use 'continue' to jump back to the start. + break; + } + + swapCurrentQueue(); +} + +void LLMediaDataClient::swapCurrentQueue() +{ + // Swap + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + // If its empty, swap back + if (getCurrentQueue()->empty()) + { + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + } +} + +LLMediaDataClient::request_queue_t *LLMediaDataClient::getCurrentQueue() +{ + return (mCurrentQueueIsTheSortedQueue) ? &mSortedQueue : &mRoundRobinQueue; +} + +// dump the queue +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) +{ + int i = 0; + LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); + LLMediaDataClient::request_queue_t::const_iterator end = q.end(); + while (iter != end) + { + s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString(); + iter++; + i++; + } + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::QueueTimer +// Queue of LLMediaDataClientObject smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) +: LLEventTimer(time), mMDC(mdc) +{ + mMDC->setIsRunning(true); +} + +LLMediaDataClient::QueueTimer::~QueueTimer() +{ + LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; + mMDC->setIsRunning(false); + mMDC = NULL; +} + +// virtual +BOOL LLMediaDataClient::QueueTimer::tick() +{ + if (mMDC.isNull()) return TRUE; + return mMDC->processQueueTimer(); +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder::RetryTimer +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) +: LLEventTimer(time), mResponder(mdr) +{ +} + +// virtual +LLMediaDataClient::Responder::RetryTimer::~RetryTimer() +{ + LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; + + // XXX This is weird: Instead of doing the work in tick() (which re-schedules + // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd. + // 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(); + + // Release the ref to the responder. + mResponder = NULL; +} + +// virtual +BOOL LLMediaDataClient::Responder::RetryTimer::tick() +{ + // Don't fire again + return TRUE; +} + ////////////////////////////////////////////////////////////////////////////////////// // @@ -69,16 +471,18 @@ const U32 LLMediaDataClient::MAX_RETRIES = 4; ////////////////////////////////////////////////////////////////////////////////////// /*static*/U32 LLMediaDataClient::Request::sNum = 0; -LLMediaDataClient::Request::Request(const std::string &cap_name, +LLMediaDataClient::Request::Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc) - : mCapName(cap_name), - mPayload(sd_payload), - mObject(obj), - mNum(++sNum), - mRetryCount(0), - mMDC(mdc) +: mCapName(cap_name), + mPayload(sd_payload), + mObject(obj), + mNum(++sNum), + mRetryCount(0), + mMDC(mdc), + mMarkedSent(false), + mScore((F64)0.0) { } @@ -89,7 +493,7 @@ LLMediaDataClient::Request::~Request() mObject = NULL; } - + std::string LLMediaDataClient::Request::getCapability() const { return getObject()->getCapabilityUrl(getCapName()); @@ -99,11 +503,11 @@ std::string LLMediaDataClient::Request::getCapability() const // discover it. LLMediaDataClient::Request::Type LLMediaDataClient::Request::getType() const { - if (mCapName == "ObjectMediaNavigate") + if (0 == strcmp(mCapName, "ObjectMediaNavigate")) { return NAVIGATE; } - else if (mCapName == "ObjectMedia") + else if (0 == strcmp(mCapName, "ObjectMedia")) { const std::string &verb = mPayload["verb"]; if (verb == "GET") @@ -133,10 +537,13 @@ const char *LLMediaDataClient::Request::getTypeAsString() const case NAVIGATE: return "NAVIGATE"; break; + case ANY: + return "ANY"; + break; } return ""; } - + void LLMediaDataClient::Request::reEnqueue() const { @@ -147,7 +554,7 @@ void LLMediaDataClient::Request::reEnqueue() const F32 LLMediaDataClient::Request::getRetryTimerDelay() const { return (mMDC == NULL) ? LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY : - mMDC->mRetryTimerDelay; + mMDC->mRetryTimerDelay; } U32 LLMediaDataClient::Request::getMaxNumRetries() const @@ -155,52 +562,38 @@ U32 LLMediaDataClient::Request::getMaxNumRetries() const return (mMDC == NULL) ? LLMediaDataClient::MAX_RETRIES : mMDC->mMaxNumRetries; } +void LLMediaDataClient::Request::markSent(bool flag) +{ + if (mMarkedSent != flag) + { + mMarkedSent = flag; + if (!mMarkedSent) + { + mNum = ++sNum; + } + } +} + +void LLMediaDataClient::Request::updateScore() +{ + F64 tmp = mObject->getMediaInterest(); + if (tmp != mScore) + { + LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL; + mScore = tmp; + } +} + std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) { - s << "" - << "" << r.getNum() << "" - << "" << r.getTypeAsString() << "" - << "" << r.getObject()->getID() << "" - << "" << r.getRetryCount() << "" - << " "; + s << "request: num=" << r.getNum() + << " type=" << r.getTypeAsString() + << " ID=" << r.getObject()->getID() + << " #retries=" << r.getRetryCount(); return s; } - -////////////////////////////////////////////////////////////////////////////////////// -// -// LLMediaDataClient::Responder::RetryTimer -// -////////////////////////////////////////////////////////////////////////////////////// - -LLMediaDataClient::Responder::RetryTimer::RetryTimer(F32 time, Responder *mdr) - : LLEventTimer(time), mResponder(mdr) -{ -} - -// virtual -LLMediaDataClient::Responder::RetryTimer::~RetryTimer() -{ - LL_DEBUGS("LLMediaDataClient") << "~RetryTimer" << *(mResponder->getRequest()) << LL_ENDL; - - // XXX This is weird: Instead of doing the work in tick() (which re-schedules - // a timer, which might be risky), do it here, in the destructor. Yes, it is very odd. - // 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(); - - // Release the ref to the responder. - mResponder = NULL; -} - -// virtual -BOOL LLMediaDataClient::Responder::RetryTimer::tick() -{ - // Don't fire again - return TRUE; -} - - + ////////////////////////////////////////////////////////////////////////////////////// // // LLMediaDataClient::Responder @@ -208,7 +601,7 @@ BOOL LLMediaDataClient::Responder::RetryTimer::tick() ////////////////////////////////////////////////////////////////////////////////////// LLMediaDataClient::Responder::Responder(const request_ptr_t &request) - : mRequest(request) +: mRequest(request) { } @@ -224,20 +617,20 @@ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) if (status == HTTP_SERVICE_UNAVAILABLE) { F32 retry_timeout = mRequest->getRetryTimerDelay(); - + mRequest->incRetryCount(); if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) { - LL_INFOS("LLMediaDataClient") << *mRequest << "got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; - + 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; + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " << + mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; } } else { @@ -246,288 +639,10 @@ void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) } } - /*virtual*/ void LLMediaDataClient::Responder::result(const LLSD& content) { - LL_INFOS("LLMediaDataClient") << *mRequest << "result : " << ll_print_sd(content) << LL_ENDL; -} - - -////////////////////////////////////////////////////////////////////////////////////// -// -// LLMediaDataClient::Comparator -// -////////////////////////////////////////////////////////////////////////////////////// - -// static -bool LLMediaDataClient::compareRequests(const request_ptr_t &o1, const request_ptr_t &o2) -{ - 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 = getObjectScore(o1->getObject()); - F64 o2_score = getObjectScore(o2->getObject()); - return ( o1_score > o2_score ); -} - -// static -F64 LLMediaDataClient::getObjectScore(const LLMediaDataClientObject::ptr_t &obj) -{ - // *TODO: make this less expensive? - 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 = obj->getTotalMediaInterest() + 1.0; - - return interest/dist; -} - -////////////////////////////////////////////////////////////////////////////////////// -// -// LLMediaDataClient::PriorityQueue -// Queue of LLMediaDataClientObject smart pointers to request media for. -// -////////////////////////////////////////////////////////////////////////////////////// - -// dump the queue -std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) -{ - int i = 0; - LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); - LLMediaDataClient::request_queue_t::const_iterator end = q.end(); - while (iter != end) - { - s << "\t" << i << "]: " << (*iter)->getObject()->getID().asString(); - iter++; - i++; - } - return s; -} - -// find the given object in the queue. -bool LLMediaDataClient::find(const LLMediaDataClientObject::ptr_t &obj) const -{ - request_queue_t::const_iterator iter = pRequestQueue->begin(); - request_queue_t::const_iterator end = pRequestQueue->end(); - while (iter != end) - { - if (obj->getID() == (*iter)->getObject()->getID()) - { - return true; - } - iter++; - } - return false; -} - -////////////////////////////////////////////////////////////////////////////////////// -// -// LLMediaDataClient::QueueTimer -// Queue of LLMediaDataClientObject smart pointers to request media for. -// -////////////////////////////////////////////////////////////////////////////////////// - -LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) - : LLEventTimer(time), mMDC(mdc) -{ - mMDC->setIsRunning(true); -} - -LLMediaDataClient::QueueTimer::~QueueTimer() -{ - LL_DEBUGS("LLMediaDataClient") << "~QueueTimer" << LL_ENDL; - 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; - } - - request_queue_t &queue = *(mMDC->pRequestQueue); - - if(!queue.empty()) - { - LL_INFOS("LLMediaDataClient") << "QueueTimer::tick() started, queue is: " << queue << LL_ENDL; - - // Re-sort the list every time... - // XXX Is this really what we want? - queue.sort(LLMediaDataClient::compareRequests); - } - - // quick retry loop for cases where we shouldn't wait for the next timer tick - while(true) - { - if (queue.empty()) - { - LL_DEBUGS("LLMediaDataClient") << "queue empty: " << queue << LL_ENDL; - return TRUE; - } - - // Peel one off of the items from the queue, and execute request - request_ptr_t request = queue.front(); - llassert(!request.isNull()); - const LLMediaDataClientObject *object = (request.isNull()) ? NULL : request->getObject(); - bool performed_request = false; - bool error = false; - llassert(NULL != object); - - if(object->isDead()) - { - // This object has been marked dead. Pop it and move on to the next item in the queue immediately. - LL_INFOS("LLMediaDataClient") << "Skipping " << *request << ": object is dead!" << LL_ENDL; - queue.pop_front(); - continue; // jump back to the start of the quick retry loop - } - - if (NULL != object && 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 (request.isNull()) - { - LL_WARNS("LLMediaDataClient") << "Not Sending request: NULL request!" << LL_ENDL; - } - else if (NULL == object) - { - LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " NULL object!" << LL_ENDL; - } - else if (!object->hasMedia()) - { - LL_WARNS("LLMediaDataClient") << "Not Sending request for " << *request << " hasMedia() is false!" << LL_ENDL; - } - error = true; - } - bool exceeded_retries = request->getRetryCount() > mMDC->mMaxNumRetries; - if (performed_request || exceeded_retries || error) // Try N times before giving up - { - if (exceeded_retries) - { - 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_front(); - } - else { - request->incRetryCount(); - } - - // end of quick retry loop -- any cases where we want to loop will use 'continue' to jump back to the start. - break; - } - - LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue is now: " << (*(mMDC->pRequestQueue)) << LL_ENDL; - - return queue.empty(); -} - -void LLMediaDataClient::startQueueTimer() -{ - if (! mQueueTimerIsRunning) - { - LL_INFOS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; - // LLEventTimer automagically takes care of the lifetime of this object - new QueueTimer(mQueueTimerDelay, this); - } - else { - LL_DEBUGS("LLMediaDataClient") << "not starting queue timer (it's already running, right???)" << LL_ENDL; - } -} - -void LLMediaDataClient::stopQueueTimer() -{ - mQueueTimerIsRunning = false; -} - -void LLMediaDataClient::request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload) -{ - if (object.isNull() || ! 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_back(const_cast(request)); - LL_DEBUGS("LLMediaDataClient") << "Queue:" << (*pRequestQueue) << LL_ENDL; - // Start the timer if not already running - startQueueTimer(); -} - -////////////////////////////////////////////////////////////////////////////////////// -// -// 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 request_queue_t(); -} - -LLMediaDataClient::~LLMediaDataClient() -{ - stopQueueTimer(); - - // This should clear the queue, and hopefully call all the destructors. - LL_DEBUGS("LLMediaDataClient") << "~LLMediaDataClient destructor: queue: " << - (pRequestQueue->empty() ? " " : " ") << (*pRequestQueue) << LL_ENDL; - delete pRequestQueue; - pRequestQueue = NULL; -} - -bool LLMediaDataClient::isEmpty() const -{ - return (NULL == pRequestQueue) ? true : pRequestQueue->empty(); -} - -bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) const -{ - return (NULL == pRequestQueue) ? false : find(object); + LL_DEBUGS("LLMediaDataClient") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL; } ////////////////////////////////////////////////////////////////////////////////////// @@ -569,7 +684,7 @@ void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) } sd_payload[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; - LL_INFOS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "update media data: " << object->getID() << " " << ll_print_sd(sd_payload) << LL_ENDL; request(object, sd_payload); } @@ -581,7 +696,7 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content) llassert(type == LLMediaDataClient::Request::GET || type == LLMediaDataClient::Request::UPDATE) if (type == LLMediaDataClient::Request::GET) { - LL_INFOS("LLMediaDataClient") << *(getRequest()) << "GET returned: " << ll_print_sd(content) << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL; // Look for an error if (content.has("error")) @@ -598,12 +713,13 @@ void LLObjectMediaDataClient::Responder::result(const LLSD& content) 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; + 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]); + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + content[LLTextureEntry::MEDIA_VERSION_KEY]); } } else if (type == LLMediaDataClient::Request::UPDATE) diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h index 812e9cbdec..75d32e707b 100755 --- a/indra/newview/llmediadataclient.h +++ b/indra/newview/llmediadataclient.h @@ -55,15 +55,19 @@ public: // 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; + virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0; // Return the total "interest" of the media (on-screen area) - virtual F64 getTotalMediaInterest() const = 0; + virtual F64 getMediaInterest() const = 0; // Return the given cap url virtual std::string getCapabilityUrl(const std::string &name) const = 0; // Return whether the object has been marked dead virtual bool isDead() const = 0; + // Returns a media version number for the object + virtual U32 getMediaVersion() const = 0; + // Returns whether the object is "interesting enough" to fetch + virtual bool isInterestingEnough() const = 0; + // Returns whether we've seen this object yet or not + virtual bool isNew() const = 0; // smart pointer typedef LLPointer ptr_t; @@ -79,11 +83,15 @@ public: 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; + const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000; + const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000; // Constructor LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, - U32 max_retries = MAX_RETRIES); + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE); // Make the request void request(const LLMediaDataClientObject::ptr_t &object, const LLSD &payload); @@ -94,7 +102,13 @@ public: bool isEmpty() const; // Returns true iff the given object is in the queue - bool isInQueue(const LLMediaDataClientObject::ptr_t &object) const; + bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + bool removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + // Called only by the Queue timer and tests (potentially) + bool processQueueTimer(); protected: // Destructor @@ -107,11 +121,12 @@ protected: enum Type { GET, UPDATE, - NAVIGATE + NAVIGATE, + ANY }; - Request(const std::string &cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc); - const std::string &getCapName() const { return mCapName; } + Request(const char *cap_name, const LLSD& sd_payload, LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + const char *getCapName() const { return mCapName; } const LLSD &getPayload() const { return mPayload; } LLMediaDataClientObject *getObject() const { return mObject; } @@ -132,6 +147,12 @@ protected: F32 getRetryTimerDelay() const; U32 getMaxNumRetries() const; + bool isNew() const { return mObject.notNull() ? mObject->isNew() : false; } + void markSent(bool flag); + bool isMarkedSent() const { return mMarkedSent; } + void updateScore(); + F64 getScore() const { return mScore; } + public: friend std::ostream& operator<<(std::ostream &s, const Request &q); @@ -139,14 +160,16 @@ protected: virtual ~Request(); // use unref(); private: - std::string mCapName; + const char *mCapName; LLSD mPayload; LLMediaDataClientObject::ptr_t mObject; // Simple tracking - const U32 mNum; + U32 mNum; static U32 sNum; U32 mRetryCount; - + F64 mScore; + bool mMarkedSent; + // Back pointer to the MDC...not a ref! LLMediaDataClient *mMDC; }; @@ -184,18 +207,23 @@ protected: }; 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: + virtual void sortQueue(); + virtual void serviceQueue(); +private: typedef std::list request_queue_t; + + void enqueue(const Request*); + + // Return whether the given object is/was in the queue + static LLMediaDataClient::request_ptr_t findOrRemove(request_queue_t &queue, const LLMediaDataClientObject::ptr_t &obj, bool remove, Request::Type type); // Comparator for sorting static bool compareRequests(const request_ptr_t &o1, const request_ptr_t &o2); @@ -215,34 +243,40 @@ private: // back-pointer LLPointer mMDC; }; - - // Return whether the given object is in the queue - bool find(const LLMediaDataClientObject::ptr_t &obj) const; void startQueueTimer(); void stopQueueTimer(); void setIsRunning(bool val) { mQueueTimerIsRunning = val; } - + + void swapCurrentQueue(); + request_queue_t *getCurrentQueue(); + const F32 mQueueTimerDelay; const F32 mRetryTimerDelay; const U32 mMaxNumRetries; + const U32 mMaxSortedQueueSize; + const U32 mMaxRoundRobinQueueSize; bool mQueueTimerIsRunning; - request_queue_t *pRequestQueue; + request_queue_t mSortedQueue; + request_queue_t mRoundRobinQueue; + bool mCurrentQueueIsTheSortedQueue; }; -// MediaDataResponder specific for the ObjectMedia cap +// MediaDataClient specific for the ObjectMedia cap class LLObjectMediaDataClient : public LLMediaDataClient { public: LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, - U32 max_retries = MAX_RETRIES) + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) {} - ~LLObjectMediaDataClient() {} + virtual ~LLObjectMediaDataClient() {} void fetchMedia(LLMediaDataClientObject *object); void updateMedia(LLMediaDataClientObject *object); @@ -264,7 +298,7 @@ protected: }; -// MediaDataResponder specific for the ObjectMediaNavigate cap +// MediaDataClient specific for the ObjectMediaNavigate cap class LLObjectMediaNavigateClient : public LLMediaDataClient { public: @@ -273,10 +307,12 @@ public: LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, - U32 max_retries = MAX_RETRIES) + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) {} - ~LLObjectMediaNavigateClient() {} + virtual ~LLObjectMediaNavigateClient() {} void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 3fa0e7bf8f..88eba14553 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -94,7 +94,7 @@ BOOL LLPanelMediaSettingsGeneral::postBuild() mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY ); mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY ); mControls = getChild< LLComboBox >( LLMediaEntry::CONTROLS_KEY ); - mCurrentURL = getChild< LLLineEditor >( LLMediaEntry::CURRENT_URL_KEY ); + mCurrentURL = getChild< LLTextBox >( LLMediaEntry::CURRENT_URL_KEY ); mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY ); mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY ); @@ -284,7 +284,7 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& media_ { LLMediaEntry::AUTO_SCALE_KEY, self->mAutoScale, "LLCheckBoxCtrl" }, { LLMediaEntry::AUTO_ZOOM_KEY, self->mAutoZoom, "LLCheckBoxCtrl" }, { LLMediaEntry::CONTROLS_KEY, self->mControls, "LLComboBox" }, - { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLLineEditor" }, + { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLTextBox" }, { LLMediaEntry::HEIGHT_PIXELS_KEY, self->mHeightPixels, "LLSpinCtrl" }, { LLMediaEntry::HOME_URL_KEY, self->mHomeURL, "LLLineEditor" }, { LLMediaEntry::FIRST_CLICK_INTERACT_KEY, self->mFirstClick, "LLCheckBoxCtrl" }, @@ -518,4 +518,9 @@ void LLPanelMediaSettingsGeneral::updateCurrentUrl() bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str ); mCurrentURL->setText(value_str); mCurrentURL->setTentative(identical); + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + mCurrentURL->setText(LLTrans::getString("Multiple Media")); + } } diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h index 7782b25d63..c6895b1dc9 100644 --- a/indra/newview/llpanelmediasettingsgeneral.h +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -99,7 +99,7 @@ private: LLSpinCtrl* mWidthPixels; LLSpinCtrl* mHeightPixels; LLLineEditor* mHomeURL; - LLLineEditor* mCurrentURL; + LLTextBox* mCurrentURL; LLMediaCtrl* mPreviewMedia; LLTextBox* mFailWhiteListText; }; diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp index d87c7608d5..aea6b0aa3b 100644 --- a/indra/newview/llpanelmediasettingssecurity.cpp +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -167,6 +167,9 @@ void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); }; }; + + // initial update - hides/shows status messages etc. + self->updateWhitelistEnableStatus(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 1f24a68253..57e4ed0c1e 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -630,7 +630,22 @@ bool LLViewerMedia::priorityComparitor(const LLViewerMediaImpl* i1, const LLView static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2) { - return (i1->getProximityDistance() < i2->getProximityDistance()); + if(i1->getProximityDistance() < i2->getProximityDistance()) + { + return true; + } + else if(i1->getProximityDistance() > i2->getProximityDistance()) + { + return false; + } + else + { + // Both objects have the same distance. This most likely means they're two faces of the same object. + // They may also be faces on different objects with exactly the same distance (like HUD objects). + // We don't actually care what the sort order is for this case, as long as it's stable and doesn't change when you enable/disable media. + // Comparing the impl pointers gives a completely arbitrary ordering, but it will be stable. + return (i1 < i2); + } } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index cf61994fea..b92d024ac9 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -92,7 +92,7 @@ static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); class LLMediaDataClientObjectImpl : public LLMediaDataClientObject { public: - LLMediaDataClientObjectImpl(LLVOVolume *obj) : mObject(obj) {} + LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) {} LLMediaDataClientObjectImpl() { mObject = NULL; } virtual U8 getMediaDataCount() const @@ -128,14 +128,19 @@ public: 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 void updateObjectMediaData(LLSD const &data, const std::string &version_string) + { mObject->updateObjectMediaData(data, version_string); } - virtual F64 getTotalMediaInterest() const - { return mObject->getTotalMediaInterest(); } + virtual F64 getMediaInterest() const + { + F64 tmp = mObject->getTotalMediaInterest(); + return (tmp < 0.0) ? mObject->getPixelArea() : tmp; + } + virtual bool isInterestingEnough() const + { + // TODO: use performance manager to control this + return true; + } virtual std::string getCapabilityUrl(const std::string &name) const { return mObject->getRegion()->getCapability(name); } @@ -143,8 +148,15 @@ public: virtual bool isDead() const { return mObject->isDead(); } + virtual U32 getMediaVersion() const + { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); } + + virtual bool isNew() const + { return mNew; } + private: LLPointer mObject; + bool mNew; }; @@ -165,6 +177,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mSpotLightPriority = 0.f; mMediaImplList.resize(getNumTEs()); + mLastFetchedMediaVersion = -1; } LLVOVolume::~LLVOVolume() @@ -190,7 +203,9 @@ void LLVOVolume::markDead() { if (!mDead) { - // TODO: tell LLMediaDataClient to remove this object from its queue + LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast(this), false); + sObjectMediaClient->removeFromQueue(obj); + sObjectMediaNavigateClient->removeFromQueue(obj); // Detach all media impls from this object for(U32 i = 0 ; i < mMediaImplList.size() ; i++) @@ -210,8 +225,12 @@ void LLVOVolume::initClass() 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); + const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize"); + const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize"); + sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries, + max_sorted_queue_size, max_round_robin_queue_size); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, + max_retries, max_sorted_queue_size, max_round_robin_queue_size); } // static @@ -406,7 +425,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, // If the media changed at all, request new media data LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " << ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; - requestMediaDataUpdate(); + requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED); } else { LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " << @@ -1698,16 +1717,16 @@ LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id) return result; } -void LLVOVolume::requestMediaDataUpdate() +void LLVOVolume::requestMediaDataUpdate(bool isNew) { - sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this)); + sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew)); } bool LLVOVolume::isMediaDataBeingFetched() const { // I know what I'm doing by const_casting this away: this is just // a wrapper class that is only going to do a lookup. - return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast(this))); + return sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast(this), false)); } void LLVOVolume::cleanUpMediaImpls() @@ -1725,18 +1744,25 @@ void LLVOVolume::cleanUpMediaImpls() } } -void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array) +void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version) { // media_data_array is an array of media entry maps + // media_version is the version string in the response. + U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version); - //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl; - - LLSD::array_const_iterator iter = media_data_array.beginArray(); - LLSD::array_const_iterator end = media_data_array.endArray(); - U8 texture_index = 0; - for (; iter != end; ++iter, ++texture_index) + // Only update it if it is newer! + if ( (S32)fetched_version > mLastFetchedMediaVersion) { - syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/); + mLastFetchedMediaVersion = fetched_version; + //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl; + + LLSD::array_const_iterator iter = media_data_array.beginArray(); + LLSD::array_const_iterator end = media_data_array.endArray(); + U8 texture_index = 0; + for (; iter != end; ++iter, ++texture_index) + { + syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/); + } } } @@ -1904,7 +1930,7 @@ void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plu llinfos << "broadcasting navigate with URI " << new_location << llendl; - sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this), face_index, new_location); + sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location); } } @@ -1968,7 +1994,7 @@ void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, void LLVOVolume::sendMediaDataUpdate() { - sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this)); + sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false)); } void LLVOVolume::removeMediaImpl(S32 texture_index) @@ -2063,7 +2089,7 @@ viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const F64 LLVOVolume::getTotalMediaInterest() const { - F64 interest = (F64)0.0; + F64 interest = (F64)-1.0; // means not interested; int i = 0; const int end = getNumTEs(); for ( ; i < end; ++i) @@ -2071,6 +2097,7 @@ F64 LLVOVolume::getTotalMediaInterest() const const viewer_media_t &impl = getMediaImpl(i); if (!impl.isNull()) { + if (interest == (F64)-1.0) interest = (F64)0.0; interest += impl->getInterest(); } } diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 06e214b41e..0655c13d5a 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -238,7 +238,7 @@ public: // Update this object's media data with the given media data array // (typically this is only called upon a response from a server request) - void updateObjectMediaData(const LLSD &media_data_array); + void updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version); // Bounce back media at the given index to its current URL (or home URL, if current URL is empty) void mediaNavigateBounceBack(U8 texture_index); @@ -270,13 +270,16 @@ public: // Returns 'true' iff the media data for this object is in flight bool isMediaDataBeingFetched() const; + // Returns the "last fetched" media version, or -1 if not fetched yet + S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; } + protected: S32 computeLODDetail(F32 distance, F32 radius); BOOL calcLOD(); LLFace* addFace(S32 face_index); void updateTEData(); - void requestMediaDataUpdate(); + void requestMediaDataUpdate(bool isNew); void cleanUpMediaImpls(); void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ; void removeMediaImpl(S32 texture_index) ; @@ -300,6 +303,7 @@ private: LLPointer mSculptTexture; LLPointer mLightTexture; media_list_t mMediaImplList; + S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1 // statics public: diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index e3851de8e7..5e68850e30 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2669,16 +2669,15 @@ even though the user gets a free copy. top_pad="5" name="media_tex" width="260"> - Media URL + Media -