# HG changeset patch
# User rosfran
# Date 1165030820 0
# Node ID d021c43d190ce01331f28706c748f2c3742623ae
# Parent  49f0333ad361d0b7c31c8c8232857aba9f0a6664
[svn r176] Added the Monitor socket handler to receive backend event messages.

diff -r 49f0333ad361 -r d021c43d190c gmyth/src/gmyth_monitor_handler.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gmyth/src/gmyth_monitor_handler.c	Sat Dec 02 03:40:20 2006 +0000
@@ -0,0 +1,429 @@
+/* vim: set sw=2: -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2; c-indent-level: 2-*- */
+/**
+ * GMyth Library
+ *
+ * @file gmyth/gmyth_monitor_handler.c
+ * 
+ * @brief <p> GMythMonitorHandler deals with the streaming media events remote/local
+ * that are sent to the MythTV frontend.
+ *
+ * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
+ * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
+ *
+ *//*
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * GStreamer MythTV plug-in properties:
+ * - location (backend server hostname/URL) [ex.: myth://192.168.1.73:28722/1000_1092091.nuv]
+ * - path (qurl - remote file to be opened)
+ * - port number *   
+ */
+
+#include "gmyth_uri.h"
+#include "gmyth_livetv.h"
+#include "gmyth_util.h"
+#include "gmyth_socket.h"
+#include "gmyth_stringlist.h"
+#include "gmyth_monitor_handler.h"
+#include "gmyth_debug.h"
+
+#include <unistd.h>
+#include <glib.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define GMYTHTV_QUERY_HEADER		"QUERY_FILETRANSFER "
+
+#define GMYTHTV_VERSION							30
+
+#define GMYTHTV_TRANSFER_MAX_WAITS	700
+
+#define GMYTHTV_BUFFER_SIZE					8*1024
+
+#ifdef GMYTHTV_ENABLE_DEBUG
+#define GMYTHTV_ENABLE_DEBUG				1
+#else
+#undef GMYTHTV_ENABLE_DEBUG
+#endif
+
+/* this NDEBUG is to maintain compatibility with GMyth library */
+#ifndef NDEBUG
+#define GMYTHTV_ENABLE_DEBUG				1
+#endif
+
+GThread *monitor_th = NULL;
+
+enum myth_sock_types {
+  GMYTH_PLAYBACK_TYPE = 0,
+  GMYTH_MONITOR_TYPE,
+  GMYTH_FILETRANSFER_TYPE,
+  GMYTH_RINGBUFFER_TYPE
+};
+
+static GStaticMutex st_mutex = G_STATIC_MUTEX_INIT;
+
+static gboolean* myth_control_sock_listener( GIOChannel *io_channel );
+//static gboolean myth_control_sock_listener( GIOChannel *io_channel, GIOCondition condition, 
+//				gpointer data );
+
+static GMutex*				mutex 					 = NULL;
+
+static GCond*					io_watcher_cond  = NULL;
+
+static GMainContext*	io_watcher_context  = NULL;
+
+static void gmyth_monitor_handler_class_init          (GMythMonitorHandlerClass *klass);
+static void gmyth_monitor_handler_init                (GMythMonitorHandler *object);
+
+static void gmyth_monitor_handler_dispose  (GObject *object);
+static void gmyth_monitor_handler_finalize (GObject *object);
+
+static gboolean gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor);
+
+void gmyth_monitor_handler_close( GMythMonitorHandler *monitor );
+
+static gboolean myth_control_acquire_context( gboolean do_wait );
+
+static gboolean myth_control_release_context( );
+
+G_DEFINE_TYPE(GMythMonitorHandler, gmyth_monitor_handler, G_TYPE_OBJECT)
+
+static void
+gmyth_monitor_handler_class_init (GMythMonitorHandlerClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->dispose  = gmyth_monitor_handler_dispose;
+  gobject_class->finalize = gmyth_monitor_handler_finalize;
+}
+
+static void
+gmyth_monitor_handler_init (GMythMonitorHandler *monitor)
+{ 
+  g_return_if_fail( monitor != NULL );
+
+  monitor->event_sock = NULL;
+  monitor->hostname = NULL;
+  monitor->port = 0;
+  monitor->actual_index = 0;
+  
+  monitor->backend_msgs = g_hash_table_new( g_int_hash, g_int_equal );
+    
+  /* it is used for signalizing the event socket consumer thread */
+  io_watcher_cond = g_cond_new();
+  
+  /* mutex to control access to the event socket consumer thread */
+  mutex = g_mutex_new();
+}
+
+static void
+gmyth_monitor_handler_dispose  (GObject *object)
+{
+  G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->dispose (object);
+}
+
+static void
+gmyth_monitor_handler_finalize (GObject *object)
+{
+  g_signal_handlers_destroy (object);
+
+  G_OBJECT_CLASS (gmyth_monitor_handler_parent_class)->finalize (object);
+}
+
+// fixme: do we need the card_id????
+GMythMonitorHandler*
+gmyth_monitor_handler_new (void)
+{
+  GMythMonitorHandler *monitor = GMYTH_MONITOR_HANDLER ( g_object_new (
+				GMYTH_MONITOR_HANDLER_TYPE, FALSE ));
+
+  return monitor;
+}
+
+gboolean
+gmyth_monitor_handler_open (GMythMonitorHandler *monitor, gchar *hostname, gint port)
+{
+  gboolean ret = TRUE;
+
+  if (monitor->hostname != NULL) {
+    g_free (monitor->hostname);
+    monitor->hostname = NULL;
+  }
+
+  monitor->hostname = g_strdup( hostname );
+  monitor->port = port;
+
+  gmyth_debug ("Monitor event socket --- hostname: %s, port %d\n", monitor->hostname, monitor->port);
+  
+  /* configure the event socket */
+  if (monitor->event_sock == NULL) { 
+    if (!gmyth_connect_to_backend_monitor (monitor)) {
+      g_printerr( "Connection to backend failed (Event Socket).\n" );
+      ret = FALSE;
+    }
+  } else {
+    g_warning("Remote monitor event socket already created.\n");
+  }
+
+  return ret;
+
+}
+
+static gboolean*
+//myth_control_sock_listener( GIOChannel *io_channel, GIOCondition condition, gpointer data )
+myth_control_sock_listener( GIOChannel *io_channel )
+{
+
+  GIOStatus io_status;
+  GError *error = NULL;
+  GIOCondition io_cond;
+  GIOCondition condition;
+  gchar *trash = NULL;
+  GByteArray *byte_array = NULL;
+  guint recv = 0;
+  gboolean* ret = g_new0( gboolean, 1 );
+  //gboolean ret = TRUE;
+  gsize len = 0;
+  
+  //GMythMonitorHandler *monitor = (GMythMonitorHandler*)data;
+  
+  //*ret = TRUE;
+  //myth_control_acquire_context (TRUE);
+  
+  if ( io_channel == NULL ) {
+  	g_debug ("Monitor socket is NULL!\n");
+  	*ret = FALSE;
+  	goto clean_up;
+  }
+  gmyth_debug ("Listening on Monitor socket...!\n");
+  
+  while (TRUE) {
+	  	
+	  //condition = g_io_channel_get_buffer_condition( io_channel );
+	  
+	  //myth_control_acquire_context (TRUE);
+	  
+	  //while ( !has_io_access ) 
+	  //	g_cond_wait( io_watcher_cond, mutex );
+	  	
+	  //myth_control_acquire_context (TRUE);  
+	  
+	  if (condition & G_IO_HUP) {
+	    gmyth_debug ("Read end of pipe died!\n");
+	    *ret = FALSE;
+	    goto clean_up;
+	  }
+	    
+	  if ( ( condition & G_IO_IN ) != 0 ) {
+	  	byte_array = g_byte_array_new();
+	  	io_status = g_io_channel_set_encoding( io_channel, NULL, &error );
+	  	guint buffer_size = GMYTHTV_BUFFER_SIZE;
+	    do 
+	    {
+	      trash = g_new0( gchar, buffer_size );
+	
+	      io_status = g_io_channel_read_chars( io_channel, trash, 
+	      		buffer_size, &len, &error);
+	
+	      gmyth_debug ( "[%s] Received data buffer from IO binary channel... %d bytes gone!\n", 
+	      		__FUNCTION__, len );
+	      		
+	      recv += len;
+	      
+	      byte_array = g_byte_array_append( byte_array, (const guint8*)trash, len );
+	      
+	      if ( trash != NULL )
+	      	g_free( trash );
+	      	
+	      io_cond = g_io_channel_get_buffer_condition( io_channel );
+	
+	    } while ( ( io_cond & G_IO_IN ) != 0 );
+	  }
+	  
+	  g_usleep( 300 );
+	  
+  }
+  //ret = g_io_channel_read_chars ( source, &msg, &len, NULL, &err);
+  if ( io_status == G_IO_STATUS_ERROR ) {
+    gmyth_debug ("[%s] Error reading: %s\n", __FUNCTION__, error != NULL ? error->message : "" );
+   	*ret = FALSE;
+   	goto clean_up;   	
+  }
+  gmyth_debug ("\n[%s]\tEVENT: Read %d bytes: %s\n\n", __FUNCTION__, recv, byte_array->data != NULL ? (gchar*)byte_array->data : "[no event data]" );
+  
+  //g_hash_table_insert( monitor->backend_msgs, (gpointer)monitor->actual_index,
+  //				  byte_array->data );
+  
+  //monitor->actual_index += recv;
+  
+  if ( byte_array != NULL ) {
+  	g_byte_array_free( byte_array, TRUE );
+  	byte_array = NULL;
+  }
+
+clean_up:
+  
+  return ret;
+
+}
+
+static gboolean
+gmyth_connect_to_backend_monitor (GMythMonitorHandler *monitor)
+{
+  gboolean ret = TRUE;
+
+  /* Creates the event socket */
+  if (monitor->event_sock != NULL) {
+    g_object_unref (monitor->event_sock);
+    monitor->event_sock = NULL;
+  }
+
+  monitor->event_sock = gmyth_socket_new();
+
+  /* Connects the socket, send Mythtv ANN Monitor and verify Mythtv protocol version */ 
+  if (!gmyth_socket_connect_to_backend_events ( monitor->event_sock,
+          monitor->hostname, monitor->port, FALSE ) ) 
+  {
+    g_object_unref (monitor->event_sock);
+    monitor->event_sock = NULL;
+    ret = FALSE;
+  }
+  
+  return ret;
+}    
+
+gboolean 
+gmyth_monitor_handler_start (GMythMonitorHandler *monitor)
+{
+	gboolean ret = TRUE;
+	
+  monitor_th = g_thread_create( (GThreadFunc)myth_control_sock_listener, 
+  					monitor->event_sock->sd_io_ch, TRUE, NULL );
+
+/*
+  io_watcher_context = g_main_context_default();
+  //GMainLoop *loop = g_main_loop_new( NULL, TRUE );
+
+  GSource *source;
+
+  if ( monitor->event_sock->sd_io_ch != NULL ) {
+    source = g_io_create_watch( monitor->event_sock->sd_io_ch, G_IO_IN );
+    //monitor->event_sock->sd_io_ch
+  } else {
+  	ret = FALSE;
+  	goto cleanup;
+  }
+
+  g_source_set_callback ( source, (GSourceFunc)myth_control_sock_listener, NULL, NULL );
+
+  g_source_attach( source, io_watcher_context );
+  
+  if (source==NULL) {
+    gmyth_debug( "[%s] Error adding watch listener function to the IO control channel!\n", __FUNCTION__ );
+    goto cleanup;
+  }
+  */
+
+  gmyth_debug ( "[%s]\tOK! Starting listener on the MONITOR event socket...\n", __FUNCTION__ );
+
+  //g_main_loop_run( loop );
+
+cleanup:
+  //if ( source != NULL )
+  //  g_source_unref( source );
+    
+  gmyth_debug( "[%s] Watch listener function over the IO control channel? %s!!!\n", 
+  			__FUNCTION__, ( ret == TRUE ? "YES" : "NO" ) );
+
+  return ret;
+}
+
+void
+gmyth_monitor_handler_close( GMythMonitorHandler *monitor )
+{
+	
+  if (monitor->event_sock) {
+    g_object_unref( monitor->event_sock );
+    monitor->event_sock = NULL;
+  }
+
+  if (monitor->hostname) {
+    g_free( monitor->hostname );
+    monitor->hostname = NULL;
+  }
+  
+}
+
+static gboolean 
+myth_control_acquire_context( gboolean do_wait ) 
+{
+	
+	gboolean ret = TRUE;	
+	//guint max_iter = 50;
+	
+	//g_mutex_lock( mutex );
+	
+  //while ( !has_io_access ) 
+  //	g_cond_wait( io_watcher_cond, mutex );
+  	
+  //has_io_access = FALSE;
+  
+  //myth_control_acquire_context (FALSE);
+   
+  /* 
+  if ( do_wait ) {
+  	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
+  		ret = FALSE;
+  } else if ( !g_main_context_acquire( io_watcher_context ) )
+  	ret = FALSE;
+  */
+  	
+  //g_static_mutex_lock( &st_mutex );
+  
+  return ret;
+  
+}
+
+static gboolean 
+myth_control_release_context( ) 
+{
+	
+	gboolean ret = TRUE;
+    
+  //g_static_mutex_unlock( &st_mutex );
+  
+	//g_main_context_release( io_watcher_context );
+  
+  //g_main_context_wakeup( io_watcher_context );
+  
+  //has_io_access = TRUE;
+
+  //g_cond_broadcast( io_watcher_cond );
+  
+  //g_mutex_unlock( mutex );  
+ 
+  return ret;
+  
+}
+
diff -r 49f0333ad361 -r d021c43d190c gmyth/src/gmyth_monitor_handler.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gmyth/src/gmyth_monitor_handler.h	Sat Dec 02 03:40:20 2006 +0000
@@ -0,0 +1,100 @@
+/* vim: set sw=2: -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2; c-indent-level: 2-*- */
+/**
+ * GMyth Library
+ *
+ * @file gmyth/gmyth_monitor_handler.h
+ * 
+ * @brief <p> GMythMonitorHandler deals with the streaming media events remote/local
+ * that are sent to the MythTV frontend.
+ *
+ * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
+ * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
+ *
+ *//*
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+ 
+#ifndef __GMYTH_MONITOR_HANDLER_H__
+#define __GMYTH_MONITOR_HANDLER_H__
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include "gmyth_socket.h"
+#include "gmyth_uri.h"
+#include "gmyth_livetv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#define G_BEGIN_DECLS
+
+#define GMYTH_MONITOR_HANDLER_TYPE               (gmyth_monitor_handler_get_type ())
+#define GMYTH_MONITOR_HANDLER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GMYTH_MONITOR_HANDLER_TYPE, GMythMonitorHandler))
+#define GMYTH_MONITOR_HANDLER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GMYTH_MONITOR_HANDLER_TYPE, GMythMonitorHandlerClass))
+#define IS_GMYTH_MONITOR_HANDLER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GMYTH_MONITOR_HANDLER_TYPE))
+#define IS_GMYTH_MONITOR_HANDLER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GMYTH_MONITOR_HANDLER_TYPE))
+#define GMYTH_MONITOR_HANDLER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GMYTH_MONITOR_HANDLER_TYPE, GMythMonitorHandlerClass))
+
+#define GMYTHTV_MONITOR_HANDLER_READ_ERROR	-314
+
+typedef struct _GMythMonitorHandler         GMythMonitorHandler;
+typedef struct _GMythMonitorHandlerClass    GMythMonitorHandlerClass;
+
+struct _GMythMonitorHandlerClass
+{
+	GObjectClass parent_class;
+
+	/* callbacks */
+	/* no one for now */
+};
+
+struct _GMythMonitorHandler
+{
+	GObject parent;
+
+	/* MythTV version number */	
+	gint mythtv_version;
+
+	/* socket descriptors */
+	GMythSocket *event_sock;
+
+	gchar *hostname;
+	gint port;
+	
+	gint64 actual_index;
+	
+	/* stores the messages coming from the backend */
+	GHashTable *backend_msgs;
+};
+
+GType          gmyth_monitor_handler_get_type        (void);
+
+GMythMonitorHandler* gmyth_monitor_handler_new (void);
+
+gboolean gmyth_monitor_handler_open (GMythMonitorHandler *monitor, gchar *hostname, gint port);
+
+gboolean gmyth_monitor_handler_start (GMythMonitorHandler *monitor);
+
+void gmyth_monitor_handler_close (GMythMonitorHandler *monitor);
+
+#define G_END_DECLS
+
+#endif /* __GMYTH_MONITOR_HANDLER_H__ */