/* * @author Hallyson Melo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GST_MYTHTV_ID_NUM 1 #define MYTHTV_VERSION_DEFAULT 30 #define MYTHTV_TRANSFER_MAX_WAITS 100 /* internal GnomeVFS plug-in buffer size ( 120 Kbytes ) */ #define MYTHTV_BUFFER_SIZE 1024*80 /* internally sized GnomeVFS plug-in buffer ( 4 Kbytes ) */ #define MYTHTV_MAX_VFS_BUFFER_SIZE 4096 /* maximum number of bytes to be requested to the MythTV backend ( 64 Kbytes ) */ #define MYTHTV_MAX_REQUEST_SIZE 1024*64 static GnomeVFSResult do_read (GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, gpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize * bytes_read, GnomeVFSContext * context); typedef struct { GMythFileTransfer *file_transfer; GMythLiveTV *livetv; gchar *channel_name; gint mythtv_version; gint64 content_size; guint64 bytes_read; GByteArray *buffer; gsize buffer_remain; gboolean is_livetv; gboolean configured; } MythtvHandle; //static MythtvHandle *myth_handle = NULL; static GnomeVFSResult do_open (GnomeVFSMethod * method, GnomeVFSMethodHandle ** method_handle, GnomeVFSURI * uri, GnomeVFSOpenMode mode, GnomeVFSContext * context) { MythtvHandle *myth_handle = NULL; GMythBackendInfo *backend_info = NULL; GMythURI *gmyth_uri = NULL; gboolean ret = TRUE; _GNOME_VFS_METHOD_PARAM_CHECK (method_handle != NULL); _GNOME_VFS_METHOD_PARAM_CHECK (uri != NULL); if (mode & GNOME_VFS_OPEN_WRITE) { return GNOME_VFS_ERROR_NOT_PERMITTED; } if (gnome_vfs_uri_get_host_name (uri) == NULL) { return GNOME_VFS_ERROR_INVALID_HOST_NAME; } if ((NULL == myth_handle) || !myth_handle->configured) { myth_handle = g_new0 (MythtvHandle, 1); myth_handle->configured = FALSE; myth_handle->is_livetv = FALSE; /* Initialize mythtv handler */ myth_handle->file_transfer = NULL; myth_handle->livetv = NULL; myth_handle->mythtv_version = MYTHTV_VERSION_DEFAULT; myth_handle->bytes_read = 0; myth_handle->content_size = (GnomeVFSFileSize) - 1; /* Creates and fills out the backend info structure */ backend_info = gmyth_backend_info_new_with_uri (gnome_vfs_unescape_string (gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE), "")); /* creates an instance of */ gmyth_uri = gmyth_uri_new_with_value (gnome_vfs_unescape_string (gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE), "")); myth_handle->is_livetv = gmyth_uri_is_livetv (gmyth_uri); /* Connect to the backend */ if (gmyth_uri != NULL && myth_handle->is_livetv == TRUE) { if (NULL == myth_handle->livetv) { myth_handle->livetv = gmyth_livetv_new (); myth_handle->channel_name = gmyth_uri_get_channel_name (gmyth_uri); g_debug ("[%s] Channel name = %s\n", __FUNCTION__, myth_handle->channel_name); if (myth_handle->channel_name != NULL) { if (gmyth_livetv_channel_name_setup (myth_handle->livetv, myth_handle->channel_name, backend_info) == FALSE) { g_object_unref (gmyth_uri); ret = FALSE; } } else { if (gmyth_livetv_setup (myth_handle->livetv, backend_info) == FALSE) { g_object_unref (gmyth_uri); ret = FALSE; } } } if (NULL == myth_handle->file_transfer) { myth_handle->file_transfer = gmyth_livetv_create_file_transfer (myth_handle->livetv); if (NULL == myth_handle->file_transfer) { ret = FALSE; g_debug ("MythTV FileTransfer is NULL!\n"); return GNOME_VFS_ERROR_NOT_OPEN; } if (!gmyth_file_transfer_open (myth_handle->file_transfer, myth_handle->livetv->uri != NULL ? gmyth_uri_get_path (myth_handle-> livetv-> uri) : myth_handle->livetv->proginfo-> pathname->str)) { g_debug ("Couldn't open MythTV FileTransfer is NULL!\n"); g_object_unref (myth_handle-> file_transfer); myth_handle->file_transfer = NULL; ret = FALSE; } } /* if - FileTransfer is NULL, or not */ } else { if (NULL == myth_handle->file_transfer) { myth_handle->file_transfer = gmyth_file_transfer_new (backend_info); /* Verifies if the file exists */ if (!gmyth_util_file_exists (backend_info, gmyth_uri_get_path (gmyth_uri))) { g_object_unref (backend_info); ret = FALSE; } /* sets the Playback monitor connection */ ret = gmyth_file_transfer_open (myth_handle-> file_transfer, gmyth_uri_get_path (gmyth_uri)); } } /* if - LiveTV or not? */ if (ret == FALSE) { g_debug ("MythTV FileTransfer open error.\n"); return GNOME_VFS_ERROR_NOT_OPEN; } myth_handle->configured = TRUE; g_object_unref (backend_info); if (gmyth_uri != NULL) g_object_unref (gmyth_uri); myth_handle->buffer = g_byte_array_sized_new (MYTHTV_BUFFER_SIZE); myth_handle->buffer_remain = 0; g_return_val_if_fail (myth_handle->file_transfer != NULL, GNOME_VFS_ERROR_NOT_OPEN); if ( /*myth_handle->file_transfer->filesize <= 0 && */ myth_handle->is_livetv) { myth_handle->content_size = gmyth_recorder_get_file_position (myth_handle-> livetv-> recorder); } else { myth_handle->content_size = myth_handle->file_transfer->filesize; } } /* if - configured or not? */ *method_handle = (GnomeVFSMethodHandle *) myth_handle; return GNOME_VFS_OK; } static GnomeVFSResult do_read (GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, gpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize * bytes_read, GnomeVFSContext * context) { MythtvHandle *myth_handle = (MythtvHandle *) method_handle; GnomeVFSFileSize bytes_to_read = num_bytes; if (!myth_handle->is_livetv && (myth_handle->bytes_read >= myth_handle->content_size)) return GNOME_VFS_ERROR_EOF; /* fixme: change this to min math function */ if ((myth_handle->content_size > 0)) { if (!myth_handle->is_livetv && (num_bytes > (myth_handle->content_size - myth_handle->bytes_read))) { bytes_to_read = myth_handle->content_size - myth_handle->bytes_read; } } /* Loop sending the Myth File Transfer request: * Retry whilst authentication fails and we supply it. */ if ((myth_handle->buffer_remain = myth_handle->buffer->len) < bytes_to_read) { gint buffer_size; while (MYTHTV_BUFFER_SIZE != myth_handle->buffer_remain) { /* resize buffer length request to no more than MYTHTV_MAX_REQUEST_SIZE */ if ((MYTHTV_BUFFER_SIZE - myth_handle->buffer_remain) <= MYTHTV_MAX_REQUEST_SIZE) buffer_size = MYTHTV_BUFFER_SIZE - myth_handle->buffer_remain; else buffer_size = MYTHTV_MAX_REQUEST_SIZE; GByteArray *tmp_buffer = g_byte_array_new (); g_debug ("Asking %d bytes (there is %d bytes in the buffer)\n", buffer_size, myth_handle->buffer_remain); gint len = gmyth_file_transfer_read (myth_handle-> file_transfer, tmp_buffer, buffer_size, TRUE); if ( !myth_handle->is_livetv ) { if ( len < 0 ) { g_byte_array_free (tmp_buffer, TRUE); g_debug ("Fail to read bytes"); return GNOME_VFS_ERROR_IO; } else if ( len == 0 ) { g_byte_array_free (tmp_buffer, TRUE); g_warning ("End of file probably achieved"); return GNOME_VFS_ERROR_EOF; } } /* if */ myth_handle->buffer = g_byte_array_append (myth_handle->buffer, tmp_buffer->data, len); myth_handle->buffer_remain += len; if (tmp_buffer != NULL) { g_byte_array_free (tmp_buffer, TRUE); tmp_buffer = NULL; } } /* while - iterates until fills the internal buffer */ } /* if - got from the network, or not */ bytes_to_read = (bytes_to_read > myth_handle->buffer_remain) ? myth_handle-> buffer_remain : bytes_to_read; /* gets the first buffer_size bytes from the byte array buffer variable */ g_memmove (buffer, myth_handle->buffer->data, bytes_to_read); myth_handle->bytes_read += bytes_to_read; myth_handle->buffer_remain -= bytes_to_read; /* flushs the newly buffer got from byte array */ myth_handle->buffer = g_byte_array_remove_range (myth_handle->buffer, 0, bytes_to_read); g_debug ("Got from %llu bytes from internal buffer. (there are %d bytes in the buffer, from a total of %llu dispatched.)\n", bytes_to_read, myth_handle->buffer_remain, myth_handle->bytes_read); *bytes_read = bytes_to_read; return GNOME_VFS_OK; } static GnomeVFSResult do_close (GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSContext * context) { MythtvHandle *myth_handle = (MythtvHandle *) method_handle; //if ( NULL == myth_handle || myth_handle->configured ) { if (myth_handle->file_transfer != NULL) { g_object_unref (myth_handle->file_transfer); myth_handle->file_transfer = NULL; } if (myth_handle->is_livetv && myth_handle->livetv != NULL) { g_object_unref (myth_handle->livetv); myth_handle->livetv = NULL; } if (myth_handle->buffer) { g_byte_array_free (myth_handle->buffer, TRUE); myth_handle->buffer = NULL; } myth_handle->configured = FALSE; g_free (myth_handle); myth_handle = NULL; // } return GNOME_VFS_OK; } static GnomeVFSResult do_get_file_info (GnomeVFSMethod * method, GnomeVFSURI * uri, GnomeVFSFileInfo * file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext * context) { GMythFileTransfer *file_transfer = NULL; GMythRecorder *recorder = NULL; GMythTVChain *tvchain = NULL; GMythBackendInfo *backend_info = NULL; GMythURI *gmyth_uri = NULL; GMythSocket *socket = NULL; gboolean is_livetv = FALSE; gboolean ret = TRUE; gboolean res = TRUE; /* Creates and fills out the backend info structure */ backend_info = gmyth_backend_info_new_with_uri (gnome_vfs_unescape_string (gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE), "")); /* creates an instance of */ gmyth_uri = gmyth_uri_new_with_value (gnome_vfs_unescape_string (gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_NONE), "")); is_livetv = gmyth_uri_is_livetv (gmyth_uri); file_info->valid_fields = file_info->valid_fields | GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS; file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; /* fixme: get from file extension? */ file_info->mime_type = g_strdup ("video/x-nuv"); file_info->permissions = GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_GROUP_READ; g_debug ("gnome_vfs_uri == %s | gmyth_uri == %s.\n", gnome_vfs_uri_get_path (uri), gmyth_uri_get_path (gmyth_uri)); /* Connect to the backend */ if (gmyth_uri != NULL && is_livetv == TRUE) { /* start to get file info from LiveTV remote encoder */ socket = gmyth_socket_new (); /* FIME: Implement this at gmyth_socket */ res = gmyth_socket_connect_to_backend (socket, backend_info-> hostname, backend_info->port, TRUE); if (!res) { g_debug ("[%s] LiveTV can not connect to backend", __FUNCTION__); res = FALSE; goto error; } if (gmyth_remote_util_get_free_recorder_count (socket) <= 0) { g_debug ("No free remote encoder available."); res = FALSE; goto error; } /* Gets the recorder num */ recorder = remote_request_next_free_recorder (socket, -1); if ( socket != NULL ) g_object_unref (socket); if (recorder == NULL) { g_debug ("[%s] None remote encoder available", __FUNCTION__); res = FALSE; goto error; } /* Init remote encoder. Opens its control socket. */ res = gmyth_recorder_setup (recorder); if (!res) { g_debug ("[%s] Fail while setting remote encoder\n", __FUNCTION__); res = FALSE; goto error; } /* Creates livetv chain handler */ tvchain = gmyth_tvchain_new (); gmyth_tvchain_initialize (tvchain, backend_info); if (tvchain == NULL || tvchain->tvchain_id == NULL) { res = FALSE; goto error; } /* Spawn live tv. Uses the socket to send mythprotocol data to start livetv in the backend (remotelly) */ res = gmyth_recorder_spawntv (recorder, gmyth_tvchain_get_id (tvchain)); if (!res) { g_warning ("[%s] Fail while spawn tv\n", __FUNCTION__); res = FALSE; goto error; } sleep(1); /* DEBUG message */ GMythProgramInfo *prog_info = gmyth_recorder_get_current_program_info (recorder); if (prog_info != NULL) { g_debug ("path = %s", prog_info->pathname->str); file_info->name = g_strdup (g_strrstr (prog_info->pathname->str, "/")); } else { file_info->name = g_strdup ("LiveTV.nuv"); file_info->size = gmyth_recorder_get_file_position (recorder); } if (recorder != NULL) g_object_unref (recorder); if (prog_info != NULL) g_object_unref (prog_info); if (tvchain != NULL) g_object_unref (tvchain); file_info->size = (GnomeVFSFileSize) - 1; } else { /* start to get file info from remote file encoder */ file_transfer = gmyth_file_transfer_new (backend_info); /* Verifies if the file exists */ if (!gmyth_util_file_exists (backend_info, gmyth_uri_get_path (gmyth_uri))) { g_object_unref (backend_info); return GNOME_VFS_ERROR_NOT_FOUND; } /* sets the Playback monitor connection */ ret = gmyth_file_transfer_open (file_transfer, gmyth_uri_get_path (gmyth_uri)); file_info->name = g_strdup (gnome_vfs_uri_get_path (uri)); } /* if - LiveTV or not? */ if (ret == FALSE) { g_debug ("MythTV FileTransfer open error\n"); return GNOME_VFS_ERROR_NOT_OPEN; } if (ret == TRUE && file_transfer != NULL) { file_info->size = gmyth_file_transfer_get_filesize (file_transfer); if (file_transfer) g_object_unref (file_transfer); } file_info->block_count = GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT; file_info->io_block_size = GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE; error: if (backend_info) g_object_unref (backend_info); if (!res) return GNOME_VFS_ERROR_IO; return GNOME_VFS_OK; } static gboolean do_is_local (GnomeVFSMethod * method, const GnomeVFSURI * uri) { return FALSE; } static GnomeVFSMethod method = { sizeof (GnomeVFSMethod), do_open, NULL, do_close, do_read, NULL, NULL, NULL, NULL, NULL, NULL, NULL, do_get_file_info, NULL, do_is_local, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; GnomeVFSMethod * vfs_module_init (const char *method_name, const char *args) { return &method; } void vfs_module_shutdown (GnomeVFSMethod * method) { }