branches/gmyth-0.1b/src/gmyth_file_transfer.c
author rosfran
Wed Feb 14 21:28:49 2007 +0000 (2007-02-14)
branchtrunk
changeset 360 6d5596b9eb95
permissions -rwxr-xr-x
[svn r362] Some fixes in the GIOWatcher clean-ups, and changed the API version number.
     1 /**
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_file_transfer.c
     5  * 
     6  * @brief <p> GMythFileTransfer deals with the file streaming media remote/local
     7  * transfering to the MythTV frontend.
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
    11  *
    12  *//*
    13  * 
    14  * This program is free software; you can redistribute it and/or modify
    15  * it under the terms of the GNU Lesser General Public License as published by
    16  * the Free Software Foundation; either version 2 of the License, or
    17  * (at your option) any later version.
    18  *
    19  * This program is distributed in the hope that it will be useful,
    20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    22  * GNU General Public License for more details.
    23  *
    24  * You should have received a copy of the GNU Lesser General Public License
    25  * along with this program; if not, write to the Free Software
    26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    27  *
    28  * GStreamer MythTV plug-in properties:
    29  * - location (backend server hostname/URL) [ex.: myth://192.168.1.73:28722/1000_1092091.nuv]
    30  * - path (qurl - remote file to be opened)
    31  * - port number *   
    32  */
    33  
    34 #ifdef HAVE_CONFIG_H
    35 #include "config.h"
    36 #endif
    37 
    38 #include "gmyth_file_transfer.h"
    39 #include "gmyth_livetv.h"
    40 #include "gmyth_util.h"
    41 #include "gmyth_socket.h"
    42 #include "gmyth_stringlist.h"
    43 #include "gmyth_debug.h"
    44 #include "gmyth_uri.h"
    45 #include "gmyth_marshal.h"
    46 
    47 #include <unistd.h>
    48 #include <glib.h>
    49 
    50 #include <arpa/inet.h>
    51 #include <sys/types.h>
    52 #include <sys/socket.h>
    53 #include <netdb.h>
    54 #include <errno.h>
    55 #include <stdlib.h>
    56 #include <assert.h>
    57 
    58 #define GMYTHTV_QUERY_HEADER		"QUERY_FILETRANSFER "
    59 
    60 /* default values to the file transfer parameters */
    61 #define GMYTHTV_USER_READ_AHEAD			TRUE
    62 #define GMYTHTV_RETRIES							-1
    63 #define GMYTHTV_FILE_SIZE						0
    64 
    65 #define GMYTHTV_BUFFER_SIZE					64*1024
    66 
    67 #define GMYTHTV_VERSION							30
    68 
    69 #define GMYTHTV_TRANSFER_MAX_WAITS	700
    70 
    71 #ifdef GMYTHTV_ENABLE_DEBUG
    72 #define GMYTHTV_ENABLE_DEBUG				1
    73 #else
    74 #undef GMYTHTV_ENABLE_DEBUG
    75 #endif
    76 
    77 /* this NDEBUG is to maintain compatibility with GMyth library */
    78 #ifndef NDEBUG
    79 #define GMYTHTV_ENABLE_DEBUG				1
    80 #endif
    81 
    82 enum myth_sock_types {
    83   GMYTH_PLAYBACK_TYPE = 0,
    84   GMYTH_MONITOR_TYPE,
    85   GMYTH_FILETRANSFER_TYPE,
    86   GMYTH_RINGBUFFER_TYPE
    87 };
    88 
    89 #define GMYTH_FILE_TRANSFER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GMYTH_FILE_TRANSFER_TYPE, GMythFileTransferPrivate))
    90 
    91 struct _GMythFileTransferPrivate {
    92 	
    93 	GMythLiveTV *livetv;
    94 	
    95 	gboolean do_next_program_chain;
    96 	
    97 };
    98 
    99 static void gmyth_file_transfer_class_init          (GMythFileTransferClass *klass);
   100 static void gmyth_file_transfer_init                (GMythFileTransfer *object);
   101 
   102 static void gmyth_file_transfer_dispose  (GObject *object);
   103 static void gmyth_file_transfer_finalize (GObject *object);
   104 
   105 static void gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   106 										gint msg_code, gpointer livetv_transfer );
   107 
   108 static gboolean gmyth_connect_to_backend (GMythFileTransfer *transfer);
   109 
   110 void gmyth_file_transfer_close( GMythFileTransfer *transfer );
   111 
   112 static gboolean myth_control_acquire_context( gboolean do_wait );
   113 
   114 static gboolean myth_control_release_context( );
   115 
   116 G_DEFINE_TYPE(GMythFileTransfer, gmyth_file_transfer, G_TYPE_OBJECT)
   117 
   118 static void
   119 gmyth_file_transfer_class_init (GMythFileTransferClass *klass)
   120 {
   121   GObjectClass *gobject_class;
   122   GMythFileTransferClass *gtransfer_class;
   123 
   124   gobject_class = (GObjectClass *) klass;
   125   gtransfer_class = (GMythFileTransferClass *) gobject_class;
   126 
   127   gobject_class->dispose  = gmyth_file_transfer_dispose;
   128   gobject_class->finalize = gmyth_file_transfer_finalize;
   129   
   130   g_type_class_add_private (gobject_class, sizeof (GMythFileTransferPrivate));
   131   
   132 	gtransfer_class->program_info_changed_handler = gmyth_file_transfer_program_info_changed;
   133   
   134 	gtransfer_class->program_info_changed_handler_signal_id = 
   135 		  g_signal_new ( "program-info-changed",
   136 		                 G_TYPE_FROM_CLASS (gtransfer_class),
   137 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   138 		                 0,
   139 										 NULL,
   140 										 NULL,
   141 										 gmyth_marshal_VOID__INT_POINTER,
   142 										 G_TYPE_NONE,
   143 										 2,
   144 										 G_TYPE_INT,
   145 										 G_TYPE_POINTER );
   146 
   147 }
   148 
   149 static void
   150 gmyth_file_transfer_init (GMythFileTransfer *transfer)
   151 { 
   152   g_return_if_fail( transfer != NULL );
   153 
   154   transfer->readposition = 0;
   155   transfer->filesize = GMYTHTV_FILE_SIZE;
   156   
   157   transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   158   
   159   transfer->priv->do_next_program_chain = FALSE;
   160   transfer->priv->livetv = NULL;
   161 
   162   transfer->control_sock = NULL;
   163   transfer->sock = NULL;
   164   
   165   transfer->mutex = g_mutex_new();
   166   
   167 	g_signal_connect ( G_OBJECT (transfer), "program-info-changed",
   168               (GCallback)(GMYTH_FILE_TRANSFER_GET_CLASS(transfer)->program_info_changed_handler),
   169               NULL );
   170 
   171 }
   172 
   173 static void
   174 gmyth_file_transfer_dispose  (GObject *object)
   175 {
   176 	GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (object);
   177 	
   178   if ( transfer->mutex != NULL )  
   179   {
   180   	g_mutex_free( transfer->mutex );
   181   	transfer->mutex = NULL;
   182   }
   183   
   184   if ( transfer->control_sock != NULL )  
   185   {
   186   	g_object_unref( transfer->control_sock );
   187   	transfer->control_sock = NULL;
   188   }  
   189   
   190   if ( transfer->sock != NULL )  
   191   {
   192   	g_object_unref( transfer->sock );
   193   	transfer->sock = NULL;
   194   }
   195     
   196   if ( transfer->filename != NULL )  
   197   {
   198   	g_free( transfer->filename );
   199   	transfer->filename = NULL;
   200   }
   201 
   202   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->dispose (object);
   203 }
   204 
   205 static void
   206 gmyth_file_transfer_finalize (GObject *object)
   207 {
   208   g_signal_handlers_destroy (object);
   209 
   210   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->finalize (object);
   211 }
   212 
   213 // fixme: do we need the card_id????
   214 GMythFileTransfer*
   215 gmyth_file_transfer_new ( const GMythBackendInfo *backend_info)
   216 {
   217   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   218   
   219   transfer->backend_info = (GMythBackendInfo *)backend_info;
   220   g_object_ref (transfer->backend_info);
   221 
   222   return transfer;
   223 }
   224 
   225 GMythFileTransfer* 
   226 gmyth_file_transfer_new_with_uri (const gchar* uri_str)
   227 {
   228   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   229 
   230   transfer->backend_info = gmyth_backend_info_new_with_uri (uri_str);
   231 
   232   return transfer;
   233 }
   234 
   235 gboolean
   236 gmyth_file_transfer_open (GMythFileTransfer *transfer, const gchar* filename)
   237 {
   238   gboolean ret = TRUE;
   239   
   240   g_return_val_if_fail (transfer != NULL, FALSE);
   241   g_return_val_if_fail (filename != NULL, FALSE);
   242   
   243   if (transfer->filename != NULL)
   244   {
   245     g_free (transfer->filename);
   246     transfer->filename = NULL;
   247   }
   248 
   249   transfer->filename = g_strdup (filename);
   250   
   251   /* configure the control socket */
   252   if (transfer->control_sock == NULL) { 
   253     if (!gmyth_connect_to_backend (transfer)) {
   254       gmyth_debug ("Connection to backend failed (Control Socket).\n");
   255       ret = FALSE;
   256     }
   257   } else {
   258     g_warning("Remote transfer control socket already created.\n");
   259   }
   260   
   261   gmyth_debug ("Got file with size = %lld.\n", transfer->filesize);
   262   
   263   return ret;
   264 
   265 }
   266 
   267 static gboolean
   268 gmyth_connect_to_backend (GMythFileTransfer *transfer)
   269 {
   270   GString *base_str = NULL;
   271   GString *hostname = NULL;
   272   GMythStringList *strlist = NULL; 
   273   gboolean ret = TRUE;
   274 
   275   g_return_val_if_fail (transfer != NULL, FALSE );
   276 
   277   base_str = g_string_new ("");
   278 
   279   /* Creates the control socket */
   280   if (transfer->control_sock != NULL) {
   281     g_object_unref (transfer->control_sock);
   282     transfer->control_sock = NULL;
   283   }
   284 
   285   transfer->control_sock = gmyth_socket_new();
   286 
   287   // Connects the socket, send Mythtv ANN command and verify Mythtv protocol version 
   288   if (!gmyth_socket_connect_to_backend (transfer->control_sock,
   289         transfer->backend_info->hostname, transfer->backend_info->port, TRUE)) {
   290 
   291     g_object_unref (transfer->control_sock);
   292     transfer->control_sock = NULL;
   293     return FALSE;
   294   }
   295     
   296   /* Creates the data socket */
   297   if (transfer->sock != NULL) {
   298     g_object_unref (transfer->sock);
   299     transfer->sock = NULL;
   300   }
   301 
   302   transfer->sock = gmyth_socket_new ();
   303   gmyth_socket_connect (transfer->sock, transfer->backend_info->hostname, transfer->backend_info->port);
   304 
   305   strlist = gmyth_string_list_new();
   306   hostname = gmyth_socket_get_local_hostname();
   307   gmyth_debug( "[%s] MythTV version (from backend) = %d.\n", __FUNCTION__, transfer->control_sock->mythtv_version );
   308   if ( transfer->control_sock->mythtv_version > 26 )
   309   	g_string_printf( base_str, "ANN FileTransfer %s 1 -1", hostname->str);
   310   else
   311   	g_string_printf( base_str, "ANN FileTransfer %s", hostname->str);
   312 
   313   gmyth_string_list_append_string (strlist, base_str );
   314   gmyth_string_list_append_char_array (strlist, transfer->filename);
   315 
   316   gmyth_socket_write_stringlist (transfer->sock, strlist );
   317   gmyth_socket_read_stringlist (transfer->sock, strlist );
   318     
   319   /* file identification used in future file transfer requests to backend */
   320   transfer->file_id = gmyth_string_list_get_int( strlist, 1 );
   321 
   322   /* Myth URI stream file size - decoded using two 8-bytes sequences (64 bits/long long types) */
   323   transfer->filesize = gmyth_util_decode_long_long( strlist, 2 );
   324 
   325   gmyth_debug ( "[%s] ***** Received: recordernum = %d, filesize = %" G_GUINT64_FORMAT "\n", __FUNCTION__,
   326           transfer->file_id, transfer->filesize );
   327 
   328   if (transfer->filesize < 0 ) {
   329       gmyth_debug ( "[%s] Got filesize equals to %llu is lesser than 0 [invalid stream file]\n", __FUNCTION__, transfer->filesize );
   330       g_object_unref (transfer->sock);
   331       transfer->sock = NULL;
   332       ret = FALSE;
   333       goto cleanup;
   334   }
   335   
   336 cleanup:
   337 
   338   if ( strlist != NULL )
   339     g_object_unref( strlist );
   340 
   341   g_string_free (base_str, TRUE);
   342   g_string_free (hostname, TRUE);
   343 
   344   return ret;
   345 }    
   346 
   347 void
   348 gmyth_file_transfer_emit_program_info_changed_signal ( GMythFileTransfer *transfer, gint msg_code,
   349 			gpointer live_tv ) 
   350 {
   351 				/*
   352 	g_signal_emit_by_name ( G_OBJECT(transfer),
   353          "program-info-changed",
   354          msg_code, live_tv );
   355          */
   356          
   357   gmyth_debug( "Calling signal handler... [FILE_TRANSFER]" );
   358 
   359   g_signal_emit ( transfer,
   360          GMYTH_FILE_TRANSFER_GET_CLASS (transfer)->program_info_changed_handler_signal_id,
   361          0, /* details */
   362          msg_code, live_tv );
   363 
   364 }
   365 
   366 gboolean
   367 gmyth_file_transfer_is_open (GMythFileTransfer *transfer)
   368 {
   369     GMythStringList *strlist;
   370     GString *query;
   371 
   372     g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   373     g_return_val_if_fail (transfer->sock != NULL, FALSE);
   374 
   375     strlist = gmyth_string_list_new();
   376     query = g_string_new (GMYTHTV_QUERY_HEADER);
   377     g_string_append_printf (query, "%d", transfer->file_id );
   378 
   379     gmyth_string_list_append_string (strlist, query );
   380     gmyth_string_list_append_char_array( strlist, "IS_OPEN" );
   381 
   382     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   383     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   384 
   385     g_string_free (query, TRUE);
   386     g_object_unref (strlist);
   387 
   388     return ( strlist != NULL && gmyth_string_list_get_int( strlist, 0 ) == 1 );
   389 }
   390 
   391 void
   392 gmyth_file_transfer_close( GMythFileTransfer *transfer )
   393 {
   394   GMythStringList *strlist;
   395   GString *query;
   396 
   397   g_return_if_fail (transfer->control_sock != NULL);
   398 
   399   strlist = gmyth_string_list_new( );
   400   query = g_string_new (GMYTHTV_QUERY_HEADER);
   401   g_string_append_printf( query, "%d", transfer->file_id);
   402 
   403   gmyth_string_list_append_string( strlist, query );
   404   gmyth_string_list_append_char_array( strlist, "DONE" );
   405 
   406   if ( gmyth_socket_sendreceive_stringlist(transfer->control_sock, strlist) <= 0 ) {
   407     // fixme: time out???
   408     g_printerr( "Remote file timeout.\n" );
   409   }
   410 
   411   g_string_free (query, TRUE);
   412   g_object_unref (strlist);
   413 
   414   if (transfer->sock) {
   415     g_object_unref( transfer->sock );
   416     transfer->sock = NULL;
   417   }
   418 
   419   if (transfer->control_sock) {
   420     g_object_unref( transfer->control_sock );
   421     transfer->control_sock = NULL;
   422   } 
   423   
   424 }
   425 
   426 gint64
   427 gmyth_file_transfer_seek(GMythFileTransfer *transfer, guint64 pos, gint whence)
   428 {
   429   GMythStringList *strlist = gmyth_string_list_new();
   430   GString *query;
   431 
   432   g_return_val_if_fail (transfer->sock != NULL, -1);
   433   g_return_val_if_fail (transfer->control_sock != NULL, -1);
   434 
   435   strlist = gmyth_string_list_new();
   436   query = g_string_new (GMYTHTV_QUERY_HEADER);
   437   g_string_append_printf (query, "%d", transfer->file_id);
   438   
   439   myth_control_acquire_context( TRUE );
   440 
   441   gmyth_string_list_append_string( strlist, query );
   442   gmyth_string_list_append_char_array( strlist, "SEEK" );
   443   gmyth_string_list_append_uint64( strlist, pos );
   444   
   445   gmyth_string_list_append_int( strlist, whence );  
   446 
   447   if (pos > 0 )
   448     gmyth_string_list_append_uint64( strlist, pos );
   449   else
   450     gmyth_string_list_append_uint64( strlist, transfer->readposition );
   451   
   452   gmyth_socket_sendreceive_stringlist( transfer->control_sock, strlist );
   453 
   454   gint64 retval = gmyth_string_list_get_int64(strlist, 0);
   455   transfer->readposition = retval;
   456   gmyth_debug ( "[%s] got reading position pointer from the streaming = %lld\n", 
   457       __FUNCTION__, retval );
   458 
   459   myth_control_release_context( );
   460 
   461   return retval;
   462 }
   463 
   464 static gboolean 
   465 myth_control_acquire_context( gboolean do_wait )
   466 {
   467 	gboolean ret = TRUE;	
   468 	//guint max_iter = 50;
   469 	
   470 	//g_mutex_lock( mutex );
   471 	
   472   //while ( !has_io_access ) 
   473   //	g_cond_wait( io_watcher_cond, mutex );
   474   	
   475   //has_io_access = FALSE;
   476   
   477   //myth_control_acquire_context (FALSE);
   478    
   479   /* 
   480   if ( do_wait ) {
   481   	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
   482   		ret = FALSE;
   483   } else if ( !g_main_context_acquire( io_watcher_context ) )
   484   	ret = FALSE;
   485   */
   486   	
   487   //g_static_mutex_lock( &st_mutex );
   488   
   489   return ret;
   490 }
   491 
   492 static gboolean 
   493 myth_control_release_context( ) 
   494 {
   495 	gboolean ret = TRUE;
   496     
   497   //g_static_mutex_unlock( &st_mutex );
   498   
   499 	//g_main_context_release( io_watcher_context );
   500   
   501   //g_main_context_wakeup( io_watcher_context );
   502   
   503   //has_io_access = TRUE;
   504 
   505   //g_cond_broadcast( io_watcher_cond );
   506   
   507   //g_mutex_unlock( mutex );  
   508  
   509   return ret;
   510 }
   511 
   512 gint 
   513 gmyth_file_transfer_read(GMythFileTransfer *transfer, GByteArray *data, gint size, gboolean read_unlimited)
   514 {
   515   gint bytes_sent = 0;
   516   gsize bytes_read = 0;
   517   gsize total_read = 0;
   518   
   519   GError *error = NULL;
   520   
   521   GIOChannel *io_channel;
   522   GIOChannel *io_channel_control;
   523 
   524   GIOCondition io_cond;
   525   GIOCondition io_cond_control;
   526   GIOStatus io_status = G_IO_STATUS_NORMAL, io_status_control = G_IO_STATUS_NORMAL;
   527   
   528   gboolean ret = TRUE;   
   529 
   530   GString *query;
   531   
   532   g_return_val_if_fail ( data != NULL, -2 );
   533 
   534   io_channel = transfer->sock->sd_io_ch;
   535   io_channel_control = transfer->control_sock->sd_io_ch;
   536 
   537   io_status = g_io_channel_set_encoding( io_channel, NULL, &error );
   538   if ( io_status == G_IO_STATUS_NORMAL )
   539     gmyth_debug ( "[%s] Setting encoding to binary data socket).\n", __FUNCTION__ );
   540 
   541   io_cond = g_io_channel_get_buffer_condition( io_channel );  
   542 
   543   io_cond_control = g_io_channel_get_buffer_condition( io_channel );
   544   if ( transfer->sock == NULL || ( io_status == G_IO_STATUS_ERROR ) ) {
   545     g_printerr( "gmyth_file_transfer_read(): Called with no raw socket.\n" );
   546     //exit(0); // fixme remove this
   547     return -1;
   548   }
   549 
   550   if ( transfer->control_sock == NULL || ( io_status_control == G_IO_STATUS_ERROR ) ) {
   551     g_printerr( "gmyth_file_transfer_read(): Called with no control socket.\n" );
   552     //exit(0); // fixme remove this
   553     return -1;
   554   }
   555 
   556   query = g_string_new (GMYTHTV_QUERY_HEADER);
   557   g_string_append_printf ( query, "%d", transfer->file_id );
   558   gmyth_debug ("[%s] Transfer_query = %s\n", __FUNCTION__, query->str );
   559   
   560   /* send requests to the maximum number of REQUEST_BLOCK messages */
   561   guint max_tries = 5;
   562   
   563   myth_control_acquire_context( TRUE );
   564   
   565   while (total_read == 0 && --max_tries > 0) {
   566     GMythStringList *strlist = gmyth_string_list_new();
   567 
   568     gmyth_string_list_append_char_array( strlist, query->str );
   569     gmyth_string_list_append_char_array( strlist, "REQUEST_BLOCK" );
   570     gmyth_string_list_append_int( strlist, size );
   571 
   572     // Request the block to the backend
   573     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   574 
   575     // Receives the backand answer    
   576     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   577 
   578     if ( strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   579     { 
   580 	    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   581 	    gmyth_debug ( "[%s] got SENT buffer message = %d\n", __FUNCTION__, bytes_sent );
   582 
   583     	if ( bytes_sent >= 0 ) 
   584     	{
   585         gchar *data_buffer = g_new0 ( gchar, bytes_sent );    
   586    	    while ( 0 != bytes_sent ) 
   587    	    { 
   588           io_status = g_io_channel_read_chars( io_channel, data_buffer + total_read, 
   589 	    				(gsize) bytes_sent,	&bytes_read, &error );
   590 
   591 	        total_read += bytes_read;
   592           bytes_sent -= bytes_read;
   593       
   594             /* append new data to the increasing byte array */
   595 	    		data = g_byte_array_append (data, (const guint8*)data_buffer, bytes_read);
   596 	        gmyth_debug ("Total transfer data read: %d\n", total_read);
   597         }
   598         g_free (data_buffer);
   599       } /* if */
   600     } else if ( !(transfer->priv != NULL && transfer->priv->livetv != NULL &&
   601 					transfer->priv->do_next_program_chain) ) {
   602     	total_read = GMYTHTV_FILE_TRANSFER_READ_ERROR;
   603     	g_object_unref (strlist);
   604     	strlist = NULL;
   605     	break;
   606     }
   607     g_object_unref (strlist);
   608     strlist = NULL;
   609   }
   610   
   611   if ( bytes_sent == 0 && max_tries == 0 ) 
   612 	{
   613 		gmyth_debug( "Trying to move to the next program chain..." );
   614 		transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);
   615 		
   616 		if ( transfer->priv != NULL && transfer->priv->livetv != NULL &&
   617 					transfer->priv->do_next_program_chain )
   618 		{
   619 		
   620 			total_read = GMYTHTV_FILE_TRANSFER_NEXT_PROG_CHAIN;	
   621 		
   622 	  		g_mutex_lock( transfer->mutex );
   623 	  		
   624 	  		ret = gmyth_livetv_next_program_chain( transfer->priv->livetv );
   625 	  		
   626 	  		g_mutex_unlock( transfer->mutex );
   627   		
   628 			if ( !ret )
   629 				gmyth_debug( "Cannot change to the next program chain!" );
   630 			else
   631 				gmyth_debug( "OK!!! MOVED to the next program chain [%s]!", 
   632 										(gmyth_tvchain_get_id( transfer->priv->livetv->tvchain ))->str );						
   633 		}
   634 
   635 	} /* if */
   636   
   637   myth_control_release_context( );
   638   g_string_free (query, TRUE);
   639 
   640   if ( error != NULL ) {
   641     gmyth_debug ("Cleaning-up ERROR: %s [msg = %s, code = %d]\n", __FUNCTION__, error->message, 
   642 	    error->code);
   643     g_error_free (error);
   644   }
   645   
   646   if ( total_read > 0 )
   647   	transfer->readposition += total_read;  	
   648   	
   649   return total_read;
   650 }
   651 
   652 static void 
   653 gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   654 										gint msg_code, gpointer livetv_transfer )
   655 {
   656 	GMythLiveTV *livetv = GMYTH_LIVETV( livetv_transfer );
   657 	
   658 	gmyth_debug( "Program info changed! ( file transfer orig. = %p, ptr. = [%s], user data = [%s] )", transfer, 
   659 		livetv_transfer != NULL ? "[NOT NULL]" : "[NULL]", livetv != NULL ? "[NOT NULL]" : "[NULL]" );
   660 	
   661 	if ( livetv != NULL && transfer == livetv->file_transfer )
   662 	{
   663 		gmyth_debug( "YES, the requested program info movement on the LiveTV transfer is authentical!" );
   664 	}
   665 	
   666 	transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   667 	
   668 	transfer->priv->livetv = livetv;	
   669 	transfer->priv->do_next_program_chain = TRUE;
   670 	
   671 	//g_object_unref( transfer );
   672 }
   673 
   674 gboolean 
   675 gmyth_file_transfer_settimeout( GMythFileTransfer *transfer, gboolean fast )
   676 {
   677 
   678   GMythStringList *strlist = NULL;
   679 
   680   g_return_val_if_fail (transfer->sock != NULL, FALSE);
   681   g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   682 
   683 //  if ( transfer->timeoutisfast == fast )
   684 //    return;
   685 
   686   strlist = gmyth_string_list_new(); 
   687   gmyth_string_list_append_char_array( strlist, GMYTHTV_QUERY_HEADER );
   688   gmyth_string_list_append_char_array( strlist, "SET_TIMEOUT" );
   689   gmyth_string_list_append_int( strlist, fast ); 
   690 
   691   gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   692   gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   693 
   694 //  transfer->timeoutisfast = fast;
   695 
   696   return TRUE;
   697 }
   698 
   699 
   700 guint64
   701 gmyth_file_transfer_get_filesize (GMythFileTransfer *transfer)
   702 {
   703     g_return_val_if_fail (transfer != NULL, 0);
   704     return transfer->filesize;
   705 }