From 9d37fb4eeb15f7ac35ebaa3fd7c5cc11e5b1a1dc Mon Sep 17 00:00:00 2001 From: Armin Weatherwax Date: Tue, 7 Sep 2010 10:26:01 +0200 Subject: First LL update of plugins, mainly documentation --- .../indra/media_plugins/base/media_plugin_base.cpp | 57 ++++++- .../indra/media_plugins/base/media_plugin_base.h | 45 ++++-- .../llmediaimplgstreamertriviallogging.h | 6 + .../gstreamer010/llmediaimplgstreamervidplug.cpp | 35 +++-- .../gstreamer010/llmediaimplgstreamervidplug.h | 2 +- .../gstreamer010/media_plugin_gstreamer010.cpp | 78 ++++++---- .../indra/media_plugins/quicktime/CMakeLists.txt | 8 + .../quicktime/media_plugin_quicktime.cpp | 170 ++++++++++++++++++--- .../media_plugins/webkit/media_plugin_webkit.cpp | 53 +++++-- linden/indra/newview/llviewerparcelmedia.cpp | 7 +- 10 files changed, 358 insertions(+), 103 deletions(-) (limited to 'linden/indra') diff --git a/linden/indra/media_plugins/base/media_plugin_base.cpp b/linden/indra/media_plugins/base/media_plugin_base.cpp index 1919419..4d5e374 100644 --- a/linden/indra/media_plugins/base/media_plugin_base.cpp +++ b/linden/indra/media_plugins/base/media_plugin_base.cpp @@ -2,6 +2,8 @@ * @file media_plugin_base.cpp * @brief Media plugin base class for LLMedia API plugin system * + * All plugins should be a subclass of MediaPluginBase. + * * $LicenseInfo:firstyear=2008&license=viewergpl$ * * Copyright (c) 2008-2009, Linden Research, Inc. @@ -36,7 +38,10 @@ // TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint //////////////////////////////////////////////////////////////////////////////// -// +/// Media plugin constructor. +/// +/// @param[in] host_send_func Function for sending messages from plugin to plugin loader shell +/// @param[in] host_user_data Message data for messages from plugin to plugin loader shell MediaPluginBase::MediaPluginBase( LLPluginInstance::sendMessageFunction host_send_func, @@ -54,6 +59,12 @@ MediaPluginBase::MediaPluginBase( mStatus = STATUS_NONE; } +/** + * Converts current media status enum value into string (STATUS_LOADING into "loading", etc.) + * + * @return Media status string ("loading", "playing", "paused", etc) + * + */ std::string MediaPluginBase::statusString() { std::string result; @@ -65,6 +76,7 @@ std::string MediaPluginBase::statusString() case STATUS_ERROR: result = "error"; break; case STATUS_PLAYING: result = "playing"; break; case STATUS_PAUSED: result = "paused"; break; + case STATUS_DONE: result = "done"; break; default: // keep the empty string break; @@ -73,6 +85,12 @@ std::string MediaPluginBase::statusString() return result; } +/** + * Set media status. + * + * @param[in] status Media status (STATUS_LOADING, STATUS_PLAYING, STATUS_PAUSED, etc) + * + */ void MediaPluginBase::setStatus(EStatus status) { if(mStatus != status) @@ -83,6 +101,13 @@ void MediaPluginBase::setStatus(EStatus status) } +/** + * Receive message from plugin loader shell. + * + * @param[in] message_string Message string + * @param[in] user_data Message data + * + */ void MediaPluginBase::staticReceiveMessage(const char *message_string, void **user_data) { MediaPluginBase *self = (MediaPluginBase*)*user_data; @@ -100,12 +125,27 @@ void MediaPluginBase::staticReceiveMessage(const char *message_string, void **us } } +/** + * Send message to plugin loader shell. + * + * @param[in] message Message data being sent to plugin loader shell + * + */ void MediaPluginBase::sendMessage(const LLPluginMessage &message) { std::string output = message.generate(); mHostSendFunction(output.c_str(), &mHostUserData); } +/** + * Notifies plugin loader shell that part of display area needs to be redrawn. + * + * @param[in] left Left X coordinate of area to redraw (0,0 is at top left corner) + * @param[in] top Top Y coordinate of area to redraw (0,0 is at top left corner) + * @param[in] right Right X-coordinate of area to redraw (0,0 is at top left corner) + * @param[in] bottom Bottom Y-coordinate of area to redraw (0,0 is at top left corner) + * + */ void MediaPluginBase::setDirty(int left, int top, int right, int bottom) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); @@ -118,6 +158,10 @@ void MediaPluginBase::setDirty(int left, int top, int right, int bottom) sendMessage(message); } +/** + * Sends "media_status" message to plugin loader shell ("loading", "playing", "paused", etc.) + * + */ void MediaPluginBase::sendStatus() { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "media_status"); @@ -141,6 +185,17 @@ extern "C" LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data); } +/** + * Plugin initialization and entry point. Establishes communication channel for messages between plugin and plugin loader shell. TODO:DOC - Please check! + * + * @param[in] host_send_func Function for sending messages from plugin to plugin loader shell + * @param[in] host_user_data Message data for messages from plugin to plugin loader shell + * @param[out] plugin_send_func Function for plugin to receive messages from plugin loader shell + * @param[out] plugin_user_data Pointer to plugin instance + * + * @return int, where 0=success + * + */ LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) { diff --git a/linden/indra/media_plugins/base/media_plugin_base.h b/linden/indra/media_plugins/base/media_plugin_base.h index 4872706..24198af 100644 --- a/linden/indra/media_plugins/base/media_plugin_base.h +++ b/linden/indra/media_plugins/base/media_plugin_base.h @@ -41,14 +41,17 @@ class MediaPluginBase { public: MediaPluginBase(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + /** Media plugin destructor. */ virtual ~MediaPluginBase() {} + /** Handle received message from plugin loader shell. */ virtual void receiveMessage(const char *message_string) = 0; static void staticReceiveMessage(const char *message_string, void **user_data); protected: + /** Plugin status. */ typedef enum { STATUS_NONE, @@ -57,12 +60,16 @@ protected: STATUS_ERROR, STATUS_PLAYING, STATUS_PAUSED, + STATUS_DONE } EStatus; + /** Plugin shared memory. */ class SharedSegmentInfo { public: + /** Shared memory address. */ void *mAddress; + /** Shared memory size. */ size_t mSize; }; @@ -71,42 +78,56 @@ protected: std::string statusString(); void setStatus(EStatus status); - // The quicktime plugin overrides this to add current time and duration to the message... + /// Note: The quicktime plugin overrides this to add current time and duration to the message. virtual void setDirty(int left, int top, int right, int bottom); + /** Map of shared memory names to shared memory. */ typedef std::map SharedSegmentMap; + /** Function to send message from plugin to plugin loader shell. */ LLPluginInstance::sendMessageFunction mHostSendFunction; + /** Message data being sent to plugin loader shell by mHostSendFunction. */ void *mHostUserData; + /** Flag to delete plugin instance (self). */ bool mDeleteMe; + /** Pixel array to display. TODO:DOC are pixels always 24-bit RGB format, aligned on 32-bit boundary? Also: calling this a pixel array may be misleading since 1 pixel > 1 char. */ unsigned char* mPixels; + /** TODO:DOC what's this for -- does a texture have its own piece of shared memory? updated on size_change_request, cleared on shm_remove */ std::string mTextureSegmentName; + /** Width of plugin display in pixels. */ int mWidth; + /** Height of plugin display in pixels. */ int mHeight; + /** Width of plugin texture. */ int mTextureWidth; + /** Height of plugin texture. */ int mTextureHeight; + /** Pixel depth (pixel size in bytes). */ int mDepth; + /** Current status of plugin. */ EStatus mStatus; + /** Map of shared memory segments. */ SharedSegmentMap mSharedSegments; }; -// The plugin must define this function to create its instance. +/** The plugin must define this function to create its instance. + * It should look something like this: + * @code + * { + * MediaPluginFoo *self = new MediaPluginFoo(host_send_func, host_user_data); + * *plugin_send_func = MediaPluginFoo::staticReceiveMessage; + * *plugin_user_data = (void*)self; + * + * return 0; + * } + * @endcode + */ int init_media_plugin( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data); -// It should look something like this: -/* -{ - MediaPluginFoo *self = new MediaPluginFoo(host_send_func, host_user_data); - *plugin_send_func = MediaPluginFoo::staticReceiveMessage; - *plugin_user_data = (void*)self; - - return 0; -} -*/ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h index e31d4a3..04976b9 100644 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h @@ -35,10 +35,16 @@ #include +extern "C" { +#include +#include +} + ///////////////////////////////////////////////////////////////////////// // Debug/Info/Warning macros. #define MSGMODULEFOO "(media plugin)" #define STDERRMSG(...) do{\ + fprintf(stderr, " pid:%d: ", (int)getpid());\ fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ fprintf(stderr, __VA_ARGS__);\ fputc('\n',stderr);\ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp index 25e96d4..ef8ff58 100644 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp @@ -52,7 +52,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( - (gchar*)"sink", + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (SLV_ALLCAPS) @@ -106,11 +106,10 @@ gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) slvideo = GST_SLVIDEO(bsink); -#if 0 - fprintf(stderr, "\n\ntransferring a frame of %dx%d <- %p (%d)\n\n", - slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), - slvideo->format); -#endif + DEBUGMSG("transferring a frame of %dx%d <- %p (%d)", + slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), + slvideo->format); + if (GST_BUFFER_DATA(buf)) { // copy frame and frame info into neutral territory @@ -335,7 +334,7 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, #define MAXDEPTHHACK 4 GST_OBJECT_LOCK(slvideo); - if (slvideo->resize_forced) + if (slvideo->resize_forced_always) // app is giving us a fixed size to work with { gint slwantwidth, slwantheight; slwantwidth = slvideo->resize_try_width; @@ -384,6 +383,8 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, } } + GST_OBJECT_UNLOCK(slvideo); + if (!made_bufferdata_ptr) // need to fallback to malloc at original size { GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; @@ -392,8 +393,6 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); } - GST_OBJECT_UNLOCK(slvideo); - *buf = GST_BUFFER_CAST(newbuf); return GST_FLOW_OK; @@ -457,7 +456,7 @@ gst_slvideo_init (GstSLVideo * filter, filter->retained_frame_format = SLV_PF_UNKNOWN; GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS); llgst_caps_replace (&filter->caps, caps); - filter->resize_forced = false; + filter->resize_forced_always = false; filter->resize_try_width = -1; filter->resize_try_height = -1; GST_OBJECT_UNLOCK(filter); @@ -498,12 +497,12 @@ gst_slvideo_get_property (GObject * object, guint prop_id, static gboolean plugin_init (GstPlugin * plugin) { - DEBUGMSG("\n\n\nPLUGIN INIT\n\n\n"); + DEBUGMSG("PLUGIN INIT"); GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", 0, (gchar*)"Second Life Video Sink"); - return llgst_element_register (plugin, (gchar*)"private-slvideo", + return llgst_element_register (plugin, "private-slvideo", GST_RANK_NONE, GST_TYPE_SLVIDEO); } @@ -519,14 +518,14 @@ void gst_slvideo_init_class (void) // this macro quietly refers to PACKAGE internally static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - (gchar*)"private-slvideoplugin", - (gchar*)"SL Video sink plugin", - plugin_init, (gchar*)"0.1", (gchar*)GST_LICENSE_UNKNOWN, - (gchar*)"Second Life", - (gchar*)"http://www.secondlife.com/"); + "private-slvideoplugin", + "SL Video sink plugin", + plugin_init, "0.1", GST_LICENSE_UNKNOWN, + "Second Life", + "http://www.secondlife.com/"); #undef PACKAGE ll_gst_plugin_register_static (&gst_plugin_desc); - DEBUGMSG(stderr, "\n\n\nCLASS INIT\n\n\n"); + DEBUGMSG("CLASS INIT"); } #endif // LL_GSTREAMER010_ENABLED diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h index f6d55b8..8cdc72d 100644 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h @@ -88,7 +88,7 @@ struct _GstSLVideo int retained_frame_width, retained_frame_height; SLVPixelFormat retained_frame_format; // sticky resize info - bool resize_forced; + bool resize_forced_always; int resize_try_width; int resize_try_height; }; diff --git a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp index 77b7c13..5b3152d 100644 --- a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp +++ b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp @@ -104,7 +104,7 @@ private: void mouseUp( int x, int y ); void mouseMove( int x, int y ); - bool sizeChanged(); + void sizeChanged(); static bool mDoneInit; @@ -114,13 +114,16 @@ private: int mDepth; - // media natural size + // media NATURAL size int mNaturalWidth; int mNaturalHeight; - int mNaturalRowbytes; - // previous media natural size so we can detect changes - int mPreviousNaturalWidth; - int mPreviousNaturalHeight; + // media current size + int mCurrentWidth; + int mCurrentHeight; + int mCurrentRowbytes; + // previous media size so we can detect changes + int mPreviousWidth; + int mPreviousHeight; // desired render size from host int mWidth; int mHeight; @@ -148,7 +151,7 @@ MediaPluginGStreamer010::MediaPluginGStreamer010( void *host_user_data ) : MediaPluginBase(host_send_func, host_user_data), mBusWatchID ( 0 ), - mNaturalRowbytes ( 4 ), + mCurrentRowbytes ( 4 ), mTextureFormatPrimary ( GL_RGBA ), mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ), mSeekWanted(false), @@ -194,6 +197,7 @@ MediaPluginGStreamer010::processGSTEvents(GstBus *bus, } else { + // TODO: grok 'duration' message type DEBUGMSG("Got GST message type: %s", LLGST_MESSAGE_TYPE_NAME (message)); } @@ -428,8 +432,8 @@ MediaPluginGStreamer010::update(int milliseconds) { DEBUGMSG("NEW FRAME READY"); - if (mVideoSink->retained_frame_width != mNaturalWidth || - mVideoSink->retained_frame_height != mNaturalHeight) + if (mVideoSink->retained_frame_width != mCurrentWidth || + mVideoSink->retained_frame_height != mCurrentHeight) // *TODO: also check for change in format { // just resize container, don't consume frame @@ -456,39 +460,38 @@ MediaPluginGStreamer010::update(int milliseconds) GST_OBJECT_UNLOCK(mVideoSink); - mNaturalRowbytes = neww * newd; + mCurrentRowbytes = neww * newd; DEBUGMSG("video container resized to %dx%d", neww, newh); mDepth = newd; - mNaturalWidth = neww; - mNaturalHeight = newh; + mCurrentWidth = neww; + mCurrentHeight = newh; sizeChanged(); return true; } if (mPixels && - mNaturalHeight <= mHeight && - mNaturalWidth <= mWidth && + mCurrentHeight <= mHeight && + mCurrentWidth <= mWidth && !mTextureSegmentName.empty()) { - // we're gonna totally consume this frame - reset 'ready' flag - mVideoSink->retained_frame_ready = FALSE; + mVideoSink->retained_frame_ready = FALSE; int destination_rowbytes = mWidth * mDepth; - for (int row=0; rowretained_frame_data - [mNaturalRowbytes * row], - mNaturalRowbytes); + [mCurrentRowbytes * row], + mCurrentRowbytes); } GST_OBJECT_UNLOCK(mVideoSink); DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST"); - setDirty(0,0,mNaturalWidth,mNaturalHeight); + setDirty(0,0,mCurrentWidth,mCurrentHeight); } else { @@ -835,27 +838,35 @@ MediaPluginGStreamer010::startup() } -bool +void MediaPluginGStreamer010::sizeChanged() { // the shared writing space has possibly changed size/location/whatever - // Check to see whether the movie's natural size has updated - if (mNaturalWidth != mPreviousNaturalWidth || - mNaturalHeight != mPreviousNaturalHeight) + // Check to see whether the movie's NATURAL size has been set yet + if (1 == mNaturalWidth && + 1 == mNaturalHeight) { - mPreviousNaturalWidth = mNaturalWidth; - mPreviousNaturalHeight = mNaturalHeight; + mNaturalWidth = mCurrentWidth; + mNaturalHeight = mCurrentHeight; + DEBUGMSG("Media NATURAL size better detected as %dx%d", + mNaturalWidth, mNaturalHeight); + } + + // if the size has changed then the shm has changed and the app needs telling + if (mCurrentWidth != mPreviousWidth || + mCurrentHeight != mPreviousHeight) + { + mPreviousWidth = mCurrentWidth; + mPreviousHeight = mCurrentHeight; LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); message.setValue("name", mTextureSegmentName); message.setValueS32("width", mNaturalWidth); message.setValueS32("height", mNaturalHeight); - DEBUGMSG("<--- Sending size change request to application with name: '%s' - size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); + DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); sendMessage(message); } - - return true; } @@ -940,10 +951,12 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) // lame to have to decide this now, it depends on the movie. Oh well. mDepth = 4; + mCurrentWidth = 1; + mCurrentHeight = 1; + mPreviousWidth = 1; + mPreviousHeight = 1; mNaturalWidth = 1; mNaturalHeight = 1; - mPreviousNaturalWidth = 1; - mPreviousNaturalHeight = 1; mWidth = 1; mHeight = 1; mTextureWidth = 1; @@ -984,7 +997,6 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress); mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - } else if(message_name == "shm_remove") { @@ -1063,7 +1075,7 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) INFOMSG("**** = REAL RESIZE REQUEST FROM APP"); GST_OBJECT_LOCK(mVideoSink); - mVideoSink->resize_forced = true; + mVideoSink->resize_forced_always = true; mVideoSink->resize_try_width = texture_width; mVideoSink->resize_try_height = texture_height; GST_OBJECT_UNLOCK(mVideoSink); diff --git a/linden/indra/media_plugins/quicktime/CMakeLists.txt b/linden/indra/media_plugins/quicktime/CMakeLists.txt index db11c9a..f0b8f0d 100644 --- a/linden/indra/media_plugins/quicktime/CMakeLists.txt +++ b/linden/indra/media_plugins/quicktime/CMakeLists.txt @@ -56,6 +56,14 @@ add_dependencies(media_plugin_quicktime ${LLCOMMON_LIBRARIES} ) +if (WINDOWS) + set_target_properties( + media_plugin_quicktime + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + if (QUICKTIME) add_definitions(-DLL_QUICKTIME_ENABLED=1) diff --git a/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 51cc8dd..6c8c41d 100644 --- a/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -49,6 +49,7 @@ #include "Movies.h" #include "QDoffscreen.h" #include "FixMath.h" + #include "QTLoadLibraryUtils.h" #endif // TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint @@ -72,11 +73,14 @@ private: int mCurVolume; bool mMediaSizeChanging; bool mIsLooping; + std::string mMovieTitle; + bool mReceivedTitle; const int mMinWidth; const int mMaxWidth; const int mMinHeight; const int mMaxHeight; F64 mPlayRate; + std::string mNavigateURL; enum ECommand { COMMAND_NONE, @@ -175,6 +179,11 @@ private: setStatus(STATUS_ERROR); return; }; + + mNavigateURL = url; + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message.setValue("uri", mNavigateURL); + sendMessage(message); // do pre-roll actions (typically fired for streaming movies but not always) PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); @@ -199,6 +208,9 @@ private: bool unload() { + // new movie and have to get title again + mReceivedTitle = false; + if ( mMovieHandle ) { StopMovie( mMovieHandle ); @@ -382,11 +394,18 @@ private: static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) { - //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; // TODO: //LLMediaEvent event( self ); //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); + + // Send a "navigate complete" event. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message.setValue("uri", self->mNavigateURL); + message.setValueS32("result_code", 200); + message.setValue("result_string", "OK"); + self->sendMessage(message); }; @@ -400,7 +419,7 @@ private: { if ( mCommand == COMMAND_PLAY ) { - if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING ) + if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING || mStatus == STATUS_DONE ) { long state = GetMovieLoadState( mMovieHandle ); @@ -426,7 +445,7 @@ private: else if ( mCommand == COMMAND_STOP ) { - if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED ) + if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED || mStatus == STATUS_DONE ) { if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) { @@ -508,11 +527,17 @@ private: if ( ! mMovieController ) return; - // service QuickTime - // Calling it this way doesn't have good behavior on Windows... -// MoviesTask( mMovieHandle, milliseconds ); - // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle. -// MoviesTask( mMovieHandle, 0 ); + // this wasn't required in 1.xx viewer but we have to manually + // work the Windows message pump now + #if defined( LL_WINDOWS ) + MSG msg; + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + GetMessage( &msg, NULL, 0, 0 ); + TranslateMessage( &msg ); + DispatchMessage( &msg ); + }; + #endif MCIdle( mMovieController ); @@ -525,11 +550,14 @@ private: // update state machine processState(); - // special code for looping - need to rewind at the end of the movie - if ( mIsLooping ) + // see if title arrived and if so, update member variable with contents + checkTitle(); + + // QT call to see if we are at the end - can't do with controller + if ( IsMovieDone( mMovieHandle ) ) { - // QT call to see if we are at the end - can't do with controller - if ( IsMovieDone( mMovieHandle ) ) + // special code for looping - need to rewind at the end of the movie + if ( mIsLooping ) { // go back to start rewind(); @@ -542,8 +570,16 @@ private: // set the volume MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); }; - }; - }; + } + else + { + if(mStatus == STATUS_PLAYING) + { + setStatus(STATUS_DONE); + } + } + } + }; int getDataWidth() const @@ -586,6 +622,19 @@ private: }; }; + F64 getLoadedDuration() + { + TimeValue duration; + if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr) + { + // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie. + duration = GetMovieDuration( mMovieHandle ); + } + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)duration / (F64)scale; + }; + F64 getDuration() { TimeValue duration = GetMovieDuration( mMovieHandle ); @@ -643,6 +692,77 @@ private: { }; + //////////////////////////////////////////////////////////////////////////////// + // Grab movie title into mMovieTitle - should be called repeatedly + // until it returns true since movie title takes a while to become + // available. + const bool getMovieTitle() + { + // grab meta data from movie + QTMetaDataRef media_data_ref; + OSErr result = QTCopyMovieMetaData( mMovieHandle, &media_data_ref ); + if ( noErr != result ) + return false; + + // look up "Display Name" in meta data + OSType meta_data_key = kQTMetaDataCommonKeyDisplayName; + QTMetaDataItem item = kQTMetaDataItemUninitialized; + result = QTMetaDataGetNextItem( media_data_ref, kQTMetaDataStorageFormatWildcard, + 0, kQTMetaDataKeyFormatCommon, + (const UInt8 *)&meta_data_key, + sizeof( meta_data_key ), &item ); + if ( noErr != result ) + return false; + + // find the size of the title + ByteCount size; + result = QTMetaDataGetItemValue( media_data_ref, item, NULL, 0, &size ); + if ( noErr != result || size <= 0 /*|| size > 1024 FIXME: arbitrary limit */ ) + return false; + + // allocate some space and grab it + UInt8* item_data = new UInt8( size + 1 ); + memset( item_data, 0, ( size + 1 ) * sizeof( UInt8* ) ); + result = QTMetaDataGetItemValue( media_data_ref, item, item_data, size, NULL ); + if ( noErr != result ) + { + delete [] item_data; + return false; + }; + + // save it + if ( strlen( (char*)item_data ) ) + mMovieTitle = std::string( (char* )item_data ); + else + mMovieTitle = ""; + + // clean up + delete [] item_data; + + return true; + }; + + // called regularly to see if title changed + void checkTitle() + { + // we did already receive title so keep checking + if ( ! mReceivedTitle ) + { + // grab title from movie meta data + if ( getMovieTitle() ) + { + // pass back to host application + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", mMovieTitle ); + sendMessage( message ); + + // stop looking once we find a title for this movie. + // TODO: this may to be reset if movie title changes + // during playback but this is okay for now + mReceivedTitle = true; + }; + }; + }; }; MediaPluginQuickTime::MediaPluginQuickTime( @@ -664,6 +784,8 @@ MediaPluginQuickTime::MediaPluginQuickTime( mCurVolume = 0x99; mMediaSizeChanging = false; mIsLooping = false; + mMovieTitle = std::string(); + mReceivedTitle = false; mCommand = COMMAND_NONE; mPlayRate = 0.0f; mStatus = STATUS_NONE; @@ -700,22 +822,29 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; // Normally a plugin would only specify one of these two subclasses, but this is a demo... -// versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; message.setValueLLSD("versions", versions); #ifdef LL_WINDOWS - if ( InitializeQTML( 0L ) != noErr ) + + // QuickTime 7.6.4 has an issue (that was not present in 7.6.2) with initializing QuickTime + // according to this article: http://lists.apple.com/archives/QuickTime-API/2009/Sep/msg00097.html + // The solution presented there appears to work. + QTLoadLibrary("qtcf.dll"); + + // main initialization for QuickTime - only required on Windows + OSErr result = InitializeQTML( 0L ); + if ( result != noErr ) { //TODO: If no QT on Windows, this fails - respond accordingly. - //return false; } else { -// std::cerr << "QuickTime initialized" << std::endl; + //std::cerr << "QuickTime initialized" << std::endl; }; #endif + // required for both Windows and Mac EnterMovies(); std::string plugin_version = "QuickTime media plugin, QuickTime version "; @@ -773,10 +902,7 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) else if(message_name == "shm_added") { SharedSegmentInfo info; - U64 address_lo = message_in.getValueU32("address"); - U64 address_hi = message_in.hasValue("address_1") ? message_in.getValueU32("address_1") : 0; - info.mAddress = (void*)((address_lo) | - (address_hi * (U64(1)<<31))); + info.mAddress = message_in.getValuePointer("address"); info.mSize = (size_t)message_in.getValueS32("size"); std::string name = message_in.getValue("name"); diff --git a/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp index f115c28..91efdae 100644 --- a/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -75,6 +75,8 @@ public: private: + std::string mProfileDir; + enum { INIT_STATE_UNINITIALIZED, // Browser instance hasn't been set up yet @@ -95,6 +97,12 @@ private: int mLastMouseY; bool mFirstFocus; + void setInitState(int state) + { +// std::cerr << "changing init state to " << state << std::endl; + mInitState = state; + } + //////////////////////////////////////////////////////////////////////////////// // void update(int milliseconds) @@ -186,7 +194,6 @@ private: #else std::string component_dir = application_dir; #endif - std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? // window handle - needed on Windows and must be app window. #if LL_WINDOWS @@ -198,7 +205,7 @@ private: #endif // main browser initialization - bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, profileDir, native_window_handle ); + bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); if ( result ) { // create single browser window @@ -208,13 +215,15 @@ private: // Enable plugins LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_DARWIN - // Disable plugins - LLQtWebKit::getInstance()->enablePlugins(false); + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); #elif LL_LINUX - // Disable plugins - LLQtWebKit::getInstance()->enablePlugins(false); + // Enable plugins + LLQtWebKit::getInstance()->enablePlugins(true); #endif - + // Enable cookies + LLQtWebKit::getInstance()->enableCookies( true ); + // tell LLQtWebKit about the size of the browser window LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); @@ -227,11 +236,11 @@ private: // don't flip bitmap LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); - // Set the background color to black - mostly for initial login page + // set background color to be black - mostly for initial login page LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, 0x00, 0x00, 0x00 ); // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. - mInitState = INIT_STATE_NAVIGATING; + setInitState(INIT_STATE_NAVIGATING); // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially @@ -285,7 +294,7 @@ private: { if(mInitState == INIT_STATE_WAIT_REDRAW) { - mInitState = INIT_STATE_RUNNING; + setInitState(INIT_STATE_RUNNING); } // flag that an update is required @@ -307,7 +316,7 @@ private: if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) { - mInitState = INIT_STATE_WAIT_REDRAW; + setInitState(INIT_STATE_WAIT_REDRAW); } } @@ -330,7 +339,7 @@ private: } else if(mInitState == INIT_STATE_NAVIGATING) { - mInitState = INIT_STATE_NAVIGATE_COMPLETE; + setInitState(INIT_STATE_NAVIGATE_COMPLETE); } } @@ -495,7 +504,16 @@ private: { // std::cerr << "unicode input, code = 0x" << std::hex << (unsigned long)(wstr[i]) << std::dec << std::endl; - LLQtWebKit::getInstance()->unicodeInput(mBrowserWindowId, wstr[i], modifiers); + if(wstr[i] == 32) + { + // For some reason, the webkit plugin really wants the space bar to come in through the key-event path, not the unicode path. + LLQtWebKit::getInstance()->keyEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_DOWN, 32, modifiers); + LLQtWebKit::getInstance()->keyEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_UP, 32, modifiers); + } + else + { + LLQtWebKit::getInstance()->unicodeInput(mBrowserWindowId, wstr[i], modifiers); + } } checkEditState(); @@ -576,6 +594,9 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { if(message_name == "init") { + std::string user_data_path = message_in.getValue("user_data_path"); // n.b. always has trailing platform-specific dir-delimiter + mProfileDir = user_data_path + "browser_profile"; + LLPluginMessage message("base", "init_response"); LLSD versions = LLSD::emptyMap(); versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; @@ -611,7 +632,11 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } else if(message_name == "cleanup") { - // TODO: clean up here + // DTOR most likely won't be called but the recent change to the way this process + // is (not) killed means we see this message and can do what we need to here. + // Note: this cleanup is ultimately what writes cookies to the disk + LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this ); + LLQtWebKit::getInstance()->reset(); } else if(message_name == "shm_added") { diff --git a/linden/indra/newview/llviewerparcelmedia.cpp b/linden/indra/newview/llviewerparcelmedia.cpp index c7f0c4b..d4ebbd9 100644 --- a/linden/indra/newview/llviewerparcelmedia.cpp +++ b/linden/indra/newview/llviewerparcelmedia.cpp @@ -193,6 +193,9 @@ void LLViewerParcelMedia::play(LLParcel* parcel) S32 media_width = parcel->getMediaWidth(); S32 media_height = parcel->getMediaHeight(); + // Debug print + // LL_DEBUGS("Media") << "Play media type : " << mime_type << ", url : " << media_url << LL_ENDL; + if(sMediaImpl) { // If the url and mime type are the same, call play again @@ -221,7 +224,7 @@ void LLViewerParcelMedia::play(LLParcel* parcel) sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, media_width, media_height, media_auto_scale, - media_loop); + media_loop, mime_type); } } else @@ -229,7 +232,7 @@ void LLViewerParcelMedia::play(LLParcel* parcel) // There is no media impl, make a new one sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, media_width, media_height, media_auto_scale, - media_loop); + media_loop, mime_type); } -- cgit v1.1