Commit 18e27e61 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Rework event handling

This also adds a few utilities to wrap NPAPI types and use them more
transparently
parent d92f6105
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#pragma once #pragma once
#include <vlcpp/vlc.hpp> #include <vlcpp/vlc.hpp>
#include <memory>
enum vlc_player_action_e enum vlc_player_action_e
{ {
......
...@@ -18,8 +18,6 @@ AM_CPPFLAGS = $(LIBVLC_CFLAGS) -Inpapi-sdk $(MOZILLA_CFLAGS) -I$(top_srcdir)/vlc ...@@ -18,8 +18,6 @@ AM_CPPFLAGS = $(LIBVLC_CFLAGS) -Inpapi-sdk $(MOZILLA_CFLAGS) -I$(top_srcdir)/vlc
libvlcplugin_la_SOURCES = \ libvlcplugin_la_SOURCES = \
common.h \ common.h \
locking.h \ locking.h \
events.h \
events.cpp \
vlcshell.h \ vlcshell.h \
vlcshell.cpp \ vlcshell.cpp \
vlcplugin.h \ vlcplugin.h \
......
/*****************************************************************************
* events.cpp: events for the VLC Plugin
*****************************************************************************
* Copyright (C) 2002-2012 VLC authors and VideoLAN
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Jean-Paul Saman <jpsaman@videolan.org>
* Sergey Radionov <rsatom@gmail.com>
* Cheng Sun <chengsun9@gmail.com>
* Yannick Brehon <y.brehon@qiplay.com>
* Jean-Baptiste Kempf <jb@videolan.org>
* JP Dinger <jpd@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "vlcplugin.h"
#include "events.h"
/*****************************************************************************
* Event Object
*****************************************************************************/
void handle_input_event(const libvlc_event_t* event, void *param)
{
VlcPluginBase *plugin = (VlcPluginBase*)param;
switch( event->type )
{
case libvlc_MediaPlayerMediaChanged:
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerPlaying:
case libvlc_MediaPlayerPaused:
case libvlc_MediaPlayerStopped:
case libvlc_MediaPlayerForward:
case libvlc_MediaPlayerBackward:
case libvlc_MediaPlayerEndReached:
case libvlc_MediaPlayerEncounteredError:
plugin->event_callback(event, NULL, 0);
break;
default: /* ignore all other libvlc_event_type_t */
break;
}
}
void handle_changed_event(const libvlc_event_t* event, void *param)
{
uint32_t npcount = 1;
NPVariant *npparam = (NPVariant *) NPN_MemAlloc( sizeof(NPVariant) * npcount );
VlcPluginBase *plugin = (VlcPluginBase*)param;
switch( event->type )
{
case libvlc_MediaPlayerBuffering:
DOUBLE_TO_NPVARIANT(event->u.media_player_buffering.new_cache, npparam[0]);
break;
case libvlc_MediaPlayerTimeChanged:
DOUBLE_TO_NPVARIANT(event->u.media_player_time_changed.new_time, npparam[0]);
break;
case libvlc_MediaPlayerPositionChanged:
DOUBLE_TO_NPVARIANT(event->u.media_player_position_changed.new_position, npparam[0]);
break;
case libvlc_MediaPlayerSeekableChanged:
BOOLEAN_TO_NPVARIANT(event->u.media_player_seekable_changed.new_seekable, npparam[0]);
break;
case libvlc_MediaPlayerPausableChanged:
BOOLEAN_TO_NPVARIANT(event->u.media_player_pausable_changed.new_pausable, npparam[0]);
break;
case libvlc_MediaPlayerTitleChanged:
INT32_TO_NPVARIANT(event->u.media_player_title_changed.new_title, npparam[0]);
break;
case libvlc_MediaPlayerLengthChanged:
DOUBLE_TO_NPVARIANT(event->u.media_player_length_changed.new_length, npparam[0]);
break;
default: /* ignore all other libvlc_event_type_t */
NPN_MemFree( npparam );
return;
}
plugin->event_callback(event, npparam, npcount);
}
static vlcplugin_event_t vlcevents[] = {
{ "MediaPlayerMediaChanged", libvlc_MediaPlayerMediaChanged, handle_input_event },
{ "MediaPlayerNothingSpecial", libvlc_MediaPlayerNothingSpecial, handle_input_event },
{ "MediaPlayerOpening", libvlc_MediaPlayerOpening, handle_input_event },
{ "MediaPlayerBuffering", libvlc_MediaPlayerBuffering, handle_changed_event },
{ "MediaPlayerPlaying", libvlc_MediaPlayerPlaying, handle_input_event },
{ "MediaPlayerPaused", libvlc_MediaPlayerPaused, handle_input_event },
{ "MediaPlayerStopped", libvlc_MediaPlayerStopped, handle_input_event },
{ "MediaPlayerForward", libvlc_MediaPlayerForward, handle_input_event },
{ "MediaPlayerBackward", libvlc_MediaPlayerBackward, handle_input_event },
{ "MediaPlayerEndReached", libvlc_MediaPlayerEndReached, handle_input_event },
{ "MediaPlayerEncounteredError", libvlc_MediaPlayerEncounteredError, handle_input_event },
{ "MediaPlayerTimeChanged", libvlc_MediaPlayerTimeChanged, handle_changed_event },
{ "MediaPlayerPositionChanged", libvlc_MediaPlayerPositionChanged, handle_changed_event },
{ "MediaPlayerSeekableChanged", libvlc_MediaPlayerSeekableChanged, handle_changed_event },
{ "MediaPlayerPausableChanged", libvlc_MediaPlayerPausableChanged, handle_changed_event },
{ "MediaPlayerTitleChanged", libvlc_MediaPlayerTitleChanged, handle_changed_event },
{ "MediaPlayerLengthChanged", libvlc_MediaPlayerLengthChanged, handle_changed_event },
};
EventObj::EventObj() : _em(NULL), _already_in_deliver(false)
{
plugin_lock_init(&lock);
}
EventObj::~EventObj()
{
plugin_lock_destroy(&lock);
}
void EventObj::deliver(NPP browser)
{
if(_already_in_deliver)
return;
plugin_lock(&lock);
_already_in_deliver = true;
for( ev_l::iterator iter = _elist.begin(); iter != _elist.end(); ++iter )
{
NPVariant *params = iter->params();
uint32_t count = iter->count();
for( lr_l::iterator j = _llist.begin(); j != _llist.end(); ++j )
{
if( j->event_type() == iter->event_type() )
{
NPVariant result;
NPObject *listener = j->listener();
assert( listener );
NPN_InvokeDefault( browser, listener, params, count, &result );
NPN_ReleaseVariantValue( &result );
for( uint32_t n = 0; n < count; n++ )
{
if( NPVARIANT_IS_STRING(params[n]) )
{
NPN_MemFree( (void*) NPVARIANT_TO_STRING(params[n]).UTF8Characters );
}
else if( NPVARIANT_IS_OBJECT(params[n]) )
{
NPN_ReleaseObject( NPVARIANT_TO_OBJECT(params[n]) );
NPN_MemFree( (void*)NPVARIANT_TO_OBJECT(params[n]) );
}
}
if (params) NPN_MemFree( params );
}
}
}
_elist.clear();
_already_in_deliver = false;
plugin_unlock(&lock);
}
void EventObj::callback(const libvlc_event_t* event,
NPVariant *npparams, uint32_t count)
{
plugin_lock(&lock);
_elist.push_back(VLCEvent(event->type, npparams, count));
plugin_unlock(&lock);
}
vlcplugin_event_t *EventObj::find_event(const NPString &name) const
{
for( size_t i = 0; i < ARRAY_SIZE(vlcevents); i++ )
{
if( strncmp(vlcevents[i].name, name.UTF8Characters, strlen(vlcevents[i].name)) == 0 )
return &vlcevents[i];
}
return NULL;
}
bool EventObj::insert(const NPString &name, NPObject *listener, bool bubble)
{
vlcplugin_event_t *event = find_event(name);
if( !event )
return false;
for( lr_l::iterator iter = _llist.begin(); iter != _llist.end(); ++iter )
{
if( iter->listener() == listener &&
event->libvlc_type == iter->event_type() &&
iter->bubble() == bubble )
{
return false;
}
}
_llist.push_back( Listener(event, listener, bubble) );
return true;
}
bool EventObj::remove(const NPString &name, NPObject *listener, bool bubble)
{
vlcplugin_event_t *event = find_event(name);
if( !event )
return false;
for( lr_l::iterator iter = _llist.begin(); iter !=_llist.end(); iter++ )
{
if( iter->event_type() == event->libvlc_type &&
iter->listener() == listener &&
iter->bubble() == bubble )
{
iter = _llist.erase(iter);
return true;
}
}
return false;
}
void EventObj::hook_manager( libvlc_event_manager_t *em, void *userdata )
{
if( !em )
return;
_em = em;
/* attach all libvlc events we need */
for( size_t i = 0; i < ARRAY_SIZE(vlcevents); i++ )
{
libvlc_event_attach( _em, vlcevents[i].libvlc_type,
vlcevents[i].libvlc_callback,
userdata );
}
}
void EventObj::unhook_manager( void *userdata )
{
if( !_em )
return;
/* detach all libvlc events */
for( size_t i = 0; i < ARRAY_SIZE(vlcevents); i++ )
{
libvlc_event_detach( _em, vlcevents[i].libvlc_type,
vlcevents[i].libvlc_callback,
userdata );
}
}
/*****************************************************************************
* vlcplugin.h: a VLC plugin for Mozilla
*****************************************************************************
* Copyright (C) 2002-2012 VideoLAN
* $Id$
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Damien Fouilleul <damienf.fouilleul@laposte.net>
* Jean-Paul Saman <jpsaman@videolan.org>
* Sergey Radionov <rsatom@gmail.com>
* Jean-Baptiste Kempf <jb@videolan.org>
* Cheng Sun <chengsun9@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef __VLCPLUGIN_EVENTS_H__
#define __VLCPLUGIN_EVENTS_H__
#include <assert.h>
#include <vector>
#include "common.h"
#include "../common/vlc_player.h"
typedef struct {
const char *name; /* event name */
const libvlc_event_type_t libvlc_type; /* libvlc event type */
libvlc_callback_t libvlc_callback; /* libvlc callback function */
} vlcplugin_event_t;
class EventObj
{
private:
class Listener
{
public:
Listener(vlcplugin_event_t *event, NPObject *p_object, bool b_bubble):
_event(event), _listener(p_object), _bubble(b_bubble)
{
assert(event);
assert(p_object);
}
libvlc_event_type_t event_type() const { return _event->libvlc_type; }
NPObject *listener() const { return _listener; }
bool bubble() const { return _bubble; }
private:
vlcplugin_event_t *_event;
NPObject *_listener;
bool _bubble;
};
class VLCEvent
{
public:
VLCEvent(libvlc_event_type_t libvlc_event_type, NPVariant *npparams, uint32_t npcount):
_libvlc_event_type(libvlc_event_type), _npparams(npparams), _npcount(npcount)
{}
libvlc_event_type_t event_type() const { return _libvlc_event_type; }
NPVariant *params() const { return _npparams; }
uint32_t count() const { return _npcount; }
private:
libvlc_event_type_t _libvlc_event_type;
NPVariant *_npparams;
uint32_t _npcount;
};
public:
EventObj();
virtual ~EventObj();
void unhook_manager(void *);
void hook_manager(libvlc_event_manager_t *, void *);
void deliver(NPP browser);
void callback(const libvlc_event_t *event, NPVariant *npparams, uint32_t count);
bool insert(const NPString &name, NPObject *listener, bool bubble);
bool remove(const NPString &name, NPObject *listener, bool bubble);
private:
libvlc_event_manager_t *_em; /* libvlc media_player event manager */
vlcplugin_event_t *find_event(const NPString &name) const;
typedef std::vector<Listener> lr_l;
typedef std::vector<VLCEvent> ev_l;
lr_l _llist; /* list of registered listeners with 'addEventListener' method */
ev_l _elist; /* scheduled events list for delivery to browser */
plugin_lock_t lock;
bool _already_in_deliver;
};
#endif
...@@ -190,10 +190,9 @@ RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index, ...@@ -190,10 +190,9 @@ RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index,
case ID_root_addeventlistener: case ID_root_addeventlistener:
case ID_root_removeeventlistener: case ID_root_removeeventlistener:
if( (3 != argCount) || if( (2 < argCount) ||
!NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[0]) ||
!NPVARIANT_IS_OBJECT(args[1]) || !NPVARIANT_IS_OBJECT(args[1]) )
!NPVARIANT_IS_BOOLEAN(args[2]) )
break; break;
if( !VlcPluginBase::canUseEventListener() ) if( !VlcPluginBase::canUseEventListener() )
...@@ -204,27 +203,19 @@ RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index, ...@@ -204,27 +203,19 @@ RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index,
VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>(); VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
bool b;
if( ID_root_addeventlistener == index ) if( ID_root_addeventlistener == index )
{ {
NPN_RetainObject( NPVARIANT_TO_OBJECT(args[1]) ); p_plugin->subscribe( NPVARIANT_TO_STRING(args[0]).UTF8Characters,
b = p_plugin->events.insert( NPVARIANT_TO_STRING(args[0]), NPVARIANT_TO_OBJECT(args[1] ) );
NPVARIANT_TO_OBJECT(args[1]),
NPVARIANT_TO_BOOLEAN(args[2]) );
if( !b )
NPN_ReleaseObject( NPVARIANT_TO_OBJECT(args[1]) );
} }
else else
{ {
b = p_plugin->events.remove( NPVARIANT_TO_STRING(args[0]), p_plugin->unsubscribe( NPVARIANT_TO_STRING(args[1]).UTF8Characters,
NPVARIANT_TO_OBJECT(args[1]), NPVARIANT_TO_OBJECT( args[1] ) );
NPVARIANT_TO_BOOLEAN(args[2]) );
if( b )
NPN_ReleaseObject( NPVARIANT_TO_OBJECT(args[1]) );
} }
VOID_TO_NPVARIANT(result); VOID_TO_NPVARIANT(result);
return b ? INVOKERESULT_NO_ERROR : INVOKERESULT_GENERIC_ERROR; return INVOKERESULT_NO_ERROR;
} }
return INVOKERESULT_NO_SUCH_METHOD; return INVOKERESULT_NO_SUCH_METHOD;
} }
......
/*****************************************************************************
* utils.hpp: NPAPI utility functions
*****************************************************************************
* Copyright (C) 2015 VLC authors and VideoLAN
* $Id$
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef UTILS_NPP
#define UTILS_NPP
#include <npruntime.h>
#include <memory>
#include <type_traits>
#include <cstring>
namespace npapi
{
// We don't want conversion of unknown types to work like any other types.
// This returns void, so if ( traits<std::vector<...>>::is() ) will fail to build.
// This is also true for conversions to & from types we don't support
template <typename T, typename Enable = void>
struct traits;
template <>
struct traits<std::nullptr_t>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_NULL( v );
}
static void from( std::nullptr_t, NPVariant& v )
{
NULL_TO_NPVARIANT( v );
}
};
template <>
struct traits<bool>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_BOOLEAN( v );
}
static bool to( const NPVariant& v )
{
return NPVARIANT_TO_BOOLEAN( v );
}
static void from( bool b, NPVariant& v )
{
BOOLEAN_TO_NPVARIANT( b, v );
}
};
template <typename T>
struct traits<T, typename std::enable_if<
std::is_integral<T>::value &&
!std::is_same<T, bool>::value
>::type>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_INT32( v );
}
static int to( const NPVariant& v )
{
return NPVARIANT_TO_INT32( v );
}
static void from( T i, NPVariant& v )
{
INT32_TO_NPVARIANT( (int)i, v );
}
};
template <>
struct traits<NPObject*>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_OBJECT( v );
}
static NPObject* to( const NPVariant& v )
{
return NPVARIANT_TO_OBJECT( v );
}
static void from( NPObject* o, NPVariant& v )
{
NPN_RetainObject( o );
OBJECT_TO_NPVARIANT( o, v );
}
};
template <typename T>
struct traits<T, typename std::enable_if<
std::is_floating_point<T>::value
>::type>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_DOUBLE( v );
}
static double to( const NPVariant& v )
{
return NPVARIANT_TO_DOUBLE( v );
}
static void from( T d, NPVariant& v )
{
DOUBLE_TO_NPVARIANT( (double)d, v );
}
};
template <>
struct traits<NPString>
{
static bool is( const NPVariant& v )
{
return NPVARIANT_IS_STRING( v );
}
static NPString to( const NPVariant& v )
{
return NPVARIANT_TO_STRING( v );
}
static void from( NPString s, NPVariant& v )
{
NPUTF8* buff = (NPUTF8*)NPN_MemAlloc(s.UTF8Length);
strcpy( buff, s.UTF8Characters );
STRINGZ_TO_NPVARIANT( buff, v );
}
};
// This needs to be the exact size of a NPVariant
// That means no smart pointer, no virtual function, just
// good old functions to wrap a type.
class Variant
{
public:
Variant()
: m_variant{}
{
memset( &m_variant, 0, sizeof( m_variant ) );
}
Variant( const NPVariant& v )
: m_variant( v )
{
}
Variant(const Variant& v)
{
memset( &m_variant, 0, sizeof( m_variant ) );
*this = v;
}
template <typename T>
Variant(const T& t)
{
memset( &m_variant, 0, sizeof( m_variant ) );
traits<T>::from( t, m_variant );
}
Variant& operator=(const Variant& v)
{
if ( &v == this )
return *this;
release();
if (v.is<NPString>())
{
traits<NPString>::from( (NPString)v, m_variant );
return *this;
}
m_variant = v.m_variant;
if (v.is<NPObject*>())
NPN_RetainObject( *this );
return *this;
}
Variant(Variant&& v)
{
m_variant = v.m_variant;
memset( &v.m_variant, 0, sizeof( m_variant ) );
}
Variant& operator=(Variant&& v)
{
release();
m_variant = v.m_variant;
memset( &v.m_variant, 0, sizeof( m_variant ) );
}
template <typename T>
bool is() const
{
return traits<T>::is( m_variant );
}
// /!\ Warning /!\ This does not retain the value for strings & objects
// If you wish to hold on to this value, build a new Variant so it becomes
// managed
template <typename T>
operator T() const
{
assert(traits<T>::is( m_variant ));
return traits<T>::to( m_variant );
}
operator const NPVariant() const
{
return m_variant;
}
operator const NPVariant*() const
{
return &m_variant;
}
~Variant()
{
release();
}
void release()
{
if ( is<NPString>() || is<NPObject*>() )
NPN_ReleaseVariantValue( &m_variant );
}
private:
NPVariant m_variant;
};
class VariantArray
{
using VPtr = std::unique_ptr<Variant[]>;
public:
VariantArray()
: m_variants( nullptr )
, m_size( 0 )
{
}
VariantArray(unsigned int nbValue)
: m_variants( VPtr( new Variant[nbValue] ) )
, m_size( nbValue )
{
}
Variant& operator[](size_t idx)
{
return m_variants[idx];
}
const Variant& operator[](size_t idx) const
{
return m_variants[idx];
}
// Warning: this assumes the same binary layout between Variant & NPVariant
operator NPVariant*()
{
return (NPVariant*)m_variants.get();
}
operator const NPVariant*() const
{
return (const NPVariant*)m_variants.get();
}
size_t size() const
{
return m_size;
}
VariantArray(const Variant&) = delete;
VariantArray& operator=(const Variant&) = delete;
private:
VPtr m_variants;
size_t m_size;
};
// Private implementation namespace
namespace details