static int gio_ftruncate (VFSFile * file, int64_t length) { FileData * data = vfs_get_handle (file); GError * error = 0; g_seekable_truncate (data->seekable, length, NULL, & error); CHECK_ERROR ("truncate", vfs_get_filename (file)); return 0; FAILED: return -1; }
int neon_vfs_fclose_impl (VFSFile * file) { struct neon_handle * h = vfs_get_handle (file); if (h->reader_status.reading) kill_reader (h); if (h->request) ne_request_destroy (h->request); if (h->session) ne_session_destroy (h->session); handle_free (h); return 0; }
static int probe_buffer_fseek (VFSFile * file, int64_t offset, int whence) { ProbeBuffer * p = vfs_get_handle (file); if (whence == SEEK_END) return -1; if (whence == SEEK_CUR) offset += p->at; g_return_val_if_fail (offset >= 0, -1); increase_buffer (p, offset); if (offset > p->filled) return -1; p->at = offset; return 0; }
static int64_t gio_fwrite (const void * buf, int64_t size, int64_t nitems, VFSFile * file) { FileData * data = vfs_get_handle (file); GError * error = 0; if (! data->ostream) { gio_error ("Cannot write to %s: not open for writing.", vfs_get_filename (file)); return 0; } int64_t written = g_output_stream_write (data->ostream, buf, size * nitems, 0, & error); CHECK_ERROR ("write to", vfs_get_filename (file)); return (size > 0) ? written / size : 0; FAILED: return 0; }
static int64_t gio_fread (void * buf, int64_t size, int64_t nitems, VFSFile * file) { FileData * data = vfs_get_handle (file); GError * error = 0; if (! data->istream) { gio_error ("Cannot read from %s: not open for reading.", vfs_get_filename (file)); return 0; } int64_t readed = g_input_stream_read (data->istream, buf, size * nitems, 0, & error); CHECK_ERROR ("read from", vfs_get_filename (file)); return (size > 0) ? readed / size : 0; FAILED: return 0; }
static int64_t gio_fsize (VFSFile * file) { FileData * data = vfs_get_handle (file); GError * error = 0; /* Audacious core expects one of two cases: * 1) File size is known and file is seekable. * 2) File size is unknown and file is not seekable. * Therefore, we return -1 for size if file is not seekable. */ if (! g_seekable_can_seek (data->seekable)) return -1; GFileInfo * info = g_file_query_info (data->file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0, & error); CHECK_ERROR ("get size of", vfs_get_filename (file)); int64_t size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE); g_object_unref (info); return size; FAILED: return -1; }
static char * probe_buffer_get_metadata (VFSFile * file, const char * field) { return vfs_get_metadata (((ProbeBuffer *) vfs_get_handle (file))->file, field); }
static int64_t probe_buffer_fsize (VFSFile * file) { return vfs_fsize (((ProbeBuffer *) vfs_get_handle (file))->file); }
static bool_t probe_buffer_feof (VFSFile * file) { ProbeBuffer * p = vfs_get_handle (file); return (p->at < p->filled) ? FALSE : vfs_feof (p->file); }
static int64_t probe_buffer_ftell (VFSFile * file) { return ((ProbeBuffer *) vfs_get_handle (file))->at; }
static int64_t gio_ftell (VFSFile * file) { FileData * data = vfs_get_handle (file); return g_seekable_tell (data->seekable); }
static int64_t neon_fread_real (void * ptr, int64_t size, int64_t nmemb, VFSFile * file) { struct neon_handle * h = vfs_get_handle (file); if (! h->request) { _ERROR ("<%p> No request to read from, seek gone wrong?", (void *) h); return 0; } if (! size || ! nmemb || h->eof) return 0; /* If the buffer is empty, wait for the reader thread to fill it. */ pthread_mutex_lock (& h->reader_status.mutex); for (int retries = 0; retries < NEON_RETRY_COUNT; retries ++) { if (used_rb_locked (& h->rb) / size > 0 || ! h->reader_status.reading || h->reader_status.status != NEON_READER_RUN) break; pthread_cond_broadcast (& h->reader_status.cond); pthread_cond_wait (& h->reader_status.cond, & h->reader_status.mutex); } pthread_mutex_unlock (& h->reader_status.mutex); if (! h->reader_status.reading) { if (h->reader_status.status != NEON_READER_EOF || h->content_length != -1) { /* There is no reader thread yet. Read the first bytes from * the network ourselves, and then fire up the reader thread * to keep the buffer filled up. */ _DEBUG ("<%p> Doing initial buffer fill", h); FillBufferResult ret = fill_buffer (h); if (ret == FILL_BUFFER_ERROR) { _ERROR ("<%p> Error while reading from the network", (void *) h); return 0; } /* We have some data in the buffer now. * Start the reader thread if we did not reach EOF during * the initial fill */ pthread_mutex_lock (& h->reader_status.mutex); if (ret == FILL_BUFFER_SUCCESS) { h->reader_status.reading = TRUE; _DEBUG ("<%p> Starting reader thread", h); pthread_create (& h->reader, NULL, reader_thread, h); h->reader_status.status = NEON_READER_RUN; } else if (ret == FILL_BUFFER_EOF) { _DEBUG ("<%p> No reader thread needed (stream has reached EOF during fill)", h); h->reader_status.reading = FALSE; h->reader_status.status = NEON_READER_EOF; } pthread_mutex_unlock (& h->reader_status.mutex); } } else { /* There already is a reader thread. Look if it is in good shape. */ pthread_mutex_lock (& h->reader_status.mutex); switch (h->reader_status.status) { case NEON_READER_INIT: case NEON_READER_RUN: /* All is well, nothing to be done. */ break; case NEON_READER_ERROR: /* A reader error happened. Log it, and treat it like an EOF * condition, by falling through to the NEON_READER_EOF codepath. */ _DEBUG ("<%p> NEON_READER_ERROR happened. Terminating reader thread and marking EOF.", h); h->reader_status.status = NEON_READER_EOF; pthread_mutex_unlock (& h->reader_status.mutex); if (h->reader_status.reading) kill_reader (h); pthread_mutex_lock (& h->reader_status.mutex); case NEON_READER_EOF: /* If there still is data in the buffer, carry on. * If not, terminate the reader thread and return 0. */ if (! used_rb_locked (& h->rb)) { _DEBUG ("<%p> Reached end of stream", h); pthread_mutex_unlock (& h->reader_status.mutex); if (h->reader_status.reading) kill_reader (h); h->eof = TRUE; return 0; } break; case NEON_READER_TERM: /* The reader thread terminated gracefully, most likely on our own request. * We should not get here. */ g_warn_if_reached (); pthread_mutex_unlock (& h->reader_status.mutex); return 0; } pthread_mutex_unlock (& h->reader_status.mutex); } /* Deliver data from the buffer */ if (! used_rb (& h->rb)) { /* The buffer is still empty, we can deliver no data! */ _ERROR ("<%p> Buffer still underrun, fatal.", (void *) h); return 0; } int belem = used_rb (& h->rb) / size; if (h->icy_metaint) { if (! h->icy_metaleft) { /* The next data in the buffer is a ICY metadata announcement. * Get the length byte */ unsigned char icy_metalen; read_rb (& h->rb, & icy_metalen, 1); /* We need enough data in the buffer to * a) Read the complete ICY metadata block * b) deliver at least one byte to the reader */ _DEBUG ("<%p> Expecting %d bytes of ICY metadata", h, (icy_metalen*16)); if (free_rb (& h->rb) - icy_metalen * 16 < size) { /* There is not enough data. We do not have much choice at this point, * so we'll deliver the metadata as normal data to the reader and * hope for the best. */ _ERROR ("<%p> Buffer underrun when reading metadata. Expect " "audio degradation", (void *) h); h->icy_metaleft = h->icy_metaint + icy_metalen * 16; } else { /* Grab the metadata from the buffer and send it to the parser */ char icy_metadata[NEON_ICY_BUFSIZE]; read_rb (& h->rb, icy_metadata, icy_metalen * 16); parse_icy (& h->icy_metadata, icy_metadata, icy_metalen * 16); h->icy_metaleft = h->icy_metaint; } } /* The maximum number of bytes we can deliver is determined * by the number of bytes left until the next metadata announcement */ belem = MIN (used_rb (& h->rb), h->icy_metaleft) / size; } nmemb = MIN (belem, nmemb); read_rb (& h->rb, ptr, nmemb * size); /* Signal the network thread to continue reading */ pthread_mutex_lock (& h->reader_status.mutex); if (h->reader_status.status == NEON_READER_EOF) { if (! free_rb_locked (& h->rb)) { _DEBUG ("<%p> stream EOF reached and buffer empty", h); h->eof = TRUE; } } else pthread_cond_broadcast (& h->reader_status.cond); pthread_mutex_unlock (& h->reader_status.mutex); h->pos += nmemb * size; h->icy_metaleft -= nmemb * size; return nmemb; }