diff -r 000000000000 -r 60bfeabc0af6 branches/gmyth-0.1b/src/gmyth_livetv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/branches/gmyth-0.1b/src/gmyth_livetv.c Thu Feb 08 20:57:31 2007 +0000 @@ -0,0 +1,668 @@ +/** + * GMyth Library + * + * @file gmyth/gmyth_livetv.c + * + * @brief

GMythLiveTV starts a remote TV session with the MythTV backend. + * + * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia. + * @author Rosfran Lins Borges + * + *//* + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gmyth_livetv.h" +#include "gmyth_remote_util.h" +#include "gmyth_tvchain.h" +#include "gmyth_socket.h" +#include "gmyth_backendinfo.h" +#include "gmyth_debug.h" + +#include "gmyth_file_transfer.h" +#include "gmyth_monitor_handler.h" + +static void gmyth_livetv_class_init (GMythLiveTVClass *klass); +static void gmyth_livetv_init (GMythLiveTV *object); + +static void gmyth_livetv_dispose (GObject *object); +static void gmyth_livetv_finalize (GObject *object); + +static gint tvchain_curr_index = -1; + +static GStaticMutex lock = G_STATIC_MUTEX_INIT; + +#define GMYTHTV_TRANSFER_MAX_WAITS 100 + +G_DEFINE_TYPE(GMythLiveTV, gmyth_livetv, G_TYPE_OBJECT) + +static void +gmyth_livetv_class_init (GMythLiveTVClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->dispose = gmyth_livetv_dispose; + gobject_class->finalize = gmyth_livetv_finalize; +} + +static void +gmyth_livetv_init (GMythLiveTV *livetv) +{ + livetv->backend_info = NULL; + livetv->local_hostname = NULL; + livetv->file_transfer = NULL; + livetv->setup_done = FALSE; + + livetv->recorder = NULL; + livetv->tvchain = NULL; + livetv->proginfo = NULL; + livetv->uri = NULL; + +} + +static void +gmyth_livetv_dispose (GObject *object) +{ + G_OBJECT_CLASS (gmyth_livetv_parent_class)->dispose (object); +} + +static void +gmyth_livetv_finalize (GObject *object) +{ + g_signal_handlers_destroy (object); + + GMythLiveTV *livetv = GMYTH_LIVETV (object); + + gmyth_debug ("Finalizing livetv"); + + if ( livetv->monitor != NULL ) { + g_object_unref (livetv->monitor); + livetv->monitor = NULL; + } + + if ( livetv->recorder != NULL ) { + g_object_unref (livetv->recorder); + livetv->recorder = NULL; + } + + if ( livetv->tvchain != NULL ) { + g_object_unref (livetv->tvchain); + livetv->tvchain = NULL; + } + + if ( livetv->proginfo != NULL ) { + g_object_unref (livetv->proginfo); + livetv->proginfo = NULL; + } + + if ( livetv->file_transfer != NULL ) { + g_object_unref (livetv->file_transfer); + livetv->file_transfer = NULL; + } + + if ( livetv->backend_info != NULL ) { + g_object_unref (livetv->backend_info); + livetv->backend_info = NULL; + } + + if ( livetv->uri != NULL ) + { + g_object_unref (livetv->uri); + livetv->uri = NULL; + } + + G_OBJECT_CLASS ( gmyth_livetv_parent_class )->finalize ( object ); +} + +GMythLiveTV* +gmyth_livetv_new () +{ + GMythLiveTV *livetv = GMYTH_LIVETV ( g_object_new( GMYTH_LIVETV_TYPE, NULL ) ); + + return livetv; +} + +static void +gmyth_livetv_monitor_signal_handler( GMythMonitorHandler *monitor, gint msg_code, + gchar* message, gpointer user_data ) +{ + GMythLiveTV *live_tv = GMYTH_LIVETV ( user_data ); + //g_object_ref( live_tv ); + + gmyth_debug( "LIVETV Signal handler ( msg = %s, code = %d, live_tv param = %s, user_data = %s )\n", message, msg_code, live_tv != NULL ? "" : + "NULL", user_data != NULL ? "" : "NULL" ); + + if ( NULL == live_tv ) + { + gmyth_debug( "LiveTV_obj is equals to NULL!!!" ); + return; + } + + switch ( msg_code ) + { + + case GMYTH_BACKEND_PROGRAM_INFO_CHANGED: + { + gmyth_debug( "LIVETV Program Changed request received [ msg = %s ]. Watching if the new "\ + "TV Chain ID is the same as the old one...\n", message ); + if ( g_ascii_strcasecmp ( message, (gmyth_tvchain_get_id( live_tv->tvchain ))->str ) != 0 ) { + gmyth_debug( "OK!!! MOVED to the next program chain [actual == %s]!", + (gmyth_tvchain_get_id( live_tv->tvchain ))->str ); + /* advertises the FileTransfer about the program info changed */ + if ( live_tv->file_transfer != NULL ) + { + gmyth_debug( "Emitting signal to the FileTransfer... [ \"program-info-changed \" ]" ); + + gmyth_file_transfer_emit_program_info_changed_signal( live_tv->file_transfer, + msg_code, (gpointer)live_tv ); + + //gmyth_livetv_monitor_handler_stop( live_tv ); + } else + gmyth_debug( "LIVETV file_transfer is NULL!!! Cannot move to the next program chain event received.\n"); + } + } + case GMYTH_BACKEND_DONE_RECORDING: + { + gmyth_debug( "LIVETV Program Changed request received [ msg = %s ]. Watching if the new "\ + "TV Chain ID is the same as the old one...\n", message ); + if ( g_ascii_strcasecmp ( message, (gmyth_tvchain_get_id( live_tv->tvchain ))->str ) != 0 ) { + gmyth_debug( "OK!!! MOVED to the next program chain [actual == %s]!", + (gmyth_tvchain_get_id( live_tv->tvchain ))->str ); + /* advertises the FileTransfer about the program info changed */ + if ( live_tv->file_transfer != NULL ) + { + gmyth_debug( "Emitting signal to the FileTransfer... [ \"program-info-changed \" ]" ); + + gmyth_file_transfer_emit_program_info_changed_signal( live_tv->file_transfer, + msg_code, (gpointer)live_tv ); + + //gmyth_livetv_monitor_handler_stop( live_tv ); + } else + gmyth_debug( "LIVETV file_transfer is NULL!!! Cannot move to the next program chain event received.\n"); + } + + break; + } + default: + break; + } /* switch (Monitor Handler messages) */ + +} + +gboolean +gmyth_livetv_monitor_handler_start( GMythLiveTV *livetv ) +{ + gboolean res = TRUE; + + if ( livetv->monitor != NULL ) + { + g_object_unref( livetv->monitor ); + livetv->monitor = NULL; + } + + livetv->monitor = gmyth_monitor_handler_new ( ); + + res = gmyth_monitor_handler_open (livetv->monitor, livetv->backend_info->hostname, + livetv->backend_info->port ); + + if ( res == TRUE ) + { + gmyth_debug("Connect MythTV Monitor event socket! Trying to start the message handler..."); + + res = gmyth_monitor_handler_start ( livetv->monitor ); + + if (res) + { + gmyth_debug("MythTV Monitor event socket connected and listening!"); + g_signal_connect ( G_OBJECT (livetv->monitor), "backend-events-handler", + (GCallback)gmyth_livetv_monitor_signal_handler, + livetv ); + } + else + { + gmyth_debug("Problems when trying to start MythTV Monitor event socket!"); + goto error; + } + } + +error: + return res; + +} + +void +gmyth_livetv_monitor_handler_stop( GMythLiveTV *livetv ) +{ + + if ( livetv->monitor != NULL ) + { + g_object_unref( livetv->monitor ); + livetv->monitor = NULL; + } + +} + + +/* +static gchar* +gmyth_livetv_create_remote_url( GMythLiveTV *livetv ) +{ + gchar *uri = g_strdup(""); + gmyth_backend_info_get_remote_h + + //gmyth_backend(livetv->backend_info) + + return uri; +} +*/ + +static gboolean +gmyth_livetv_setup_recorder_channel_name ( GMythLiveTV *livetv, gchar* channel, GMythBackendInfo *backend_info ) +{ + gboolean res = TRUE; + + GMythSocket *socket = gmyth_socket_new (); + + livetv->backend_info = backend_info; + + g_static_mutex_lock( &lock ); + + // FIME: Implement this at gmyth_socket + res = gmyth_socket_connect_to_backend (socket, livetv->backend_info->hostname, + livetv->backend_info->port, TRUE); + if (!res) { + g_warning ("[%s] LiveTV can not connect to backend", __FUNCTION__); + res = FALSE; + goto error; + } + + livetv->is_livetv = TRUE; + + livetv->local_hostname = gmyth_socket_get_local_hostname (); + + if ( livetv->local_hostname == NULL ) { + res = FALSE; + goto error; + } + + // Gets the recorder num + livetv->recorder = remote_request_next_free_recorder (socket, -1); + gmyth_socket_close_connection (socket); + + if ( livetv->recorder == NULL ) { + g_warning ("[%s] None remote encoder available", __FUNCTION__); + res = FALSE; + goto error; + } + + // Init remote encoder. Opens its control socket. + res = gmyth_recorder_setup(livetv->recorder); + if ( !res ) { + g_warning ("[%s] Fail while setting remote encoder\n", __FUNCTION__); + res = FALSE; + goto error; + } + + // Creates livetv chain handler + livetv->tvchain = GMYTH_TVCHAIN ( g_object_new(GMYTH_TVCHAIN_TYPE, NULL) ); + gmyth_tvchain_initialize ( livetv->tvchain, livetv->backend_info ); + + if ( livetv->tvchain == NULL || livetv->tvchain->tvchain_id == NULL ) { + res = FALSE; + goto error; + } + + // Spawn live tv. Uses the socket to send mythprotocol data to start livetv in the backend (remotelly) + res = gmyth_recorder_spawntv ( livetv->recorder, + gmyth_tvchain_get_id(livetv->tvchain) ); + if (!res) { + g_warning ("[%s] Fail while spawn tv\n", __FUNCTION__); + res = FALSE; + goto error; + } + + if ( res == TRUE ) { + /* loop finished, set the max tries variable to zero again... */ + gint wait_to_transfer = 0; + + while (wait_to_transfer++ < GMYTHTV_TRANSFER_MAX_WAITS && + (gmyth_recorder_is_recording (livetv->recorder) == FALSE)) + g_usleep (500); + + /* IS_RECORDING again, just like the MythTV backend does... */ + gmyth_recorder_is_recording (livetv->recorder); + + if ( channel != NULL ) + { + /* Pauses remote encoder. */ + res = gmyth_recorder_pause_recording(livetv->recorder); + if ( !res ) { + g_warning ("[%s] Fail while pausing remote encoder\n", __FUNCTION__); + res = FALSE; + goto error; + } + + if ( gmyth_recorder_check_channel_name( livetv->recorder, channel ) ) + { + if ( gmyth_recorder_set_channel_name( livetv->recorder, channel ) ) + { + g_print( "[%s] Channel changed!!! [%s].\n", __FUNCTION__, channel ); + } + } + + } + + sleep (9); /* FIXME: this is evil (tpm) */ + } + + /* DEBUG message */ + GMythProgramInfo* prog_info = gmyth_recorder_get_current_program_info( livetv->recorder ); + + if ( NULL == prog_info ) + { + gmyth_debug( "ProgramInfo is equals to NULL!!!" ); + + return FALSE; + } + /* prints program info data text */ + gmyth_debug( "New ProgramInfo...\n" ); + gmyth_program_info_print( prog_info ); + /* DEBUG message */ + gmyth_debug( "Old ProgramInfo...\n" ); + gmyth_program_info_print( livetv->proginfo ); + + /* check if the program chain could be obtained from the MythTV protocol message */ + if ( prog_info != NULL ) + { + livetv->proginfo = prog_info; + /* testing change channel */ + //gmyth_recorder_spawntv_no_tvchain( livetv->recorder ); + } else { + + /* check for the program info in the TV program chain could be obtained + from the MythTV MySQL database */ + + /* Reload all TV chain from Mysql database. */ + gmyth_tvchain_reload_all (livetv->tvchain); + + if ( livetv->tvchain == NULL ) { + res = FALSE; + goto error; + } + + /* Get program info from database using chanid and starttime */ + livetv->proginfo = gmyth_tvchain_get_program_at (livetv->tvchain, tvchain_curr_index++ ); + if ( livetv->proginfo == NULL ) { + g_warning ("[%s] LiveTV not successfully started.\n", __FUNCTION__ ); + res = FALSE; + goto error; + } else { + res = TRUE; + gmyth_debug ("GMythLiveTV: All requests to backend to start TV were OK. [%s]\n", livetv->proginfo->pathname->str ); + } + + } + + livetv->uri = (GMythURI*)gmyth_backend_info_get_uri( backend_info ); + + g_static_mutex_unlock( &lock ); + + if ( !gmyth_livetv_monitor_handler_start( livetv ) ) + { + res = FALSE; + gmyth_debug( "LiveTV MONITOR handler error on setup!" ); + goto error; + } + + livetv->setup_done = TRUE; + + return res; + +error: + g_print( "[%s] ERROR running LiveTV setup.\n", __FUNCTION__ ); + + if ( livetv->local_hostname != NULL ) { + g_string_free( livetv->local_hostname, FALSE ); + res = FALSE; + } + + if ( livetv->recorder != NULL ) { + g_object_unref (livetv->recorder); + livetv->recorder = NULL; + } + + if ( livetv->tvchain != NULL ) { + g_object_unref (livetv->tvchain); + livetv->tvchain = NULL; + } + + if ( livetv->proginfo != NULL ) { + g_object_unref (livetv->proginfo); + livetv->proginfo = NULL; + } + + if ( livetv->monitor != NULL ) { + g_object_unref (livetv->monitor); + livetv->monitor = NULL; + } + + return res; + +} + +static gboolean +gmyth_livetv_setup_recorder ( GMythLiveTV *livetv, gint channel, GMythBackendInfo *backend_info ) +{ + return gmyth_livetv_setup_recorder_channel_name ( livetv, ( channel != -1 ) ? + g_strdup_printf( "%d", channel ) : NULL, backend_info ); +} + +gboolean +gmyth_livetv_channel_setup ( GMythLiveTV *livetv, gint channel, GMythBackendInfo *backend_info ) +{ + return gmyth_livetv_setup_recorder ( livetv, channel, backend_info ); +} + +gboolean +gmyth_livetv_channel_name_setup ( GMythLiveTV *livetv, gchar* channel, GMythBackendInfo *backend_info ) +{ + return gmyth_livetv_setup_recorder_channel_name ( livetv, channel, backend_info ); +} + +gboolean +gmyth_livetv_setup ( GMythLiveTV *livetv, GMythBackendInfo *backend_info ) +{ + return gmyth_livetv_setup_recorder ( livetv, -1, backend_info ); +} + +gboolean +gmyth_livetv_next_program_chain ( GMythLiveTV *livetv ) +{ + gboolean res = TRUE; + GMythProgramInfo *prog_info = NULL; + + if ( !livetv->setup_done ) + { + gmyth_debug ( "Call the setup function first!" ); + res= FALSE; + goto error; + } + + //if ( !gmyth_livetv_monitor_handler_start( livetv ) ) + // goto error; + prog_info = gmyth_recorder_get_current_program_info( livetv->recorder ); + + if ( NULL == prog_info ) + { + gmyth_debug( "ProgramInfo is equals to NULL!!!" ); + + return FALSE; + } + /* prints program info data text */ + gmyth_program_info_print( prog_info ); + + if ( prog_info != NULL ) { + res = TRUE; + livetv->proginfo = prog_info; + gmyth_debug ("GMythLiveTV: All requests to backend to start TV were OK, program info changed."); + } else { + g_warning ("[%s] LiveTV not successfully started on the next program chain.\n", __FUNCTION__ ); + res = FALSE; + goto error; + } + + livetv->setup_done = TRUE; + + return res; + +error: + g_print( "[%s] ERROR running LiveTV setup.\n", __FUNCTION__ ); + + if ( livetv->local_hostname != NULL ) { + g_string_free( livetv->local_hostname, FALSE ); + res = FALSE; + } + + if ( livetv->recorder != NULL ) { + g_object_unref (livetv->recorder); + livetv->recorder = NULL; + } + + if ( livetv->tvchain != NULL ) { + g_object_unref (livetv->tvchain); + livetv->tvchain = NULL; + } + + if ( livetv->proginfo != NULL ) { + g_object_unref (livetv->proginfo); + livetv->proginfo = NULL; + } + + return res; + +} + +GMythFileTransfer * +gmyth_livetv_create_file_transfer( GMythLiveTV *livetv ) +{ + //GMythURI* uri = NULL; + + if ( NULL == livetv ) + goto done; + + if ( !livetv->setup_done ) + { + gmyth_debug( "Error: You must do the LiveTV setup, just before generating the FileTransfer from LiveTV source!" ); + goto done; + } + + if ( livetv->proginfo != NULL ) + gmyth_debug( "URI path = %s.\n", livetv->proginfo->pathname->str ); + else + gmyth_debug( "URI path = %s.\n", livetv->uri->uri->str ); + + g_static_mutex_lock( &lock ); + + if ( livetv->file_transfer != NULL ) + { + /*gmyth_file_transfer_close( livetv->file_transfer );*/ + g_object_unref( livetv->file_transfer ); + livetv->file_transfer = NULL; + } + + livetv->file_transfer = gmyth_file_transfer_new( livetv->backend_info ); + + if ( NULL == livetv->file_transfer ) + { + gmyth_debug( "Error: couldn't create the FileTransfer from LiveTV source!" ); + goto done; + } + + if ( livetv->uri != NULL ) + { + if ( livetv->uri->path != NULL ) + { + g_string_free( livetv->uri->path, FALSE ); + livetv->uri->path = NULL; + } + livetv->uri->path = g_string_new( g_strrstr( livetv->proginfo->pathname->str, "/" ) ); + } else { + livetv->uri = gmyth_uri_new_with_value( livetv->proginfo->pathname->str ); + } + + if ( NULL == livetv->uri ) + { + gmyth_debug( "Couldn't parse the URI to start LiveTV! [ uri = %s ]", livetv->proginfo->pathname->str ); + goto done; + } + + if ( !gmyth_file_transfer_open( livetv->file_transfer, livetv->uri != NULL ? gmyth_uri_get_path(livetv->uri) : + livetv->proginfo->pathname->str ) ) + { + gmyth_debug( "Error: couldn't open the FileTransfer from LiveTV source!" ); + g_object_unref( livetv->file_transfer ); + livetv->file_transfer = NULL; + goto done; + } + + g_static_mutex_unlock( &lock ); + +done: + /* + if ( uri != NULL ) + { + g_object_unref( uri ); + uri = NULL; + } + */ + + return livetv->file_transfer; + +} + +/* FIXME: How to proceed differently between livetv and recorded content */ +void +gmyth_livetv_stop_playing (GMythLiveTV *livetv) +{ + gmyth_debug ("Stopping the LiveTV...\n"); + + if (livetv->is_livetv) { + if ( !gmyth_recorder_stop_livetv (livetv->recorder) ) { + g_warning ("[%s] Error while stoping remote encoder", __FUNCTION__); + } + } +} + +gboolean +gmyth_livetv_is_playing (GMythLiveTV *livetv) +{ + return TRUE; +} + +void +gmyth_livetv_start_playing (GMythLiveTV *livetv) +{ + + // TODO + +} +