#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.
*/
*/
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);
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);
}
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,
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;
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);
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;
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);;
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;
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];
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)
* 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;
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] != '-')
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;
}
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"), \
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"), \
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 *);
#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))
/**
/**
* get the name of the given audio format
+ *
* \param i the audio format number
*
* This returns a pointer to statically allocated memory so it
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;
-
}
/*
}
/**
- * 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
*