]> git.tue.mpg.de Git - paraslash.git/commitdiff
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 c268255a7026df872a6e0d8c17b06b2bdee5ea96..f6b869810889cdd47680daaf8c5ef920e810203f 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 daac3279729ab5f9001a2f4e15366b21943c2d1e..7925aee32de83cb33c7ce586649edadd9b89605a 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 637884b4b895a9279c5c928ea4028dbddb1a74c7..30623d1b321b8dfed84b07c33034fc20e05dd351 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 5ae3331f91145ae0ce30b36e476f49d6f4b3abc7..0fd30e9b6bd292fa83bb6725caa1fd31ad127f99 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 e2ed10cfabc094f1cd89a6f1ba152c38cc8e0ad6..d26d9094be93c236cb3bd7b6dadd5903e553c80d 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 1df5fd6f68e2e17ce119324d24b371c0230005d8..7a3286fdf471f816b43c16c72603058f04fe7ade 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 50c86ed7d8ef7069e6d9baea3df7d18c7e6ae3c2..e12176d2df3ce08eeb457aa05d10a418206d59f8 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;