2 * Copyright (C) 2009 Andre Noll <maan@tuebingen.mpg.de>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file wma_afh.c The audio format handler for WMA files. */
16 #include "portable_io.h"
21 #define FOR_EACH_FRAME(_f, _buf, _size, _ps) for (_f = (_buf); \
22 _f + (_ps) < (_buf) + (_size); \
26 * Must be called on a frame boundary, e.g. start + header_len.
27 * \return Frame count, superframe count via *num_superframes.
29 static int count_frames(const char *buf, int buf_size, uint32_t packet_size,
32 int fc = 0, sfc = 0; /* frame count, superframe count */
36 FOR_EACH_FRAME(p, (uint8_t *)buf, buf_size, packet_size) {
37 fc += p[WMA_FRAME_SKIP] & 0x0f;
40 PARA_INFO_LOG("%d frames, %d superframes\n", fc, sfc);
41 *num_superframes = sfc;
46 * put_utf8() and get_str16() below are based on macros in libavutil/common.h
47 * of the mplayer source code, copyright (c) 2006 Michael Niedermayer
52 * Convert a 32-bit Unicode character to its UTF-8 encoded form.
54 * Writes up to 4 bytes for values in the valid UTF-8 range and up to 7 bytes
55 * in the general case, depending on the length of the converted Unicode
58 * \param result Where the converted UTF-8 bytes are written.
60 static int put_utf8(uint32_t val, char *result)
70 bytes = DIV_ROUND_UP(wma_log2(in), 5);
71 shift = (bytes - 1) * 6;
72 *out++ = (256 - (256 >> bytes)) | (in >> shift);
75 *out++ = 0x80 | ((in >> shift) & 0x3f);
80 static char *get_str16(const char *in, int len)
83 int out_size = 0, out_len = 0;
89 if (out_len + 7 + 1 >= out_size) {
90 out_size = 2 * out_size + 50;
91 out = para_realloc(out, out_size);
95 out_len += put_utf8(x, out + out_len);
104 static const char content_description_header[] = {
105 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
106 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
109 static const char extended_content_header[] = {
110 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11,
111 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50
114 static const char year_tag_header[] = { /* WM/Year */
115 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x59, 0x00,
116 0x65, 0x00, 0x61, 0x00, 0x72, 0x00
119 static const char album_tag_header[] = { /* WM/AlbumTitle */
120 0x57, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x41, 0x00,
121 0x6c, 0x00, 0x62, 0x00, 0x75, 0x00, 0x6d, 0x00,
122 0x54, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00,
126 static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
128 const char *p, *end = buf + buf_size, *q;
129 uint16_t len1, len2, len3, len4;
131 p = search_pattern(content_description_header,
132 sizeof(content_description_header), buf, buf_size);
133 if (!p || p + 34 >= end) {
134 PARA_NOTICE_LOG("content description header not found\n");
146 /* ignore length of the rating information */
150 ti->title = get_str16(p, len1);
154 ti->artist = get_str16(p, len2);
158 ti->comment = get_str16(p, len4);
160 p = search_pattern(extended_content_header, sizeof(extended_content_header),
163 PARA_NOTICE_LOG("extended content header not found\n");
166 q = search_pattern(year_tag_header, sizeof(year_tag_header),
169 const char *r = q + sizeof(year_tag_header) + 6;
171 ti->year = get_str16(r, end - r);
173 q = search_pattern(album_tag_header, sizeof(album_tag_header),
176 const char *r = q + sizeof(album_tag_header) + 6;
178 ti->album = get_str16(r, end - r);
182 static void set_chunk_tv(int frames_per_chunk, int frequency,
183 struct timeval *result)
185 uint64_t x = (uint64_t)frames_per_chunk * 2048 * 1000 * 1000
188 result->tv_sec = x / 1000 / 1000;
189 result->tv_usec = x % (1000 * 1000);
190 PARA_INFO_LOG("chunk time: %lums\n", tv2ms(result));
193 /* Must be called on a frame boundary. */
194 static int wma_make_chunk_table(char *buf, size_t buf_size, uint32_t packet_size,
195 struct afh_info *afhi)
197 const uint8_t *f, *start = (uint8_t *)buf;
198 int j, frames_per_chunk;
199 size_t ct_size = 250;
200 int ret, count = 0, num_frames, num_superframes;
202 afhi->chunk_table = para_malloc(ct_size * sizeof(uint32_t));
203 afhi->chunk_table[0] = 0;
204 afhi->chunk_table[1] = afhi->header_len;
206 num_frames = count_frames(buf, buf_size, packet_size,
209 if (num_frames == 0 || num_superframes == 0)
211 afhi->seconds_total = num_frames * 2048 /* FIXME */
213 frames_per_chunk = num_frames / num_superframes / 2;
214 PARA_INFO_LOG("%d frames per chunk\n", frames_per_chunk);
216 FOR_EACH_FRAME(f, start, buf_size, packet_size) {
217 count += f[WMA_FRAME_SKIP] & 0x0f;
218 while (count > j * frames_per_chunk) {
222 afhi->chunk_table = para_realloc(
224 ct_size * sizeof(uint32_t));
226 afhi->chunk_table[j] = f - start + afhi->header_len
230 afhi->chunks_total = j;
231 set_max_chunk_size(afhi);
232 set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
235 free(afhi->chunk_table);
239 static int wma_get_file_info(char *map, size_t numbytes, __a_unused int fd,
240 struct afh_info *afhi)
243 struct asf_header_info ahi;
245 ret = read_asf_header(map, numbytes, &ahi);
250 afhi->bitrate = ahi.bit_rate / 1000;
251 if (ahi.sample_rate == 0)
253 afhi->frequency = ahi.sample_rate;
254 afhi->channels = ahi.channels;
255 afhi->header_len = ahi.header_len;
257 afhi->techinfo = make_message("%s%s%s%s%s",
258 ahi.use_exp_vlc? "exp vlc" : "",
259 (ahi.use_bit_reservoir && ahi.use_exp_vlc)? ", " : "",
260 ahi.use_bit_reservoir? "bit reservoir" : "",
261 (ahi.use_variable_block_len &&
262 (ahi.use_exp_vlc || ahi.use_bit_reservoir)? ", " : ""),
263 ahi.use_variable_block_len? "vbl" : ""
265 wma_make_chunk_table(map + ahi.header_len, numbytes - ahi.header_len,
266 ahi.packet_size, afhi);
267 read_asf_tags(map, ahi.header_len, &afhi->tags);
276 struct tag_object_nums {
277 int content_descr_obj_num;
278 int extended_content_descr_obj_num;
281 struct afs_top_level_header_object {
283 uint32_t num_objects;
284 uint8_t reserved1, reserved2;
285 struct asf_object *objects;
288 #define CHECK_HEADER(_p, _h) (memcmp((_p), (_h), sizeof((_h))) == 0)
290 static int read_asf_objects(const char *src, size_t size, uint32_t num_objects,
291 struct asf_object *objs, struct tag_object_nums *ton)
296 for (i = 0, p = src; i < num_objects; p += objs[i++].size) {
297 if (p + 24 > src + size)
299 objs[i].ptr = (char *)p;
300 objs[i].size = read_u64(p + 16);
301 if (p + objs[i].size > src + size)
304 if (CHECK_HEADER(p, content_description_header))
305 ton->content_descr_obj_num = i;
306 else if (CHECK_HEADER(p, extended_content_header))
307 ton->extended_content_descr_obj_num = i;
312 static const char top_level_header_object_guid[] = {
313 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11,
314 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c
317 static int convert_utf8_to_utf16(char *src, char **dst)
320 size_t sz, inbytes, outbytes, inbytesleft, outbytesleft;
321 char *inbuf, *outbuf;
325 *dst = para_calloc(2);
329 * Without specifying LE (little endian), iconv includes a byte order
330 * mark (e.g. 0xFFFE) at the beginning.
332 cd = iconv_open("UTF-16LE", "UTF-8");
333 if (cd == (iconv_t)-1) {
335 return -ERRNO_TO_PARA_ERROR(errno);
338 /* even though src is in UTF-8, strlen() should DTRT */
339 inbytes = inbytesleft = strlen(src);
340 outbytes = outbytesleft = 4 * inbytes + 2; /* hope that's enough */
341 *dst = outbuf = para_malloc(outbytes);
342 sz = iconv(cd, ICONV_CAST &inbuf, &inbytesleft, &outbuf, &outbytesleft);
343 if (sz == (size_t)-1) {
344 ret = -ERRNO_TO_PARA_ERROR(errno);
349 assert(outbytes >= outbytesleft);
350 assert(outbytes - outbytesleft < INT_MAX - 2);
351 ret = outbytes - outbytesleft;
352 outbuf = para_realloc(*dst, ret + 2);
353 outbuf[ret] = outbuf[ret + 1] = '\0';
356 PARA_INFO_LOG("converted %s to %d UTF-16 bytes\n", src, ret);
358 if (iconv_close(cd) < 0)
359 PARA_WARNING_LOG("iconv_close: %s\n", strerror(errno));
363 /* The content description object contains artist, title, comment. */
364 static int make_cdo(struct taginfo *tags, const struct asf_object *cdo,
365 struct asf_object *result)
367 const char *cr, *rating; /* orig data */
368 uint16_t orig_title_bytes, orig_artist_bytes, orig_cr_bytes,
369 orig_comment_bytes, orig_rating_bytes;
370 /* pointers to new UTF-16 tags */
371 char *artist = NULL, *title = NULL, *comment = NULL;
372 /* number of bytes in UTF-16 for the new tags */
373 int artist_bytes, title_bytes, comment_bytes, ret;
374 char *p, null[2] = "\0\0";
378 ret = convert_utf8_to_utf16(tags->artist, &artist);
382 ret = convert_utf8_to_utf16(tags->title, &title);
386 ret = convert_utf8_to_utf16(tags->comment, &comment);
393 * Sizes of the five fields (stored as 16-bit numbers) are
394 * located after the header (16 bytes) and the cdo size (8
397 orig_title_bytes = read_u16(cdo->ptr + 24);
398 orig_artist_bytes = read_u16(cdo->ptr + 26);
399 orig_cr_bytes = read_u16(cdo->ptr + 28);
400 orig_comment_bytes = read_u16(cdo->ptr + 30);
401 orig_rating_bytes = read_u16(cdo->ptr + 32);
402 cr = cdo->ptr + 34 + orig_title_bytes + orig_artist_bytes;
403 rating = cr + orig_cr_bytes + orig_comment_bytes;
405 orig_title_bytes = 2;
406 orig_artist_bytes = 2;
408 orig_comment_bytes = 2;
409 orig_rating_bytes = 2;
414 /* compute size of result cdo */
415 result->size = 16 + 8 + 5 * 2 + title_bytes + artist_bytes
416 + orig_cr_bytes + comment_bytes + orig_rating_bytes;
417 PARA_DEBUG_LOG("cdo is %zu bytes\n", (size_t)result->size);
418 p = result->ptr = para_malloc(result->size);
419 memcpy(p, content_description_header, 16);
421 write_u64(p, result->size);
423 write_u16(p, title_bytes);
425 write_u16(p, artist_bytes);
427 write_u16(p, orig_cr_bytes);
429 write_u16(p, comment_bytes);
431 write_u16(p, orig_rating_bytes);
433 memcpy(p, title, title_bytes);
435 memcpy(p, artist, artist_bytes);
437 memcpy(p, cr, orig_cr_bytes);
439 memcpy(p, comment, comment_bytes);
441 memcpy(p, rating, orig_rating_bytes);
442 p += orig_rating_bytes;
443 assert(p - result->ptr == result->size);
452 /* The extended content description object contains album and year. */
453 static int make_ecdo(struct taginfo *tags, struct asf_object *result)
456 char *p, *album = NULL, *year = NULL, null[2] = "\0\0";
457 int album_bytes, year_bytes;
461 ret = convert_utf8_to_utf16(tags->album, &album);
465 ret = convert_utf8_to_utf16(tags->year, &year);
469 result->size = 16 + 8 + 2; /* GUID, size, count */
470 /* name_length + name + null + data type + val length + val */
471 result->size += 2 + sizeof(album_tag_header) + 2 + 2 + 2 + album_bytes;
472 result->size += 2 + sizeof(year_tag_header) + 2 + 2 + 2 + year_bytes;
474 p = result->ptr = para_malloc(result->size);
475 memcpy(p, extended_content_header, 16);
477 write_u64(p, result->size);
479 write_u16(p, 2); /* count */
483 write_u16(p, sizeof(album_tag_header) + 2);
485 memcpy(p, album_tag_header, sizeof(album_tag_header));
486 p += sizeof(album_tag_header);
489 write_u16(p, 0); /* data type (UTF-16) */
491 write_u16(p, album_bytes);
493 memcpy(p, album, album_bytes);
497 write_u16(p, sizeof(year_tag_header));
499 memcpy(p, year_tag_header, sizeof(year_tag_header));
500 p += sizeof(year_tag_header);
503 write_u16(p, 0); /* data type (UTF-16) */
505 write_u16(p, year_bytes);
507 memcpy(p, year, year_bytes);
509 assert(p - result->ptr == result->size);
517 static int write_output_file(int fd, const char *map, size_t mapsize,
518 struct afs_top_level_header_object *top, struct tag_object_nums *ton,
519 struct asf_object *cdo, struct asf_object *ecdo)
522 uint64_t sz; /* of the new header object */
523 uint32_t num_objects;
526 sz = 16 + 8 + 4 + 1 + 1; /* top-level header object */
527 for (i = 0; i < top->num_objects; i++) {
528 if (i == ton->content_descr_obj_num)
530 if (i == ton->extended_content_descr_obj_num)
532 sz += top->objects[i].size;
536 num_objects = top->num_objects;
537 if (ton->content_descr_obj_num < 0)
539 if (ton->extended_content_descr_obj_num < 0)
541 ret = xwrite(fd, top_level_header_object_guid, 16);
545 ret = xwrite(fd, tmp, 8);
548 write_u32(tmp, num_objects);
549 ret = xwrite(fd, tmp, 4);
552 write_u8(tmp, top->reserved1);
553 ret = xwrite(fd, tmp, 1);
556 write_u8(tmp, top->reserved2);
557 ret = xwrite(fd, tmp, 1);
561 * Write cto and ecto as objects 0 and 1 if they did not exist in the
564 if (ton->content_descr_obj_num < 0) {
565 ret = xwrite(fd, cdo->ptr, cdo->size);
569 if (ton->extended_content_descr_obj_num < 0) {
570 ret = xwrite(fd, ecdo->ptr, ecdo->size);
575 for (i = 0; i < top->num_objects; i++) {
576 char *buf = top->objects[i].ptr;
577 sz = top->objects[i].size;
578 if (i == ton->content_descr_obj_num) {
581 } else if (i == ton->extended_content_descr_obj_num) {
585 ret = xwrite(fd, buf, sz);
589 ret = xwrite(fd, map + top->size, mapsize - top->size);
594 static int wma_rewrite_tags(const char *map, size_t mapsize,
595 struct taginfo *tags, int fd,
596 __a_unused const char *filename)
598 struct afs_top_level_header_object top;
599 struct tag_object_nums ton = {-1, -1};
601 /* (extended) content description object */
602 struct asf_object cdo = {.ptr = NULL}, ecdo = {.ptr = NULL};
605 /* guid + size + num_objects + 2 * reserved */
606 if (mapsize < 16 + 8 + 4 + 1 + 1)
608 if (memcmp(map, top_level_header_object_guid, 16))
611 top.size = read_u64(p);
612 PARA_INFO_LOG("header_size: %lu\n", (long unsigned)top.size);
613 if (top.size >= mapsize)
616 top.num_objects = read_u32(p);
617 PARA_NOTICE_LOG("%u header objects\n", top.num_objects);
618 if (top.num_objects > top.size / 24)
621 top.reserved1 = read_u8(p);
623 top.reserved2 = read_u8(p);
624 if (top.reserved2 != 2)
626 p++; /* objects start at p */
627 top.objects = para_malloc(top.num_objects * sizeof(struct asf_object));
628 ret = read_asf_objects(p, top.size - (p - map), top.num_objects,
632 ret = make_cdo(tags, ton.content_descr_obj_num >= 0?
633 top.objects + ton.content_descr_obj_num : NULL, &cdo);
636 ret = make_ecdo(tags, &ecdo);
639 ret = write_output_file(fd, map, mapsize, &top, &ton, &cdo,
648 static const char * const wma_suffixes[] = {"wma", NULL};
651 * The init function of the wma audio format handler.
653 * \param afh Pointer to the struct to initialize.
655 void wma_afh_init(struct audio_format_handler *afh)
657 afh->get_file_info = wma_get_file_info;
658 afh->suffixes = wma_suffixes;
659 afh->rewrite_tags = wma_rewrite_tags;