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