/* * @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 #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 80*1024 /* 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 64*1024 typedef struct { GMythFile *file; GMythLiveTV *livetv; GMythBackendInfo *backend_info; GMythURI *gmyth_uri; GMythRecorder *live_recorder; gboolean started; gint64 offset; gboolean is_livetv; /* it is, or not a Live TV content transfer */ gboolean is_local_file; /* tell if the file is local to the current content transfer */ gchar *channel_name; gint mythtv_version; gboolean configured; } MythtvHandle; static GnomeVFSResult do_read(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, gpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize * bytes_read, GnomeVFSContext * context); static GnomeVFSResult myth_connection_start(MythtvHandle * method_handle); static void myth_destroy_handle(MythtvHandle * method_handle); static GnomeVFSResult myth_handle_new(GnomeVFSURI * uri, MythtvHandle ** method_handle); static GnomeVFSResult myth_get_file_info(MythtvHandle * myth_handle, GnomeVFSURI * uri, GnomeVFSFileInfo * info); static GnomeVFSResult myth_handle_new(GnomeVFSURI * uri, MythtvHandle ** method_handle) { gchar *tmp_str1; gchar *tmp_str2; _GNOME_VFS_METHOD_PARAM_CHECK(*method_handle == NULL); if (gnome_vfs_uri_get_host_name(uri) == NULL) { return GNOME_VFS_ERROR_INVALID_HOST_NAME; } *method_handle = g_new0(MythtvHandle, 1); (*method_handle)->mythtv_version = MYTHTV_VERSION_DEFAULT; (*method_handle)->is_livetv = FALSE; (*method_handle)->is_local_file = FALSE; tmp_str1 = gnome_vfs_uri_to_string(uri, GNOME_VFS_URI_HIDE_NONE); tmp_str2 = gnome_vfs_unescape_string(tmp_str1, ""); gchar *tmp_str3 = strstr(tmp_str2, ".nuv.avi"); if (tmp_str3 != NULL) { tmp_str3[4] = '\0'; } (*method_handle)->backend_info = gmyth_backend_info_new_with_uri(tmp_str2); (*method_handle)->gmyth_uri = gmyth_uri_new_with_value(tmp_str2); g_free(tmp_str1); g_free(tmp_str2); return GNOME_VFS_OK; } static void myth_destroy_handle(MythtvHandle * method_handle) { //TODO: abort if in tranfer state if (method_handle->backend_info != NULL) { g_object_unref(method_handle->backend_info); method_handle->backend_info = NULL; } if (method_handle->channel_name != NULL) { g_free(method_handle->channel_name); method_handle->channel_name = NULL; } if (method_handle->livetv != NULL) { g_object_unref(method_handle->livetv); method_handle->livetv = NULL; } if (method_handle->file != NULL) { g_object_unref(method_handle->file); method_handle->file = NULL; } if (method_handle->gmyth_uri != NULL) { g_object_unref(method_handle->gmyth_uri); method_handle->gmyth_uri = NULL; } g_free(method_handle); } static GnomeVFSResult myth_get_file_info(MythtvHandle * myth_handle, GnomeVFSURI * uri, GnomeVFSFileInfo * info) { GMythURI *gmyth_uri; GMythBackendInfo *backend_info; gboolean is_livetv; gboolean is_local; _GNOME_VFS_METHOD_PARAM_CHECK(info != NULL); g_debug("%s - %d", __FUNCTION__, __LINE__); if (myth_handle == NULL) { gchar *tmp_str1; gchar *tmp_str2; tmp_str1 = gnome_vfs_uri_to_string(uri, GNOME_VFS_URI_HIDE_NONE); tmp_str2 = gnome_vfs_unescape_string(tmp_str1, ""); backend_info = gmyth_backend_info_new_with_uri(tmp_str2); gmyth_uri = gmyth_uri_new_with_value(tmp_str2); g_free(tmp_str1); g_free(tmp_str2); } else { backend_info = g_object_ref(myth_handle->backend_info); gmyth_uri = g_object_ref(myth_handle->gmyth_uri); } info->valid_fields = 0; info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS; info->type = GNOME_VFS_FILE_TYPE_REGULAR; /* fixme: get from file extension? */ info->mime_type = g_strdup("video/x-nuv"); info->permissions = GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_GROUP_READ; info->name = g_strdup(gmyth_uri_get_path(gmyth_uri)); /* file size for remote files */ is_livetv = gmyth_uri_is_livetv(gmyth_uri); if (is_livetv == FALSE) { GMythFile *file = NULL; gboolean ret = FALSE; /* Verifies if the file exists */ if (!gmyth_util_file_exists(backend_info, gmyth_uri_get_path(gmyth_uri))) { g_object_unref(file); g_object_unref(backend_info); g_debug("NOT FOUND %s/%d", __FUNCTION__, __LINE__); return GNOME_VFS_ERROR_NOT_FOUND; } is_local = gmyth_uri_is_local_file(gmyth_uri); if (is_local == TRUE) { file = GMYTH_FILE(gmyth_file_local_new(backend_info)); ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(file)); } else { file = GMYTH_FILE(gmyth_file_transfer_new(backend_info)); ret = gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(file), gmyth_uri_get_path(gmyth_uri)); } if (!ret) { g_object_unref(file); g_object_unref(backend_info); g_debug("NOT FOUND %s/%d", __FUNCTION__, __LINE__); return GNOME_VFS_ERROR_NOT_FOUND; } info->size = gmyth_file_get_filesize(file); info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE; g_object_unref(file); } g_object_unref(backend_info); g_object_unref(gmyth_uri); return GNOME_VFS_OK; } static GnomeVFSResult myth_connection_start(MythtvHandle * method_handle) { GnomeVFSResult result = GNOME_VFS_OK; _GNOME_VFS_METHOD_PARAM_CHECK(method_handle != NULL); _GNOME_VFS_METHOD_PARAM_CHECK(method_handle->backend_info != NULL); /* Connect to the backend */ if ((method_handle->is_livetv = gmyth_uri_is_livetv(method_handle->gmyth_uri)) == TRUE) { method_handle->livetv = gmyth_livetv_new(method_handle->backend_info); method_handle->channel_name = gmyth_uri_get_channel_name(method_handle->gmyth_uri); if (method_handle->channel_name != NULL) { if (gmyth_livetv_channel_name_setup(method_handle->livetv, method_handle->channel_name) == FALSE) { result = GNOME_VFS_ERROR_INVALID_URI; goto error; } } else if (gmyth_livetv_setup(method_handle->livetv) == FALSE) { result = GNOME_VFS_ERROR_INVALID_URI; goto error; } method_handle->file = GMYTH_FILE(gmyth_livetv_create_file_transfer(method_handle->livetv)); if (method_handle->file == NULL) { result = GNOME_VFS_ERROR_INVALID_URI; g_debug("MythTV FileTransfer is NULL!\n"); goto error; } if (!gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(method_handle->file), method_handle->livetv->uri != NULL ? gmyth_uri_get_path(method_handle->livetv-> uri) : method_handle-> livetv->proginfo->pathname->str)) { g_debug("Couldn't open MythTV FileTransfer is NULL!\n"); result = GNOME_VFS_ERROR_NOT_OPEN; goto error; } } else { gboolean ret = TRUE; /* Verifies if the file exists */ if (!gmyth_util_file_exists(method_handle->backend_info, gmyth_uri_get_path(method_handle-> gmyth_uri))) { g_debug("NOT FOUND %s/%d", __FUNCTION__, __LINE__); goto error; } if ((method_handle->is_local_file = gmyth_uri_is_local_file(method_handle->gmyth_uri)) == TRUE) { method_handle->file = GMYTH_FILE(gmyth_file_local_new(method_handle->backend_info)); ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(method_handle->file)); } else { method_handle->file = GMYTH_FILE(gmyth_file_transfer_new(method_handle->backend_info)); ret = gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(method_handle->file), gmyth_uri_get_path(method_handle-> gmyth_uri)); } /* sets the Playback monitor connection */ if (!ret) { g_debug("NOT FOUND %s/%d", __FUNCTION__, __LINE__); result = GNOME_VFS_ERROR_NOT_FOUND; goto error; } } /* if - LiveTV or not? */ method_handle->configured = TRUE; if (method_handle->file == NULL) { result = GNOME_VFS_ERROR_NOT_OPEN; } error: return result; } static GnomeVFSResult do_open(GnomeVFSMethod * method, GnomeVFSMethodHandle ** method_handle, GnomeVFSURI * uri, GnomeVFSOpenMode mode, GnomeVFSContext * context) { MythtvHandle *myth_handle = NULL; GnomeVFSResult result = GNOME_VFS_OK; _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_INVALID_OPEN_MODE; } result = myth_handle_new(uri, &myth_handle); if (result != GNOME_VFS_OK) return result; result = myth_connection_start(myth_handle); if (result != GNOME_VFS_OK) { myth_destroy_handle(myth_handle); myth_handle = NULL; return result; } *method_handle = (GnomeVFSMethodHandle *) myth_handle; return result; } static GnomeVFSResult do_create(GnomeVFSMethod * method, GnomeVFSMethodHandle ** method_handle, GnomeVFSURI * uri, GnomeVFSOpenMode mode, gboolean exclusive, guint perm, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_close(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSContext * context) { MythtvHandle *myth_handle = (MythtvHandle *) method_handle; myth_destroy_handle(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) { GnomeVFSResult retval = GNOME_VFS_OK; MythtvHandle *myth_handle; GMythFileReadResult result; GByteArray *myth_buffer = g_byte_array_new(); _GNOME_VFS_METHOD_PARAM_CHECK(method_handle != NULL); myth_handle = (MythtvHandle *) method_handle; if (myth_handle->is_local_file) result = gmyth_file_local_read(GMYTH_FILE_LOCAL(myth_handle->file), myth_buffer, num_bytes, myth_handle->is_livetv); else result = gmyth_file_transfer_read(GMYTH_FILE_TRANSFER(myth_handle->file), myth_buffer, num_bytes, myth_handle->is_livetv); if (result == GMYTH_FILE_READ_ERROR) { retval = GNOME_VFS_ERROR_IO; } if (result == GMYTH_FILE_READ_EOF) { retval = GNOME_VFS_ERROR_EOF; } if (myth_buffer->len > 0) { g_memmove(buffer, myth_buffer->data, myth_buffer->len); *bytes_read = (GnomeVFSFileSize) myth_buffer->len; myth_handle->offset += myth_buffer->len; g_byte_array_free(myth_buffer, TRUE); } return retval; } static GnomeVFSResult do_write(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, gconstpointer buffer, GnomeVFSFileSize num_bytes, GnomeVFSFileSize * bytes_written, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_seek(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSSeekPosition whence, GnomeVFSFileOffset offset, GnomeVFSContext * context) { MythtvHandle *myth_handle; guint64 whence_p = 0; gint64 new_offset = 0; _GNOME_VFS_METHOD_PARAM_CHECK(method_handle != NULL); myth_handle = (MythtvHandle *) method_handle; g_debug("seek offset %" G_GINT64_FORMAT " whence %d", offset, whence); if (gmyth_uri_is_livetv(myth_handle->gmyth_uri)) return GNOME_VFS_ERROR_NOT_SUPPORTED; switch (whence) { case GNOME_VFS_SEEK_START: whence_p = 0; break; case GNOME_VFS_SEEK_CURRENT: whence_p = myth_handle->offset; break; case GNOME_VFS_SEEK_END: return GNOME_VFS_ERROR_NOT_SUPPORTED; } new_offset = gmyth_file_transfer_seek(myth_handle->file, offset, whence_p); if (new_offset != 0) { myth_handle->offset = new_offset; return GNOME_VFS_OK; } return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_tell(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSFileSize * offset_return) { MythtvHandle *myth_handle = NULL; _GNOME_VFS_METHOD_PARAM_CHECK(method_handle != NULL); myth_handle = (MythtvHandle *) method_handle; *offset_return = myth_handle->offset; return GNOME_VFS_OK; } static GnomeVFSResult do_truncate_handle(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSFileSize where, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_open_directory(GnomeVFSMethod * method, GnomeVFSMethodHandle ** method_handle, GnomeVFSURI * uri, GnomeVFSFileInfoOptions options, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_close_directory(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_read_directory(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSFileInfo * file_info, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_get_file_info(GnomeVFSMethod * method, GnomeVFSURI * uri, GnomeVFSFileInfo * file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext * context) { return myth_get_file_info(NULL, uri, file_info); } static GnomeVFSResult do_get_file_info_from_handle(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, GnomeVFSFileInfo * file_info, GnomeVFSFileInfoOptions options, GnomeVFSContext * context) { MythtvHandle *myth_handle = (MythtvHandle *) method_handle; return myth_get_file_info(myth_handle, NULL, file_info); } static gboolean do_is_local(GnomeVFSMethod * method, const GnomeVFSURI * uri) { return FALSE; } static GnomeVFSResult do_make_directory(GnomeVFSMethod * method, GnomeVFSURI * uri, guint perm, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_remove_directory(GnomeVFSMethod * method, GnomeVFSURI * uri, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_move(GnomeVFSMethod * method, GnomeVFSURI * old_uri, GnomeVFSURI * new_uri, gboolean force_replace, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_unlink(GnomeVFSMethod * method, GnomeVFSURI * uri, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_check_same_fs(GnomeVFSMethod * method, GnomeVFSURI * a, GnomeVFSURI * b, gboolean * same_fs_return, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_set_file_info(GnomeVFSMethod * method, GnomeVFSURI * uri, const GnomeVFSFileInfo * info, GnomeVFSSetFileInfoMask mask, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_truncate(GnomeVFSMethod * method, GnomeVFSURI * uri, GnomeVFSFileSize where, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_find_directory(GnomeVFSMethod * method, GnomeVFSURI * near_uri, GnomeVFSFindDirectoryKind kind, GnomeVFSURI ** result_uri, gboolean create_if_needed, gboolean find_if_needed, guint permissions, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_create_symbolic_link(GnomeVFSMethod * method, GnomeVFSURI * uri, const char *target_reference, GnomeVFSContext * context) { return GNOME_VFS_ERROR_READ_ONLY; } static GnomeVFSResult do_monitor_add(GnomeVFSMethod * method, GnomeVFSMethodHandle ** method_handle_return, GnomeVFSURI * uri, GnomeVFSMonitorType monitor_type) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_monitor_cancel(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSResult do_file_control(GnomeVFSMethod * method, GnomeVFSMethodHandle * method_handle, const char *operation, gpointer operation_data, GnomeVFSContext * context) { return GNOME_VFS_ERROR_NOT_SUPPORTED; } static GnomeVFSMethod method = { sizeof(GnomeVFSMethod), do_open, do_create, do_close, do_read, do_write, do_seek, do_tell, do_truncate_handle, do_open_directory, do_close_directory, do_read_directory, do_get_file_info, do_get_file_info_from_handle, do_is_local, do_make_directory, do_remove_directory, do_move, do_unlink, do_check_same_fs, do_set_file_info, do_truncate, do_find_directory, do_create_symbolic_link, do_monitor_add, do_monitor_cancel, do_file_control }; GnomeVFSMethod * vfs_module_init(const char *method_name, const char *args) { return &method; } void vfs_module_shutdown(GnomeVFSMethod * method) { }