From 28029c51a8f26ac22cc49ab3458620290d026724 Mon Sep 17 00:00:00 2001 From: Yannick Brehon Date: Thu, 28 Jan 2010 14:36:22 +0100 Subject: [PATCH] Mozilla plugin event listeners. This is the patch as supplied, which needs work and as of yet doesn't compile on non-unix. Fixes pending. -- jpd --- THANKS | 2 + projects/mozilla/control/npolibvlc.cpp | 197 +++++++++++++++++++++++++ projects/mozilla/control/npolibvlc.h | 25 +++- projects/mozilla/support/npunix.c | 14 ++ projects/mozilla/vlcplugin.cpp | 94 ++++++++++++ projects/mozilla/vlcplugin.h | 113 ++++++++++++++ 6 files changed, 444 insertions(+), 1 deletion(-) diff --git a/THANKS b/THANKS index 54fb75abdb..25cb3cb1ee 100644 --- a/THANKS +++ b/THANKS @@ -22,6 +22,7 @@ Alexey Salmin - Russian localisation Alexis Ballier - Additional options in configure Alexis de Lattre - Documentation, packaging, IGMPv3 support and various fixes Amanpreet Singh Alam - Punjabi translation +Amir Gouini - VLC mozilla plugin event listerners. Andrea Guzzo - dc1394 firewire support André de Barros Martins Ribeiro - Brazilian portuguese localization Andre Pang - Annodex support @@ -316,6 +317,7 @@ William Hawkins - Speex RTP payload format Xavier Maillard - audio converters Xavier Marchesini - Win32 fixes Xènia Albà Cantero - Catalan translation +Yannick Bréhon - VLC mozilla plugin event listerners. Ye zhang - Fix for VLM RTSP concurent LEAVE make VLC crash Yuehua Zhao - real video codec Yuksel Yildirim - Turkish localisation diff --git a/projects/mozilla/control/npolibvlc.cpp b/projects/mozilla/control/npolibvlc.cpp index b412de3bcb..976d7733e5 100644 --- a/projects/mozilla/control/npolibvlc.cpp +++ b/projects/mozilla/control/npolibvlc.cpp @@ -50,6 +50,71 @@ return INVOKERESULT_GENERIC_ERROR; \ } } while(false) +#define ERROR_EVENT_NOT_FOUND "ERROR: One or more events could not be found." +#define ERROR_API_VERSION "ERROR: NPAPI version not high enough. (Gecko >= 1.9 needed)" + +// Make a copy of an NPVariant. +NPVariant copyNPVariant(const NPVariant& original) +{ + NPVariant res; + + if (NPVARIANT_IS_STRING(original)) + STRINGZ_TO_NPVARIANT(strdup(NPVARIANT_TO_STRING(original).utf8characters), res); + else if (NPVARIANT_IS_INT32(original)) + INT32_TO_NPVARIANT(NPVARIANT_TO_INT32(original), res); + else if (NPVARIANT_IS_DOUBLE(original)) + DOUBLE_TO_NPVARIANT(NPVARIANT_TO_DOUBLE(original), res); + else if (NPVARIANT_IS_OBJECT(original)) + { + NPObject *obj = NPVARIANT_TO_OBJECT(original); + NPN_RetainObject(obj); + OBJECT_TO_NPVARIANT(obj, res); + } + else if (NPVARIANT_IS_BOOLEAN(original)) + BOOLEAN_TO_NPVARIANT(NPVARIANT_TO_BOOLEAN(original), res); + + return res; +} + +// Parse an event Array given as a NPObject by JS. +// This function is similar to LibvlcPlaylistNPObject::parseOptions, +// but we don't use it because it's not clearly accessible and the FIXME flags +// implie that it might be modified. +bool parseEventArray(NPObject *obj, eventtypes_bitmap_t &eventToGet, NPP instance) +{ + NPIdentifier propId = NPN_GetStringIdentifier("length"); + NPVariant value; + + if (!NPN_GetProperty(instance, obj, propId, &value)) + return false; + + int count = NPVARIANT_TO_INT32(value); + NPN_ReleaseVariantValue(&value); + if (count == 0) + return false; + + int nOptions = 0; + while (nOptions < count) + { + propId = NPN_GetIntIdentifier(nOptions); + // if there is no other string in the array. + if( ! NPN_GetProperty(instance, obj, propId, &value) ) + break; + + // if the element is not a string. + if( ! NPVARIANT_IS_STRING(value) ) + { + NPN_ReleaseVariantValue(&value); + break; + } + + if (!eventToGet.add_event(NPVARIANT_TO_STRING(value).utf8characters)) + return false; + nOptions++; + } + return true; +} + /* ** implementation of libvlc root object */ @@ -80,6 +145,7 @@ const NPUTF8 * const LibvlcRootNPObject::propertyNames[] = "playlist", "subtitle", "video", + "events", "VersionInfo", }; COUNTNAMES(LibvlcRootNPObject,propertyCount,propertyNames); @@ -91,6 +157,7 @@ enum LibvlcRootNPObjectPropertyIds ID_root_playlist, ID_root_subtitle, ID_root_video, + ID_root_events, ID_root_VersionInfo, }; @@ -122,6 +189,14 @@ LibvlcRootNPObject::getProperty(int index, NPVariant &result) InstantObj( videoObj ); OBJECT_TO_NPVARIANT(NPN_RetainObject(videoObj), result); return INVOKERESULT_NO_ERROR; + case ID_root_events: + // create child object in lazyman fashion to avoid + // ownership problem with firefox + if( ! eventObj ) + eventObj = NPN_CreateObject(_instance, + RuntimeNPClass::getClass()); + OBJECT_TO_NPVARIANT(NPN_RetainObject(eventObj), result); + return INVOKERESULT_NO_ERROR; case ID_root_VersionInfo: return invokeResultString(libvlc_get_version(),result); default: @@ -1986,3 +2061,125 @@ LibvlcDeinterlaceNPObject::invoke(int index, const NPVariant *args, return INVOKERESULT_NO_ERROR; } + + +/* +** implementation of libvlc event object +*/ + +const NPUTF8 * const LibvlcEventNPObject::propertyNames[] = +{ +}; + +enum LibvlcEventNPObjectPropertyIds +{ +}; +COUNTNAMES(LibvlcEventNPObject,propertyCount,propertyNames); + +const NPUTF8 * const LibvlcEventNPObject::methodNames[] = +{ + "addListener", + "removeListeners", +}; +COUNTNAMES(LibvlcEventNPObject,methodCount,methodNames); + +enum LibvlcEventNPObjectMethodIds +{ + ID_event_addListener, + ID_event_removeListeners, +}; + +bool LibvlcEventNPObject::parseArgs(const NPVariant *args, uint32_t argCount, + eventtypes_bitmap_t &eventToGet) +{ + if (argCount > 2) + eventToGet.clear(); + + for (int argIndex = 2; argIndex < argCount; argIndex++) + { + if (NPVARIANT_IS_STRING(args[argIndex])) + { + if (!eventToGet.add_event(NPVARIANT_TO_STRING(args[argIndex]).utf8characters)) + return false; + } + else if (NPVARIANT_IS_OBJECT(args[argIndex])) + { + if (!parseEventArray(NPVARIANT_TO_OBJECT(args[argIndex]), eventToGet, _instance)) + return false; + } + else + return false; + } + return true; +} + +RuntimeNPObject::InvokeResult +LibvlcEventNPObject::invoke(int index, const NPVariant *args, + uint32_t argCount, NPVariant &result) +{ + /* is plugin still running */ + if( isPluginRunning() ) + { + libvlc_exception_t ex; + libvlc_exception_init(&ex); + + switch( index ) + { + case ID_event_addListener: + if (argCount >= 2) + { + // Checks if the first argument is a NPObject + if (!NPVARIANT_IS_OBJECT(args[0])) + return INVOKERESULT_NO_SUCH_METHOD; + + // Checks if the browser has the NPAPI version 0.19 at least. + if (!VlcPlugin::canUseEventListener()) + { + NPN_SetException(this, strdup(ERROR_API_VERSION)); + return INVOKERESULT_GENERIC_ERROR; + } + + VlcPlugin* p_plugin = getPrivate(); + + // Gets the binary field corresponding to the events the + // listener must listen to if specified. + // Else, listen to all events. + eventtypes_bitmap_t eventToGet; + eventToGet.set_all_events(); + + if (!parseArgs(args, argCount, eventToGet)) + { + NPN_SetException(this, strdup(ERROR_EVENT_NOT_FOUND)); + return INVOKERESULT_GENERIC_ERROR; + } + + NPObject *listener = NPVARIANT_TO_OBJECT(args[0]); + NPN_RetainObject(listener); + + EventListener *eventListener = new EventListener(); + eventListener->listener = listener; + eventListener->id = copyNPVariant(args[1]); + eventListener->eventMap = eventToGet; + + p_plugin->eventToCatch.add_event(eventToGet); + + p_plugin->eventListeners.push_back(eventListener); + + return INVOKERESULT_NO_ERROR; + } + return INVOKERESULT_NO_SUCH_METHOD; + case ID_event_removeListeners: + if (argCount == 0) + { + VlcPlugin* p_plugin = getPrivate(); + p_plugin->eventListeners.clear(); + p_plugin->eventToCatch.clear(); + return INVOKERESULT_NO_ERROR; + } + return INVOKERESULT_NO_SUCH_METHOD; + default: + ; + } + } + return INVOKERESULT_GENERIC_ERROR; +} diff --git a/projects/mozilla/control/npolibvlc.h b/projects/mozilla/control/npolibvlc.h index a9896581a6..25e1adce69 100644 --- a/projects/mozilla/control/npolibvlc.h +++ b/projects/mozilla/control/npolibvlc.h @@ -38,7 +38,8 @@ protected: inputObj(NULL), playlistObj(NULL), subtitleObj(NULL), - videoObj(NULL) {}; + videoObj(NULL), + eventObj(NULL) {}; virtual ~LibvlcRootNPObject(); @@ -58,6 +59,7 @@ private: NPObject *playlistObj; NPObject *subtitleObj; NPObject *videoObj; + NPObject *eventObj; }; class LibvlcAudioNPObject: public RuntimeNPObject @@ -262,3 +264,24 @@ protected: InvokeResult invoke(int index, const NPVariant *args, uint32_t argCount, NPVariant &result); }; + +class LibvlcEventNPObject: public RuntimeNPObject +{ +protected: + friend class RuntimeNPClass; + + LibvlcEventNPObject(NPP instance, const NPClass *aClass) : + RuntimeNPObject(instance, aClass) {}; + virtual ~LibvlcEventNPObject() {}; + + static const int propertyCount; + static const NPUTF8 * const propertyNames[]; + + static const int methodCount; + static const NPUTF8 * const methodNames[]; + + InvokeResult invoke(int index, const NPVariant *args, uint32_t argCount, NPVariant &result); + + bool parseArgs(const NPVariant *args, uint32_t argCount, + eventtypes_bitmap_t &eventToGet); +}; diff --git a/projects/mozilla/support/npunix.c b/projects/mozilla/support/npunix.c index 41ceff25a3..52096bc908 100644 --- a/projects/mozilla/support/npunix.c +++ b/projects/mozilla/support/npunix.c @@ -100,6 +100,16 @@ NPN_Version(int* plugin_major, int* plugin_minor, *netscape_minor = gNetscapeFuncs.version & 0xFF; } +void +NPN_PluginThreadAsyncCall(NPP plugin, + void (*func)(void *), + void *userData) +{ +#if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) >= 20) + return (*gNetscapeFuncs.pluginthreadasynccall)(plugin, func, userData); +#endif +} + NPError NPN_GetValue(NPP instance, NPNVariable variable, void *r_value) { @@ -846,6 +856,10 @@ NP_Initialize(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs) gNetscapeFuncs.memfree = nsTable->memfree; gNetscapeFuncs.memflush = nsTable->memflush; gNetscapeFuncs.reloadplugins = nsTable->reloadplugins; +#if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) >= 20) + gNetscapeFuncs.pluginthreadasynccall = + nsTable->pluginthreadasynccall; +#endif #ifdef OJI if( minor >= NPVERS_HAS_LIVECONNECT ) { diff --git a/projects/mozilla/vlcplugin.cpp b/projects/mozilla/vlcplugin.cpp index 80cf5dd7b5..aa5010135c 100644 --- a/projects/mozilla/vlcplugin.cpp +++ b/projects/mozilla/vlcplugin.cpp @@ -36,6 +36,7 @@ #include "control/npolibvlc.h" #include +#include /***************************************************************************** * VlcPlugin constructor and destructor @@ -86,6 +87,48 @@ static bool boolValue(const char *value) { !strcasecmp(value, "yes") ); } +void eventAsync(void *param) +{ + VlcPlugin *plugin = (VlcPlugin*)param; + NPVariant result; + NPVariant params[2]; + + pthread_mutex_lock(&plugin->mutex); + + for (int i = 0; i < plugin->eventList.size(); i++) + { + for (int j = 0; j < plugin->eventListeners.size(); j++) + { + libvlc_event_type_t event = plugin->eventList[i]; + + if (plugin->eventListeners[j]->eventMap.have_event(event)) + { + STRINGZ_TO_NPVARIANT(libvlc_event_type_name(event), params[0]); + params[1] = plugin->eventListeners[j]->id; + NPN_InvokeDefault(plugin->getBrowser(), plugin->eventListeners[j]->listener, params, 2, &result); + NPN_ReleaseVariantValue(&result); + } + } + } + plugin->eventList.clear(); + + pthread_mutex_unlock(&plugin->mutex); +} + +void event_callback(const libvlc_event_t* event, void *param) +{ + VlcPlugin *plugin = (VlcPlugin*)param; + + pthread_mutex_lock(&plugin->mutex); + + if (plugin->eventToCatch.have_event(event->type)) + plugin->eventList.push_back(event->type); + + pthread_mutex_unlock(&plugin->mutex); + + NPN_PluginThreadAsyncCall(plugin->getBrowser(), eventAsync, plugin); +} + NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[]) { /* prepare VLC command line */ @@ -259,6 +302,9 @@ NPError VlcPlugin::init(int argc, char* const argn[], char* const argv[]) /* new APIs */ p_scriptClass = RuntimeNPClass::getClass(); + if (pthread_mutex_init(&mutex, NULL) != 0) + return NPERR_GENERIC_ERROR; + return NPERR_NO_ERROR; } @@ -274,6 +320,15 @@ VlcPlugin::~VlcPlugin() libvlc_media_list_release( libvlc_media_list ); if( libvlc_instance ) libvlc_release(libvlc_instance); + + for (int i = 0; i < eventListeners.size(); i++) + { + NPN_ReleaseObject(eventListeners[i]->listener); + NPN_ReleaseVariantValue(&(eventListeners[i]->id)); + delete eventListeners[i]; + } + + pthread_mutex_destroy(&mutex); } /***************************************************************************** @@ -336,6 +391,7 @@ int VlcPlugin::playlist_add_extended_untrusted( const char *mrl, const char *nam bool VlcPlugin::playlist_select( int idx, libvlc_exception_t *ex ) { libvlc_media_t *p_m = NULL; + libvlc_event_manager_t *eventManager = NULL; libvlc_media_list_lock(libvlc_media_list); @@ -360,8 +416,29 @@ bool VlcPlugin::playlist_select( int idx, libvlc_exception_t *ex ) libvlc_media_player = libvlc_media_player_new_from_media(p_m,ex); if( libvlc_media_player ) + { set_player_window(); + // Registers the events we're interested in. + eventManager = libvlc_media_player_event_manager(libvlc_media_player, ex); + + libvlc_event_attach(eventManager, libvlc_MediaPlayerOpening, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerBuffering, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerPlaying, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerStopped, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerPaused, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerForward, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerBackward, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerEncounteredError, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerTimeChanged, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerPositionChanged, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerTitleChanged, event_callback, this, ex); + libvlc_event_attach(eventManager, libvlc_MediaPlayerSnapshotTaken, event_callback, this, ex); + + } + libvlc_media_release( p_m ); return !libvlc_exception_raised(ex); @@ -899,3 +976,20 @@ vlc_toolbar_clicked_t VlcPlugin::getToolbarButtonClicked( int i_xpos, int i_ypos } #undef BTN_SPACE #endif + +// Verifies the version of the NPAPI. +// The eventListeners use a NPAPI function available +// since Gecko 1.9. +bool VlcPlugin::canUseEventListener() +{ + int plugin_major, plugin_minor; + int browser_major, browser_minor; + + NPN_Version(&plugin_major, &plugin_minor, + &browser_major, &browser_minor); + + if (browser_minor >= 19 || browser_major > 0) + return true; + return false; +} + diff --git a/projects/mozilla/vlcplugin.h b/projects/mozilla/vlcplugin.h index 8944d86d06..7cc0519641 100644 --- a/projects/mozilla/vlcplugin.h +++ b/projects/mozilla/vlcplugin.h @@ -31,6 +31,7 @@ #include #include +#include #include "control/nporuntime.h" #if !defined(XP_MACOSX) && !defined(XP_UNIX) && !defined(XP_WIN) @@ -79,6 +80,109 @@ typedef enum vlc_toolbar_clicked_e { clicked_Unmute } vlc_toolbar_clicked_t; + +// Note that the accessor functions are unsafe, but this is handled in +// the next layer up. 64bit uints can be substituted to taste (shift=6). +template class bitmap +{ +private: + typedef uint32_t bitu_t; enum { shift=5 }; + enum { bmax=M, bpu=1<>shift]&(1<<(idx&mask)); + } + + void set(size_t idx) { bits[idx>>shift]|= 1<<(idx&mask); } + void reset(size_t idx) { bits[idx>>shift]&=~(1<<(idx&mask)); } + void toggle(size_t idx) { bits[idx>>shift]^= 1<<(idx&mask); } + size_t maxbit() const { return bmax; } + void clear() { memset(bits,0,sizeof(bits)); } + bitmap() { clear(); } + ~bitmap() { } +}; + +typedef bitmap parent; + +class eventtypes_bitmap_t: private bitmap { +private: + typedef libvlc_event_type_t event_t; + event_t find_event(const char *s) const + { + event_t i; + for(i=0;i eventListeners; // List of registered listerners. + std::vector eventList; // List of event sent by VLC that must be returned to JS. + eventtypes_bitmap_t eventToCatch; + pthread_mutex_t mutex; + + static bool canUseEventListener(); + private: bool playlist_select(int,libvlc_exception_t *); void set_player_window(); -- 2.39.2