/*
- * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2009-2010 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
_f + (_ba) + WMA_FRAME_SKIP < (_buf) + (_size); \
_f += (_ba) + WMA_FRAME_SKIP)
+/*
+ * Must be called on a frame boundary, e.g. start + header_len.
+ * \return Frame count, superframe count via *num_superframes.
+ */
static int count_frames(const char *buf, int buf_size, int block_align,
- long unsigned *num_superframes)
+ int *num_superframes)
{
- int count = 0, step = block_align + WMA_FRAME_SKIP;
- const uint8_t *p = (uint8_t *)buf + WMA_FRAME_SKIP;
+ int fc = 0, sfc = 0; /* frame count, superframe count */
+ const uint8_t *p;
- if (buf_size <= WMA_FRAME_SKIP) {
- if (num_superframes)
- *num_superframes = 0;
- return 0;
- }
- count = 0;
- step = block_align + WMA_FRAME_SKIP;
- p = (uint8_t *)buf + WMA_FRAME_SKIP;
-
- FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align)
- count += p[WMA_FRAME_SKIP] & 0x0f;
- PARA_DEBUG_LOG("%d frames\n", count);
- if (num_superframes) {
- *num_superframes = buf_size / step;
- PARA_DEBUG_LOG("%lu superframes\n", *num_superframes);
+ FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align) {
+ fc += p[WMA_FRAME_SKIP] & 0x0f;
+ sfc++;
}
- return count;
+ PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc);
+ if (num_superframes)
+ *num_superframes = sfc;
+ return fc;
}
/*
return out - result;
}
-static void get_str16(const char *in, int len, char *out, int out_size)
+static char *get_str16(const char *in, int len)
{
const char *p = in;
- char *q = out;
+ int out_size = 0, out_len = 0;
+ char *out = NULL;
len /= 2;
- while (len-- && q + 7 + 1 < out + out_size) {
- uint32_t x = read_u16(p);
+ while (len--) {
+ uint32_t x;
+ if (out_len + 7 + 1 >= out_size) {
+ out_size = 2 * out_size + 50;
+ out = para_realloc(out, out_size);
+ }
+ x = read_u16(p);
p += 2;
- q += put_utf8(x, q);
+ out_len += put_utf8(x, out + out_len);
if (x == 0)
- return;
+ return out;
}
- *q = '\0';
+ if (out)
+ out[out_len] = '\0';
+ return out;
}
static const char comment_header[] = {
static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
{
- const char *p, *end = buf + buf_size;
- char tag[255];
+ const char *p, *end = buf + buf_size, *q;
+ uint16_t len1, len2, len3, len4, len5;
p = search_pattern(comment_header, sizeof(comment_header),
buf, buf_size);
- if (p) {
- int len1, len2, len3, len4, len5;
- p += 24;
- len1 = read_u16(p);
- p += 2;
- len2 = read_u16(p);
- p += 2;
- len3 = read_u16(p);
- p += 2;
- len4 = read_u16(p);
- p += 2;
- len5 = read_u16(p);
- p += 2;
- /* TODO: Check len values */
- get_str16(p, len1, tag, sizeof(tag));
- ti->title = para_strdup(tag);
- PARA_INFO_LOG("title: %s\n", tag);
- get_str16(p + len1, len2, tag, sizeof(tag));
- ti->artist = para_strdup(tag);
- PARA_INFO_LOG("artist: %s\n", tag);
- get_str16(p + len1 + len2 + len3 + len4, len5, tag, sizeof(tag));
- ti->comment = para_strdup(tag);
- PARA_INFO_LOG("comment: %s\n", tag);
- } else
+ if (!p || p + 34 >= end) {
PARA_NOTICE_LOG("comment header not found\n");
+ goto next;
+ }
+ p += 24;
+ len1 = read_u16(p);
+ p += 2;
+ len2 = read_u16(p);
+ p += 2;
+ len3 = read_u16(p);
+ p += 2;
+ len4 = read_u16(p);
+ p += 2;
+ len5 = read_u16(p);
+ p += 2;
+ if (p + len1 >= end)
+ goto next;
+ ti->title = get_str16(p, len1);
+ p += len1;
+ if (p + len2 >= end)
+ goto next;
+ ti->artist = get_str16(p, len2);
+ p += len2 + len3 + len4;
+ if (p + len5 >= end)
+ goto next;
+ ti->comment = get_str16(p, len5);
+next:
p = search_pattern(extended_content_header, sizeof(extended_content_header),
buf, buf_size);
- if (p) {
- const char *q;
-
- q = search_pattern(year_tag_header, sizeof(year_tag_header),
- p, end - p);
- if (q) {
- const char *r = q + sizeof(year_tag_header) + 6;
- get_str16(r, end - r, tag, sizeof(tag));
- ti->year = para_strdup(tag);
- PARA_INFO_LOG("year: %s\n", tag);
- }
- q = search_pattern(album_tag_header, sizeof(album_tag_header),
- p, end - p);
- if (q) {
- const char *r = q + sizeof(album_tag_header) + 6;
- get_str16(r, end - r, tag, sizeof(tag));
- ti->album = para_strdup(tag);
- PARA_INFO_LOG("album: %s\n", tag);
- }
- return;
- } else
+ if (!p) {
PARA_NOTICE_LOG("extended content header not found\n");
+ return;
+ }
+ q = search_pattern(year_tag_header, sizeof(year_tag_header),
+ p, end - p);
+ if (q) {
+ const char *r = q + sizeof(year_tag_header) + 6;
+ if (r < end)
+ ti->year = get_str16(r, end - r);
+ }
+ q = search_pattern(album_tag_header, sizeof(album_tag_header),
+ p, end - p);
+ if (q) {
+ const char *r = q + sizeof(album_tag_header) + 6;
+ if (r < end)
+ ti->album = get_str16(r, end - r);
+ }
+}
+static void set_chunk_tv(int frames_per_chunk, int frequency,
+ struct timeval *result)
+{
+ uint64_t x = (uint64_t)frames_per_chunk * 2048 * 1000 * 1000
+ / frequency;
+
+ result->tv_sec = x / 1000 / 1000;
+ result->tv_usec = x % (1000 * 1000);
+ PARA_INFO_LOG("chunk time: %lums\n", tv2ms(result));
}
+/* Must be called on a frame boundary. */
static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
struct afh_info *afhi)
{
const uint8_t *f, *start = (uint8_t *)buf;
- int i, j, frames_per_chunk, chunk_time;
+ int j, frames_per_chunk;
size_t ct_size = 250;
+ int ret, count = 0, num_frames, num_superframes;
+
afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
afhi->chunk_table[0] = 0;
afhi->chunk_table[1] = afhi->header_len;
-
- int count = 0, num_frames;
-
num_frames = count_frames(buf, buf_size, block_align,
- &afhi->chunks_total);
- PARA_ERROR_LOG("%d frames\n", num_frames);
+ &num_superframes);
+ ret = -E_NO_WMA;
+ if (num_frames == 0 || num_superframes == 0)
+ goto fail;
afhi->seconds_total = num_frames * 2048 /* FIXME */
/ afhi->frequency;
- frames_per_chunk = num_frames / afhi->chunks_total;
- i = 0;
+ frames_per_chunk = num_frames / num_superframes / 2;
+ PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk);
j = 1;
FOR_EACH_FRAME(f, start, buf_size, block_align) {
count += f[WMA_FRAME_SKIP] & 0x0f;
afhi->chunk_table,
ct_size * sizeof(uint32_t));
}
- PARA_DEBUG_LOG("ct[%d]: %zu\n", j, f - start);
- afhi->chunk_table[j] = f - start;
+ afhi->chunk_table[j] = f - start + afhi->header_len + block_align + WMA_FRAME_SKIP;
}
}
afhi->chunks_total = j;
- chunk_time = num_frames * 1000 / afhi->frequency * 2048
- / afhi->chunks_total;
- PARA_ERROR_LOG("ct: %d\n", chunk_time);
- afhi->chunk_tv.tv_sec = chunk_time / 1000;
- afhi->chunk_tv.tv_usec = (chunk_time % 1000) * 1000;
- //set_chunk_tv(num_frames, j, afhi->frequency, &afhi->chunk_tv);
+ set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
return 1;
+fail:
+ free(afhi->chunk_table);
+ return ret;
}
static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd,
ret = read_asf_header(map, numbytes, &ahi);
if (ret < 0)
return ret;
+ if (ret == 0)
+ return -E_NO_WMA;
afhi->bitrate = ahi.bit_rate / 1000;
+ if (ahi.sample_rate == 0)
+ return -E_NO_WMA;
afhi->frequency = ahi.sample_rate;
afhi->channels = ahi.channels;
afhi->header_len = ahi.header_len;
afhi->header_offset = 0;
- read_asf_tags(map, ahi.header_len, &afhi->tags);
wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len,
ahi.block_align, afhi);
+ read_asf_tags(map, ahi.header_len, &afhi->tags);
return 0;
}