diff -r 9539c10021ba -r 52b800cc01fc autobuild.xml --- a/autobuild.xml Tue Sep 25 13:04:14 2012 -0400 +++ b/autobuild.xml Sun Nov 11 17:27:32 2012 -0600 @@ -843,30 +843,6 @@ - gstreamer - - license - lgpl - license_file - LICENSES/gstreamer.txt - name - gstreamer - platforms - - linux - - archive - - hash - ddbc0a64ad788107877fee777403592c - url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gstreamer-linux-20101013.tar.bz2 - - name - linux - - - gtk-atk-pango-glib license @@ -903,6 +879,42 @@ + gstreamer + + license + lgpl + license_file + LICENSES/gstreamer.txt + name + gstreamer + platforms + + linux + + archive + + hash + ddbc0a64ad788107877fee777403592c + url + http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/gstreamer-linux-20101013.tar.bz2 + + name + linux + + windows + + archive + + hash + 063358a877595303075ccca8c546266f + url + http://download.kokuaviewer.org/files/libs/gstreamer-glib-iconv-libxml-plugins-autobuild-windows-20110816.tar.bz2 + + name + windows + + + havok-source license diff -r 9539c10021ba -r 52b800cc01fc doc/contributions.txt --- a/doc/contributions.txt Tue Sep 25 13:04:14 2012 -0400 +++ b/doc/contributions.txt Sun Nov 11 17:27:32 2012 -0600 @@ -186,6 +186,7 @@ STORM-1532 Armin Weatherwax VWR-8436 + OPEN-151 ArminasX Saiman Arya Braveheart Asaeda Meltingdots @@ -255,6 +256,7 @@ VWR-26066 VWR-26458 WEB-262 + OPEN-151 Bryn Oh Buckaroo Mu Bulli Schumann @@ -519,6 +521,7 @@ VWR-2948 VWR-3605 VWR-8617 + OPEN-151 Jack Abraham Jagga Meredith JB Kraft @@ -770,6 +773,7 @@ VWR-8454 VWR-8689 VWR-9007 + OPEN-151 Medhue Simoni Mel Vanbeeck Melinda Latynina @@ -895,6 +899,7 @@ STORM-1087 STORM-1090 STORM-1828 + OPEN-151 Nicoladie Gymnast Nounouch Hapmouche VWR-238 diff -r 9539c10021ba -r 52b800cc01fc indra/cmake/Copy3rdPartyLibs.cmake --- a/indra/cmake/Copy3rdPartyLibs.cmake Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/cmake/Copy3rdPartyLibs.cmake Sun Nov 11 17:27:32 2012 -0600 @@ -33,10 +33,74 @@ set(debug_src_dir "${ARCH_PREBUILT_DIRS_DEBUG}") set(debug_files + alut.dll + openal32.dll openjpegd.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll + + # gstreamer dlls - not plugins + avcodec-gpl-52.dll + avdevice-gpl-52.dll + avfilter-gpl-1.dll + avformat-gpl-52.dll + avutil-gpl-50.dll + iconv.dll + liba52-0.dll + libbz2.dll + libcelt-0.dll + libdca-0.dll + libexpat-1.dll + libfaad-2.dll + libFLAC-8.dll + libgcrypt-11.dll + libgio-2.0-0.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgnutls-26.dll + libgobject-2.0-0.dll + libgpg-error-0.dll + libgstapp-0.10.dll + libgstaudio-0.10.dll + libgstbase-0.10.dll + libgstcontroller-0.10.dll + libgstdataprotocol-0.10.dll + libgstfft-0.10.dll + libgstinterfaces-0.10.dll + libgstnet-0.10.dll + libgstnetbuffer-0.10.dll + libgstpbutils-0.10.dll + libgstphotography-0.10.dll + libgstreamer-0.10.dll + libgstriff-0.10.dll + libgstrtp-0.10.dll + libgstrtsp-0.10.dll + libgstsdp-0.10.dll + libgstsignalprocessor-0.10.dll + libgsttag-0.10.dll + libgstvideo-0.10.dll + libgthread-2.0-0.dll + libmms-0.dll + libmpeg2-0.dll + libneon-27.dll + libogg-0.dll + liboil-0.3-0.dll + libsoup-2.4-1.dll + libtasn1-3.dll + libtheora-0.dll + libtheoradec-1.dll + libvorbis-0.dll + libvorbisenc-2.dll + libvorbisfile-3.dll + libwavpack-1.dll + libx264-67.dll + libxml2-2.dll + libxml2.dll + SDL.dll + xvidcore.dll + z.dll + ssleay32.dll libeay32.dll libcollada14dom22-d.dll @@ -46,10 +110,74 @@ set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}") set(release_files + alut.dll + openal32.dll openjpeg.dll libapr-1.dll libaprutil-1.dll libapriconv-1.dll + + # gstreamer dlls - not plugins + avcodec-gpl-52.dll + avdevice-gpl-52.dll + avfilter-gpl-1.dll + avformat-gpl-52.dll + avutil-gpl-50.dll + iconv.dll + liba52-0.dll + libbz2.dll + libcelt-0.dll + libdca-0.dll + libexpat-1.dll + libfaad-2.dll + libFLAC-8.dll + libgcrypt-11.dll + libgio-2.0-0.dll + libglib-2.0-0.dll + libgmodule-2.0-0.dll + libgnutls-26.dll + libgobject-2.0-0.dll + libgpg-error-0.dll + libgstapp-0.10.dll + libgstaudio-0.10.dll + libgstbase-0.10.dll + libgstcontroller-0.10.dll + libgstdataprotocol-0.10.dll + libgstfft-0.10.dll + libgstinterfaces-0.10.dll + libgstnet-0.10.dll + libgstnetbuffer-0.10.dll + libgstpbutils-0.10.dll + libgstphotography-0.10.dll + libgstreamer-0.10.dll + libgstriff-0.10.dll + libgstrtp-0.10.dll + libgstrtsp-0.10.dll + libgstsdp-0.10.dll + libgstsignalprocessor-0.10.dll + libgsttag-0.10.dll + libgstvideo-0.10.dll + libgthread-2.0-0.dll + libmms-0.dll + libmpeg2-0.dll + libneon-27.dll + libogg-0.dll + liboil-0.3-0.dll + libsoup-2.4-1.dll + libtasn1-3.dll + libtheora-0.dll + libtheoradec-1.dll + libvorbis-0.dll + libvorbisenc-2.dll + libvorbisfile-3.dll + libwavpack-1.dll + libx264-67.dll + libxml2-2.dll + libxml2.dll + SDL.dll + xvidcore.dll + z.dll + ssleay32.dll libeay32.dll libcollada14dom22.dll @@ -217,6 +345,7 @@ libllqtwebkit.dylib libminizip.a libndofdev.dylib + libopenal.1.dylib libhunspell-1.3.0.dylib libexception_handler.dylib libcollada14dom.dylib diff -r 9539c10021ba -r 52b800cc01fc indra/cmake/GStreamer010Plugin.cmake --- a/indra/cmake/GStreamer010Plugin.cmake Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/cmake/GStreamer010Plugin.cmake Sun Nov 11 17:27:32 2012 -0600 @@ -6,33 +6,69 @@ pkg_check_modules(GSTREAMER010 REQUIRED gstreamer-0.10) pkg_check_modules(GSTREAMER010_PLUGINS_BASE REQUIRED gstreamer-plugins-base-0.10) -elseif (LINUX) - use_prebuilt_binary(gstreamer) - # possible libxml should have its own .cmake file instead - use_prebuilt_binary(libxml) + +else (STANDALONE) + + # Possibly libxml and glib should have their own .cmake file instead... + use_prebuilt_binary(gstreamer) # includes glib, libxml, and iconv on Windows set(GSTREAMER010_FOUND ON FORCE BOOL) set(GSTREAMER010_PLUGINS_BASE_FOUND ON FORCE BOOL) + + if (WINDOWS) + # gstreamer-plugins are packaged with gstreamer now. + # In case someone wants to have 2 packages again in future uncomment: + # use_prebuilt_binary(gst_plugins) + set(GSTREAMER010_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include/gstreamer-0.10 + ${LIBS_PREBUILT_DIR}/include/glib + ${LIBS_PREBUILT_DIR}/include/libxml2 + ) + else (WINDOWS) + use_prebuilt_binary(glib) # gstreamer needs glib + use_prebuilt_binary(libxml) set(GSTREAMER010_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/gstreamer-0.10 ${LIBS_PREBUILT_DIR}/include/glib-2.0 ${LIBS_PREBUILT_DIR}/include/libxml2 ) + endif (WINDOWS) + +endif (STANDALONE) + +if (WINDOWS) + # We don't need to explicitly link against gstreamer itself, because # LLMediaImplGStreamer probes for the system's copy at runtime. set(GSTREAMER010_LIBRARIES + gstaudio-0.10.lib + gstbase-0.10.lib + gstreamer-0.10.lib + gstvideo-0.10.lib #slvideoplugin + gstinterfaces-0.10.lib + gobject-2.0 + gmodule-2.0 + gthread-2.0 + glib-2.0 + ) +else (WINDOWS) + set(GSTREAMER010_LIBRARIES + gstvideo-0.10 + gstaudio-0.10 + gstbase-0.10 + gstreamer-0.10 gobject-2.0 gmodule-2.0 dl gthread-2.0 + rt glib-2.0 ) -endif (STANDALONE) +endif (WINDOWS) + if (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) + if (NOT DARWIN) set(GSTREAMER010 ON CACHE BOOL "Build with GStreamer-0.10 streaming media support.") + add_definitions(-DLL_GSTREAMER010_ENABLED=1) + endif (NOT DARWIN) endif (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) - -if (GSTREAMER010) - add_definitions(-DLL_GSTREAMER010_ENABLED=1) -endif (GSTREAMER010) - diff -r 9539c10021ba -r 52b800cc01fc indra/cmake/OPENAL.cmake --- a/indra/cmake/OPENAL.cmake Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/cmake/OPENAL.cmake Sun Nov 11 17:27:32 2012 -0600 @@ -2,11 +2,7 @@ include(Linking) include(Prebuilt) -if (LINUX) set(OPENAL ON CACHE BOOL "Enable OpenAL") -else (LINUX) - set(OPENAL OFF CACHE BOOL "Enable OpenAL") -endif (LINUX) if (OPENAL) set(OPENAL_LIB_INCLUDE_DIRS "${LIBS_PREBUILT_DIR}/include/AL") diff -r 9539c10021ba -r 52b800cc01fc indra/llmessage/llcurl.h --- a/indra/llmessage/llcurl.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llmessage/llcurl.h Sun Nov 11 17:27:32 2012 -0600 @@ -252,7 +252,7 @@ CURL* mCurlEasyHandle; struct curl_slist* mHeaders; - + struct curl_slist* mAliases; std::stringstream mRequest; LLChannelDescriptors mChannels; LLIOPipe::buffer_ptr_t mOutput; diff -r 9539c10021ba -r 52b800cc01fc indra/llmessage/llcurl.cpp --- a/indra/llmessage/llcurl.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llmessage/llcurl.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -286,6 +286,7 @@ LLCurl::Easy::Easy() : mHeaders(NULL), + mAliases(NULL), mCurlEasyHandle(NULL) { mErrorBuffer[0] = 0; @@ -318,6 +319,7 @@ releaseEasyHandle(mCurlEasyHandle); --gCurlEasyCount; curl_slist_free_all(mHeaders); + curl_slist_free_all(mAliases); for_each(mStrings.begin(), mStrings.end(), DeletePointerArray()); if (mResponder && LLCurl::sNotQuitting) //aborted @@ -338,6 +340,11 @@ curl_slist_free_all(mHeaders); mHeaders = NULL; } + if (mAliases) + { + curl_slist_free_all(mAliases); + mAliases = NULL; + } mRequest.str(""); mRequest.clear(); @@ -378,6 +385,7 @@ void LLCurl::Easy::setHeaders() { setopt(CURLOPT_HTTPHEADER, mHeaders); + setopt(CURLOPT_HTTP200ALIASES, mAliases); // Just in case -- MC } void LLCurl::Easy::getTransferInfo(LLCurl::TransferInfo* info) @@ -525,6 +533,11 @@ setopt(CURLOPT_SSL_VERIFYHOST, 0); setopt(CURLOPT_TIMEOUT, llmax(time_out, CURL_REQUEST_TIMEOUT)); + mAliases = curl_slist_append(mAliases, "ICY 200 OK"); + mAliases = curl_slist_append(mAliases, "ICY 402 Service Unavailabe"); + setopt(CURLOPT_HTTP200ALIASES, mAliases); + //setopt(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); // Possible streaming fix? -- MC + setoptString(CURLOPT_URL, url); mResponder = responder; diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginclassmedia.h --- a/indra/llplugin/llpluginclassmedia.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginclassmedia.h Sun Nov 11 17:27:32 2012 -0600 @@ -36,6 +36,7 @@ #include #include "v4color.h" + class LLPluginClassMedia : public LLPluginProcessParentOwner { LOG_CLASS(LLPluginClassMedia); @@ -249,6 +250,11 @@ // This is valid during MEDIA_EVENT_CLICK_LINK_HREF and MEDIA_EVENT_GEOMETRY_CHANGE std::string getClickUUID() const { return mClickUUID; }; + #if LL_WINDOWS + //Open a debug console for this plugin. + void showConsole(); + #endif + // These are valid during MEDIA_EVENT_DEBUG_MESSAGE std::string getDebugMessageText() const { return mDebugMessageText; }; std::string getDebugMessageLevel() const { return mDebugMessageLevel; }; @@ -279,6 +285,11 @@ // Hang the plugin. If you use this outside of a testbed, you will be punished. void hangPlugin(); + // This sends the message "base", "cleanup" to the plugin + // Don't call this unless you know what you're doing + // and you know this is exactly what you want to do + void forceCleanUpPlugin(); + /////////////////////////////////// // media time class functions bool pluginSupportsMediaTime(void); diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginclassmedia.cpp --- a/indra/llplugin/llpluginclassmedia.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginclassmedia.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -1230,6 +1230,15 @@ return !version.empty(); } +#if LL_WINDOWS +void LLPluginClassMedia::showConsole() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "show_console"); + + sendMessage(message); +} +#endif + void LLPluginClassMedia::focus(bool focused) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "focus"); @@ -1374,6 +1383,12 @@ sendMessage(message); } +void LLPluginClassMedia::forceCleanUpPlugin() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASE, "cleanup"); + + sendMessage(message); +} //////////////////////////////////////////////////////////// // MARK: media_time class functions diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llplugininstance.cpp --- a/indra/llplugin/llplugininstance.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llplugininstance.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -123,7 +123,7 @@ { result = init_function(staticReceiveMessage, (void*)this, &mPluginSendMessageFunction, &mPluginUserData); - if(result != APR_SUCCESS) + if((result != APR_SUCCESS) || (mPluginUserData == NULL)) { LL_WARNS("Plugin") << "call to init function failed with error " << result << LL_ENDL; } @@ -139,7 +139,8 @@ */ void LLPluginInstance::sendMessage(const std::string &message) { - if(mPluginSendMessageFunction) + // Don't check for a NULL mPluginUserData *ON PAIN OF DEATH* -- MC + if (mPluginSendMessageFunction && mPluginUserData) { LL_DEBUGS("Plugin") << "sending message to plugin: \"" << message << "\"" << LL_ENDL; mPluginSendMessageFunction(message.c_str(), &mPluginUserData); diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginprocesschild.h --- a/indra/llplugin/llpluginprocesschild.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginprocesschild.h Sun Nov 11 17:27:32 2012 -0600 @@ -29,6 +29,8 @@ #ifndef LL_LLPLUGINPROCESSCHILD_H #define LL_LLPLUGINPROCESSCHILD_H +#include + #include "llpluginmessage.h" #include "llpluginmessagepipe.h" #include "llplugininstance.h" @@ -68,6 +70,10 @@ // Inherited from LLPluginInstanceMessageListener /* virtual */ void receivePluginMessage(const std::string &message); +#if LL_WINDOWS + void createConsole(); +#endif + private: enum EState @@ -80,6 +86,7 @@ STATE_PLUGIN_INITIALIZING, // plugin is processing init message STATE_RUNNING, // steady state (processing messages) STATE_UNLOADING, // plugin has sent shutdown_response and needs to be unloaded + STATE_UNLOADING_CLEANED, // plugin has (hopefully) cleaned up what it has allocated, now it wants to be cleaned STATE_UNLOADED, // plugin has been unloaded STATE_ERROR, // generic bailout state STATE_DONE // state machine will sit in this state after either error or normal termination. diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginprocesschild.cpp --- a/indra/llplugin/llpluginprocesschild.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginprocesschild.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -32,6 +32,15 @@ #include "llplugininstance.h" #include "llpluginmessagepipe.h" #include "llpluginmessageclasses.h" +#include "llpluginclassmedia.h" + +#if LL_WINDOWS +#include +#include +#include +#include +#include +#endif static const F32 HEARTBEAT_SECONDS = 1.0f; static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f; // Each call to idle will give the plugin this much time. @@ -205,6 +214,16 @@ } setState(STATE_UNLOADED); break; + + // Special case for when the plugin knows it's cleaned up -- MC + case STATE_UNLOADING_CLEANED: + if (mInstance) + { + delete mInstance; + mInstance = NULL; + } + setState(STATE_UNLOADED); + break; case STATE_UNLOADED: killSockets(); @@ -410,8 +429,14 @@ } else if(message_name == "sleep_time") { - mSleepTime = llmax(parsed.getValueReal("time"), 1.0 / 100.0); // clamp to maximum of 100Hz + mSleepTime = parsed.getValueReal("time"); } + #if LL_WINDOWS + else if(message_name == "show_console") + { + createConsole(); + } + #endif else if(message_name == "crash") { // Crash the plugin @@ -520,6 +545,17 @@ LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL; } } + else if (message_name == "cleanup_reply") + { + LL_DEBUGS("PluginChild") << "cleanup_reply message received" << LL_ENDL; + passMessage = false; + + setState(STATE_UNLOADING_CLEANED); + + // Clean up this and tell the parent + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "cleanup_reply"); + sendMessageToParent(message); + } } } @@ -561,3 +597,63 @@ } } } + +#if LL_WINDOWS +void LLPluginProcessChild::createConsole() +{ + if (!AttachConsole(-1)) + { + // error creating console + TCHAR buffer[1024]; + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL , GetLastError(), 0, buffer, 1024, NULL)) + { + LL_WARNS("PluginChild") << "Error creating console: " << buffer << LL_ENDL; + } + else + { + LL_WARNS("PluginChild") << "Error creating console, error code: " << GetLastError() << LL_ENDL; + } + return; + } + + HANDLE std_o_handle = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE std_e_handle = GetStdHandle(STD_ERROR_HANDLE); + if ((std_o_handle == INVALID_HANDLE_VALUE) || (std_e_handle == INVALID_HANDLE_VALUE)) + { + LL_WARNS("PluginChild") << "couldn't create console, std out or err not available" << LL_ENDL; + return; + } + + int crt_o_filedesc = _open_osfhandle((long)std_o_handle, _O_TEXT); + int crt_e_filedesc = _open_osfhandle((long)std_e_handle, _O_TEXT); + if ((crt_o_filedesc == -1) || (crt_e_filedesc == -1)) + { + LL_WARNS("PluginChild") << "_open_osfhandle failed" << LL_ENDL; + return; + } + // replace stdout handle + FILE* fp_crt_o = _fdopen(crt_o_filedesc, "w"); + if (!fp_crt_o) + { + LL_WARNS("PluginChild") << "_fdopen stdout handle failed" << LL_ENDL; + return; + } + *stdout = *fp_crt_o; + std::cout.clear(); // clear anything before AllocCo + + // replace stderr handle + FILE* fp_crt_e = _fdopen(crt_e_filedesc, "w"); + if (!fp_crt_e) + { + LL_WARNS("PluginChild") << "_fdopen stderr handle failed" << LL_ENDL; + return; + } + *stderr = *fp_crt_e; + std::cerr.clear(); // clear anything before AllocConsole(); + + // set the screen buffer to be big enough to let us scroll text + CONSOLE_SCREEN_BUFFER_INFO coninfo; + GetConsoleScreenBufferInfo(std_o_handle, &coninfo); + coninfo.dwSize.Y = 500; // max console lines + SetConsoleScreenBufferSize(std_o_handle, coninfo.dwSize);} +#endif \ No newline at end of file diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginprocessparent.h --- a/indra/llplugin/llpluginprocessparent.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginprocessparent.h Sun Nov 11 17:27:32 2012 -0600 @@ -81,6 +81,11 @@ // Go to the proper error state void errorState(void); + // Go to STATE_CLEANUP + // Don't call this unless you know what you're doing + // and you know this is exactly what you want to do + void cleanupState(); + void setSleepTime(F64 sleep_time, bool force_send = false); F64 getSleepTime(void) const { return mSleepTime; }; @@ -172,6 +177,8 @@ bool mDebug; bool mBlocked; bool mPolledInput; + U32 mPortToBind; + U32 mBindRetryCount; LLProcessPtr mDebugger; diff -r 9539c10021ba -r 52b800cc01fc indra/llplugin/llpluginprocessparent.cpp --- a/indra/llplugin/llpluginprocessparent.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/llplugin/llpluginprocessparent.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -34,6 +34,7 @@ #include "stringize.h" #include "llapr.h" +#include "llrand.h" //virtual LLPluginProcessParentOwner::~LLPluginProcessParentOwner() @@ -156,6 +157,12 @@ setState(STATE_LAUNCH_FAILURE); else setState(STATE_ERROR); + mBindRetryCount = 0; +} + +void LLPluginProcessParent::cleanupState() +{ + setState(STATE_CLEANUP); } void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug) @@ -165,7 +172,9 @@ mPluginFile = plugin_filename; mPluginDir = plugin_dir; mCPUUsage = 0.0f; - mDebug = debug; + mDebug = debug; + mPortToBind = 0; + mBindRetryCount = 0; setState(STATE_INITIALIZED); } @@ -275,7 +284,6 @@ case STATE_INITIALIZED: { - apr_status_t status = APR_SUCCESS; apr_sockaddr_t* addr = NULL; mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); @@ -287,14 +295,28 @@ &addr, "127.0.0.1", APR_INET, - 0, // port 0 = ephemeral ("find me a port") + mPortToBind, // initially port 0 = ephemeral ("find me a port") 0, gAPRPoolp); if(ll_apr_warn_status(status)) { + // A non-zero value for mPortToBind could get us here killSockets(); - errorState(); + if (mBindRetryCount > 9) + { + LL_WARNS("PluginParent") << "apr_sockaddr_info_get could not return successful, out of retries. Bailing out" << LL_ENDL; + errorState(); + } + else + { + LL_WARNS("PluginParent") << "Port " << mPortToBind + << " is possibly in use, retrying with a different port. Retry count " << mBindRetryCount+1 << " of 10" << LL_ENDL; + ++mBindRetryCount; + mPortToBind = ll_rand(16383) + 49152; // See comment on dynamic ports + setState(STATE_INITIALIZED); // Necessary? + idle_again = true; + } break; } @@ -324,7 +346,7 @@ { LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; - killSockets(); + //killSockets(); errorState(); break; } @@ -964,6 +986,11 @@ mSharedMemoryRegions.erase(iter); } } + else if (message_name == "cleanup_reply") + { + LL_DEBUGS("PluginParent") << "cleanup_reply message received" << LL_ENDL; + cleanupState(); + } else { LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL; diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/CMakeLists.txt --- a/indra/media_plugins/gstreamer010/CMakeLists.txt Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/CMakeLists.txt Sun Nov 11 17:27:32 2012 -0600 @@ -45,10 +45,16 @@ ) set(media_plugin_gstreamer010_HEADER_FILES + llmediaimplgstreamer.h llmediaimplgstreamervidplug.h llmediaimplgstreamer_syms.h llmediaimplgstreamertriviallogging.h ) + +set_source_files_properties(${media_plugin_gstreamer010_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND media_plugin_gstreamer010_SOURCE_FILES ${media_plugin_gstreamer010_HEADER_FILES}) add_library(media_plugin_gstreamer010 SHARED diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/llmediaimplgstreamer.h --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h Sun Nov 11 17:27:32 2012 -0600 @@ -36,9 +36,6 @@ extern "C" { #include #include - -#include "apr_pools.h" -#include "apr_dso.h" } diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h Sun Nov 11 17:27:32 2012 -0600 @@ -31,16 +31,21 @@ #include -extern "C" { -#include -#include -} ///////////////////////////////////////////////////////////////////////// // Debug/Info/Warning macros. +#if LL_WINDOWS +#include // !impru +#include // impru +#define LL_GETPID GetCurrentProcessId +#else +#include +#include +#define LL_GETPID getpid +#endif #define MSGMODULEFOO "(media plugin)" #define STDERRMSG(...) do{\ - fprintf(stderr, " pid:%d: ", (int)getpid());\ + fprintf(stderr, " pid:%d: ", (int)LL_GETPID());\ fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ fprintf(stderr, __VA_ARGS__);\ fputc('\n',stderr);\ diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h Sun Nov 11 17:27:32 2012 -0600 @@ -35,6 +35,9 @@ #include #include #include +#ifdef LL_LINUX + #include +#endif } G_BEGIN_DECLS diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -34,15 +34,29 @@ #include #include -#include "llmediaimplgstreamer_syms.h" #include "llmediaimplgstreamertriviallogging.h" +// #include "llthread.h" #include "llmediaimplgstreamervidplug.h" - GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); #define GST_CAT_DEFAULT gst_slvideo_debug +/* Filter signals and args *//* +enum +{ + *//* FILL ME *//* + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +#define SLV_SIZECAPS ", width=(int){1,2,4,8,16,32,64,128,256,512,1024}, height=(int){1,2,4,8,16,32,64,128,256,512,1024} " +#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS +*/ #define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] " #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS @@ -74,9 +88,9 @@ }; GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - llgst_element_class_add_pad_template (element_class, - llgst_static_pad_template_get (&sink_factory)); - llgst_element_class_set_details (element_class, &element_details); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_set_details (element_class, &element_details); } @@ -87,7 +101,7 @@ slvideo = GST_SLVIDEO (object); if (slvideo->caps) { - llgst_caps_unref(slvideo->caps); + gst_caps_unref(slvideo->caps); } G_OBJECT_CLASS(parent_class)->finalize (object); @@ -98,7 +112,7 @@ gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) { GstSLVideo *slvideo; - llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); slvideo = GST_SLVIDEO(bsink); @@ -190,7 +204,7 @@ GstSLVideo *slvideo; slvideo = GST_SLVIDEO(bsink); - return llgst_caps_ref (slvideo->caps); + return gst_caps_ref (slvideo->caps); } @@ -200,21 +214,32 @@ { GstSLVideo *filter; GstStructure *structure; +// GstCaps *intersection; GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); filter = GST_SLVIDEO(bsink); - int width, height; +/* + intersection = gst_caps_intersect (filter->caps, caps); + if (gst_caps_is_empty (intersection)) + { + // no overlap between our caps and requested caps + return FALSE; + } + gst_caps_unref(intersection); +*/ + int width = 0; + int height = 0; gboolean ret; const GValue *fps; const GValue *par; - structure = llgst_caps_get_structure (caps, 0); - ret = llgst_structure_get_int (structure, "width", &width); - ret = ret && llgst_structure_get_int (structure, "height", &height); - fps = llgst_structure_get_value (structure, "framerate"); + structure = gst_caps_get_structure (caps, 0); + ret = gst_structure_get_int (structure, "width", &width); + ret = ret && gst_structure_get_int (structure, "height", &height); + fps = gst_structure_get_value (structure, "framerate"); ret = ret && (fps != NULL); - par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); + par = gst_structure_get_value (structure, "pixel-aspect-ratio"); if (!ret) return FALSE; @@ -224,34 +249,35 @@ filter->width = width; filter->height = height; - - filter->fps_n = llgst_value_get_fraction_numerator(fps); - filter->fps_d = llgst_value_get_fraction_denominator(fps); + filter->fps_n = gst_value_get_fraction_numerator(fps); + filter->fps_d = gst_value_get_fraction_denominator(fps); if (par) { - filter->par_n = llgst_value_get_fraction_numerator(par); - filter->par_d = llgst_value_get_fraction_denominator(par); + filter->par_n = gst_value_get_fraction_numerator(par); + filter->par_d = gst_value_get_fraction_denominator(par); } else { filter->par_n = 1; filter->par_d = 1; } + GST_VIDEO_SINK_WIDTH(filter) = width; GST_VIDEO_SINK_HEIGHT(filter) = height; - + // crufty lump - we *always* accept *only* RGBX now. /* + filter->format = SLV_PF_UNKNOWN; - if (0 == strcmp(llgst_structure_get_name(structure), + if (0 == strcmp(gst_structure_get_name(structure), "video/x-raw-rgb")) { int red_mask; int green_mask; int blue_mask; - llgst_structure_get_int(structure, "red_mask", &red_mask); - llgst_structure_get_int(structure, "green_mask", &green_mask); - llgst_structure_get_int(structure, "blue_mask", &blue_mask); + gst_structure_get_int(structure, "red_mask", &red_mask); + gst_structure_get_int(structure, "green_mask", &green_mask); + gst_structure_get_int(structure, "blue_mask", &blue_mask); if ((unsigned int)red_mask == 0xFF000000 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0x0000FF00) @@ -265,12 +291,13 @@ filter->format = SLV_PF_BGRX; //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); } - }*/ - + + }*/ + filter->format = SLV_PF_RGBX; GST_OBJECT_UNLOCK(filter); - + return TRUE; } @@ -317,15 +344,15 @@ // we can ignore these and reverse-negotiate our preferred dimensions with // the peer if we like - we need to do this to obey dynamic resize requests // flowing in from the app. - structure = llgst_caps_get_structure (caps, 0); - if (!llgst_structure_get_int(structure, "width", &width) || - !llgst_structure_get_int(structure, "height", &height)) + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int(structure, "width", &width) || + !gst_structure_get_int(structure, "height", &height)) { GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps); return GST_FLOW_NOT_NEGOTIATED; } - GstBuffer *newbuf = llgst_buffer_new(); + GstBuffer *newbuf = gst_buffer_new(); bool made_bufferdata_ptr = false; #define MAXDEPTHHACK 4 @@ -345,19 +372,19 @@ GstCaps *desired_caps; GstStructure *desired_struct; - desired_caps = llgst_caps_copy (caps); - desired_struct = llgst_caps_get_structure (desired_caps, 0); + desired_caps = gst_caps_copy (caps); + desired_struct = gst_caps_get_structure (desired_caps, 0); GValue value = {0}; g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantwidth); - llgst_structure_set_value (desired_struct, "width", &value); + gst_structure_set_value (desired_struct, "width", &value); g_value_unset(&value); g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantheight); - llgst_structure_set_value (desired_struct, "height", &value); + gst_structure_set_value (desired_struct, "height", &value); - if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), + if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), desired_caps)) { // todo: re-use buffers from a pool? @@ -368,13 +395,13 @@ GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); + gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); made_bufferdata_ptr = true; } else { // peer hates our cap suggestion INFOMSG("peer hates us :("); - llgst_caps_unref(desired_caps); + gst_caps_unref(desired_caps); } } } @@ -386,7 +413,7 @@ GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); + gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); } *buf = GST_BUFFER_CAST(newbuf); @@ -428,6 +455,20 @@ #undef LLGST_DEBUG_FUNCPTR } +/* +static void +gst_slvideo_update_caps (GstSLVideo * slvideo) +{ + GstCaps *caps; + + // GStreamer will automatically convert colourspace if necessary. + // GStreamer will automatically resize media to one of these enumerated + // powers-of-two that we ask for (yay GStreamer!) + caps = gst_caps_from_string (SLV_ALLCAPS); + + gst_caps_replace (&slvideo->caps, caps); +} +*/ /* initialize the new element * instantiate pads and add them to element @@ -450,24 +491,24 @@ filter->retained_frame_width = filter->width; filter->retained_frame_height = filter->height; filter->retained_frame_format = SLV_PF_UNKNOWN; - GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS); - llgst_caps_replace (&filter->caps, caps); + GstCaps *caps = gst_caps_from_string (SLV_ALLCAPS); + gst_caps_replace (&filter->caps, caps); filter->resize_forced_always = false; filter->resize_try_width = -1; filter->resize_try_height = -1; GST_OBJECT_UNLOCK(filter); + + //gst_slvideo_update_caps(filter); } static void gst_slvideo_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - llg_return_if_fail (GST_IS_SLVIDEO (object)); + g_return_if_fail (GST_IS_SLVIDEO (object)); - switch (prop_id) { - default: + if (prop_id) { G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; } } @@ -475,12 +516,10 @@ gst_slvideo_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - llg_return_if_fail (GST_IS_SLVIDEO (object)); + g_return_if_fail (GST_IS_SLVIDEO (object)); - switch (prop_id) { - default: + if (prop_id) { G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; } } @@ -498,7 +537,7 @@ GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", 0, (gchar*)"Second Life Video Sink"); - return llgst_element_register (plugin, "private-slvideo", + return gst_element_register (plugin, "private-slvideo", GST_RANK_NONE, GST_TYPE_SLVIDEO); } @@ -508,20 +547,19 @@ some g++ versions buggily avoid __attribute__((constructor)) functions - so we provide an explicit plugin init function. */ -#define PACKAGE (gchar*)"packagehack" -// this macro quietly refers to PACKAGE internally -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - (gchar*)"private-slvideoplugin", - (gchar*)"SL Video sink plugin", - plugin_init, (gchar*)"1.0", (gchar*)"LGPL", - (gchar*)"Second Life", - (gchar*)"http://www.secondlife.com/"); -#undef PACKAGE + void gst_slvideo_init_class (void) { - ll_gst_plugin_register_static (&gst_plugin_desc); - DEBUGMSG("CLASS INIT"); + gst_plugin_register_static( GST_VERSION_MAJOR, + GST_VERSION_MINOR, + (const gchar *)"private-slvideoplugin", + (gchar *)"SL Video sink plugin", + plugin_init, + (const gchar *)"0.1", + GST_LICENSE_UNKNOWN, + (const gchar *)"Second Life", + (const gchar *)"Second Life", + (const gchar *)"http://www.secondlife.com/" ); } #endif // LL_GSTREAMER010_ENABLED diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp --- a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -28,6 +28,19 @@ #include "linden_common.h" + + +// Needed for _getcwd() RC +#ifdef LL_WINDOWS +#include +#include +#include +#endif + +#ifdef LL_DARWIN +#include +#endif + #include "llgl.h" #include "llplugininstance.h" @@ -35,36 +48,40 @@ #include "llpluginmessageclasses.h" #include "media_plugin_base.h" -#if LL_GSTREAMER010_ENABLED +//#if LL_GSTREAMER010_ENABLED extern "C" { #include +#include } #include "llmediaimplgstreamer.h" #include "llmediaimplgstreamertriviallogging.h" #include "llmediaimplgstreamervidplug.h" - -#include "llmediaimplgstreamer_syms.h" - ////////////////////////////////////////////////////////////////////////////// // class MediaPluginGStreamer010 : public MediaPluginBase { public: MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginGStreamer010(); /* virtual */ void receiveMessage(const char *message_string); static bool startup(); static bool closedown(); + static void set_gst_plugin_path(); + gboolean processGSTEvents(GstBus *bus, GstMessage *message); + // basic log file writing + static bool writeToLog(const char* str, ...); + private: + ~MediaPluginGStreamer010(); + std::string getVersion(); bool navigateTo( const std::string urlIn ); bool seek( double time_sec ); @@ -76,7 +93,7 @@ bool play(double rate); bool getTimePos(double &sec_out); - static const double MIN_LOOP_SEC = 1.0F; + #define MIN_LOOP_SEC 1.0F bool mIsLooping; @@ -132,11 +149,12 @@ bool mSeekWanted; double mSeekDestination; + + std::string mLastTitle; // Very GStreamer-specific GMainLoop *mPump; // event pump for this media GstElement *mPlaybin; - GstElement *mVisualizer; GstSLVideo *mVideoSink; }; @@ -155,12 +173,11 @@ mSeekDestination(0.0), mPump ( NULL ), mPlaybin ( NULL ), - mVisualizer ( NULL ), + mVideoSink ( NULL ), mCommand ( COMMAND_NONE ) { - std::ostringstream str; - INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid())); + writeToLog((char*)"MediaPluginGStreamer010 PID=%u", U32(LL_GETPID())); } /////////////////////////////////////////////////////////////////////////////// @@ -180,6 +197,38 @@ } #endif // LL_GST_REPORT_STATE_CHANGES +// static +bool MediaPluginGStreamer010::writeToLog(const char* str, ...) +{ + LLFILE* fp = LLFile::fopen("media_plugin_gstreamer010.log", "a"); + + if (!fp) + { + return false; + } + + time_t timeptr = time(NULL); + struct tm* ltime = localtime(&timeptr); + char strbuf[1024]; + char strmsg[1024]; + sprintf(strbuf, "[%d:%d:%d] ", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); + va_list arglist; + va_start(arglist, str); + vsprintf(strmsg, str, arglist); + strcat(strbuf, strmsg); + + // write to log file + fprintf(fp, strbuf); + fprintf(fp, "\n"); + fclose(fp); + + // mirror in console window if we have one + puts(strbuf); + + return true; +} + +// static gboolean MediaPluginGStreamer010::processGSTEvents(GstBus *bus, GstMessage *message) @@ -188,152 +237,176 @@ return TRUE; // shield against GStreamer bug if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED && - GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING) + GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING && + GST_MESSAGE_TYPE(message) != GST_MESSAGE_TAG) { - DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); - } - else - { - // TODO: grok 'duration' message type - DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); + writeToLog((char*)"Got GST message type: %s", GST_MESSAGE_TYPE_NAME (message)); } - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_BUFFERING: { - // NEEDS GST 0.10.11+ - if (llgst_message_parse_buffering) + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_BUFFERING: { + // NEEDS GST 0.10.11+ and America discovered by C.Columbus gint percent = 0; - llgst_message_parse_buffering(message, &percent); - DEBUGMSG("GST buffering: %d%%", percent); - } - break; - } - case GST_MESSAGE_STATE_CHANGED: { - GstState old_state; - GstState new_state; - GstState pending_state; - llgst_message_parse_state_changed(message, - &old_state, - &new_state, - &pending_state); -#ifdef LL_GST_REPORT_STATE_CHANGES - // not generally very useful, and rather spammy. - DEBUGMSG("state change (old,,pending): %s,<%s>,%s", - get_gst_state_name(old_state), - get_gst_state_name(new_state), - get_gst_state_name(pending_state)); -#endif // LL_GST_REPORT_STATE_CHANGES + gst_message_parse_buffering(message, &percent); + writeToLog((char*)"GST buffering: %d%%", percent); - switch (new_state) { - case GST_STATE_VOID_PENDING: - break; - case GST_STATE_NULL: - break; - case GST_STATE_READY: - setStatus(STATUS_LOADED); - break; - case GST_STATE_PAUSED: - setStatus(STATUS_PAUSED); - break; - case GST_STATE_PLAYING: - setStatus(STATUS_PLAYING); break; } - break; - } - case GST_MESSAGE_ERROR: { - GError *err = NULL; - gchar *debug = NULL; + case GST_MESSAGE_STATE_CHANGED: { + GstState old_state; + GstState new_state; + GstState pending_state; + gst_message_parse_state_changed(message, + &old_state, + &new_state, + &pending_state); + #ifdef LL_GST_REPORT_STATE_CHANGES + // not generally very useful, and rather spammy. + writeToLog((char*)"state change (old,,pending): %s,<%s>,%s", + get_gst_state_name(old_state), + get_gst_state_name(new_state), + get_gst_state_name(pending_state)); + #endif // LL_GST_REPORT_STATE_CHANGES - llgst_message_parse_error (message, &err, &debug); - WARNMSG("GST error: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); - - mCommand = COMMAND_STOP; - - setStatus(STATUS_ERROR); - - break; - } - case GST_MESSAGE_INFO: { - if (llgst_message_parse_info) + switch (new_state) + { + case GST_STATE_VOID_PENDING: + break; + case GST_STATE_NULL: + break; + case GST_STATE_READY: + setStatus(STATUS_LOADED); + break; + case GST_STATE_PAUSED: + setStatus(STATUS_PAUSED); + break; + case GST_STATE_PLAYING: + setStatus(STATUS_PLAYING); + break; + } + break; + } + case GST_MESSAGE_ERROR: + { + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_error (message, &err, &debug); + writeToLog((char*)"GST error: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + mCommand = COMMAND_STOP; + + setStatus(STATUS_ERROR); + + break; + } + case GST_MESSAGE_INFO: { GError *err = NULL; gchar *debug = NULL; - llgst_message_parse_info (message, &err, &debug); - INFOMSG("GST info: %s", err?err->message:"(unknown)"); + gst_message_parse_info (message, &err, &debug); + writeToLog((char*)"GST info: %s", err?err->message:"(unknown)"); if (err) g_error_free (err); g_free (debug); + + break; } - break; - } - case GST_MESSAGE_WARNING: { - GError *err = NULL; - gchar *debug = NULL; + case GST_MESSAGE_WARNING: + { + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_warning (message, &err, &debug); + writeToLog((char*)"GST warning: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + break; + } + case GST_MESSAGE_TAG: + { + GstTagList *new_tags; - llgst_message_parse_warning (message, &err, &debug); - WARNMSG("GST warning: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); + gst_message_parse_tag( message, &new_tags ); - break; - } - case GST_MESSAGE_EOS: - /* end-of-stream */ - DEBUGMSG("GST end-of-stream."); - if (mIsLooping) + gchar *title = NULL; + + if ( gst_tag_list_get_string(new_tags, GST_TAG_TITLE, &title) ) + { + //writeToLog((char*)"Title: %s", title); + std::string newtitle(title); + gst_tag_list_free(new_tags); + + if ( newtitle != mLastTitle && !newtitle.empty() ) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", newtitle ); + sendMessage( message ); + mLastTitle = newtitle; + } + g_free(title); + } + + break; + } + case GST_MESSAGE_EOS: { - DEBUGMSG("looping media..."); - double eos_pos_sec = 0.0F; - bool got_eos_position = getTimePos(eos_pos_sec); - - if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) + /* end-of-stream */ + writeToLog((char*)"GST end-of-stream."); + if (mIsLooping) { - // if we know that the movie is really short, don't - // loop it else it can easily become a time-hog - // because of GStreamer spin-up overhead - DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); - // inject a COMMAND_PAUSE - mCommand = COMMAND_PAUSE; - } - else - { -#undef LLGST_LOOP_BY_SEEKING -// loop with a stop-start instead of a seek, because it actually seems rather -// faster than seeking on remote streams. -#ifdef LLGST_LOOP_BY_SEEKING - // first, try looping by an explicit rewind - bool seeksuccess = seek(0.0); - if (seeksuccess) + //writeToLog((char*)"looping media..."); + double eos_pos_sec = 0.0F; + bool got_eos_position = getTimePos(eos_pos_sec); + + if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) { - play(1.0); + // if we know that the movie is really short, don't + // loop it else it can easily become a time-hog + // because of GStreamer spin-up overhead + writeToLog((char*)"really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); + // inject a COMMAND_PAUSE + mCommand = COMMAND_PAUSE; } else -#endif // LLGST_LOOP_BY_SEEKING - { // use clumsy stop-start to loop - DEBUGMSG("didn't loop by rewinding - stopping and starting instead..."); - stop(); - play(1.0); + { + #undef LLGST_LOOP_BY_SEEKING + // loop with a stop-start instead of a seek, because it actually seems rather + // faster than seeking on remote streams. + #ifdef LLGST_LOOP_BY_SEEKING + // first, try looping by an explicit rewind + bool seeksuccess = seek(0.0); + if (seeksuccess) + { + play(1.0); + } + else + #endif // LLGST_LOOP_BY_SEEKING + { // use clumsy stop-start to loop + writeToLog((char*)"didn't loop by rewinding - stopping and starting instead..."); + stop(); + play(1.0); + } } } - } - else // not a looping media - { - // inject a COMMAND_STOP - mCommand = COMMAND_STOP; - } - break; - default: - /* unhandled message */ - break; + else // not a looping media + { + // inject a COMMAND_STOP + mCommand = COMMAND_STOP; + } + } break; + + default: + /* unhandled message */ + break; } /* we want to be notified again the next time there is a message @@ -364,7 +437,7 @@ setStatus(STATUS_LOADING); - DEBUGMSG("Setting media URI: %s", urlIn.c_str()); + writeToLog((char*)"Setting media URI: %s", urlIn.c_str()); mSeekWanted = false; @@ -392,13 +465,13 @@ if (!mDoneInit) return false; // error - DEBUGMSG("updating media..."); + //writeToLog((char*)"updating media..."); // sanity check if (NULL == mPump || NULL == mPlaybin) { - DEBUGMSG("dead media..."); + writeToLog((char*)"dead media..."); return false; } @@ -428,7 +501,7 @@ GST_OBJECT_LOCK(mVideoSink); if (mVideoSink->retained_frame_ready) { - DEBUGMSG("NEW FRAME READY"); + writeToLog((char*)"NEW FRAME READY"); if (mVideoSink->retained_frame_width != mCurrentWidth || mVideoSink->retained_frame_height != mCurrentHeight) @@ -459,7 +532,7 @@ GST_OBJECT_UNLOCK(mVideoSink); mCurrentRowbytes = neww * newd; - DEBUGMSG("video container resized to %dx%d", + writeToLog((char*)"video container resized to %dx%d", neww, newh); mDepth = newd; @@ -487,7 +560,7 @@ } GST_OBJECT_UNLOCK(mVideoSink); - DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST"); + writeToLog((char*)"NEW FRAME REALLY TRULY CONSUMED, TELLING HOST"); setDirty(0,0,mCurrentWidth,mCurrentHeight); } @@ -498,7 +571,7 @@ GST_OBJECT_UNLOCK(mVideoSink); - DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize"); + writeToLog((char*)"NEW FRAME not consumed, still waiting for a shm segment and/or shm resize"); } return true; @@ -537,11 +610,11 @@ bool MediaPluginGStreamer010::pause() { - DEBUGMSG("pausing media..."); + writeToLog((char*)"pausing media..."); // todo: error-check this? if (mDoneInit && mPlaybin) { - llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); + gst_element_set_state(mPlaybin, GST_STATE_PAUSED); return true; } return false; @@ -550,11 +623,11 @@ bool MediaPluginGStreamer010::stop() { - DEBUGMSG("stopping media..."); + writeToLog((char*)"stopping media..."); // todo: error-check this? if (mDoneInit && mPlaybin) { - llgst_element_set_state(mPlaybin, GST_STATE_READY); + gst_element_set_state(mPlaybin, GST_STATE_READY); return true; } return false; @@ -564,12 +637,11 @@ MediaPluginGStreamer010::play(double rate) { // NOTE: we don't actually support non-natural rate. - - DEBUGMSG("playing media... rate=%f", rate); + writeToLog((char*)"playing media... rate=%f", rate); // todo: error-check this? if (mDoneInit && mPlaybin) { - llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); + gst_element_set_state(mPlaybin, GST_STATE_PLAYING); return true; } return false; @@ -582,11 +654,14 @@ // possible, as many gst-plugins-base versions up to at least // November 2008 have critical race-conditions in setting volume - sigh if (mVolume == volume) + { return true; // nothing to do, everything's fine + } mVolume = volume; if (mDoneInit && mPlaybin) { + writeToLog("MediaPluginGStreamer010::receiveMessage: set_volume: %f", volume); g_object_set(mPlaybin, "volume", mVolume, NULL); return true; } @@ -600,13 +675,13 @@ bool success = false; if (mDoneInit && mPlaybin) { - success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, + success = gst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); } - DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d", + writeToLog((char*)"MEDIA SEEK REQUEST to %f sec result was %d", float(time_sec), int(success)); return success; } @@ -619,11 +694,9 @@ { gint64 pos; GstFormat timefmt = GST_FORMAT_TIME; - got_position = - llgst_element_query_position && - llgst_element_query_position(mPlaybin, - &timefmt, - &pos); + got_position = gst_element_query_position(mPlaybin, + &timefmt, + &pos); got_position = got_position && (timefmt == GST_FORMAT_TIME); // GStreamer may have other ideas, but we consider the current position @@ -662,10 +735,10 @@ setStatus(STATUS_LOADING); - DEBUGMSG("setting up media..."); + writeToLog((char*)"setting up media..."); mIsLooping = false; - mVolume = 0.1234567; // minor hack to force an initial volume update + mVolume = (float) 0.1234567; // minor hack to force an initial volume update // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); @@ -676,7 +749,7 @@ } // instantiate a playbin element to do the hard work - mPlaybin = llgst_element_factory_make ("playbin", "play"); + mPlaybin = gst_element_factory_make ("playbin", "play"); if (!mPlaybin) { setStatus(STATUS_ERROR); @@ -684,53 +757,25 @@ } // get playbin's bus - GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); if (!bus) { setStatus(STATUS_ERROR); return false; // error } - mBusWatchID = llgst_bus_add_watch (bus, + mBusWatchID = gst_bus_add_watch (bus, llmediaimplgstreamer_bus_callback, this); - llgst_object_unref (bus); + gst_object_unref (bus); -#if 0 // not quite stable/correct yet - // get a visualizer element (bonus feature!) - char* vis_name = getenv("LL_GST_VIS_NAME"); - if (!vis_name || - (vis_name && std::string(vis_name)!="none")) - { - if (vis_name) - { - mVisualizer = llgst_element_factory_make (vis_name, "vis"); - } - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("goom", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis"); - if (!mVisualizer) - { - // That's okay, we don't NEED this. - } - } - } - } - } -#endif if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate a custom video sink mVideoSink = - GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); + GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { - WARNMSG("Could not instantiate private-slvideo element."); + writeToLog((char*)"Could not instantiate private-slvideo element."); // todo: cleanup. setStatus(STATUS_ERROR); return false; // error @@ -740,11 +785,6 @@ g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); } - if (mVisualizer) - { - g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL); - } - return true; } @@ -754,7 +794,7 @@ if (!mDoneInit) return false; // error - DEBUGMSG("unloading media..."); + writeToLog((char*)"unloading media..."); // stop getting callbacks for this bus g_source_remove(mBusWatchID); @@ -762,17 +802,11 @@ if (mPlaybin) { - llgst_element_set_state (mPlaybin, GST_STATE_NULL); - llgst_object_unref (GST_OBJECT (mPlaybin)); + gst_element_set_state (mPlaybin, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (mPlaybin)); mPlaybin = NULL; } - - if (mVisualizer) - { - llgst_object_unref (GST_OBJECT (mVisualizer)); - mVisualizer = NULL; - } - + if (mPump) { g_main_loop_quit(mPump); @@ -802,7 +836,13 @@ // Init the glib type system - we need it. g_type_init(); + set_gst_plugin_path(); + // removed case gst_segtrap_set_enabled doesn't exist + // Because: Latest stable gstreamer at the time writing this: 0.10.31 +// gst_segtrap_set_enabled(FALSE);// Since 0.10.10 , was released Sep 2006 + +/* // Get symbols! #if LL_DARWIN if (! grab_gst_syms("libgstreamer-0.10.dylib", @@ -815,27 +855,27 @@ "libgstvideo-0.10.so.0") ) #endif { - WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); + writeToLog((char*)"Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); return false; } - - if (llgst_segtrap_set_enabled) - { - llgst_segtrap_set_enabled(FALSE); - } - else - { - WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); - } - +*/ +// if (gst_segtrap_set_enabled) +// { + gst_segtrap_set_enabled(FALSE); +// } +// else +// { +// writeToLog((char*)"gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); +// } +/* #if LL_LINUX // Gstreamer tries a fork during init, waitpid-ing on it, // which conflicts with any installed SIGCHLD handler... struct sigaction tmpact, oldact; - if (llgst_registry_fork_set_enabled) { - // if we can disable SIGCHLD-using forking behaviour, - // do it. - llgst_registry_fork_set_enabled(false); + if (gst_registry_fork_set_enabled) { + // if we can disable SIGCHLD-using forking behaviour, + // do it. + gst_registry_fork_set_enabled(false); } else { // else temporarily install default SIGCHLD handler @@ -846,47 +886,164 @@ sigaction(SIGCHLD, &tmpact, &oldact); } #endif // LL_LINUX - +*/ // Protect against GStreamer resetting the locale, yuck. static std::string saved_locale; saved_locale = setlocale(LC_ALL, NULL); // finally, try to initialize GStreamer! GError *err = NULL; - gboolean init_gst_success = llgst_init_check(NULL, NULL, &err); + gboolean init_gst_success = gst_init_check(NULL, NULL, &err); // restore old locale setlocale(LC_ALL, saved_locale.c_str() ); -#if LL_LINUX - // restore old SIGCHLD handler - if (!llgst_registry_fork_set_enabled) - sigaction(SIGCHLD, &oldact, NULL); -#endif // LL_LINUX + if (!init_gst_success) // fail { if (err) { - WARNMSG("GST init failed: %s", err->message); + writeToLog((char*)"GST init failed: %s", err->message); g_error_free(err); } else { - WARNMSG("GST init failed for unspecified reason."); + writeToLog((char*)"GST init failed for unspecified reason."); } return false; } - + + // Set up logging facilities + gst_debug_remove_log_function( gst_debug_log_default ); +// gst_debug_add_log_function( gstreamer_log, NULL ); + // Init our custom plugins - only really need do this once. gst_slvideo_init_class(); + // List the plugins GStreamer can find + writeToLog((char*)"Found GStreamer plugins:"); + GList *list; + GstRegistry *registry = gst_registry_get_default(); + std::string loaded = "No"; + for (list = gst_registry_get_plugin_list(registry); + list != NULL; + list = g_list_next(list)) + { + GstPlugin *list_plugin = (GstPlugin *)list->data; + if (gst_plugin_is_loaded(list_plugin)) loaded = "Yes"; + writeToLog((char*)"%s, loaded? %s", gst_plugin_get_name(list_plugin), loaded.c_str()); + } + gst_plugin_list_free(list); + mDoneInit = true; } return true; } +void MediaPluginGStreamer010::set_gst_plugin_path() +{ + // Linux sets GST_PLUGIN_PATH in wrapper.sh, not here. +#if LL_WINDOWS || LL_DARWIN + + std::string imp_dir = ""; + + // Get the current working directory: +#if LL_WINDOWS + char* raw_dir; + raw_dir = _getcwd(NULL,0); + if( raw_dir != NULL ) + { + imp_dir = std::string( raw_dir ); + } + + +#elif LL_DARWIN + CFBundleRef main_bundle = CFBundleGetMainBundle(); + if( main_bundle != NULL ) + { + CFURLRef bundle_url = CFBundleCopyBundleURL( main_bundle ); + if( bundle_url != NULL ) + { + #ifndef MAXPATHLEN + #define MAXPATHLEN 1024 + #endif + char raw_dir[MAXPATHLEN]; + if( CFURLGetFileSystemRepresentation( bundle_url, true, (UInt8 *)raw_dir, MAXPATHLEN) ) + { + imp_dir = std::string( raw_dir ) + "/Contents/MacOS/"; + } + CFRelease(bundle_url); + } + } +#endif + + if( imp_dir == "" ) + { + writeToLog((char*)"Could not get application directory, not setting GST_PLUGIN_PATH."); + return; + } + + writeToLog((char*)"Install directory is at %s", imp_dir.c_str()); + + // ":" on Mac and 'Nix, ";" on Windows + std::string separator = G_SEARCHPATH_SEPARATOR_S; + + // Grab the current path, if it's set. + std::string old_plugin_path = ""; + char *old_path = getenv("GST_PLUGIN_PATH"); + if(old_path == NULL) + { + writeToLog((char*)"Did not find user-set GST_PLUGIN_PATH."); + } + else + { + old_plugin_path = separator + std::string( old_path ); + } + std::string plugin_path = + "GST_PLUGIN_PATH=" + +#if LL_WINDOWS + imp_dir + "\\gstreamer-plugins" + +#elif LL_DARWIN + imp_dir + separator + + imp_dir + "/../Resources/lib/gstreamer-plugins" + +#endif + old_plugin_path; + + int put_result; + + // Place GST_PLUGIN_PATH in the environment settings +#if LL_WINDOWS + put_result = _putenv( (char*)plugin_path.c_str() ); +#elif LL_DARWIN + put_result = putenv( (char*)plugin_path.c_str() ); +#endif + + if( put_result == -1 ) + { + writeToLog((char*)"Setting GST_PLUGIN_PATH failed!"); + } + else + { + writeToLog((char*)"GST_PLUGIN_PATH set to %s", getenv("GST_PLUGIN_PATH")); + } + + // Don't load system plugins. We only want to use ours, to avoid conflicts. +#if LL_WINDOWS + put_result = _putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" ); +#elif LL_DARWIN + put_result = putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" ); +#endif + + if( put_result == -1 ) + { + writeToLog((char*)"Setting GST_PLUGIN_SYSTEM_PATH=\"\" failed!"); + } + +#endif // LL_WINDOWS || LL_DARWIN +} + void MediaPluginGStreamer010::sizeChanged() @@ -899,7 +1056,7 @@ { mNaturalWidth = mCurrentWidth; mNaturalHeight = mCurrentHeight; - DEBUGMSG("Media NATURAL size better detected as %dx%d", + writeToLog((char*)"Media NATURAL size better detected as %dx%d", mNaturalWidth, mNaturalHeight); } @@ -914,7 +1071,7 @@ message.setValue("name", mTextureSegmentName); message.setValueS32("width", mNaturalWidth); message.setValueS32("height", mNaturalHeight); - DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); + writeToLog((char*)"<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); sendMessage(message); } } @@ -925,23 +1082,25 @@ bool MediaPluginGStreamer010::closedown() { + if (!mDoneInit) return false; // error - ungrab_gst_syms(); mDoneInit = false; + writeToLog("GStreamer010 closed down"); + return true; } MediaPluginGStreamer010::~MediaPluginGStreamer010() { - DEBUGMSG("MediaPluginGStreamer010 destructor"); + //writeToLog((char*)"MediaPluginGStreamer010 destructor"); closedown(); - DEBUGMSG("GStreamer010 closing down"); + writeToLog("GStreamer010 destructor"); } @@ -949,11 +1108,10 @@ MediaPluginGStreamer010::getVersion() { std::string plugin_version = "GStreamer010 media plugin, GStreamer version "; - if (mDoneInit && - llgst_version) + if (mDoneInit) // && gst_version) { guint major, minor, micro, nano; - llgst_version(&major, &minor, µ, &nano); + gst_version(&major, &minor, µ, &nano); plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO); } else @@ -965,7 +1123,7 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) { - //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\""; LLPluginMessage message_in; @@ -986,11 +1144,11 @@ if ( load() ) { - DEBUGMSG("GStreamer010 media instance set up"); + writeToLog((char*)"GStreamer010 media instance set up"); } else { - WARNMSG("GStreamer010 media instance failed to set up"); + writeToLog((char*)"GStreamer010 media instance failed to set up"); } message.setValue("plugin_version", getVersion()); @@ -1006,8 +1164,19 @@ } else if(message_name == "cleanup") { + writeToLog("MediaPluginGStreamer010::receiveMessage: cleanup"); unload(); closedown(); + + // Reply once we're done + LLPluginMessage message("base", "cleanup_reply"); + sendMessage(message); + + // Now suicide. Because It is the only honorable thing to do. + // JUST BE CAREFUL! + // http://www.parashift.com/c++-faq-lite/delete-this.html + delete this; + return; } else if(message_name == "shm_added") { @@ -1017,7 +1186,7 @@ std::string name = message_in.getValue("name"); std::ostringstream str; - INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress); + writeToLog((char*)"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)); } @@ -1025,7 +1194,7 @@ { std::string name = message_in.getValue("name"); - DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str()); + writeToLog((char*)"MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str()); SharedSegmentMap::iterator iter = mSharedSegments.find(name); if(iter != mSharedSegments.end()) @@ -1043,7 +1212,7 @@ } else { - WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!"); + writeToLog((char*)"MediaPluginGStreamer010::receiveMessage: unknown shared memory region!"); } // Send the response so it can be cleaned up. @@ -1054,7 +1223,7 @@ else { std::ostringstream str; - INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str()); + writeToLog((char*)"MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str()); } } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) @@ -1097,7 +1266,7 @@ S32 texture_height = message_in.getValueS32("texture_height"); std::ostringstream str; - INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height); + writeToLog((char*)"---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height); LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); message.setValue("name", name); @@ -1113,8 +1282,8 @@ SharedSegmentMap::iterator iter = mSharedSegments.find(name); if(iter != mSharedSegments.end()) { - INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height); - INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height); + writeToLog((char*)"*** Got size change with matching shm, new size is %d x %d", width, height); + writeToLog((char*)"*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height); mPixels = (unsigned char*)iter->second.mAddress; mTextureSegmentName = name; @@ -1124,7 +1293,7 @@ if (texture_width > 1 || texture_height > 1) // not a dummy size from the app, a real explicit forced size { - INFOMSG("**** = REAL RESIZE REQUEST FROM APP"); + writeToLog((char*)"**** = REAL RESIZE REQUEST FROM APP"); GST_OBJECT_LOCK(mVideoSink); mVideoSink->resize_forced_always = true; @@ -1206,13 +1375,23 @@ } else { - INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str()); + writeToLog((char*)"MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str()); } } } int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) { + // init log file + LLFILE* fp = LLFile::fopen("media_plugin_gstreamer010.log", "w"); + if (fp) + { + time_t timeptr = time(NULL); + fprintf(fp, "%s", asctime(localtime(&timeptr))); + fprintf(fp, "<--- Begin media_plugin_gstreamer010 initialization --->\n"); + fclose(fp); + } + if (MediaPluginGStreamer010::startup()) { MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data); @@ -1227,40 +1406,40 @@ } } -#else // LL_GSTREAMER010_ENABLED +//#else // LL_GSTREAMER010_ENABLED +// +//// Stubbed-out class with constructor/destructor (necessary or windows linker +//// will just think its dead code and optimize it all out) +//class MediaPluginGStreamer010 : public MediaPluginBase +//{ +//public: +// MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); +// ~MediaPluginGStreamer010(); +// /* virtual */ void receiveMessage(const char *message_string); +//}; +// +//MediaPluginGStreamer010::MediaPluginGStreamer010( +// LLPluginInstance::sendMessageFunction host_send_func, +// void *host_user_data ) : +// MediaPluginBase(host_send_func, host_user_data) +//{ +// // no-op +//} +// +//MediaPluginGStreamer010::~MediaPluginGStreamer010() +//{ +// // no-op +//} +// +//void MediaPluginGStreamer010::receiveMessage(const char *message_string) +//{ +// // no-op +//} +// +//// We're building without GStreamer enabled. Just refuse to initialize. +//int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +//{ +// return -1; +//} -// Stubbed-out class with constructor/destructor (necessary or windows linker -// will just think its dead code and optimize it all out) -class MediaPluginGStreamer010 : public MediaPluginBase -{ -public: - MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - ~MediaPluginGStreamer010(); - /* virtual */ void receiveMessage(const char *message_string); -}; - -MediaPluginGStreamer010::MediaPluginGStreamer010( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) -{ - // no-op -} - -MediaPluginGStreamer010::~MediaPluginGStreamer010() -{ - // no-op -} - -void MediaPluginGStreamer010::receiveMessage(const char *message_string) -{ - // no-op -} - -// We're building without GStreamer enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - return -1; -} - -#endif // LL_GSTREAMER010_ENABLED +//#endif // LL_GSTREAMER010_ENABLED diff -r 9539c10021ba -r 52b800cc01fc indra/media_plugins/webkit/media_plugin_webkit.cpp --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -52,6 +52,8 @@ #if LL_WINDOWS # include +# include +# include #else # include # include @@ -136,7 +138,7 @@ void setInitState(int state) { -// std::cerr << "changing init state to " << state << std::endl; + std::cout << "changing init state to " << state << std::endl; mInitState = state; } @@ -180,12 +182,12 @@ unsigned int buffer_size = rowspan * height; #endif // !LL_QTWEBKIT_USES_PIXMAPS -// std::cerr << "webkit plugin: updating" << std::endl; + //std::cout << "webkit plugin: updating" << std::endl; // TODO: should get rid of this memcpy if possible if ( mPixels && browser_pixels ) { -// std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; + //std::cout << " memcopy of " << buffer_size << " bytes" << std::endl; #if LL_QTWEBKIT_USES_PIXMAPS // copy the pixel data upside-down because of the co-ord system @@ -200,7 +202,7 @@ if ( mWidth > 0 && mHeight > 0 ) { -// std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl; + //std::cout << "Setting dirty, " << mWidth << " x " << mHeight << std::endl; setDirty( 0, 0, mWidth, mHeight ); } @@ -220,7 +222,7 @@ char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) { - llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl; + std::cout << "Couldn't get cwd - probably too long - failing to init." << std::endl; return false; } std::string application_dir = std::string( cwd ); @@ -274,7 +276,7 @@ #if LL_WINDOWS char window_title[ MAX_PATH ]; GetConsoleTitleA( window_title, MAX_PATH ); - void* native_window_handle = (void*)FindWindowA( NULL, window_title ); + void* native_window_handle = (void*)FindWindowA( NULL, window_title ); #else void* native_window_handle = 0; #endif @@ -768,7 +770,7 @@ break; } -// std::cerr << "key event " << (int)key_event << ", native_key_data = " << native_key_data << std::endl; + std::cout << "key event " << (int)key_event << ", native_key_data = " << native_key_data << std::endl; uint32_t native_scan_code = 0; uint32_t native_virtual_key = 0; @@ -786,7 +788,7 @@ { uint32_t key = LLQtWebKit::KEY_NONE; -// std::cerr << "unicode input, native_key_data = " << native_key_data << std::endl; + std::cout << "unicode input, native_key_data = " << native_key_data << std::endl; if(utf8str.size() == 1) { @@ -864,7 +866,7 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : MediaPluginBase(host_send_func, host_user_data) { -// std::cerr << "MediaPluginWebKit constructor" << std::endl; + std::cout << "MediaPluginWebKit constructor" << std::endl; mBrowserWindowId = 0; mInitState = INIT_STATE_UNINITIALIZED; @@ -896,12 +898,12 @@ // clean up LLQtWebKit::getInstance()->reset(); -// std::cerr << "MediaPluginWebKit destructor" << std::endl; + std::cout << "MediaPluginWebKit destructor" << std::endl; } void MediaPluginWebKit::receiveMessage(const char *message_string) { -// std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + //std::cout << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl; LLPluginMessage message_in; if(message_in.parse(message_string) >= 0) @@ -947,10 +949,10 @@ info.mSize = (size_t)message_in.getValueS32("size"); std::string name = message_in.getValue("name"); -// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name -// << ", size: " << info.mSize -// << ", address: " << info.mAddress -// << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name + << ", size: " << info.mSize + << ", address: " << info.mAddress + << std::endl; mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); @@ -959,7 +961,7 @@ { std::string name = message_in.getValue("name"); -// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl; SharedSegmentMap::iterator iter = mSharedSegments.find(name); if(iter != mSharedSegments.end()) @@ -974,7 +976,7 @@ } else { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; } // Send the response so it can be cleaned up. @@ -984,7 +986,7 @@ } else { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; } } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) @@ -997,6 +999,12 @@ } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { + // These are spammy, comment out the line below if you need 'em -- MC + if (message_name != "mouse_event") + { + std::cout << "MediaPluginWebKit::receiveMessage: LLPLUGIN_MESSAGE_CLASS_MEDIA (non mouse_event) message: " << message_string << std::endl; + } + if(message_name == "init") { mTarget = message_in.getValue("target"); @@ -1025,6 +1033,7 @@ else { // if initialization failed, we're done. + std::cerr << "Fatal error: initialization failed" << std::endl; mDeleteMe = true; } @@ -1078,8 +1087,8 @@ // size changed so tell the browser LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); - // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight - // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; + std::cout << "webkit plugin: set size to " << mWidth << " x " << mHeight + << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId); @@ -1091,7 +1100,7 @@ else { // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. - // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; + std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; mDeleteMe = true; return; } @@ -1099,6 +1108,7 @@ else { // Setting up the browser window failed. This is a fatal error. + std::cerr << "Fatal error: setting up the browser window failed" << std::endl; mDeleteMe = true; } @@ -1122,7 +1132,7 @@ { std::string uri = message_in.getValue("uri"); -// std::cout << "loading URI: " << uri << std::endl; + std::cout << "loading URI: " << uri << std::endl; if(!uri.empty()) { @@ -1205,22 +1215,22 @@ unicodeInput(text, decodeModifiers(modifiers), native_key_data); } - if(message_name == "edit_cut") + else if(message_name == "edit_cut") { LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT ); checkEditState(); } - if(message_name == "edit_copy") + else if(message_name == "edit_copy") { LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY ); checkEditState(); } - if(message_name == "edit_paste") + else if(message_name == "edit_paste") { LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE ); checkEditState(); } - if(message_name == "pick_file_response") + else if(message_name == "pick_file_response") { onPickFileResponse(message_in.getValue("file")); } @@ -1301,7 +1311,7 @@ } else { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; } } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) @@ -1454,12 +1464,12 @@ } else { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; }; } else { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl; + std::cout << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl; }; } } @@ -1471,6 +1481,36 @@ int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) { +#ifdef LL_WINDOWS + // Make sure no files in a user's PATH interfere with the plugin. Even if + // their PATH is empty, set it anyway before anything is initialized + // getcwd can put us in llplugin or the .exe directory, depending-- MC + char* raw_dir = _getcwd(NULL, 0); + if (raw_dir != NULL) + { + std::string cur_dir(raw_dir); + std::string new_path = "PATH="; + char* old_path = getenv("PATH"); + if (old_path != NULL) + { + new_path += cur_dir + ";" + cur_dir + "\\llplugin" + ";" + std::string(old_path); + } + else + { + new_path += cur_dir + ";" + cur_dir + "\\llplugin"; + } + + if (_putenv(new_path.c_str()) == -1) + { + std::cout << "Setting PATH environment variable to " << new_path << " failed!" << std::endl; + } + else + { + std::cout << "Setting PATH environment variable to " << new_path << std::endl; + } + } +#endif // LL_WINDOWS + MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); *plugin_send_func = MediaPluginWebKit::staticReceiveMessage; *plugin_user_data = (void*)self; diff -r 9539c10021ba -r 52b800cc01fc indra/newview/CMakeLists.txt --- a/indra/newview/CMakeLists.txt Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/newview/CMakeLists.txt Sun Nov 11 17:27:32 2012 -0600 @@ -1675,6 +1675,7 @@ SLPlugin media_plugin_quicktime media_plugin_webkit + media_plugin_gstreamer010 winmm_shim windows-crash-logger windows-updater diff -r 9539c10021ba -r 52b800cc01fc indra/newview/llviewermedia.cpp --- a/indra/newview/llviewermedia.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/newview/llviewermedia.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -27,7 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llviewermedia.h" - +#include "llpluginclassmedia.h" #include "llagent.h" #include "llagentcamera.h" #include "llappviewer.h" @@ -76,6 +76,7 @@ #include // for SkinFolder listener #include + /*static*/ const char* LLViewerMedia::AUTO_PLAY_MEDIA_SETTING = "ParcelMediaAutoPlayEnable"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING = "MediaShowOnOthers"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING = "MediaShowWithinParcel"; @@ -1875,7 +1876,13 @@ const std::string plugin_dir = gDirUtilp->getLLPluginDir(); if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))) { - return media_source; + #if LL_WINDOWS + if (gSavedSettings.getBOOL("ShowConsoleWindow")) + { + media_source->showConsole(); + } + #endif + return media_source; } else { @@ -3169,9 +3176,27 @@ resetPreviousMediaState(); LLSD args; - args["PLUGIN"] = LLMIMETypes::implType(mCurrentMimeType); - // SJB: This is getting called every frame if the plugin fails to load, continuously respawining the alert! - //LLNotificationsUtil::add("MediaPluginFailed", args); + std::string plugin_name = LLMIMETypes::implType(mMimeType); + args["PLUGIN"] = plugin_name; + + // These should really be hardcoded in LLMimeTypes, if anywhere -- MC + std::string notification_name; + LLStringUtil::toLower(plugin_name); + if (plugin_name.find("quicktime") != std::string::npos) + { + notification_name = "MediaPluginFailedQuickTime"; + } + else if (plugin_name.find("webkit") != std::string::npos) + { + notification_name = "MediaPluginFailedWebkit"; + } + else + { + notification_name = "MediaPluginFailed"; + } + + LLNotificationsUtil::add(notification_name, args); + } break; diff -r 9539c10021ba -r 52b800cc01fc indra/newview/llviewermedia_streamingaudio.cpp --- a/indra/newview/llviewermedia_streamingaudio.cpp Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/newview/llviewermedia_streamingaudio.cpp Sun Nov 11 17:27:32 2012 -0600 @@ -53,34 +53,100 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url) { + if (url.empty()) + { + return; + } + + std::string test_url(url); + //llinfos << "Starting internet stream: " << test_url << llendl; + // note: it's okay if mURL is empty here + if (mURL != test_url) + { + // stop any previous stream that was playing + // this happens on parcel crossings, usually + stop(); + + if (mMediaPlugin) + { + mMediaPlugin->reset(); + mMediaPlugin = initializeMedia("audio/mpeg"); + } + } + mURL = test_url; + +#ifdef LL_DARWIN + // We need to change http:// streams to icy:// in order to use them with quicktime. + // This isn't a good place to put this, but none of this is good, so... -- MC + LLURI uri(test_url); + std::string scheme = uri.scheme(); + if ((scheme.empty() || "http" == scheme || "https" == scheme) && + ((test_url.length() > 4) && + (test_url.substr(test_url.length()-4, 4) != ".pls") && // Shoutcast listen.pls playlists + (test_url.substr(test_url.length()-4, 4) != ".m3u")) // Icecast liten.m3u playlists + ) + { + std::string temp_url = "icy:" + uri.opaque(); + test_url = temp_url; + } +#endif //LL_DARWIN + if (!mMediaPlugin) // lazy-init the underlying media plugin { mMediaPlugin = initializeMedia("audio/mpeg"); // assumes that whatever media implementation supports mp3 also supports vorbis. llinfos << "streaming audio mMediaPlugin is now " << mMediaPlugin << llendl; } + else if (mMediaPlugin->isPluginExited()) // If our reset didn't work right, try again + { + mMediaPlugin->reset(); + mMediaPlugin = initializeMedia("audio/mpeg"); + } if(!mMediaPlugin) return; - if (!url.empty()) { - llinfos << "Starting internet stream: " << url << llendl; - mURL = url; - mMediaPlugin->loadURI ( url ); + if (!url.empty()) + { + std::string test_url(url); + // We need to change http:// streams to icy:// in order to use them with quicktime. + // This isn't a good place to put this, but none of this is good, so... -- MC +#ifdef LL_DARWIN + LLURI uri(test_url); + std::string scheme = uri.scheme(); + if ((scheme.empty() || "http" == scheme || "https" == scheme) && + ((test_url.length() > 4) && + (test_url.substr(test_url.length()-4, 4) != ".pls") && // Shoutcast listen.pls playlists + (test_url.substr(test_url.length()-4, 4) != ".m3u")) // Icecast liten.m3u playlists + ) + { + std::string temp_url = "icy:" + uri.opaque(); + test_url = temp_url; + } +#endif //LL_DARWIN + //llinfos << "Starting internet stream: " << test_url << llendl; + mURL = test_url; + mMediaPlugin->loadURI ( test_url ); mMediaPlugin->start(); - llinfos << "Playing stream..." << llendl; - } else { - llinfos << "setting stream to NULL"<< llendl; - mURL.clear(); - mMediaPlugin->stop(); + llinfos << "Playing internet stream: " << mURL << llendl; + mMediaPlugin->start(); + llinfos << "Attempting to play internet stream: " << mURL << llendl; + } + else + { + //llinfos << "setting stream to NULL"<< llendl; + stop(); } } void LLStreamingAudio_MediaPlugins::stop() { + llinfos << "Stopping internet stream." << llendl; if(mMediaPlugin) { mMediaPlugin->stop(); + // MURDER DEATH KILL -- MC + //mMediaPlugin->forceCleanUpPlugin(); } mURL.clear(); diff -r 9539c10021ba -r 52b800cc01fc indra/newview/skins/default/xui/en/mime_types.xml --- a/indra/newview/skins/default/xui/en/mime_types.xml Tue Sep 25 13:04:14 2012 -0400 +++ b/indra/newview/skins/default/xui/en/mime_types.xml Sun Nov 11 17:27:32 2012 -0600 @@ -142,7 +142,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -197,7 +197,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -274,7 +274,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -285,7 +285,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -296,7 +296,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -307,7 +307,7 @@ audio - media_plugin_quicktime + media_plugin_gstreamer010 @@ -431,6 +431,17 @@ media_plugin_quicktime + + + + movie + + + media_plugin_quicktime + + + + + + audio + + + media_plugin_quicktime + + + + + + audio + + + media_plugin_quicktime + + + + + + audio + + + media_plugin_quicktime + + + + + + audio + + + media_plugin_quicktime + +