X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=wma_afh.c;h=d9a834836a5a9bc79458e1fc32619b13980bb538;hp=2d463900e7be81d148cd3ab6dce554d61ce3044a;hb=ffb2eaa90429f6d5c3d369509efcdf91c5463dad;hpb=620c2a3eb4c3395c5be73f663435740ecd878775 diff --git a/wma_afh.c b/wma_afh.c index 2d463900..d9a83483 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Andre Noll + * Copyright (C) 2009-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -27,30 +27,25 @@ _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; } /* @@ -88,20 +83,28 @@ static int put_utf8(uint32_t val, char *result) 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[] = { @@ -128,86 +131,97 @@ static const char album_tag_header[] = { /* WM/AlbumTitle */ 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 count = 0, num_frames; + 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; num_frames = count_frames(buf, buf_size, block_align, - &afhi->chunks_total); - PARA_INFO_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; - while (count > j * frames_per_chunk && f > start) { + while (count > j * frames_per_chunk) { j++; if (j >= ct_size) { ct_size *= 2; @@ -215,18 +229,15 @@ static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align, 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_INFO_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, @@ -238,14 +249,18 @@ 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; }