0.3.3 (to be announced) "axiomatic perspectivity"
-------------------------------------------------
+Internal code cleanups and improved tag handling.
+
- para_server uses the generic scheduling code.
- overhaul of the virtual streaming system.
+ - mp3: id3 version 2 support via libid3tag (optional)
+ - ogg: vorbis comment support.
+ - mp3 audio format handler cleanups.
-----------------------------------------
0.3.2 (2008-04-11) "probabilistic parity"
-----------------------------------------
+
The new para_afh executable, scheduling and documentation improvements.
- new ls option: -lc (list chunk table)
http://www.underbit.com/products/mad/. If you prefer to use
the libmad package provided by your distributor, make sure
to install the corresponding development package as well.
- Note that libmad is not necessary for sending mp3 files.
+
+ For version 2 id3 tag support, you'll need libid3tag which
+ is also available through the above link. Without libid3tag,
+ only version 1 tags are recognized.
+
+ Note that libmad is not necessary for the server side,
+ i.e. for sending mp3 files.
- *ogg vorbis*: For ogg vorbis streams you'll need libogg,
libvorbis, libvorbisfile: http://www.xiph.org/downloads/.
/*
* Init m4a file and write some tech data to given pointers.
*/
-static int aac_get_file_info(char *map, size_t numbytes,
+static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
struct afh_info *afhi)
{
int i;
ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
- sprintf(afhi->info_string, "%s:\n%s:\n%s:\n",
+ afhi->info_string = make_message("%s:\n%s:\n%s:\n",
status_item_list[SI_AUDIO_FILE_INFO],
status_item_list[SI_TAGINFO1],
status_item_list[SI_TAGINFO2]
int main(int argc, char **argv)
{
- int ret, audio_format_num;
+ int ret, audio_format_num, fd;
void *audio_file_data;
size_t audio_file_size;
struct afh_info afhi;
goto out;
afh_init();
ret = mmap_full_file(conf.inputs[0], O_RDONLY, &audio_file_data,
- &audio_file_size, NULL);
+ &audio_file_size, &fd);
if (ret < 0)
goto out;
- ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size, &afhi);
+ ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size,
+ fd, &afhi);
if (ret < 0)
goto out;
audio_format_num = ret;
/** \endcond */
-/** Size of the audio_file info string. */
-#define AUDIO_FILE_INFO_SIZE 256
-
/** Audio format dependent information. */
struct afh_info {
/** The number of chunks this audio file contains. */
/** The length of the audio file in seconds. */
long unsigned seconds_total;
/** A string that gets filled in by the audio format handler. */
- char info_string[AUDIO_FILE_INFO_SIZE];
+ char *info_string;
/**
* The table that specifies the offset of the individual pieces in
* the current audio file.
*
* \sa struct afh_info
*/
- int (*get_file_info)(char *map, size_t numbytes,
+ int (*get_file_info)(char *map, size_t numbytes, int fd,
struct afh_info *afi);
};
void afh_init(void);
int guess_audio_format(const char *name);
int compute_afhi(const char *path, char *data, size_t size,
- struct afh_info *afhi);
+ int fd, struct afh_info *afhi);
const char *audio_format_name(int);
void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
void *map, const char **buf, size_t *len);
void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len);
+char *make_taginfo(char *title, char *artist, char *album, char *year,
+ char *comment);
return -1;
}
+char *make_taginfo(char *title, char *artist, char *album, char *year,
+ char *comment)
+{
+ return make_message("%s: %s, by %s\n" /* taginfo1 */
+ "%s: A: %s, Y: %s, C: %s\n", /* taginfo 2*/
+ status_item_list[SI_TAGINFO1],
+ (title && *title)? title : "(title tag not set)",
+ (artist && *artist)? artist : "(artist tag not set)",
+ status_item_list[SI_TAGINFO2],
+ (album && *album)? album : "(album tag not set)",
+ (year && *year)? year : "????",
+ (comment && *comment)? comment : "(comment tag not set)"
+ );
+}
+
/**
* 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 fd The open file descriptor.
* \param afhi Result pointer.
*
* \return The number of the audio format on success, \p -E_AUDIO_FORMAT if no
* 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,
+int compute_afhi(const char *path, char *data, size_t size, int fd,
struct afh_info *afhi)
{
int ret, i, format;
format = guess_audio_format(path);
if (format >= 0) {
- ret = afl[format].get_file_info(data, size, afhi);
+ ret = afl[format].get_file_info(data, size, fd, afhi);
if (ret >= 0)
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);
+ ret = afl[i].get_file_info(data, size, fd, afhi);
if (ret >= 0)
return i;
PARA_WARNING_LOG("%s\n", para_strerror(-ret));
afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
ms2tv(read_u16(buf + AFHI_EOF_OFFSET), &afhi->eof_tv);
- strcpy(afhi->info_string, buf + AFHI_INFO_STRING_OFFSET);
+ afhi->info_string = para_strdup(buf + AFHI_INFO_STRING_OFFSET);
}
static unsigned sizeof_chunk_table(struct afh_info *afhi)
ret = get_afhi_of_row(aft_row, &afd->afhi);
if (ret < 0)
return ret;
+ afd->afhi.chunk_table = NULL;
ret = osl_open_disk_object(audio_file_table, aft_row,
AFTCOL_CHUNKS, &chunk_table_obj);
if (ret < 0)
- return ret;
- afd->afhi.chunk_table = NULL;
+ goto err;
ret = mmap_full_file(path, O_RDONLY, &map.data,
&map.size, &afd->fd);
if (ret < 0)
load_chunk_table(&afd->afhi, chunk_table_obj.data);
{
struct ls_data d = {
- .afhi = afd->afhi,
+ .afhi = afd->afhi, /* struct copy */
.afsi = old_afsi,
.path = path,
.score = score,
.hash = file_hash
};
struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1};
- ret = make_status_items(&d, &pb);
+ ret = make_status_items(&d, &pb); /* frees info string */
+ afd->afhi.info_string = NULL;
if (ret < 0)
goto err;
strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE);
ret = save_afd(afd);
err:
free(afd->afhi.chunk_table);
+ free(afd->afhi.info_string);
osl_close_disk_object(&chunk_table_obj);
return ret;
}
char asc_hash[2 * HASH_SIZE + 1];
char *att_lines, *lyrics_lines, *image_lines, *filename_lines;
- if (opts->mode == LS_MODE_SHORT)
- return para_printf(b, "%s\n", d->path);
- if (opts->mode == LS_MODE_CHUNKS)
- return print_chunk_table(d, b);
+ if (opts->mode == LS_MODE_SHORT) {
+ ret = para_printf(b, "%s\n", d->path);
+ goto out;
+ }
+ if (opts->mode == LS_MODE_CHUNKS) {
+ ret = print_chunk_table(d, b);
+ goto out;
+ }
get_attribute_bitmap(&afsi->attributes, att_buf);
ret = get_local_time(&afsi->last_played, last_played_time,
sizeof(last_played_time), current_time, opts->mode);
if (ret < 0)
- return ret;
+ goto out;
get_duration_buf(afhi->seconds_total, duration_buf, opts);
if (have_score) {
if (opts->mode == LS_MODE_LONG)
}
if (opts->mode == LS_MODE_LONG) {
- return para_printf(b,
+ ret = para_printf(b,
"%s" /* score */
"%s " /* attributes */
"%*d " /* image_id */
last_played_time,
d->path
);
+ goto out;
}
hash_to_asc(d->hash, asc_hash);
att_lines = make_attribute_lines(att_buf, afsi);
last_played_time,
bn? bn : "?");
if (ret < 0)
- return ret;
+ goto out;
}
ret = para_printf(b,
"%s" /* filename stuff */
status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
);
if (ret < 0)
- return ret;
+ goto out;
if (opts->mode == LS_MODE_MBOX) {
struct osl_object lyrics_def;
lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def);
free(lyrics_lines);
free(image_lines);
free(filename_lines);
+out:
+ free(afhi->info_string);
return ret;
}
d->path = path;
ret = get_hash_of_row(aft_row, &d->hash);
if (ret < 0)
- return ret;
+ goto err;
w = &options->widths;
GET_NUM_DIGITS(d->afsi.image_id, &num_digits);
w->image_id_width = PARA_MAX(w->image_id_width, num_digits);
d->score = score;
}
return 1;
+err:
+ free(d->afhi.info_string);
+ return ret;
}
static void com_ls_callback(int fd, const struct osl_object *query)
static int add_one_audio_file(const char *path, void *private_data)
{
- int ret, send_ret = 1;
+ int ret, send_ret = 1, fd;
uint8_t format_num = -1;
struct private_add_data *pad = private_data;
struct afh_info afhi, *afhi_ptr = NULL;
goto out_free;
}
/* We still want to add this file. Compute its hash. */
- ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, NULL);
+ ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, &fd);
if (ret < 0)
goto out_free;
hash_function(map.data, map.size, hash);
* there is a hash sister and FORCE was not given.
*/
if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
- ret = compute_afhi(path, map.data, map.size, &afhi);
+ ret = compute_afhi(path, map.data, map.size, fd, &afhi);
if (ret < 0)
goto out_unmap;
format_num = ret;
goto out_free;
out_unmap:
+ close(fd);
munmap(map.data, map.size);
out_free:
if (ret < 0 && send_ret >= 0)
send_ret = send_va_buffer(pad->fd, "failed to add %s (%s)\n", path,
para_strerror(-ret));
free(obj.data);
- if (afhi_ptr)
+ if (afhi_ptr) {
free(afhi_ptr->chunk_table);
+ free(afhi_ptr->info_string);
+ }
/* Stop adding files only on send errors. */
return send_ret;
}
CPPFLAGS="$OLD_CPPFLAGS"
LDFLAGS="$OLD_LDFLAGS"
LIBS="$OLD_LIBS"
+###################################################################### libid3tag
+AC_MSG_CHECKING(for libid3tag)
+AC_TRY_LINK([
+ #include <id3tag.h>
+],[
+ struct id3_tag t = {.flags = 0};
+],[have_libid3tag=yes],[have_libid3tag=no])
+AC_MSG_RESULT($have_libid3tag)
+if test ${have_libid3tag} = yes; then
+ AC_DEFINE(HAVE_LIBID3TAG, 1, define to 1 you have libid3tag)
+ server_ldflags="$server_ldflags -lid3tag"
+ afh_ldflags="$afh_ldflags -lid3tag"
+else
+ AC_MSG_WARN([no support for id3v2 tags])
+fi
########################################################################### alsa
have_alsa="yes"
OLD_CPPFLAGS="$CPPFLAGS"
~~~~~~~~~~~~~~~~~~~~~~~~
unix socket credentials: $have_ucred
audio formats supported by para_server/para_afh: $server_audio_formats
+id3 version2 support: $have_libid3tag
senders supported by para_server: $senders
receivers supported by para_audiod/para_recv: $receivers
filters supported by para_audiod/para_filter: $filters
ret = get_afhi_of_row(row, &afhi);
if (ret< 0)
return ret;
+ free(afhi.info_string); /* don't need the tag info */
ret = get_audio_file_path_of_row(row, &path);
if (ret< 0)
return ret;
unsigned int emphasis;
};
-struct id3tag {
- char title[31];
- char artist[31];
- char album[31];
- char year[5];
- char comment[31];
-};
-
-struct mp3info {
- struct mp3header header;
- int id3_isvalid;
- struct id3tag id3;
- int vbr;
-};
-
/** \endcond */
static const int frequencies[3][4] = {
{22050,24000,16000,50000}, /* MPEG 2.0 */
static const int frame_size_index[] = {24000, 72000, 72000};
static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"};
-static struct mp3info mp3;
+#ifdef HAVE_LIBID3TAG
+
+#include <id3tag.h>
+
+static char *get_latin1(id3_ucs4_t const *string)
+{
+ if (!string)
+ return NULL;
+ return (char *)id3_ucs4_latin1duplicate(string);
+}
+
+char *get_stringlist(union id3_field *field)
+{
+ unsigned int k, nstrings = id3_field_getnstrings(field);
+ char *result = NULL;
+
+ for (k = 0; k < nstrings; k++) {
+ char *tmp = (char *)get_latin1(id3_field_getstrings(field, k));
+ if (result) {
+ char *tmp2 = result;
+ result = make_message("%s %s", tmp2, tmp);
+ free(tmp);
+ free(tmp2);
+ } else
+ result = tmp;
+ }
+ return result;
+}
+
+static char *get_string(union id3_field *field)
+{
+ id3_ucs4_t const *string = id3_field_getfullstring(field);
+
+ return get_latin1(string);
+}
+
+#define FOR_EACH_FIELD(f, j, fr) for (j = 0; j < (fr)->nfields && \
+ (f = id3_frame_field((fr), j)); j++)
+
+static char *get_strings(struct id3_frame *fr)
+{
+ int j;
+ union id3_field *field;
+
+ FOR_EACH_FIELD(field, j, fr) {
+ enum id3_field_type type = id3_field_type(field);
+
+ if (type == ID3_FIELD_TYPE_STRINGLIST)
+ return get_stringlist(field);
+ if (type == ID3_FIELD_TYPE_STRINGFULL)
+ return get_string(field);
+ }
+ return NULL;
+}
+
+static char *mp3_get_id3(__a_unused unsigned char *map,
+ __a_unused size_t numbytes, int fd)
+{
+ int i;
+ struct id3_tag *id3_t;
+ char *title = NULL, *artist = NULL, *album = NULL, *year = NULL,
+ *comment = NULL, *result;
+ struct id3_file *id3_f = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY);
+
+ if (!id3_f)
+ goto no_tag;
+ id3_t = id3_file_tag(id3_f);
+ if (!id3_t)
+ goto no_tag;
+ for (i = 0; i < id3_t->nframes; i++) {
+ struct id3_frame *fr = id3_t->frames[i];
+ if (!strcmp(fr->id, "TIT2")) {
+ if (!title)
+ title = get_strings(fr);
+ continue;
+ }
+ if (!strcmp(fr->id, "TPE1")) {
+ if (!artist)
+ artist = get_strings(fr);
+ continue;
+ }
+ if (!strcmp(fr->id, "TALB")) {
+ if (!album)
+ album = get_strings(fr);
+ continue;
+ }
+ if (!strcmp(fr->id, "TDRC")) {
+ if (!year)
+ year = get_strings(fr);
+ continue;
+ }
+ if (!strcmp(fr->id, "COMM")) {
+ if (!comment)
+ comment = get_strings(fr);
+ continue;
+ }
+ }
+ id3_file_close(id3_f);
+ result = make_taginfo(title, artist, album, year, comment);
+ free(title);
+ free(artist);
+ free(album);
+ free(year);
+ free(comment);
+ return result;
+no_tag:
+ if (id3_f)
+ id3_file_close(id3_f);
+ return make_message("%s: (no id3 v1/v2 tag)\n%s:\n",
+ status_item_list[SI_TAGINFO1],
+ status_item_list[SI_TAGINFO2]);
+}
+
+#else /* HAVE_LIBID3TAG */
+
+/*
+ * Remove trailing whitespace from the end of a string
+ */
+static char *unpad(char *string)
+{
+ char *pos = string + strlen(string) - 1;
+ while (para_isspace(pos[0]))
+ (pos--)[0] = 0;
+ return string;
+}
+
+static char *mp3_get_id3(unsigned char *map, size_t numbytes, __a_unused int fd)
+{
+ char title[31], artist[31], album[31], year[5], comment[31];
+ off_t fpos;
+
+ if (numbytes < 128 || strncmp("TAG", (char *)map + numbytes - 128, 3)) {
+ PARA_DEBUG_LOG("no id3 v1 tag\n");
+ return make_message("%s: (no id3 v1 tag)\n%s:\n",
+ status_item_list[SI_TAGINFO1],
+ status_item_list[SI_TAGINFO2]);
+ }
+ fpos = numbytes - 125;
+ memcpy(title, map + fpos, 30);
+ fpos += 30;
+ title[30] = '\0';
+ memcpy(artist, map + fpos, 30);
+ fpos += 30;
+ artist[30] = '\0';
+ memcpy(album, map + fpos, 30);
+ fpos += 30;
+ album[30] = '\0';
+ memcpy(year, map + fpos, 4);
+ fpos += 4;
+ year[4] = '\0';
+ memcpy(comment, map + fpos, 30);
+ comment[30] = '\0';
+ unpad(title);
+ unpad(artist);
+ unpad(album);
+ unpad(year);
+ unpad(comment);
+ return make_taginfo(title, artist, album, year, comment);
+}
+#endif /* HAVE_LIBID3TAG */
static int header_frequency(struct mp3header *h)
{
+ header->padding;
}
-static void write_info_str(struct afh_info *afhi)
-{
- int v = mp3.id3_isvalid;
-
- snprintf(afhi->info_string, MMD_INFO_SIZE,
- "%s: %cbr, %s\n" /* audio file info*/
- "%s: %s, by %s\n" /* taginfo1 */
- "%s: A: %s, Y: %s, C: %s\n", /* taginfo 2*/
- status_item_list[SI_AUDIO_FILE_INFO], mp3.vbr? 'v' : 'c',
- header_mode(&mp3.header),
- status_item_list[SI_TAGINFO1], v && *mp3.id3.title?
- mp3.id3.title : "(title tag not set)",
- v && *mp3.id3.artist?
- mp3.id3.artist : "(artist tag not set)",
- status_item_list[SI_TAGINFO2],
- v && *mp3.id3.album?
- mp3.id3.album : "(album tag not set)",
- v && *mp3.id3.year? mp3.id3.year : "????",
- v && *mp3.id3.comment?
- mp3.id3.comment : "(comment tag not set)"
- );
-}
-
-/*
- * Remove trailing whitespace from the end of a string
- */
-static char *unpad(char *string)
-{
- char *pos = string + strlen(string) - 1;
- while (para_isspace(pos[0]))
- (pos--)[0] = 0;
- return string;
-}
-
static int compare_headers(struct mp3header *h1,struct mp3header *h2)
{
if ((*(uint*)h1) == (*(uint*)h2))
* Return the length of the next frame header or zero if the end of the file is
* reached.
*/
-static int mp3_seek_next_header(unsigned char *map, size_t numbytes, off_t *fpos)
+static int mp3_seek_next_header(unsigned char *map, size_t numbytes, off_t *fpos,
+ struct mp3header *result)
{
int k, l = 0, first_len;
struct mp3header h, h2;
}
if (k == MIN_CONSEC_GOOD_FRAMES) {
*fpos = valid_start;
- memcpy(&(mp3.header), &h2, sizeof(struct mp3header));
+ *result = h2;
return first_len;
}
}
return 0;
}
-static void mp3_get_id3(unsigned char *map, size_t numbytes)
-{
- off_t fpos;
-
- mp3.id3_isvalid = 0;
- mp3.id3.title[0] = '\0';
- mp3.id3.artist[0] = '\0';
- mp3.id3.album[0] = '\0';
- mp3.id3.comment[0] = '\0';
- mp3.id3.year[0] = '\0';
- if (numbytes < 128)
- return;
- fpos = numbytes - 128;
- if (strncmp("TAG", (char *) map + fpos, 3)) {
- PARA_DEBUG_LOG("no id3 tag\n");
- return;
- }
- fpos = numbytes - 125;
- memcpy(mp3.id3.title, map + fpos, 30);
- fpos += 30;
- mp3.id3.title[30] = '\0';
- memcpy(mp3.id3.artist, map + fpos, 30);
- fpos += 30;
- mp3.id3.artist[30] = '\0';
- memcpy(mp3.id3.album, map + fpos, 30);
- fpos += 30;
- mp3.id3.album[30] = '\0';
- memcpy(mp3.id3.year, map + fpos, 4);
- fpos += 4;
- mp3.id3.year[4] = '\0';
- memcpy(mp3.id3.comment, map + fpos, 30);
- mp3.id3.comment[30] = '\0';
- mp3.id3_isvalid = 1;
- unpad(mp3.id3.title);
- unpad(mp3.id3.artist);
- unpad(mp3.id3.album);
- unpad(mp3.id3.year);
- unpad(mp3.id3.comment);
-}
-
-static int find_valid_start(unsigned char *map, size_t numbytes, off_t *fpos)
+static int find_valid_start(unsigned char *map, size_t numbytes, off_t *fpos,
+ struct mp3header *header)
{
int frame_len;
- frame_len = get_header(map, numbytes, fpos, &mp3.header);
+ frame_len = get_header(map, numbytes, fpos, header);
if (frame_len < 0)
return frame_len;
if (!frame_len) {
- frame_len = mp3_seek_next_header(map, numbytes, fpos);
+ frame_len = mp3_seek_next_header(map, numbytes, fpos, header);
if (frame_len <= 0)
return frame_len;
} else
return frame_len;
}
-static int mp3_read_info(unsigned char *map, size_t numbytes,
+static int mp3_read_info(unsigned char *map, size_t numbytes, int fd,
struct afh_info *afhi)
{
- long fl_avg = 0, freq_avg = 0, br_avg = 0;
- int ret, len = 0, old_br = -1;
+ long freq_avg = 0, br_avg = 0;
+ int ret, len = 0, old_br = -1, vbr = 0;
struct timeval total_time = {0, 0};
unsigned chunk_table_size = 1000; /* gets increased on demand */
off_t fpos = 0;
+ struct mp3header header;
+ char *taginfo;
afhi->chunks_total = 0;
afhi->chunk_table = para_malloc(chunk_table_size * sizeof(size_t));
- mp3_get_id3(map, numbytes);
- mp3.vbr = 0;
+ taginfo = mp3_get_id3(map, numbytes, fd);
while (1) {
unsigned long freq, br, fl;
struct timeval tmp, cct; /* current chunk time */
fpos += len;
- len = find_valid_start(map, numbytes, &fpos);
+ len = find_valid_start(map, numbytes, &fpos, &header);
if (len <= 0)
break;
- ret = header_frequency(&mp3.header);
+ ret = header_frequency(&header);
if (ret < 0)
continue;
freq = ret;
- ret = header_bitrate(&mp3.header);
+ ret = header_bitrate(&header);
if (ret < 0)
continue;
br = ret;
- ret = frame_length(&mp3.header);
+ ret = frame_length(&header);
if (ret < 0)
continue;
fl = ret;
freq_avg = freq;
br_avg = br;
old_br = br;
- fl_avg = fl;
continue;
}
freq_avg += ((long)freq - freq_avg) / ((long)afhi->chunks_total + 1);
- fl_avg += ((long)fl - fl_avg) / ((long)afhi->chunks_total + 1);
br_avg += ((long)br - br_avg) / ((long)afhi->chunks_total + 1);
if (old_br != br)
- mp3.vbr = 1;
+ vbr = 1;
old_br = br;
}
ret = -E_MP3_INFO;
afhi->chunk_table[afhi->chunks_total] = numbytes - 1;
afhi->bitrate = br_avg;
afhi->frequency = freq_avg;
- afhi->channels = header_channels(&mp3.header);
+ afhi->channels = header_channels(&header);
afhi->seconds_total = (tv2ms(&total_time) + 500) / 1000;
tv_divide(afhi->chunks_total, &total_time, &afhi->chunk_tv);
PARA_DEBUG_LOG("%lu chunks, each %lums\n", afhi->chunks_total,
tv2ms(&afhi->chunk_tv));
tv_scale(3, &afhi->chunk_tv, &afhi->eof_tv);
PARA_DEBUG_LOG("eof timeout: %lu\n", tv2ms(&afhi->eof_tv));
+ afhi->info_string = make_message("%s: %cbr, %s\n%s",
+ status_item_list[SI_AUDIO_FILE_INFO], vbr? 'v' : 'c',
+ header_mode(&header), taginfo);
+ free(taginfo);
return 1;
err_out:
+ free(taginfo);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
free(afhi->chunk_table);
return ret;
/*
* Read mp3 information from audio file
*/
-int mp3_get_file_info(char *map, size_t numbytes,
+static int mp3_get_file_info(char *map, size_t numbytes, int fd,
struct afh_info *afhi)
{
int ret;
- ret = mp3_read_info((unsigned char *)map, numbytes, afhi);
+ ret = mp3_read_info((unsigned char *)map, numbytes, fd, afhi);
if (ret < 0)
return ret;
- write_info_str(afhi);
if (afhi->seconds_total < 2 || !afhi->chunks_total)
return -E_MP3_INFO;
return 1;
goto err2;
PARA_DEBUG_LOG("channels: %i, rate: %li\n", vi.channels, vi.rate);
ogg_stream_packetin(stream_out, &packet);
-
ret = ogg_sync_pageout(sync_in, &page);
if (ret <= 0) {
ret = -E_SYNC_PAGEOUT;
return num_chunks;
}
+static void ogg_write_info_string(OggVorbis_File *vf, struct afh_info *afhi)
+{
+ char *taginfo;
+ vorbis_comment *vc = ov_comment(vf,-1);
+
+ if (vc) {
+ char *artist, *title, *album, *year, *comment;
+ artist = vorbis_comment_query(vc, "artist", 0);
+ title = vorbis_comment_query(vc, "title", 0);
+ album = vorbis_comment_query(vc, "album", 0);
+ year = vorbis_comment_query(vc, "year", 0);
+ comment = vorbis_comment_query(vc, "comment", 0);
+ taginfo = make_taginfo(title, artist, album, year, comment);
+ } else
+ taginfo = make_message("%s: (no vorbis comments found)\n%s:\n",
+ status_item_list[SI_TAGINFO1],
+ status_item_list[SI_TAGINFO2]);
+ afhi->info_string = make_message("%s:\n%s",
+ status_item_list[SI_AUDIO_FILE_INFO], taginfo);
+ free(taginfo);
+}
+
/*
* Init oggvorbis file and write some tech data to given pointers.
*/
-static int ogg_get_file_info(char *map, size_t numbytes,
+static int ogg_get_file_info(char *map, size_t numbytes, __a_unused int fd,
struct afh_info *afhi)
{
int ret;
afhi->bitrate = ov_bitrate(&of, 0) / 1000;
afhi->channels = vi->channels;
afhi->chunks_total = ogg_compute_chunk_table(&of, afhi, afhi->seconds_total);
- afhi->info_string[0] = '\0';
- sprintf(afhi->info_string, "%s:\n%s:\n%s:\n",
- status_item_list[SI_AUDIO_FILE_INFO],
- status_item_list[SI_TAGINFO1],
- status_item_list[SI_TAGINFO2]
- );
afhi->chunk_tv.tv_sec = 0;
afhi->chunk_tv.tv_usec = 250 * 1000;
tv_scale(10 / afhi->channels, &afhi->chunk_tv, &afhi->eof_tv);
+ ogg_write_info_string(&of, afhi);
ret = 1;
err:
ov_clear(&of); /* keeps the file open */
static void vss_eof(struct vss_task *vsst)
{
- char *tmp;
-
mmd->stream_start = *now;
if (!vsst->map)
return;
mmd->afd.afhi.chunk_tv.tv_usec = 0;
free(mmd->afd.afhi.chunk_table);
mmd->afd.afhi.chunk_table = NULL;
- tmp = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_AUDIO_FILE_INFO],
+ free(mmd->afd.afhi.info_string);
+ mmd->afd.afhi.info_string = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_AUDIO_FILE_INFO],
status_item_list[SI_TAGINFO1], status_item_list[SI_TAGINFO2]);
- strncpy(mmd->afd.afhi.info_string, tmp, sizeof(mmd->afd.afhi.info_string));
- mmd->afd.afhi.info_string[sizeof(mmd->afd.afhi.info_string) - 1] = '\0';
make_empty_status_items(mmd->afd.verbose_ls_output);
- free(tmp);
mmd->mtime = 0;
mmd->size = 0;
mmd->events++;