first draft of the mmap patch series
authorAndre Noll <maan@systemlinux.org>
Mon, 12 Mar 2007 19:38:43 +0000 (20:38 +0100)
committerAndre Noll <maan@systemlinux.org>
Mon, 12 Mar 2007 19:38:43 +0000 (20:38 +0100)
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
afh.h
fd.c
fd.h
mp3_afh.c
ogg_afh.c
vss.c

index c268255..f6b8698 100644 (file)
--- 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 daac327..7925aee 100644 (file)
--- 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 637884b..30623d1 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -19,7 +19,7 @@
 /** \file fd.c helper functions for file descriptor handling */
 
 #include "para.h"
-
+#include <sys/mman.h>
 #include <fcntl.h>
 #include <sys/select.h>
 
@@ -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 5ae3331..0fd30e9 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
  *
  *     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);
index e2ed10c..d26d909 100644 (file)
--- 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;
 
index 1df5fd6..7a3286f 100644 (file)
--- a/ogg_afh.c
+++ b/ogg_afh.c
 #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 50c86ed..e12176d 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -23,6 +23,7 @@
  */
 
 #include "server.h"
+#include <sys/mman.h> /* mmap */
 #include <sys/time.h> /* 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;