Commit 6005ce1a authored by Yannick Brehon's avatar Yannick Brehon Committed by Rémi Denis-Courmont

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
parent 250b494b
......@@ -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<LibvlcVideoNPObject>( 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<LibvlcEventNPObject>::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<VlcPlugin>();
// 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<VlcPlugin>();
p_plugin->eventListeners.clear();
p_plugin->eventToCatch.clear();
return INVOKERESULT_NO_ERROR;
}
return INVOKERESULT_NO_SUCH_METHOD;
default:
;
}
}
return INVOKERESULT_GENERIC_ERROR;
}
......@@ -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>;
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);
};
......@@ -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 )
{
......
......@@ -36,6 +36,7 @@
#include "control/npolibvlc.h"
#include <ctype.h>
#include <string>
/*****************************************************************************
* 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<LibvlcRootNPObject>::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;
}
......@@ -31,6 +31,7 @@
#include <vlc/vlc.h>
#include <npapi.h>
#include <vector>
#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<size_t M> class bitmap
{
private:
typedef uint32_t bitu_t; enum { shift=5 };
enum { bmax=M, bpu=1<<shift, mask=bpu-1, units=(bmax+bpu-1)/bpu };
bitu_t bits[units];
public:
bool get(size_t idx) const
{
return bits[idx>>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<libvlc_num_event_types> parent;
class eventtypes_bitmap_t: private bitmap<libvlc_num_event_types> {
private:
typedef libvlc_event_type_t event_t;
event_t find_event(const char *s) const
{
event_t i;
for(i=0;i<maxbit();++i)
if(!strcmp(s,libvlc_event_type_name(i)))
break;
return i;
}
public:
bool add_event(const eventtypes_bitmap_t &eventBitmap)
{
event_t i;
for(i=0;i<maxbit();++i)
if (eventBitmap.have_event(i))
set(i);
}
bool add_event(const char *s)
{
if (!strcmp(s, "all"))
{
set_all_events();
return true;
}
if (!strcmp(s, "none"))
{
clear();
return true;
}
event_t event = find_event(s);
bool b = event<maxbit();
if(b) set(event);
return b;
}
bool del_event(const char *s)
{
event_t event=find_event(s);
bool b=event<maxbit();
if(b) reset(event);
return b;
}
bool have_event(libvlc_event_type_t event) const
{
return event<maxbit()?get(event):false;
}
void clear()
{
parent::clear();
}
void set_all_events()
{
event_t i;
for(i=0;i<maxbit();++i)
set(i);
}
};
// Structure used to represent an EventListener.
// It contains the listener object that will be invoked,
// An Id given by the addEventListener function and sent
// when invoking the listener. Can be anything or nothing.
// The profile associated with the listener used to invoke
// the listener only to some events.
typedef struct s_EventListener
{
NPObject *listener;
NPVariant id;
eventtypes_bitmap_t eventMap;
} EventListener;
void event_callback(const libvlc_event_t* event, void *param);
class VlcPlugin
{
public:
......@@ -197,6 +301,15 @@ public:
bool player_has_vout( libvlc_exception_t * );
// Events related members
std::vector<EventListener*> eventListeners; // List of registered listerners.
std::vector<libvlc_event_type_t> 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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment