5 changed files with 459 additions and 107 deletions
@ -0,0 +1,237 @@ |
|||
/*****************************************************************************
|
|||
* event.c: New libvlc event control API |
|||
***************************************************************************** |
|||
* Copyright (C) 2007 the VideoLAN team |
|||
* $Id $ |
|||
* |
|||
* Authors: Filippo Carone <filippo@carone.org> |
|||
* Pierre d'Herbemont <pdherbemont # 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 <vlc/libvlc.h> |
|||
|
|||
#include "libvlc_internal.h" |
|||
#include "event_internal.h" |
|||
|
|||
struct queue_elmt { |
|||
libvlc_event_listener_t listener; |
|||
libvlc_event_t event; |
|||
struct queue_elmt * next; |
|||
}; |
|||
|
|||
struct libvlc_event_async_queue { |
|||
struct queue_elmt * elements; |
|||
vlc_mutex_t lock; |
|||
vlc_cond_t signal; |
|||
vlc_thread_t thread; |
|||
}; |
|||
|
|||
/*
|
|||
* Utilities |
|||
*/ |
|||
|
|||
static void* event_async_loop(void * arg); |
|||
|
|||
static inline struct libvlc_event_async_queue * queue(libvlc_event_manager_t * p_em) |
|||
{ |
|||
return p_em->async_event_queue; |
|||
} |
|||
|
|||
static inline bool is_queue_initialized(libvlc_event_manager_t * p_em) |
|||
{ |
|||
return queue(p_em) != NULL; |
|||
} |
|||
|
|||
/* Lock must be held */ |
|||
static void push(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event) |
|||
{ |
|||
#ifndef NDEBUG |
|||
static const long MaxQueuedItem = 300000; |
|||
long count = 0; |
|||
#endif |
|||
|
|||
struct queue_elmt * elmt = malloc(sizeof(struct queue_elmt)); |
|||
elmt->listener = *listener; |
|||
elmt->event = *event; |
|||
elmt->next = NULL; |
|||
|
|||
/* Append to the end of the queue */ |
|||
struct queue_elmt * iter = queue(p_em)->elements; |
|||
if(!iter) |
|||
{ |
|||
queue(p_em)->elements = elmt; |
|||
return; |
|||
} |
|||
|
|||
while (iter->next) { |
|||
iter = iter->next; |
|||
#ifndef NDEBUG |
|||
if(count++ > MaxQueuedItem) |
|||
{ |
|||
fprintf(stderr, "Warning: libvlc event overflow.\n"); |
|||
abort(); |
|||
} |
|||
#endif |
|||
} |
|||
iter->next = elmt; |
|||
} |
|||
|
|||
/* Lock must be held */ |
|||
static bool pop(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event) |
|||
{ |
|||
if(!queue(p_em)->elements) |
|||
return false; /* No elements */ |
|||
|
|||
*listener = queue(p_em)->elements->listener; |
|||
*event = queue(p_em)->elements->event; |
|||
|
|||
struct queue_elmt * elmt = queue(p_em)->elements; |
|||
queue(p_em)->elements = elmt->next; |
|||
free(elmt); |
|||
return true; |
|||
} |
|||
|
|||
/* Lock must be held */ |
|||
static void pop_listener(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener) |
|||
{ |
|||
struct queue_elmt * iter = queue(p_em)->elements; |
|||
struct queue_elmt * prev = NULL; |
|||
while (iter) { |
|||
if(listeners_are_equal(&iter->listener, listener)) |
|||
{ |
|||
if(!prev) |
|||
queue(p_em)->elements = iter->next; |
|||
else |
|||
prev->next = iter->next; |
|||
free(iter); |
|||
} |
|||
prev = iter; |
|||
iter = iter->next; |
|||
} |
|||
} |
|||
|
|||
/**************************************************************************
|
|||
* libvlc_event_async_fini (internal) : |
|||
* |
|||
* Destroy what might have been created by. |
|||
**************************************************************************/ |
|||
void |
|||
libvlc_event_async_fini(libvlc_event_manager_t * p_em) |
|||
{ |
|||
if(!is_queue_initialized(p_em)) return; |
|||
|
|||
vlc_thread_t thread = queue(p_em)->thread; |
|||
if(thread) |
|||
{ |
|||
vlc_cancel(thread); |
|||
vlc_join(thread, NULL); |
|||
} |
|||
|
|||
vlc_mutex_destroy(&queue(p_em)->lock); |
|||
vlc_cond_destroy(&queue(p_em)->signal); |
|||
|
|||
struct queue_elmt * iter = queue(p_em)->elements; |
|||
while (iter) { |
|||
struct queue_elmt * elemt_to_delete = iter; |
|||
iter = iter->next; |
|||
free(elemt_to_delete); |
|||
} |
|||
|
|||
free(queue(p_em)); |
|||
} |
|||
|
|||
/**************************************************************************
|
|||
* libvlc_event_async_init (private) : |
|||
* |
|||
* Destroy what might have been created by. |
|||
**************************************************************************/ |
|||
static void |
|||
libvlc_event_async_init(libvlc_event_manager_t * p_em) |
|||
{ |
|||
p_em->async_event_queue = calloc(1, sizeof(struct libvlc_event_async_queue)); |
|||
|
|||
int error = vlc_clone (&queue(p_em)->thread, event_async_loop, p_em, VLC_THREAD_PRIORITY_LOW); |
|||
if(error) |
|||
{ |
|||
free(p_em->async_event_queue); |
|||
p_em->async_event_queue = NULL; |
|||
return; |
|||
} |
|||
|
|||
vlc_mutex_init_recursive(&queue(p_em)->lock); // Beware, this is re-entrant
|
|||
vlc_cond_init(&queue(p_em)->signal); |
|||
} |
|||
|
|||
/**************************************************************************
|
|||
* libvlc_event_async_ensure_listener_removal (internal) : |
|||
* |
|||
* Make sure no more message will be issued to the listener. |
|||
**************************************************************************/ |
|||
void |
|||
libvlc_event_async_ensure_listener_removal(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener) |
|||
{ |
|||
if(!is_queue_initialized(p_em)) return; |
|||
|
|||
vlc_mutex_lock(&queue(p_em)->lock); |
|||
pop_listener(p_em, listener); |
|||
vlc_mutex_unlock(&queue(p_em)->lock); |
|||
} |
|||
|
|||
/**************************************************************************
|
|||
* libvlc_event_async_dispatch (internal) : |
|||
* |
|||
* Send an event in an asynchronous way. |
|||
**************************************************************************/ |
|||
void |
|||
libvlc_event_async_dispatch(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event) |
|||
{ |
|||
// We do a lazy init here, to prevent constructing the thread when not needed.
|
|||
vlc_mutex_lock(&p_em->object_lock); |
|||
if(!queue(p_em)) |
|||
libvlc_event_async_init(p_em); |
|||
vlc_mutex_unlock(&p_em->object_lock); |
|||
|
|||
vlc_mutex_lock(&queue(p_em)->lock); |
|||
push(p_em, listener, event); |
|||
vlc_cond_signal(&queue(p_em)->signal); |
|||
vlc_mutex_unlock(&queue(p_em)->lock); |
|||
} |
|||
|
|||
/**************************************************************************
|
|||
* event_async_loop (private) : |
|||
* |
|||
* Send queued events. |
|||
**************************************************************************/ |
|||
static void * event_async_loop(void * arg) |
|||
{ |
|||
libvlc_event_manager_t * p_em = arg; |
|||
libvlc_event_listener_t listener; |
|||
libvlc_event_t event; |
|||
|
|||
vlc_mutex_lock(&queue(p_em)->lock); |
|||
vlc_cleanup_push(vlc_cleanup_lock, &queue(p_em)->lock); |
|||
while (true) { |
|||
int has_listener = pop(p_em, &listener, &event); |
|||
if (has_listener) |
|||
listener.pf_callback( &event, listener.p_user_data ); // This might edit the queue, ->lock is recursive
|
|||
else |
|||
vlc_cond_wait(&queue(p_em)->signal, &queue(p_em)->lock); |
|||
} |
|||
vlc_cleanup_pop(); |
|||
vlc_mutex_unlock(&queue(p_em)->lock); |
|||
return NULL; |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
/*****************************************************************************
|
|||
* libvlc_internal.h : Definition of opaque structures for libvlc exported API |
|||
* Also contains some internal utility functions |
|||
***************************************************************************** |
|||
* Copyright (C) 2005-2009 the VideoLAN team |
|||
* $Id$ |
|||
* |
|||
* Authors: Clément Stenac <zorglub@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. |
|||
*****************************************************************************/ |
|||
|
|||
#ifndef _LIBVLC_EVENT_H |
|||
#define _LIBVLC_EVENT_H 1 |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
|
|||
#include <vlc/libvlc_structures.h> |
|||
#include <vlc/libvlc.h> |
|||
#include <vlc/libvlc_events.h> |
|||
|
|||
#include <vlc_common.h> |
|||
|
|||
|
|||
/*
|
|||
* Event Handling |
|||
*/ |
|||
|
|||
/* Example usage
|
|||
* |
|||
* struct libvlc_cool_object_t |
|||
* { |
|||
* ... |
|||
* libvlc_event_manager_t * p_event_manager; |
|||
* ... |
|||
* } |
|||
* |
|||
* libvlc_my_cool_object_new() |
|||
* { |
|||
* ... |
|||
* p_self->p_event_manager = libvlc_event_manager_new( p_self, |
|||
* p_self->p_libvlc_instance, p_e); |
|||
* libvlc_event_manager_register_event_type(p_self->p_event_manager, |
|||
* libvlc_MyCoolObjectDidSomething, p_e) |
|||
* ... |
|||
* } |
|||
* |
|||
* libvlc_my_cool_object_release() |
|||
* { |
|||
* ... |
|||
* libvlc_event_manager_release( p_self->p_event_manager ); |
|||
* ... |
|||
* } |
|||
* |
|||
* libvlc_my_cool_object_do_something() |
|||
* { |
|||
* ... |
|||
* libvlc_event_t event; |
|||
* event.type = libvlc_MyCoolObjectDidSomething; |
|||
* event.u.my_cool_object_did_something.what_it_did = kSomething; |
|||
* libvlc_event_send( p_self->p_event_manager, &event ); |
|||
* } |
|||
* */ |
|||
|
|||
typedef struct libvlc_event_listener_t |
|||
{ |
|||
libvlc_event_type_t event_type; |
|||
void * p_user_data; |
|||
libvlc_callback_t pf_callback; |
|||
bool is_asynchronous; |
|||
} libvlc_event_listener_t; |
|||
|
|||
typedef struct libvlc_event_manager_t |
|||
{ |
|||
void * p_obj; |
|||
struct libvlc_instance_t * p_libvlc_instance; |
|||
vlc_array_t listeners_groups; |
|||
vlc_mutex_t object_lock; |
|||
vlc_mutex_t event_sending_lock; |
|||
struct libvlc_event_async_queue * async_event_queue; |
|||
} libvlc_event_sender_t; |
|||
|
|||
|
|||
static inline bool |
|||
listeners_are_equal( libvlc_event_listener_t * listener1, |
|||
libvlc_event_listener_t * listener2 ) |
|||
{ |
|||
return listener1->event_type == listener2->event_type && |
|||
listener1->pf_callback == listener2->pf_callback && |
|||
listener1->p_user_data == listener2->p_user_data && |
|||
listener1->is_asynchronous == listener2->is_asynchronous; |
|||
} |
|||
|
|||
/* event_async.c */ |
|||
void libvlc_event_async_fini(libvlc_event_manager_t * p_em); |
|||
void libvlc_event_async_dispatch(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener, libvlc_event_t * event); |
|||
void libvlc_event_async_ensure_listener_removal(libvlc_event_manager_t * p_em, libvlc_event_listener_t * listener); |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue