branches/gmyth-0.1b/src/gmyth_tvchain.c
author rosfran
Tue Feb 06 00:33:35 2007 +0000 (2007-02-06)
branchtrunk
changeset 326 81b1f3006eb2
permissions -rw-r--r--
[svn r328] Some fixes on program info, and some memory clean ups.
renatofilho@320
     1
/**
renatofilho@320
     2
 * GMyth Library
renatofilho@320
     3
 *
renatofilho@320
     4
 * @file gmyth/gmyth_tvchain.c
renatofilho@320
     5
 * 
renatofilho@320
     6
 * @brief <p> This component contains functions for creating and accessing
renatofilho@320
     7
 * the tvchain functions for live tv playback.
renatofilho@320
     8
 * 
renatofilho@320
     9
 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
renatofilho@320
    10
 * @author Hallyson Luiz de Morais Melo <hallyson.melo@indt.org.br>
renatofilho@320
    11
 *
renatofilho@320
    12
 *//*
renatofilho@320
    13
 * 
renatofilho@320
    14
 * This program is free software; you can redistribute it and/or modify
renatofilho@320
    15
 * it under the terms of the GNU Lesser General Public License as published by
renatofilho@320
    16
 * the Free Software Foundation; either version 2 of the License, or
renatofilho@320
    17
 * (at your option) any later version.
renatofilho@320
    18
 *
renatofilho@320
    19
 * This program is distributed in the hope that it will be useful,
renatofilho@320
    20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
renatofilho@320
    21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
renatofilho@320
    22
 * GNU General Public License for more details.
renatofilho@320
    23
 *
renatofilho@320
    24
 * You should have received a copy of the GNU Lesser General Public License
renatofilho@320
    25
 * along with this program; if not, write to the Free Software
renatofilho@320
    26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
renatofilho@320
    27
 */
renatofilho@320
    28
 
renatofilho@320
    29
#ifdef HAVE_CONFIG_H
renatofilho@320
    30
#include "config.h"
renatofilho@320
    31
#endif
renatofilho@320
    32
renatofilho@320
    33
#include "gmyth_tvchain.h"
renatofilho@320
    34
renatofilho@320
    35
#include <glib.h>
renatofilho@320
    36
#include <time.h>
renatofilho@320
    37
#include <stdio.h>
renatofilho@320
    38
#include <stdlib.h>
renatofilho@320
    39
#include <assert.h>
renatofilho@320
    40
renatofilho@320
    41
#include "gmyth_epg.h"
renatofilho@320
    42
#include "gmyth_util.h"
renatofilho@320
    43
#include "gmyth_query.h"
renatofilho@320
    44
#include "gmyth_scheduler.h"
renatofilho@320
    45
#include "gmyth_debug.h"
renatofilho@320
    46
renatofilho@320
    47
static void gmyth_tvchain_class_init          (GMythTVChainClass *klass);
renatofilho@320
    48
static void gmyth_tvchain_init                (GMythTVChain *object);
renatofilho@320
    49
renatofilho@320
    50
static void gmyth_tvchain_dispose  (GObject *object);
renatofilho@320
    51
static void gmyth_tvchain_finalize (GObject *object);
renatofilho@320
    52
renatofilho@320
    53
G_DEFINE_TYPE(GMythTVChain, gmyth_tvchain, G_TYPE_OBJECT)
renatofilho@320
    54
renatofilho@320
    55
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
renatofilho@320
    56
renatofilho@320
    57
static void
renatofilho@320
    58
gmyth_tvchain_class_init (GMythTVChainClass *klass)
renatofilho@320
    59
{
renatofilho@320
    60
	GObjectClass *gobject_class;
renatofilho@320
    61
renatofilho@320
    62
	gobject_class = (GObjectClass *) klass;
renatofilho@320
    63
renatofilho@320
    64
	gobject_class->dispose  = gmyth_tvchain_dispose;
renatofilho@320
    65
	gobject_class->finalize = gmyth_tvchain_finalize;	
renatofilho@320
    66
}
renatofilho@320
    67
