afs com_add(): Recognize also audio formats different from mp3.
authorAndre Noll <maan@systemlinux.org>
Mon, 10 Sep 2007 00:05:15 +0000 (02:05 +0200)
committerAndre Noll <maan@systemlinux.org>
Mon, 10 Sep 2007 00:05:15 +0000 (02:05 +0200)
This required adding two more callbacks: path_brother_callback() and
hash_sister_callback(). These are called from add_one_audio_file()
which runs from handler context and can thus not access the database
directly.

afs.c
aft.c
error.h
vss.c
vss.h

diff --git a/afs.c b/afs.c
index 806d0fb..5e9bd11 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -674,7 +674,7 @@ static void command_post_select(struct sched *s, struct task *t)
                goto out;
        }
        if (t->ret != sizeof(buf)) {
-               PARA_NOTICE_LOG("short read (%d bytes, expected %lu)\n",
+               PARA_NOTICE_LOG("short read (%d bytes, expected %u)\n",
                        t->ret, sizeof(buf));
                t->ret = 1;
                goto out;
diff --git a/aft.c b/aft.c
index f1616ad..e1004d0 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -6,16 +6,14 @@
 #include "afs.h"
 #include "net.h"
 #include "string.h"
-
-int mp3_get_file_info(char *map, size_t numbytes,
-               struct audio_format_info *afi); /* FXIME */
+#include "vss.h"
 
 #define AFS_AUDIO_FILE_DIR "/home/mp3"
 
-static void *audio_file_table;
+static struct osl_table *audio_file_table;
 
 /**
- * Describes the structure of the mmapped-afs info struct.
+ * Describes the layout of the mmapped-afs info struct.
  *
  * \sa struct afs_info.
  */
@@ -46,19 +44,8 @@ enum afsi_offsets {
  */
 void save_afsi(struct afs_info *afsi, struct osl_object *obj)
 {
-       struct afs_info default_afs_info = {
-               .last_played = time(NULL) - 365 * 24 * 60 * 60,
-               .attributes = 0,
-               .num_played = 0,
-               .image_id = 0,
-               .lyrics_id = 0,
-               .audio_format_id = 5, /* FIXME */
-       };
        char *buf = obj->data;
 
-       if (!afsi)
-               afsi = &default_afs_info;
-
        write_u64(buf + AFSI_LAST_PLAYED_OFFSET, afsi->last_played);
        write_u64(buf + AFSI_ATTRIBUTES_OFFSET, afsi->attributes);
        write_u32(buf + AFSI_NUM_PLAYED_OFFSET, afsi->num_played);
@@ -366,6 +353,8 @@ static int load_chunk_info(struct osl_object *obj, struct audio_format_info *afh
 int aft_get_row_of_path(char *path, struct osl_row **row)
 {
        struct osl_object obj = {.data = path, .size = strlen(path) + 1};
+
+       PARA_NOTICE_LOG("audio_file_table: %p\n", audio_file_table);
        return osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row);
 }
 
@@ -715,7 +704,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        w->image_id_width, afsi->image_id,
                        w->lyrics_id_width, afsi->lyrics_id,
                        w->bitrate_width, afhi->bitrate,
-                       "mp3", /* FIXME */
+                       audio_format_name(afsi->audio_format_id), /* FIXME */
                        w->frequency_width, afhi->frequency,
                        afhi->channels,
                        duration_buf,
@@ -1156,7 +1145,7 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func)
                func);
 }
 
-static void *find_hash_sister(HASH_TYPE *hash)
+static struct osl_row *find_hash_sister(HASH_TYPE *hash)
 {
        const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
        struct osl_row *row;
@@ -1165,21 +1154,20 @@ static void *find_hash_sister(HASH_TYPE *hash)
        return row;
 }
 
-#define AFTROW_HEADER_SIZE 4
-
 enum aft_row_offsets {
        AFTROW_AFHI_OFFSET_POS = 0,
        AFTROW_CHUNKS_OFFSET_POS = 2,
-       AFTROW_HASH_OFFSET = AFTROW_HEADER_SIZE,
-       AFTROW_FLAGS_OFFSET = (AFTROW_HASH_OFFSET + HASH_SIZE),
-       AFTROW_PATH_OFFSET = (AFTROW_FLAGS_OFFSET + 4)
+       AFTROW_AUDIO_FORMAT_OFFSET = 4,
+       AFTROW_FLAGS_OFFSET = 5,
+       AFTROW_HASH_OFFSET = 9,
+       AFTROW_PATH_OFFSET = (AFTROW_HASH_OFFSET + HASH_SIZE),
 };
 
 /* never save the afsi, as the server knows it too. Note that afhi might be NULL.
  * In this case, afhi won't be stored in the buffer  */
 static void save_audio_file_info(HASH_TYPE *hash, const char *path,
-               struct audio_format_info *afhi,
-               uint32_t flags, struct osl_object *obj)
+               struct audio_format_info *afhi, uint32_t flags,
+               uint8_t audio_format_num, struct osl_object *obj)
 {
        size_t path_len = strlen(path) + 1;
        size_t afhi_size = sizeof_afhi_buf(afhi);
@@ -1188,8 +1176,10 @@ static void save_audio_file_info(HASH_TYPE *hash, const char *path,
        char *buf = para_malloc(size);
        uint16_t pos;
 
-       memcpy(buf + AFTROW_HASH_OFFSET, hash, HASH_SIZE);
+       write_u8(buf + AFTROW_AUDIO_FORMAT_OFFSET, audio_format_num);
        write_u32(buf + AFTROW_FLAGS_OFFSET, flags);
+
+       memcpy(buf + AFTROW_HASH_OFFSET, hash, HASH_SIZE);
        strcpy(buf + AFTROW_PATH_OFFSET, path);
 
        pos = AFTROW_PATH_OFFSET + path_len;
@@ -1266,6 +1256,7 @@ static int com_add_callback(const struct osl_object *query,
        int ret;
        char afsi_buf[AFSI_SIZE];
        uint32_t flags = read_u32(buf + AFTROW_FLAGS_OFFSET);
+       struct afs_info default_afsi = {.last_played = 0};
 
        hash = (HASH_TYPE *)buf + AFTROW_HASH_OFFSET;
        hash_to_asc(hash, asc);;
@@ -1353,11 +1344,14 @@ static int com_add_callback(const struct osl_object *query,
                        return ret;
        }
        /* new entry, use default afsi */
+       default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
+       default_afsi.audio_format_id = read_u8(buf + AFTROW_AUDIO_FORMAT_OFFSET);
+
        if (flags & ADD_FLAG_VERBOSE)
                PARA_NOTICE_LOG("adding %s\n", path);
        objs[AFTCOL_AFSI].data = &afsi_buf;
        objs[AFTCOL_AFSI].size = AFSI_SIZE;
-       save_afsi(NULL, &objs[AFTCOL_AFSI]);
+       save_afsi(&default_afsi, &objs[AFTCOL_AFSI]);
        ret = osl_add_and_get_row(audio_file_table, objs, &aft_row);
        if (ret < 0)
                return ret;
@@ -1369,13 +1363,44 @@ struct private_add_data {
        uint32_t flags;
 };
 
+static int path_brother_callback(const struct osl_object *query,
+               struct osl_object *result)
+{
+       char *path = query->data;
+       struct osl_row *path_brother;
+       int ret = aft_get_row_of_path(path, &path_brother);
+       if (ret < 0)
+               return ret;
+       result->data = para_malloc(sizeof(path_brother));
+       result->size = sizeof(path_brother);
+       *(struct osl_row **)(result->data) = path_brother;
+       return 1;
+}
+
+static int hash_sister_callback(const struct osl_object *query,
+               struct osl_object *result)
+{
+       HASH_TYPE *hash = query->data;
+       struct osl_row *hash_sister;
+
+       hash_sister = find_hash_sister(hash);
+       if (!hash_sister)
+               return -E_RB_KEY_NOT_FOUND;
+       result->data = para_malloc(sizeof(hash_sister));
+       result->size = sizeof(hash_sister);
+       *(struct osl_row **)(result->data) = hash_sister;
+       return 1;
+}
+
+
 static int add_one_audio_file(const char *arg, const void *private_data)
 {
        int ret;
+       uint8_t format_num = -1;
        const struct private_add_data *pad = private_data;
        struct audio_format_info afhi, *afhi_ptr = NULL;
-       struct osl_row *pb, *hs; /* path brother/hash sister */
-       struct osl_object map, obj = {.data = NULL};
+       struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
+       struct osl_object map, obj, query, result;
        char *path;
        HASH_TYPE hash[HASH_SIZE];
 
@@ -1384,24 +1409,38 @@ static int add_one_audio_file(const char *arg, const void *private_data)
        ret = verify_path(arg, &path);
        if (ret < 0)
                return ret;
-       ret = aft_get_row_of_path(path, &pb);
+       query.data = path;
+       query.size = strlen(path) + 1;
+       ret = send_callback_request(path_brother_callback, &query, &result);
        if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
                goto out_free;
+       if (ret >= 0) {
+               pb = *(struct osl_row **)result.data;
+               free(result.data);
+       }
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
                        PARA_NOTICE_LOG("lazy-ignore: %s\n", path);
                goto out_free;
        }
-       /* we still want to add this file. Compute its hash and look it up */
+       /* We still want to add this file. Compute its hash. */
        ret = mmap_full_file(path, O_RDONLY, &map);
        if (ret < 0)
                goto out_free;
        hash_function(map.data, map.size, hash);
-       hs = find_hash_sister(hash);
-       /*
-        * return success if we're pretty sure that we already know this file
-        */
+
+       /* Check whether database contains file with the same hash. */
+       query.data = hash;
+       query.size = HASH_SIZE;
+       ret = send_callback_request(hash_sister_callback, &query, &result);
+       if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
+               goto out_free;
+       if (ret >= 0) {
+               hs = *(struct osl_row **)result.data;
+               free(result.data);
+       }
+       /* Return success if we already know this file. */
        ret = 1;
        if (pb && hs && hs == pb && (!(pad->flags & ADD_FLAG_FORCE))) {
                if (pad->flags & ADD_FLAG_VERBOSE)
@@ -1413,17 +1452,18 @@ static int add_one_audio_file(const char *arg, const void *private_data)
         * there is a hash sister unless in FORCE mode.
         */
        if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
-               ret = mp3_get_file_info(map.data, map.size, &afhi);
+               ret = compute_afhi(path, map.data, map.size, &afhi);
                if (ret < 0) {
                        PARA_WARNING_LOG("audio format of %s not recognized, skipping\n", path);
                        ret = 1;
                        goto out_unmap;
                }
+               format_num = ret;
                afhi_ptr = &afhi;
        }
        munmap(map.data, map.size);
-       save_audio_file_info(hash, path, afhi_ptr, pad->flags, &obj);
-       /* ask parent to consider this entry for adding */
+       save_audio_file_info(hash, path, afhi_ptr, pad->flags, format_num, &obj);
+       /* Ask afs to consider this entry for adding. */
        ret = send_callback_request(com_add_callback, &obj, NULL);
        goto out_free;
 
@@ -1443,6 +1483,7 @@ int com_add(int fd, int argc, char * const * const argv)
        struct private_add_data pad = {.fd = fd, .flags = 0};
        struct stat statbuf;
 
+       PARA_NOTICE_LOG("argv[1]: %s\n", argv[1]);
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (arg[0] != '-')
@@ -1687,6 +1728,7 @@ int aft_init(struct table_info *ti)
                PARA_INFO_LOG("audio file table contains %d files\n", num);
                return ret;
        }
+       PARA_INFO_LOG("failed to open audio file table\n");
        audio_file_table = NULL;
        return ret == -E_NOENT? 1 : ret;
 }
diff --git a/error.h b/error.h
index 8c11391..9bc8ed5 100644 (file)
--- a/error.h
+++ b/error.h
@@ -122,11 +122,11 @@ extern const char **para_errlist[];
        PARA_ERROR(NOTDIR, "fixme"), \
        PARA_ERROR(NOENT, "fixme"), \
        PARA_ERROR(OSL_PERM, "fixme"), \
-       PARA_ERROR(BAD_TABLE, "fixme"), \
-       PARA_ERROR(BAD_TABLE_HEADER, "fixme"), \
+       PARA_ERROR(BAD_TABLE, "table not open"), \
+       PARA_ERROR(BAD_TABLE_HEADER, "table header corruption"), \
        PARA_ERROR(BAD_TABLE_DESC, "fixme"), \
-       PARA_ERROR(RB_KEY_EXISTS, "fixme"), \
-       PARA_ERROR(RB_KEY_NOT_FOUND, "fixme"), \
+       PARA_ERROR(RB_KEY_EXISTS, "key already exists in rbtree"), \
+       PARA_ERROR(RB_KEY_NOT_FOUND, "key not found in rbtree"), \
        PARA_ERROR(BAD_ID, "fixme"), \
        PARA_ERROR(INDEX_CORRUPTION, "fixme"), \
        PARA_ERROR(BAD_OFFSET, "fixme"), \
@@ -202,8 +202,8 @@ extern const char **para_errlist[];
        PARA_ERROR(FNMATCH, "fixme"), \
        PARA_ERROR(NO_MATCH, "fixme"), \
        PARA_ERROR(NO_AFHI, "fixme"), \
-       PARA_ERROR(AFT_SYNTAX, "fixme"), \
-       PARA_ERROR(AFS_STAT, "fixme"), \
+       PARA_ERROR(AFT_SYNTAX, "syntax error"), \
+       PARA_ERROR(AFS_STAT, "stat(2) failed"), \
        PARA_ERROR(HASH_MISMATCH, "fixme"), \
 
 
diff --git a/vss.c b/vss.c
index e0c0b58..3ef850c 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -38,9 +38,8 @@ extern struct sender senders[];
 static int audio_file;
 static char *map;
 
-#if 1
-       void mp3_init(struct audio_format_handler *);
-#endif
+/* The mp3 audio format handler does not need any libs. */
+void mp3_init(struct audio_format_handler *);
 
 #ifdef HAVE_OGGVORBIS
        void ogg_init(struct audio_format_handler *);
@@ -50,35 +49,53 @@ static char *map;
 #endif
 
 /**
- * the list of supported  audio formats
+ * The list of supported  audio formats.
+ *
+ * We always define the full array of audio formats even if some audio formats
+ * were not compiled in. This is because for each audio file the number of its
+ * audio format is stored in the databse. We don't want that numbers to become
+ * stale just because the user installed a new version of paraslash that
+ * supports a different set of audio formats.
+ *
+ * It can still be easily detected whether an audio format is compiled in by
+ * checking if the init function pointer is not \p NULL.
  */
 static struct audio_format_handler afl[] = {
-#if 1
        {
                .name = "mp3",
                .init = mp3_init,
        },
-#endif
-#ifdef HAVE_OGGVORBIS
        {
                .name = "ogg",
+#ifdef HAVE_OGGVORBIS
                .init = ogg_init,
-       },
 #endif
-#ifdef HAVE_FAAD
+       },
        {
                .name = "aac",
+#ifdef HAVE_FAAD
                .init = aac_afh_init,
-       },
 #endif
+       },
        {
                .name = NULL,
        }
 };
 
