/** * GMyth Library * * @file gmyth/gmyth_tvchain.c * * @brief

This component contains functions for creating and accessing * the tvchain functions for live tv playback. * * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia. * @author Hallyson Luiz de Morais Melo * *//* * * 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_tvchain.h" #include #include #include #include #include #include "gmyth_epg.h" #include "gmyth_util.h" #include "gmyth_query.h" #include "gmyth_scheduler.h" #include "gmyth_debug.h" static void gmyth_tvchain_class_init (GMythTVChainClass *klass); static void gmyth_tvchain_init (GMythTVChain *object); static void gmyth_tvchain_dispose (GObject *object); static void gmyth_tvchain_finalize (GObject *object); G_DEFINE_TYPE(GMythTVChain, gmyth_tvchain, G_TYPE_OBJECT) static GStaticMutex mutex = G_STATIC_MUTEX_INIT; static void gmyth_tvchain_class_init (GMythTVChainClass *klass) { GObjectClass *gobject_class; gobject_class = (GObjectClass *) klass; gobject_class->dispose = gmyth_tvchain_dispose; gobject_class->finalize = gmyth_tvchain_finalize; } static void gmyth_tvchain_init (GMythTVChain *tvchain) { tvchain->tvchain_id = NULL; tvchain->cur_chanid = g_string_new (""); tvchain->cur_startts = NULL; } static void gmyth_tvchain_dispose (GObject *object) { GMythTVChain *tvchain = GMYTH_TVCHAIN(object); if ( tvchain->tvchain_id != NULL ) { g_string_free( tvchain->tvchain_id, TRUE ); tvchain->tvchain_id = NULL; } if ( tvchain->tvchain_list != NULL ) { g_list_free( tvchain->tvchain_list ); tvchain->tvchain_list = NULL; } if ( tvchain->cur_chanid != NULL ) { g_string_free( tvchain->cur_chanid, TRUE ); tvchain->cur_chanid = NULL; } if ( tvchain->backend_info) { g_object_unref (tvchain->backend_info); tvchain->backend_info = NULL; } G_OBJECT_CLASS (gmyth_tvchain_parent_class)->dispose (object); } static void gmyth_tvchain_finalize (GObject *object) { g_signal_handlers_destroy (object); G_OBJECT_CLASS (gmyth_tvchain_parent_class)->finalize (object); } /** Initializes the tvchain and generates the tvchain id. * * @param tvchain The GMythTVChain instance. * @param hostname The local hostname used to generate the tvchain id. */ gboolean gmyth_tvchain_initialize (GMythTVChain *tvchain, GMythBackendInfo *backend_info) { const char *hostname; assert (tvchain); g_return_val_if_fail (backend_info != NULL, FALSE); g_object_ref (backend_info); tvchain->backend_info = backend_info; hostname = gmyth_backend_info_get_hostname (backend_info); if (tvchain->tvchain_id == NULL) { gchar *isodate = NULL; GTimeVal *cur_time = g_new0( GTimeVal, 1 ); //struct tm* gmyth_util_time_val_to_date ( const GTimeVal* time ) g_get_current_time(cur_time); isodate = gmyth_util_time_to_isoformat_from_time_val_fmt ( "%Y-%m-%dT%H:%M:%S", cur_time ); tvchain->tvchain_id = g_string_sized_new (7 + strlen (hostname) + strlen(isodate)); g_string_printf(tvchain->tvchain_id, "live-%s-%s", hostname, isodate); gmyth_debug ("[%s] tv_chain_id: %s", __FUNCTION__, tvchain->tvchain_id->str); if (isodate) g_free(isodate); if ( cur_time ) g_free( cur_time ); } else { g_warning ("[%s] TVchain already initialized", __FUNCTION__); } return TRUE; } /** Gets the tvchain id. * * @param tvchain The GMythTVChain instance. * @return The tvchain id. */ GString* gmyth_tvchain_get_id (GMythTVChain *tvchain) { g_return_val_if_fail( tvchain != NULL && tvchain->tvchain_id != NULL, NULL ); return g_string_new (tvchain->tvchain_id->str); } /** Reloads all tvchain entries in the database. * * @param tvchain The GMythTVChain instance. * @return TRUE if success, or FALSE if error. */ gboolean gmyth_tvchain_reload_all (GMythTVChain *tvchain) { MYSQL_ROW msql_row; MYSQL_RES *msql_res = NULL; GMythQuery *gmyth_query = NULL; gboolean ret = TRUE; GString *stmt_str = NULL; g_static_mutex_lock( &mutex ); /* gets the initial size of the TVChain entries list */ guint prev_size = g_list_length (tvchain->tvchain_list); gmyth_debug ("[%s] chainid: %s", __FUNCTION__, tvchain->tvchain_id->str); if ( tvchain != NULL && tvchain->tvchain_list != NULL ) { g_list_free (tvchain->tvchain_list); tvchain->tvchain_list = NULL; } // TODO: Reuse gmyth_query already connected from context gmyth_query = gmyth_query_new (); if (!gmyth_query_connect (gmyth_query, tvchain->backend_info)) { g_warning ("[%s] Could not connect to db", __FUNCTION__); g_static_mutex_unlock( &mutex ); ret = FALSE; goto done; } stmt_str = g_string_new (""); g_string_printf (stmt_str, "SELECT chanid, starttime, endtime, discontinuity, " "chainpos, hostprefix, cardtype, channame, input " "FROM tvchain " "WHERE chainid = \"%s\" ORDER BY chainpos;", tvchain->tvchain_id->str); msql_res = gmyth_query_process_statement(gmyth_query, stmt_str->str); if (msql_res != NULL) { while ((msql_row = mysql_fetch_row (msql_res)) != NULL) { struct LiveTVChainEntry *entry = g_new0 (struct LiveTVChainEntry, 1); entry->chanid = g_string_new (msql_row[0]); entry->starttime = gmyth_util_string_to_time_val ((const gchar*) msql_row[1]); entry->endtime = gmyth_util_string_to_time_val ((const gchar*) msql_row[2]); entry->discontinuity = g_ascii_strtoull (msql_row[3], NULL, 10 ) != 0; entry->hostprefix = g_string_new (msql_row[5]); entry->cardtype = g_string_new (msql_row[6]); entry->channum = g_string_new (msql_row[7]); entry->inputname = g_string_new (msql_row[8]); //m_maxpos = query.value(4).toInt() + 1; g_print( "[%s] Reading TV chain entry (channel %s): [%s, %s, %s]\n", __FUNCTION__, entry->channum->str, entry->chanid->str, (gchar*)msql_row[1], (gchar*)msql_row[2] ); /* add this to get the actual start timestamp of the last recording */ if ( tvchain->cur_startts < entry->starttime ) tvchain->cur_startts = entry->starttime; tvchain->tvchain_list = g_list_append (tvchain->tvchain_list, entry); } } else { g_warning ("gmyth_tvchain_reload_all query error!\n"); g_static_mutex_unlock( &mutex ); ret = FALSE; goto done; } g_static_mutex_unlock( &mutex ); tvchain->cur_pos = gmyth_tvchain_program_is_at (tvchain, tvchain->cur_chanid, tvchain->cur_startts); g_print( "[%s] TVChain current position = %d.\n", __FUNCTION__, tvchain->cur_pos ); if (tvchain->cur_pos < 0) tvchain->cur_pos = 0; // if (m_switchid >= 0) // m_switchid = ProgramIsAt(m_switchentry.chanid,m_switchentry.starttime); if (prev_size != g_list_length (tvchain->tvchain_list)) { gmyth_debug ("[%s] Added new recording", __FUNCTION__); } done: if ( stmt_str != NULL ) g_string_free (stmt_str, TRUE); if ( msql_res != NULL ) mysql_free_result (msql_res); if ( gmyth_query != NULL ) g_object_unref (gmyth_query); return ret; } /** Returns the internal index for the TV chain related to the given * channel and start time. * * @param tvchain The GMythTVChain instance. * @param chanid The channel id. * @param startts The program start time. */ gint gmyth_tvchain_program_is_at (GMythTVChain *tvchain, GString *chanid, GTimeVal* startts) { gint count = 0; struct LiveTVChainEntry *entry; GList *tmp_list = tvchain->tvchain_list; guint list_size = g_list_length (tvchain->tvchain_list); g_static_mutex_lock( &mutex ); for (; tmp_list && ( count < list_size ); tmp_list = tvchain->tvchain_list->next, count++) { entry = (struct LiveTVChainEntry*) tmp_list->data; if ( !g_strncasecmp (entry->chanid->str, chanid->str, chanid->len) && entry->starttime == startts ) { g_static_mutex_unlock( &mutex ); return count; } } g_static_mutex_unlock( &mutex ); return -1; } /** Get the program info associated to the tvchain. * * @param tvchain The GMythTVChain instance. * @param index The tvchain index. * @return The program info structure. */ GMythProgramInfo* gmyth_tvchain_get_program_at (GMythTVChain *tvchain, gint index) { struct LiveTVChainEntry *entry; entry = gmyth_tvchain_get_entry_at (tvchain, index); if (entry) return gmyth_tvchain_entry_to_program (tvchain, entry); return NULL; } /** Gets a LiveTVChainEntry associated to the tvchain by its index. * * @param tvchain The GMythTVChain instance. * @param index The tvchain entry index * @return The LiveTVchainEntry structure. */ struct LiveTVChainEntry* gmyth_tvchain_get_entry_at (GMythTVChain *tvchain, gint index) { struct LiveTVChainEntry* chain_entry = NULL; g_return_val_if_fail( tvchain != NULL && tvchain->tvchain_list != NULL, NULL ); g_static_mutex_lock( &mutex ); gint size = g_list_length (tvchain->tvchain_list); gint new_index = (index < 0 || index >= size) ? size - 1 : index; if (new_index >= 0) chain_entry = (struct LiveTVChainEntry*) g_list_nth_data (tvchain->tvchain_list, new_index); g_static_mutex_unlock( &mutex ); if ( chain_entry != NULL ) { gmyth_debug ("[%s] Got TV Chain entry at %d.\n", __FUNCTION__, new_index ); } else { g_warning ("[%s] failed to get entry at index %d", __FUNCTION__, index); } return chain_entry; } /** Gets the program info from backend database associated to the tv chain entry. * * @param tvchain The GMythTVChain instance. * @param entry the LiveTVChainEntry to be converted. * @return The progrma info. */ GMythProgramInfo* gmyth_tvchain_entry_to_program (GMythTVChain *tvchain, struct LiveTVChainEntry *entry) { GMythProgramInfo *proginfo = NULL; g_return_val_if_fail( tvchain != NULL, NULL ); if ( !entry || !tvchain ) { g_warning ("gmyth_tvchain_entry_to_program() received NULL argument"); return NULL; } GMythScheduler *scheduler = gmyth_scheduler_new (); gmyth_scheduler_connect( scheduler, tvchain->backend_info ); proginfo = gmyth_scheduler_get_recorded (scheduler, entry->chanid, entry->starttime); gmyth_scheduler_disconnect( scheduler ); if (proginfo) { proginfo->pathname = g_string_prepend (proginfo->pathname, entry->hostprefix->str); } else { g_warning ("tvchain_entry_to_program( chan id = %s, starttime = %ld) failed!", entry->chanid->str, entry->starttime->tv_sec); } return proginfo; }