renatofilho@320
    68
static void
renatofilho@320
    69
gmyth_tvchain_init (GMythTVChain *tvchain)
renatofilho@320
    70
{
renatofilho@320
    71
	tvchain->tvchain_id = NULL;
renatofilho@320
    72
renatofilho@320
    73
	tvchain->cur_chanid = g_string_new ("");
renatofilho@320
    74
	tvchain->cur_startts = NULL;
renatofilho@320
    75
}
renatofilho@320
    76
renatofilho@320
    77
static void
renatofilho@320
    78
gmyth_tvchain_dispose  (GObject *object)
renatofilho@320
    79
{
renatofilho@320
    80
    GMythTVChain *tvchain = GMYTH_TVCHAIN(object);
renatofilho@320
    81
renatofilho@320
    82
    if ( tvchain->tvchain_id != NULL ) {
renatofilho@320
    83
	g_string_free( tvchain->tvchain_id, TRUE );
renatofilho@320
    84
	tvchain->tvchain_id = NULL;
renatofilho@320
    85
    }
renatofilho@320
    86
renatofilho@320
    87
    if ( tvchain->tvchain_list != NULL ) {
renatofilho@320
    88
	g_list_free( tvchain->tvchain_list );
renatofilho@320
    89
	tvchain->tvchain_list = NULL;
renatofilho@320
    90
    }
renatofilho@320
    91
renatofilho@320
    92
    if ( tvchain->cur_chanid != NULL ) {
renatofilho@320
    93
	g_string_free( tvchain->cur_chanid, TRUE );
renatofilho@320
    94
	tvchain->cur_chanid = NULL;
renatofilho@320
    95
    }
renatofilho@320
    96
renatofilho@320
    97
    if ( tvchain->backend_info) {
renatofilho@320
    98
        g_object_unref (tvchain->backend_info);
renatofilho@320
    99
        tvchain->backend_info = NULL;
renatofilho@320
   100
    }
renatofilho@320
   101
renatofilho@320
   102
renatofilho@320
   103
    G_OBJECT_CLASS (gmyth_tvchain_parent_class)->dispose (object);
renatofilho@320
   104
}
renatofilho@320
   105
renatofilho@320
   106
static void
renatofilho@320
   107
gmyth_tvchain_finalize (GObject *object)
renatofilho@320
   108
{
renatofilho@320
   109
    g_signal_handlers_destroy (object);
renatofilho@320
   110
renatofilho@320
   111
    G_OBJECT_CLASS (gmyth_tvchain_parent_class)->finalize (object);
renatofilho@320
   112
}
renatofilho@320
   113
renatofilho@320
   114
/** Initializes the tvchain and generates the tvchain id.
renatofilho@320
   115
 * 
renatofilho@320
   116
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   117
 * @param hostname The local hostname used to generate the tvchain id.
renatofilho@320
   118
 */
renatofilho@320
   119
gboolean
renatofilho@320
   120
gmyth_tvchain_initialize (GMythTVChain *tvchain, GMythBackendInfo *backend_info)
renatofilho@320
   121
{
renatofilho@320
   122
    const char *hostname;
renatofilho@320
   123
renatofilho@320
   124
    assert (tvchain);
renatofilho@320
   125
    g_return_val_if_fail (backend_info != NULL, FALSE);
renatofilho@320
   126
renatofilho@320
   127
    g_object_ref (backend_info);
renatofilho@320
   128
    tvchain->backend_info = backend_info;
renatofilho@320
   129
renatofilho@320
   130
    hostname = gmyth_backend_info_get_hostname (backend_info);
renatofilho@320
   131
    
renatofilho@320
   132
    if (tvchain->tvchain_id == NULL) {
renatofilho@320
   133
	    gchar *isodate = NULL;
renatofilho@320
   134
    	GTimeVal *cur_time = g_new0( GTimeVal, 1 );
renatofilho@320
   135
    	//struct tm* gmyth_util_time_val_to_date ( const GTimeVal* time )
renatofilho@320
   136
renatofilho@320
   137
	    g_get_current_time(cur_time);
renatofilho@320
   138
    	isodate = gmyth_util_time_to_isoformat_from_time_val_fmt ( "%Y-%m-%dT%H:%M:%S", cur_time );
renatofilho@320
   139
renatofilho@320
   140
	    tvchain->tvchain_id = g_string_sized_new (7 + strlen (hostname) + strlen(isodate));
renatofilho@320
   141
    	g_string_printf(tvchain->tvchain_id, 
renatofilho@320
   142
	    	"live-%s-%s", hostname, isodate);
renatofilho@320
   143
renatofilho@320
   144
    	gmyth_debug ("[%s] tv_chain_id: %s", __FUNCTION__, tvchain->tvchain_id->str);
renatofilho@320
   145
renatofilho@320
   146
			if (isodate)
renatofilho@320
   147
	    	g_free(isodate);
renatofilho@320
   148
	    
renatofilho@320
   149
	    if ( cur_time )
renatofilho@320
   150
	    	g_free( cur_time );
renatofilho@320
   151
    } else {
renatofilho@320
   152
    	g_warning ("[%s] TVchain already initialized", __FUNCTION__);
renatofilho@320
   153
    }
renatofilho@320
   154
renatofilho@320
   155
    return TRUE;
renatofilho@320
   156
}
renatofilho@320
   157
renatofilho@320
   158
/** Gets the tvchain id.
renatofilho@320
   159
 * 
renatofilho@320
   160
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   161
 * @return The tvchain id.
renatofilho@320
   162
 */
renatofilho@320
   163
GString*
renatofilho@320
   164
gmyth_tvchain_get_id (GMythTVChain *tvchain)
renatofilho@320
   165
{
renatofilho@320
   166
	g_return_val_if_fail( tvchain != NULL && tvchain->tvchain_id != NULL, NULL );
renatofilho@320
   167
renatofilho@320
   168
	return g_string_new (tvchain->tvchain_id->str);
renatofilho@320
   169
}
renatofilho@320
   170
renatofilho@320
   171
/** Reloads all tvchain entries in the database.
renatofilho@320
   172
 * 
renatofilho@320
   173
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   174
 * @return  TRUE if success, or FALSE if error.
renatofilho@320
   175
 */
renatofilho@320
   176
gboolean
renatofilho@320
   177
gmyth_tvchain_reload_all (GMythTVChain *tvchain)
renatofilho@320
   178
{
renatofilho@320
   179
	MYSQL_ROW msql_row;
renatofilho@320
   180
	MYSQL_RES *msql_res = NULL;
renatofilho@320
   181
	GMythQuery *gmyth_query = NULL;
renatofilho@320
   182
	gboolean ret = TRUE;
renatofilho@320
   183
	GString *stmt_str = NULL;
renatofilho@320
   184
renatofilho@320
   185
	g_static_mutex_lock( &mutex );
renatofilho@320
   186
	
renatofilho@320
   187
	/* gets the initial size of the TVChain entries list */
renatofilho@320
   188
	guint prev_size = g_list_length (tvchain->tvchain_list);
renatofilho@320
   189
renatofilho@320
   190
	gmyth_debug ("[%s] chainid: %s", __FUNCTION__, tvchain->tvchain_id->str);
renatofilho@320
   191
renatofilho@320
   192
	if ( tvchain != NULL && tvchain->tvchain_list != NULL ) {
renatofilho@320
   193
		g_list_free (tvchain->tvchain_list);
renatofilho@320
   194
		tvchain->tvchain_list = NULL;
renatofilho@320
   195
	}
renatofilho@320
   196
renatofilho@320
   197
	// TODO: Reuse gmyth_query already connected from context
renatofilho@320
   198
	gmyth_query = gmyth_query_new ();
renatofilho@320
   199
	if (!gmyth_query_connect (gmyth_query, tvchain->backend_info)) {
renatofilho@320
   200
		g_warning ("[%s] Could not connect to db", __FUNCTION__);
renatofilho@320
   201
		g_static_mutex_unlock( &mutex );
renatofilho@320
   202
		ret = FALSE;
renatofilho@320
   203
		goto done;
renatofilho@320
   204
	}
renatofilho@320
   205
renatofilho@320
   206
	stmt_str = g_string_new ("");
renatofilho@320
   207
	g_string_printf (stmt_str, 
renatofilho@320
   208
			"SELECT chanid, starttime, endtime, discontinuity, "
renatofilho@320
   209
			"chainpos, hostprefix, cardtype, channame, input "
renatofilho@320
   210
			"FROM tvchain "
renatofilho@320
   211
			"WHERE chainid = \"%s\" ORDER BY chainpos;",
renatofilho@320
   212
			tvchain->tvchain_id->str);
renatofilho@320
   213
renatofilho@320
   214
	msql_res = gmyth_query_process_statement(gmyth_query, stmt_str->str);
renatofilho@320
   215
	if (msql_res != NULL) {
renatofilho@320
   216
renatofilho@320
   217
		while ((msql_row = mysql_fetch_row (msql_res)) != NULL) {
renatofilho@320
   218
			struct LiveTVChainEntry *entry = g_new0 (struct LiveTVChainEntry, 1);
renatofilho@320
   219
			entry->chanid = g_string_new (msql_row[0]);
renatofilho@320
   220
			entry->starttime = gmyth_util_string_to_time_val ((const gchar*) msql_row[1]);
renatofilho@320
   221
			entry->endtime = gmyth_util_string_to_time_val ((const gchar*) msql_row[2]);
renatofilho@320
   222
			entry->discontinuity = g_ascii_strtoull (msql_row[3], NULL, 10 ) != 0;
renatofilho@320
   223
			entry->hostprefix = g_string_new (msql_row[5]);
renatofilho@320
   224
			entry->cardtype = g_string_new (msql_row[6]);
renatofilho@320
   225
			entry->channum = g_string_new (msql_row[7]);
renatofilho@320
   226
			entry->inputname = g_string_new (msql_row[8]);
renatofilho@320
   227
renatofilho@320
   228
			//m_maxpos = query.value(4).toInt() + 1;
renatofilho@320
   229
			g_print( "[%s] Reading TV chain entry (channel %s): [%s, %s, %s]\n", __FUNCTION__, entry->channum->str, entry->chanid->str, 
renatofilho@320
   230
					(gchar*)msql_row[1], (gchar*)msql_row[2] );
renatofilho@320
   231
			
renatofilho@320
   232
			/* add this to get the actual start timestamp of the last recording */
renatofilho@320
   233
			if ( tvchain->cur_startts < entry->starttime )
renatofilho@320
   234
				tvchain->cur_startts = entry->starttime;
renatofilho@320
   235
renatofilho@320
   236
			tvchain->tvchain_list = g_list_append (tvchain->tvchain_list, entry);			
renatofilho@320
   237
		}
renatofilho@320
   238
	} else {
renatofilho@320
   239
		g_warning ("gmyth_tvchain_reload_all query error!\n");
renatofilho@320
   240
		g_static_mutex_unlock( &mutex );
renatofilho@320
   241
renatofilho@320
   242
		ret = FALSE;
renatofilho@320
   243
		goto done;
renatofilho@320
   244
	}
renatofilho@320
   245
renatofilho@320
   246
	g_static_mutex_unlock( &mutex );
renatofilho@320
   247
	
renatofilho@320
   248
	tvchain->cur_pos = gmyth_tvchain_program_is_at (tvchain, tvchain->cur_chanid, tvchain->cur_startts);
renatofilho@320
   249
	g_print( "[%s] TVChain current position = %d.\n", __FUNCTION__, tvchain->cur_pos );
renatofilho@320
   250
renatofilho@320
   251
	if (tvchain->cur_pos < 0)
renatofilho@320
   252
		tvchain->cur_pos = 0;
renatofilho@320
   253
renatofilho@320
   254
	//    if (m_switchid >= 0)
renatofilho@320
   255
	//        m_switchid = ProgramIsAt(m_switchentry.chanid,m_switchentry.starttime);
renatofilho@320
   256
renatofilho@320
   257
	if (prev_size != g_list_length (tvchain->tvchain_list)) {
renatofilho@320
   258
		gmyth_debug ("[%s] Added new recording", __FUNCTION__);
renatofilho@320
   259
	}	
renatofilho@320
   260
renatofilho@320
   261
done:
renatofilho@320
   262
	if ( stmt_str != NULL )
renatofilho@320
   263
		g_string_free (stmt_str, TRUE);
renatofilho@320
   264
renatofilho@320
   265
	if ( msql_res != NULL )
renatofilho@320
   266
		mysql_free_result (msql_res);
renatofilho@320
   267
renatofilho@320
   268
	if ( gmyth_query != NULL )
renatofilho@320
   269
		g_object_unref (gmyth_query);
renatofilho@320
   270
renatofilho@320
   271
	return ret;
renatofilho@320
   272
}
renatofilho@320
   273