-/** iterate over each supported audio format */
-#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i++)
+static inline int next_audio_format(int format)
+{
+       for (;;) {
+               if (!afl[format].name)
+                       return format;
+               format++;
+               if (afl[format].init)
+                       return format;
+       }
+
+}
 
+/** Iterate over each supported audio format. */
+#define FOR_EACH_AUDIO_FORMAT(i) for (i = 0; afl[i].name; i = next_audio_format(i))
 
 
 /**
@@ -128,6 +145,7 @@ unsigned int vss_paused(void)
 
 /**
  * get the name of the given audio format
+ *
  * \param i the audio format number
  *
  * This returns a pointer to statically allocated memory so it
@@ -226,24 +244,42 @@ static int get_audio_format(int omit)
        return -E_AUDIO_FORMAT;
 }
 
-int get_audio_file_info(const char *path, char *data, size_t size,
+/**
+ * Call get_file_info() to obtain an afhi structure.
+ *
+ * \param path The full path of the audio file.
+ * \param data Pointer to the contents of the (mapped) file.
+ * \param size The file size in bytes.
+ * \param afhi Result pointer.
+ *
+ * \return The number of the audio format on success, \p -E_AUDIO_FORMAT if no
+ * compiled in audio format handler is able to handler the file.
+ *
+ * This function tries to find an audio format handler that can interpret the
+ * file given by \a data and \a size.
+ *
+ * It first tries to determine the audio format from the filename given by \a
+ * path. If this doesn't work, all other audio format handlers are tried until
+ * one is found that can handle the file.
+ */
+int compute_afhi(const char *path, char *data, size_t size,
                struct audio_format_info *afhi)
 {
        int ret, i, format = guess_audio_format(path);
+
        if (format >= 0) {
                ret = afl[format].get_file_info(data, size, afhi);
                if (ret >= 0)
-                       return ret;
+                       return format;
        }
        FOR_EACH_AUDIO_FORMAT(i) {
                if (i == format) /* we already tried this one to no avail */
                        continue;
                ret = afl[i].get_file_info(data, size, afhi);
                if (ret >= 0)
-                       return ret;
+                       return i;
        }
        return -E_AUDIO_FORMAT;
-
 }
 
 /*
@@ -413,7 +449,7 @@ static void vss_eof(void)
 }
 
 /**
- * get the header and of the current audio file
+ * Get the header of the current audio file.
  *
  * \param header_len the length of the header is stored here
  *
diff --git a/vss.h b/vss.h
index 3374e94..9b10a5e 100644 (file)
--- a/vss.h
+++ b/vss.h
@@ -16,8 +16,8 @@ unsigned int vss_paused(void);
 char *vss_get_header(size_t *header_len);
 struct timeval *vss_chunk_time(void);
 int guess_audio_format(const char *name);
-int get_audio_file_info(const char *path, char *data, size_t size,
-               struct audio_format_info *afhi);
+int compute_afhi(const char *path, char *data, size_t size,
+       struct audio_format_info *afhi);
 const char *supported_audio_formats(void);
 int vss_get_chunk(long unsigned chunk_num, char **buf, size_t *len);