From 57a8d22ad5e81dc815390685ae5f83c0f16156e7 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 12 Mar 2007 20:38:43 +0100 Subject: [PATCH] first draft of the mmap patch series The purpose of these changes to para_server is to not call read()/fseek() in the audio format handlers and in vss.c but to use mmap() instead which should result in less code and should also be faster and less error-prone. This first patch implements the basic infrastructure for mmap but keeps the file pointer so that changes may be merged step by step and changes the ogg vorbis audio format handler to use the new mapping of the audio file. Subsequent patches will change the other audio format handlers and the virtual streaming system to also use mmap instead of read()/fseek(). --- aac_afh.c | 3 +- afh.h | 3 +- fd.c | 25 ++++++++++++++- fd.h | 3 +- mp3_afh.c | 3 +- ogg_afh.c | 96 +++++++++++++++++++++++++++++++++++-------------------- vss.c | 16 ++++++---- 7 files changed, 104 insertions(+), 45 deletions(-) diff --git a/aac_afh.c b/aac_afh.c index c268255a..f6b86981 100644 --- a/aac_afh.c +++ b/aac_afh.c @@ -116,7 +116,8 @@ static long unsigned aac_set_chunk_tv(struct audio_format_info *afi, /* * Init m4a file and write some tech data to given pointers. */ -static int aac_get_file_info(FILE *file, struct audio_format_info *afi) +static int aac_get_file_info(FILE *file, char *map, off_t numbytes, + struct audio_format_info *afi) { int i, ret, decoder_len; size_t inbuf_len, skip; diff --git a/afh.h b/afh.h index daac3279..7925aee3 100644 --- a/afh.h +++ b/afh.h @@ -108,6 +108,7 @@ struct audio_format_handler { * * \sa struct audio_format_info */ - int (*get_file_info)(FILE *audio_file, struct audio_format_info *afi); + int (*get_file_info)(FILE *audio_file, char *map, off_t numbytes, + struct audio_format_info *afi); }; diff --git a/fd.c b/fd.c index 637884b4..30623d1b 100644 --- a/fd.c +++ b/fd.c @@ -19,7 +19,7 @@ /** \file fd.c helper functions for file descriptor handling */ #include "para.h" - +#include #include #include @@ -183,3 +183,26 @@ int para_fseek(FILE *stream, long offset, int whence) int ret = fseek(stream, offset, whence); return ret < 0? -E_FSEEK : 1; } + +/** + * *paraslash's wrapper for mmap + * + * \param length number of bytes to mmap + * \param prot either PROT_NONE or the bitwise OR of one or more of + * PROT_EXEC PROT_READ PROT_WRITE + * \param flags exactly one of MAP_SHARED and MAP_PRIVATE + * \param fd the file to mmap from + * \param offset mmap start + * + * \return This function either returns a valid pointer to the mapped area + * or calls exit() on errors. + */ +void *para_mmap(size_t length, int prot, int flags, int fd, off_t offset) +{ + void *ret = mmap(NULL, length, prot, flags, fd, offset); + if (ret != MAP_FAILED) + return ret; + PARA_EMERG_LOG("mmap failed: %s", strerror(errno)); + exit(EXIT_FAILURE); +} + diff --git a/fd.h b/fd.h index 5ae3331f..0fd30e9b 100644 --- a/fd.h +++ b/fd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Andre Noll + * Copyright (C) 2006-2007 Andre Noll * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,3 +27,4 @@ __must_check int para_fread(void *dest, size_t nbytes, size_t nmemb, FILE *stream); __must_check int para_fgets(char *line, int size, FILE *f); int para_fseek(FILE *stream, long offset, int whence); +void *para_mmap(size_t length, int prot, int flags, int fd, off_t offset); diff --git a/mp3_afh.c b/mp3_afh.c index e2ed10cf..d26d9094 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -433,7 +433,8 @@ err_out: /* * Read mp3 information from audio file */ -static int mp3_get_file_info(FILE *file, struct audio_format_info *afi) +static int mp3_get_file_info(FILE *file, char *map, off_t numbytes, + struct audio_format_info *afi) { int ret; diff --git a/ogg_afh.c b/ogg_afh.c index 1df5fd6f..7a3286fd 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -32,17 +32,57 @@ #define CHUNK_SIZE 32768 static double chunk_time = 0.25; +struct ogg_datasource { + char *map; + off_t numbytes; + off_t fpos; +}; + static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource) { - FILE *f = datasource; - return fread(buf, size, nmemb, f); + struct ogg_datasource *ods = datasource; + size_t copy = PARA_MIN(ods->numbytes - ods->fpos, size * nmemb), + ret = copy / size; + if (!ret) + return 0; + memcpy(buf, ods->map + ods->fpos, copy); +// PARA_INFO_LOG("size: %zd, nmemb: %zd, ret: %zd\n", size, nmemb, ret); + ods->fpos += ret * size; + return ret; } -static int cb_seek(__a_unused void *datasource, ogg_int64_t offset, +static int cb_seek(void *datasource, ogg_int64_t offset, int whence) { - FILE *f = datasource; - return fseek(f, offset, whence); + struct ogg_datasource *ods = datasource; + switch (whence) { + case SEEK_SET: + if (offset >= 0 && offset <= ods->numbytes) { + ods->fpos = offset; + return 0; + } + errno = EINVAL; + return -1; + break; + case SEEK_END: + if (offset <= 0 && -offset <= ods->numbytes) { + ods->fpos = ods->numbytes + offset; + return 0; + } + errno = EINVAL; + return -1; + break; + case SEEK_CUR: + if ((offset >= 0 && offset + ods->fpos > ods->numbytes) || + (offset < 0 && offset + ods->fpos < 0)) { + errno = EINVAL; + return -1; + } + ods->fpos += offset; + return 0; + } + errno = EINVAL; + return -1; } /* don't do anything as vss still needs the open filehandle */ @@ -53,8 +93,8 @@ static int cb_close(__a_unused void *datasource) static long cb_tell(void *datasource) { - FILE *f = datasource; - return ftell(f); + struct ogg_datasource *ods = datasource; + return (unsigned long)ods->fpos; } static int ogg_open_callbacks(void *datasource, OggVorbis_File *vf, ov_callbacks c) @@ -79,22 +119,16 @@ static int ogg_open_callbacks(void *datasource, OggVorbis_File *vf, ov_callbacks } -static int ogg_save_header(FILE *file, struct audio_format_info *afi) +static void ogg_save_header(char *map, struct audio_format_info *afi) { - int ret; - afi->header = para_malloc(afi->header_len); - rewind(file); - ret = read(fileno(file), afi->header, afi->header_len); - if (ret == afi->header_len) - return 1; - free(afi->header); - return -E_OGG_READ; + memcpy(afi->header, map, afi->header_len); } -static int ogg_compute_header_len(FILE *file, struct audio_format_info *afi) +static int ogg_compute_header_len(char *map, off_t numbytes, + struct audio_format_info *afi) { - int ret, len, in = fileno(file); + int ret, len = PARA_MIN(numbytes, CHUNK_SIZE); unsigned int serial; char *buf; ogg_page page; @@ -108,11 +142,8 @@ static int ogg_compute_header_len(FILE *file, struct audio_format_info *afi) ogg_sync_init(sync_in); vorbis_info_init(&vi); vorbis_comment_init(&vc); - buf = ogg_sync_buffer(sync_in, CHUNK_SIZE); - len = read(in, buf, CHUNK_SIZE); - ret = -E_OGG_READ; - if (len <= 0) - goto err1; + buf = ogg_sync_buffer(sync_in, len); + memcpy(buf, map, len); ogg_sync_wrote(sync_in, len); ret = -E_SYNC_PAGEOUT; if (ogg_sync_pageout(sync_in, &page) <= 0) @@ -158,7 +189,8 @@ static int ogg_compute_header_len(FILE *file, struct audio_format_info *afi) while (ogg_stream_flush(stream_out, &page)) afi->header_len += page.body_len + page.header_len; PARA_INFO_LOG("header_len = %d\n", afi->header_len); - ret = ogg_save_header(file, afi); + ogg_save_header(map, afi); + ret = 1; err2: ogg_stream_destroy(stream_in); ogg_stream_destroy(stream_out); @@ -215,7 +247,8 @@ static long unsigned ogg_compute_chunk_table(OggVorbis_File *of, /* * Init oggvorbis file and write some tech data to given pointers. */ -static int ogg_get_file_info(FILE *file, struct audio_format_info *afi) +static int ogg_get_file_info(FILE *file, char *map, off_t numbytes, + struct audio_format_info *afi) { int ret; double time_total; @@ -223,20 +256,18 @@ static int ogg_get_file_info(FILE *file, struct audio_format_info *afi) ogg_int64_t raw_total; long vi_sampling_rate, vi_bitrate; OggVorbis_File of; - static const ov_callbacks ovc = { + const ov_callbacks ovc = { .read_func = cb_read, .seek_func = cb_seek, .close_func = cb_close, .tell_func = cb_tell }; + struct ogg_datasource ods = {.map = map, .numbytes = numbytes, .fpos = 0}; - if (!file) - return -E_OGG_NO_FILE; - ret = ogg_compute_header_len(file, afi); + ret = ogg_compute_header_len(map, numbytes, afi); if (ret < 0) return ret; - rewind(file); - ret = ogg_open_callbacks(file, &of, ovc); + ret = ogg_open_callbacks(&ods, &of, ovc); if (ret < 0) goto err; ret = -E_OGG_INFO; @@ -248,9 +279,7 @@ static int ogg_get_file_info(FILE *file, struct audio_format_info *afi) afi->seconds_total = time_total; vi_sampling_rate = vi->rate; vi_bitrate = ov_bitrate(&of, 0); - rewind(file); afi->chunks_total = ogg_compute_chunk_table(&of, afi, time_total); - rewind(file); sprintf(afi->info_string, "audio_file_info1:%lu x %lu, %ldkHz, " "%d channels, %ldkbps\n" "audio_file_info2: \n" @@ -258,7 +287,6 @@ static int ogg_get_file_info(FILE *file, struct audio_format_info *afi) afi->chunks_total, (long unsigned) (chunk_time * 1000 * 1000), vi_sampling_rate / 1000, vi->channels, vi_bitrate / 1000 ); - rewind(file); afi->chunk_tv.tv_sec = 0; afi->chunk_tv.tv_usec = 250 * 1000; tv_scale(3, &afi->chunk_tv, &afi->eof_tv); diff --git a/vss.c b/vss.c index 50c86ed7..e12176d2 100644 --- a/vss.c +++ b/vss.c @@ -23,6 +23,7 @@ */ #include "server.h" +#include /* mmap */ #include /* gettimeofday */ #include "server.cmdline.h" #include "afs.h" @@ -46,6 +47,7 @@ static char *inbuf; static size_t inbuf_size; static FILE *audio_file = NULL; +static char *map; #if 1 void mp3_init(struct audio_format_handler *); @@ -185,7 +187,7 @@ void vss_init(void) static int get_file_info(int i) { - return afl[i].get_file_info(audio_file, &mmd->afi); + return afl[i].get_file_info(audio_file, map, mmd->size, &mmd->afi); } /** @@ -238,9 +240,14 @@ static int get_audio_format(int omit) */ static int update_mmd(void) { - int i; + int i, fd = fileno(audio_file); struct stat file_status; + if (fstat(fd, &file_status) == -1) + return -E_FSTAT; + mmd->size = file_status.st_size; + mmd->mtime = file_status.st_mtime; + map = para_mmap(file_status.st_size, PROT_READ, MAP_PRIVATE, fd, 0); i = guess_audio_format(mmd->filename); if (i < 0 || get_file_info(i) < 0) i = get_audio_format(i); @@ -250,10 +257,6 @@ static int update_mmd(void) mmd->chunks_sent = 0; mmd->current_chunk = 0; mmd->offset = 0; - if (fstat(fileno(audio_file), &file_status) == -1) - return -E_FSTAT; - mmd->size = file_status.st_size; - mmd->mtime = file_status.st_mtime; mmd->events++; PARA_NOTICE_LOG("next audio file: %s\n", mmd->filename); return 1; @@ -372,6 +375,7 @@ static void vss_eof(struct audio_format_handler *af) } gettimeofday(&now, NULL); tv_add(&mmd->afi.eof_tv, &now, &eof_barrier); + munmap(map, mmd->size); fclose(audio_file); audio_file = NULL; mmd->audio_format = -1; -- 2.39.5