renatofilho@320
   274
/** Returns the internal index for the TV chain related to the given
renatofilho@320
   275
 * channel and start time.
renatofilho@320
   276
 * 
renatofilho@320
   277
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   278
 * @param chanid The channel id.
renatofilho@320
   279
 * @param startts The program start time.
renatofilho@320
   280
 */
renatofilho@320
   281
gint
renatofilho@320
   282
gmyth_tvchain_program_is_at (GMythTVChain *tvchain, GString *chanid, GTimeVal* startts)
renatofilho@320
   283
{
renatofilho@320
   284
	gint count = 0;
renatofilho@320
   285
	struct LiveTVChainEntry *entry;
renatofilho@320
   286
	GList *tmp_list = tvchain->tvchain_list;
renatofilho@320
   287
	guint list_size = g_list_length (tvchain->tvchain_list);
renatofilho@320
   288
renatofilho@320
   289
	g_static_mutex_lock( &mutex );
renatofilho@320
   290
	
renatofilho@320
   291
	for (; tmp_list && ( count < list_size ); tmp_list = tvchain->tvchain_list->next, count++)
renatofilho@320
   292
	{
renatofilho@320
   293
		entry = (struct LiveTVChainEntry*) tmp_list->data;
renatofilho@320
   294
		if ( !g_strncasecmp (entry->chanid->str, chanid->str, chanid->len)
renatofilho@320
   295
				&& entry->starttime == startts )
renatofilho@320
   296
		{
renatofilho@320
   297
			g_static_mutex_unlock( &mutex );
renatofilho@320
   298
			return count;
renatofilho@320
   299
		}
renatofilho@320
   300
	}
renatofilho@320
   301
	g_static_mutex_unlock( &mutex );
renatofilho@320
   302
renatofilho@320
   303
	return -1;	
renatofilho@320
   304
}
renatofilho@320
   305
renatofilho@320
   306
/** Get the program info associated to the tvchain.
renatofilho@320
   307
 * 
renatofilho@320
   308
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   309
 * @param index The tvchain index.
renatofilho@320
   310
 * @return The program info structure.
renatofilho@320
   311
 */
renatofilho@320
   312
GMythProgramInfo*
renatofilho@320
   313
gmyth_tvchain_get_program_at (GMythTVChain *tvchain, gint index)
renatofilho@320
   314
{
renatofilho@320
   315
	struct LiveTVChainEntry *entry;
renatofilho@320
   316
renatofilho@320
   317
	entry = gmyth_tvchain_get_entry_at (tvchain, index);
renatofilho@320
   318
renatofilho@320
   319
	if (entry)
renatofilho@320
   320
		return gmyth_tvchain_entry_to_program (tvchain, entry);	
renatofilho@320
   321
renatofilho@320
   322
	return NULL;
renatofilho@320
   323
}
renatofilho@320
   324
renatofilho@320
   325
/** Gets a LiveTVChainEntry associated to the tvchain by its index.
renatofilho@320
   326
 * 
renatofilho@320
   327
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   328
 * @param index The tvchain entry index
renatofilho@320
   329
 * @return The LiveTVchainEntry structure.
renatofilho@320
   330
 */
