/** * 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->socket = NULL; livetv->recorder = NULL; livetv->tvchain = NULL; livetv->proginfo = NULL; livetv->uri = NULL; livetv->mutex = NULL; } static void gmyth_livetv_dispose (GObject *object) { GMythLiveTV *livetv = GMYTH_LIVETV (object); if ( livetv->monitor != NULL ) { g_object_unref (livetv->monitor); livetv->monitor = NULL; } if ( livetv->recorder != NULL ) { //gmyth_livetv_stop_playing( g_object_unref (livetv->recorder); livetv->recorder = NULL; } if ( livetv->socket != NULL ) { g_object_unref (livetv->socket); livetv->socket = 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_IS_OBJECT(livetv->file_transfer) ) { 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; } if ( livetv->mutex != NULL ) { g_mutex_free (livetv->mutex); livetv->mutex = NULL; } G_OBJECT_CLASS (gmyth_livetv_parent_class)->dispose (object); } static void gmyth_livetv_finalize (GObject *object) { g_signal_handlers_destroy (object); G_OBJECT_CLASS ( gmyth_livetv_parent_class )->finalize ( object ); } GMythLiveTV* gmyth_livetv_new () { GMythLiveTV *livetv = GMYTH_LIVETV ( g_object_new( GMYTH_LIVETV_TYPE, NULL ) ); livetv->mutex = g_mutex_new(); 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 \" ]" ); /* GMythProgramInfo* prog_info = gmyth_recorder_get_current_program_info( live_tv->recorder ); if ( prog_info != NULL ) live_tv->proginfo = prog_info; */ 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... [ \"backend-done-recording\" ]" ); /* GMythProgramInfo* prog_info = gmyth_recorder_get_current_program_info( live_tv->recorder ); if ( prog_info != NULL ) live_tv->proginfo = prog_info; */ 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_STOP_LIVETV: { gmyth_debug( "LIVETV Stop LiveTV request received [ msg = %s ]. Going out the "\ "LiveTV...\n", message ); /* stops the LiveTV */ if ( live_tv != NULL ) { gmyth_debug( "Going out the LiveTV... [ \"quit-livetv\" ]" ); g_object_unref( 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; livetv->backend_info = backend_info; if ( NULL == livetv->socket ) { livetv->socket = gmyth_socket_new (); /* FIME: Implement this at gmyth_socket */ res = gmyth_socket_connect_to_backend (livetv->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; } } g_mutex_lock( livetv->mutex ); livetv->is_livetv = TRUE; livetv->local_hostname = gmyth_socket_get_local_hostname (); if ( livetv->local_hostname == NULL ) { res = FALSE; goto error; } if ( livetv->recorder != NULL ) { g_object_unref( livetv->recorder ); livetv->recorder= NULL; } if ( gmyth_remote_util_get_free_recorder_count (livetv->socket) <= 0 ) { gmyth_debug ("No free remote encoder available."); res = FALSE; goto error; } /* Gets the recorder num */ livetv->recorder = remote_request_next_free_recorder (livetv->socket, -1); gmyth_socket_close_connection (livetv->socket); if ( NULL == livetv->recorder ) { 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_new(); 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 (800); 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 ) ) { gmyth_debug( "Channel changed!!! [%s].\n", channel ); } } } /* if - changes the channel number */ sleep (2); /* 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!!!" ); gint i; gchar *channame = NULL; gmyth_debug( "Problem getting current proginfo!\n" ); /* * mythbackend must not be tuned in to a channel, so keep * changing channels until we find a valid one, or until * we decide to give up. */ for (i=1; i<1000; i++) { if ( channame != NULL ) g_free(channame); channame = g_strdup_printf( "%d", i ); if (gmyth_recorder_set_channel_name(livetv->recorder, channame) < 0) { continue; } prog_info = gmyth_recorder_get_next_program_info(livetv->recorder, BROWSE_DIRECTION_UP); if (prog_info != NULL) break; } } /* if - Program Info */ /* 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 ); */ /* GMythProgramInfo* prog_inf = gmyth_recorder_get_next_program_info( livetv->recorder, BROWSE_DIRECTION_UP ); gmyth_debug( "Next ProgramInfo...\n" ); gmyth_program_info_print( prog_inf ); */ /* check if the program chain could be obtained from the MythTV protocol message */ if ( prog_info != NULL ) { g_debug( "Program Info: %s\n", gmyth_program_info_to_string( prog_info ) ); 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_mutex_unlock( livetv->mutex ); 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, TRUE ); livetv->local_hostname = NULL; 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; } gmyth_debug( "Current ProgramInfo...\n" ); prog_info = gmyth_recorder_get_current_program_info( livetv->recorder ); if ( prog_info != NULL ) { livetv->proginfo = prog_info; } else { gmyth_debug( "ProgramInfo is equals to NULL!!! Getting the next program info..." ); prog_info = gmyth_recorder_get_next_program_info( livetv->recorder, BROWSE_DIRECTION_RIGHT ); livetv->proginfo = prog_info; } /* 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 (from program info) = %s.\n", livetv->proginfo->pathname->str ); else gmyth_debug( "URI path (from URI) = %s.\n", livetv->uri->uri->str ); g_mutex_lock( livetv->mutex ); if ( livetv->file_transfer != NULL ) { /*gmyth_file_transfer_close( livetv->file_transfer );*/ g_object_unref( livetv->file_transfer ); livetv->file_transfer = NULL; } if ( livetv->uri != NULL ) { /* if ( livetv->uri->path != NULL ) { g_string_free( livetv->uri->path, FALSE ); livetv->uri->path = NULL; } */ gmyth_debug( "URI is NULL, creating from the ProgramInfo pathname... (%s)", livetv->proginfo->pathname->str ); livetv->uri->path = g_string_erase(livetv->uri->path, 0, -1); livetv->uri->path = g_string_new( g_strrstr( livetv->proginfo->pathname->str, "/" ) ); } else { gmyth_debug( "URI is NULL, creating from the ProgramInfo pathname... (%s)", livetv->proginfo->pathname->str ); 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; } 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; } gmyth_file_transfer_settimeout( livetv->file_transfer, TRUE ); g_mutex_unlock( livetv->mutex ); 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_debug ("[%s] Error while stoping remote encoder", __FUNCTION__); } if ( !gmyth_recorder_finish_recording(livetv->recorder) ) { g_debug ("[%s] Error while finishing recording on remote encoder", __FUNCTION__); } } } gboolean gmyth_livetv_is_playing (GMythLiveTV *livetv) { return TRUE; } void gmyth_livetv_start_playing (GMythLiveTV *livetv) { // TODO }