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