renatofilho@320
   331
struct LiveTVChainEntry*
renatofilho@320
   332
gmyth_tvchain_get_entry_at (GMythTVChain *tvchain, gint index)
renatofilho@320
   333
{
renatofilho@320
   334
	struct LiveTVChainEntry* chain_entry = NULL;
renatofilho@320
   335
renatofilho@320
   336
	g_return_val_if_fail( tvchain != NULL && tvchain->tvchain_list != NULL, NULL );
renatofilho@320
   337
	
renatofilho@320
   338
	g_static_mutex_lock( &mutex );
renatofilho@320
   339
	
renatofilho@320
   340
	gint size = g_list_length (tvchain->tvchain_list);
renatofilho@320
   341
	gint new_index = (index < 0 || index >= size) ? size - 1 : index;
renatofilho@320
   342
renatofilho@320
   343
	if (new_index >= 0) 
renatofilho@320
   344
		chain_entry = (struct LiveTVChainEntry*) g_list_nth_data (tvchain->tvchain_list, new_index);
renatofilho@320
   345
renatofilho@320
   346
	g_static_mutex_unlock( &mutex );
renatofilho@320
   347
	
renatofilho@320
   348
	if ( chain_entry != NULL ) {
renatofilho@320
   349
		gmyth_debug ("[%s] Got TV Chain entry at %d.\n", __FUNCTION__, new_index );
renatofilho@320
   350
renatofilho@320
   351
	} else {
renatofilho@320
   352
		g_warning ("[%s] failed to get entry at index %d", __FUNCTION__, index);
renatofilho@320
   353
	}
renatofilho@320
   354
renatofilho@320
   355
	return chain_entry;
renatofilho@320
   356
}
renatofilho@320
   357
renatofilho@320
   358
/** Gets the program info from backend database associated to the tv chain entry.
renatofilho@320
   359
 * 
renatofilho@320
   360
 * @param tvchain The GMythTVChain instance.
renatofilho@320
   361
 * @param entry the LiveTVChainEntry to be converted.
renatofilho@320
   362
 * @return The progrma info.
renatofilho@320
   363
 */
renatofilho@320
   364
GMythProgramInfo*
renatofilho@320
   365
gmyth_tvchain_entry_to_program (GMythTVChain *tvchain, struct LiveTVChainEntry *entry)
renatofilho@320
   366
{
renatofilho@320
   367
	GMythProgramInfo *proginfo = NULL;
renatofilho@320
   368
renatofilho@320
   369
	g_return_val_if_fail( tvchain != NULL, NULL );
renatofilho@320
   370
renatofilho@320
   371
	if ( !entry || !tvchain ) {
renatofilho@320
   372
		g_warning ("gmyth_tvchain_entry_to_program() received NULL argument");
renatofilho@320
   373
		return NULL;
renatofilho@320
   374
	}
renatofilho@320
   375
renatofilho@320
   376
	GMythScheduler *scheduler = gmyth_scheduler_new ();
renatofilho@320
   377
renatofilho@320
   378
	gmyth_scheduler_connect( scheduler, tvchain->backend_info );
renatofilho@320
   379
	proginfo = gmyth_scheduler_get_recorded (scheduler, 
renatofilho@320
   380
			entry->chanid, entry->starttime);
renatofilho@320
   381
	gmyth_scheduler_disconnect( scheduler );
renatofilho@320
   382
renatofilho@320
   383
	if (proginfo) {
renatofilho@320
   384
		proginfo->pathname = g_string_prepend (proginfo->pathname, entry->hostprefix->str);
renatofilho@320
   385
	} else {
renatofilho@320
   386
		g_warning ("tvchain_entry_to_program( chan id = %s, starttime = %ld) failed!", entry->chanid->str, entry->starttime->tv_sec);
renatofilho@320
   387
	}
renatofilho@320
   388
renatofilho@320
   389
	return proginfo;
renatofilho@320
   390
}