2 * Copyright (C) 2009-2013 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file wma_afh.c The audio format handler for WMA files. */
11 #include <sys/types.h>
22 #include "portable_io.h"
26 #define FOR_EACH_FRAME(_f, _buf, _size, _ba) for (_f = (_buf); \
27 _f + (_ba) + WMA_FRAME_SKIP < (_buf) + (_size); \
28 _f += (_ba) + WMA_FRAME_SKIP)
31 * Must be called on a frame boundary, e.g. start + header_len.
32 * \return Frame count, superframe count via *num_superframes.
34 static int count_frames(const char *buf, int buf_size, int block_align,
37 int fc = 0, sfc = 0; /* frame count, superframe count */
41 FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, block_align) {
42 fc += p[WMA_FRAME_SKIP] & 0x0f;
45 PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc);
47 *num_superframes = sfc;
52 * put_utf8() and get_str16() below are based on macros in libavutil/common.h
53 * of the mplayer source code, copyright (c) 2006 Michael Niedermayer
58 * Convert a 32-bit Unicode character to its UTF-8 encoded form.
60 * Writes up to 4 bytes for values in the valid UTF-8 range and up to 7 bytes
61 * in the general case, depending on the length of the converted Unicode
64 * \param result Where the converted UTF-8 bytes are written.
66 static int put_utf8(uint32_t val, char *result)
76 bytes = (wma_log2(in) + 4) / 5;
77 shift = (bytes - 1) * 6;
78 *out++ = (256 - (256 >> bytes)) | (in >> shift);
81 *out++ = 0x80 | ((in >> shift) & 0x3f);
86 static char *get_str16(const char *in, int len)
89 int out_size = 0, out_len = 0;
95 if (out_len + 7 + 1 >= out_size) {
96 out_size = 2 * out_size + 50;
97 out = para_realloc(out, out_size);
101 out_len += put_utf8(x, out + out_len);
110 static const char comment_header[] = {
111 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
112 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
115 static const char extended_content_header[] = {
116 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11,
117 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50
120 static const char year_tag_header[] = { /* WM/Year */
121 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x59, 0x00,
122 0x65, 0x00, 0x61, 0x00, 0x72, 0x00
125 static const char album_tag_header[] = { /* WM/AlbumTitle */
126 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x41, 0x00,
127 0x6c, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6d, 0x00,
128 0x54, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00,
132 static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
134 const char *p, *end = buf + buf_size, *q;
135 uint16_t len1, len2, len3, len4, len5;
137 p = search_pattern(comment_header, sizeof(comment_header),
139 if (!p || p + 34 >= end) {
140 PARA_NOTICE_LOG("comment header not found\n");
156 ti->title = get_str16(p, len1);
160 ti->artist = get_str16(p, len2);
161 p += len2 + len3 + len4;
164 ti->comment = get_str16(p, len5);
166 p = search_pattern(extended_content_header, sizeof(extended_content_header),
169 PARA_NOTICE_LOG("extended content header not found\n");
172 q = search_pattern(year_tag_header, sizeof(year_tag_header),
175 const char *r = q + sizeof(year_tag_header) + 6;
177 ti->year = get_str16(r, end - r);
179 q = search_pattern(album_tag_header, sizeof(album_tag_header),
182 const char *r = q + sizeof(album_tag_header) + 6;
184 ti->album = get_str16(r, end - r);
188 static void set_chunk_tv(int frames_per_chunk, int frequency,
189 struct timeval *result)
191 uint64_t x = (uint64_t)frames_per_chunk * 2048 * 1000 * 1000
194 result->tv_sec = x / 1000 / 1000;
195 result->tv_usec = x % (1000 * 1000);
196 PARA_INFO_LOG("chunk time: %lums\n", tv2ms(result));
199 /* Must be called on a frame boundary. */
200 static int wma_make_chunk_table(char *buf, size_t buf_size, int block_align,
201 struct afh_info *afhi)
203 const uint8_t *f, *start = (uint8_t *)buf;
204 int j, frames_per_chunk;
205 size_t ct_size = 250;
206 int ret, count = 0, num_frames, num_superframes;
208 afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
209 afhi->chunk_table[0] = 0;
210 afhi->chunk_table[1] = afhi->header_len;
212 num_frames = count_frames(buf, buf_size, block_align,
215 if (num_frames == 0 || num_superframes == 0)
217 afhi->seconds_total = num_frames * 2048 /* FIXME */
219 frames_per_chunk = num_frames / num_superframes / 2;
220 PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk);
222 FOR_EACH_FRAME(f, start, buf_size, block_align) {
223 count += f[WMA_FRAME_SKIP] & 0x0f;
224 while (count > j * frames_per_chunk) {
228 afhi->chunk_table = para_realloc(
230 ct_size * sizeof(uint32_t));
232 afhi->chunk_table[j] = f - start + afhi->header_len + block_align + WMA_FRAME_SKIP;
235 afhi->chunks_total = j;
236 set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
239 free(afhi->chunk_table);
243 static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd,
244 struct afh_info *afhi)
247 struct asf_header_info ahi;
249 ret = read_asf_header(map, numbytes, &ahi);
254 afhi->bitrate = ahi.bit_rate / 1000;
255 if (ahi.sample_rate == 0)
257 afhi->frequency = ahi.sample_rate;
258 afhi->channels = ahi.channels;
259 afhi->header_len = ahi.header_len;
260 wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len,
261 ahi.block_align, afhi);
262 read_asf_tags(map, ahi.header_len, &afhi->tags);
266 static const char* wma_suffixes[] = {"wma", NULL};
269 * The init function of the wma audio format handler.
271 * \param afh Pointer to the struct to initialize.
273 void wma_afh_init(struct audio_format_handler *afh)
275 afh->get_file_info = wma_get_file_info;
276 afh->suffixes = wma_suffixes;