Wrap up version 1.3.3.
[minidlna.git] / tagutils / tagutils-asf.c
blobfca0597fb0482c03a52980fe3e3d4e09fed43eac
1 //=========================================================================
2 // FILENAME : tagutils-asf.c
3 // DESCRIPTION : ASF (wma/wmv) metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
8 /*
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #ifdef HAVE_MACHINE_ENDIAN_H
23 #include <machine/endian.h>
24 #else
25 #include <endian.h>
26 #endif
28 static inline uint16_t
29 le16_to_cpu(uint16_t le16)
31 #if __BYTE_ORDER == __LITTLE_ENDIAN
32 return le16;
33 #else
34 uint16_t be16 = ((le16 << 8) & 0xff00) | ((le16 >> 8) & 0x00ff);
35 return be16;
36 #endif
39 static inline uint32_t
40 le32_to_cpu(uint32_t le32)
42 #if __BYTE_ORDER == __LITTLE_ENDIAN
43 return le32;
44 #else
45 uint32_t be32 =
46 ((le32 << 24) & 0xff000000) |
47 ((le32 << 8) & 0x00ff0000) |
48 ((le32 >> 8) & 0x0000ff00) |
49 ((le32 >> 24) & 0x000000ff);
50 return be32;
51 #endif
54 static inline uint64_t
55 le64_to_cpu(uint64_t le64)
57 #if __BYTE_ORDER == __LITTLE_ENDIAN
58 return le64;
59 #else
60 uint64_t be64;
61 uint8_t *le64p = (uint8_t*)&le64;
62 uint8_t *be64p = (uint8_t*)&be64;
63 be64p[0] = le64p[7];
64 be64p[1] = le64p[6];
65 be64p[2] = le64p[5];
66 be64p[3] = le64p[4];
67 be64p[4] = le64p[3];
68 be64p[5] = le64p[2];
69 be64p[6] = le64p[1];
70 be64p[7] = le64p[0];
71 return be64;
72 #endif
75 static inline uint32_t
76 cpu_to_be32(uint32_t cpu32)
78 #if __BYTE_ORDER == __LITTLE_ENDIAN
79 uint32_t be32 =
80 ((cpu32 << 24) & 0xff000000) |
81 ((cpu32 << 8) & 0x00ff0000) |
82 ((cpu32 >> 8) & 0x0000ff00) |
83 ((cpu32 >> 24) & 0x000000ff);
84 return be32;
85 #else
86 return cpu32;
87 #endif
90 static inline uint8_t
91 fget_byte(FILE *fp)
93 uint8_t d;
95 if (fread(&d, sizeof(d), 1, fp) != 1)
96 return 0;
97 return d;
100 static inline uint16_t
101 fget_le16(FILE *fp)
103 uint16_t d;
105 if (fread(&d, sizeof(d), 1, fp) != 1)
106 return 0;
107 d = le16_to_cpu(d);
108 return d;
111 static inline uint32_t
112 fget_le32(FILE *fp)
114 uint32_t d;
116 if (fread(&d, sizeof(d), 1, fp) != 1)
117 return 0;
118 d = le32_to_cpu(d);
119 return d;
122 // NOTE: support U+0000 ~ U+FFFF only.
123 static int
124 utf16le_to_utf8(char *dst, int n, uint16_t utf16le)
126 uint16_t wc = le16_to_cpu(utf16le);
127 if (wc < 0x80)
129 if (n < 1)
130 return 0;
131 *dst++ = wc & 0xff;
132 return 1;
134 else if (wc < 0x800)
136 if (n < 2)
137 return 0;
138 *dst++ = 0xc0 | (wc>>6);
139 *dst++ = 0x80 | (wc & 0x3f);
140 return 2;
142 else
144 if (n < 3)
145 return 0;
146 *dst++ = 0xe0 | (wc>>12);
147 *dst++ = 0x80 | ((wc>>6) & 0x3f);
148 *dst++ = 0x80 | (wc & 0x3f);
149 return 3;
153 static int
154 _asf_read_file_properties(FILE *fp, asf_file_properties_t *p, uint32_t size)
156 int len;
158 len = sizeof(*p) - offsetof(asf_file_properties_t, FileID);
159 if(size < len)
160 return -1;
162 memset(p, 0, sizeof(*p));
163 p->ID = ASF_FileProperties;
164 p->Size = size;
166 if(len != fread(&p->FileID, 1, len, fp))
167 return -1;
169 return 0;
172 static void
173 _pick_dlna_profile(struct song_metadata *psong, uint16_t format)
175 /* DLNA Profile Name */
176 switch( le16_to_cpu(format) )
178 case WMA:
179 if( psong->max_bitrate < 193000 )
180 xasprintf(&(psong->dlna_pn), "WMABASE");
181 else if( psong->max_bitrate < 385000 )
182 xasprintf(&(psong->dlna_pn), "WMAFULL");
183 break;
184 case WMAPRO:
185 xasprintf(&(psong->dlna_pn), "WMAPRO");
186 break;
187 case WMALSL:
188 xasprintf(&(psong->dlna_pn), "WMALSL%s",
189 psong->channels > 2 ? "_MULT5" : "");
190 default:
191 break;
195 static int
196 _asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size)
198 asf_audio_stream_t s;
199 int len;
201 len = sizeof(s) - sizeof(s.Hdr);
202 if(len > size)
203 len = size;
205 if(len != fread(&s.wfx, 1, len, fp))
206 return -1;
208 psong->channels = le16_to_cpu(s.wfx.nChannels);
209 psong->bitrate = le32_to_cpu(s.wfx.nAvgBytesPerSec) * 8;
210 psong->samplerate = le32_to_cpu(s.wfx.nSamplesPerSec);
211 if (!psong->max_bitrate)
212 psong->max_bitrate = psong->bitrate;
213 _pick_dlna_profile(psong, s.wfx.wFormatTag);
215 return 0;
218 static int
219 _asf_read_media_stream(FILE *fp, struct song_metadata *psong, uint32_t size)
221 asf_media_stream_t s;
222 avi_audio_format_t wfx;
223 int len;
225 len = sizeof(s) - sizeof(s.Hdr);
226 if(len > size)
227 len = size;
229 memset(&s, 0, sizeof(s));
231 if(len != fread(&s.MajorType, 1, len, fp))
232 return -1;
234 if(IsEqualGUID(&s.MajorType, &ASF_MediaTypeAudio) &&
235 IsEqualGUID(&s.FormatType, &ASF_FormatTypeWave) && s.FormatSize >= sizeof(wfx))
238 if(sizeof(wfx) != fread(&wfx, 1, sizeof(wfx), fp))
239 return -1;
241 psong->channels = le16_to_cpu(wfx.nChannels);
242 psong->bitrate = le32_to_cpu(wfx.nAvgBytesPerSec) * 8;
243 psong->samplerate = le32_to_cpu(wfx.nSamplesPerSec);
244 if (!psong->max_bitrate)
245 psong->max_bitrate = psong->bitrate;
246 _pick_dlna_profile(psong, wfx.wFormatTag);
249 return 0;
252 static int
253 _asf_read_stream_object(FILE *fp, struct song_metadata *psong, uint32_t size)
255 asf_stream_object_t s;
256 int len;
258 len = sizeof(s) - sizeof(asf_object_t);
259 if(size < len)
260 return -1;
262 memset(&s, 0, sizeof(s));
264 if(len != fread(&s.StreamType, 1, len, fp))
265 return -1;
267 if(IsEqualGUID(&s.StreamType, &ASF_AudioStream))
268 _asf_read_audio_stream(fp, psong, s.TypeSpecificSize);
269 else if(IsEqualGUID(&s.StreamType, &ASF_StreamBufferStream))
270 _asf_read_media_stream(fp, psong, s.TypeSpecificSize);
271 else if(!IsEqualGUID(&s.StreamType, &ASF_VideoStream))
273 DPRINTF(E_ERROR, L_SCANNER, "Unknown asf stream type.\n");
276 return 0;
279 static int
280 _asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, uint32_t size)
282 int i, len;
283 long off;
284 asf_object_t tmp;
285 asf_extended_stream_object_t xs;
286 asf_stream_name_t nm;
287 asf_payload_extension_t pe;
289 if(size < sizeof(asf_extended_stream_object_t))
290 return -1;
292 memset(&xs, 0, sizeof(xs));
294 len = sizeof(xs) - offsetof(asf_extended_stream_object_t, StartTime);
295 if(len != fread(&xs.StartTime, 1, len, fp))
296 return -1;
297 off = sizeof(xs);
299 for(i = 0; i < xs.StreamNameCount; i++)
301 if(off + sizeof(nm) > size)
302 return -1;
303 if(sizeof(nm) != fread(&nm, 1, sizeof(nm), fp))
304 return -1;
305 off += sizeof(nm);
306 if(off + nm.Length > sizeof(asf_extended_stream_object_t))
307 return -1;
308 if(nm.Length > 0)
309 fseek(fp, nm.Length, SEEK_CUR);
310 off += nm.Length;
313 for(i = 0; i < xs.PayloadExtensionSystemCount; i++)
315 if(off + sizeof(pe) > size)
316 return -1;
317 if(sizeof(pe) != fread(&pe, 1, sizeof(pe), fp))
318 return -1;
319 off += sizeof(pe);
320 if(pe.InfoLength > 0)
321 fseek(fp, pe.InfoLength, SEEK_CUR);
322 off += pe.InfoLength;
325 if(off < size)
327 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
328 return -1;
329 if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader))
330 _asf_read_stream_object(fp, psong, tmp.Size);
333 return 0;
336 static int
337 _asf_read_header_extension(FILE *fp, struct song_metadata *psong, uint32_t size)
339 off_t pos;
340 long off;
341 asf_header_extension_t ext;
342 asf_object_t tmp;
344 if(size < sizeof(asf_header_extension_t))
345 return -1;
347 if(sizeof(ext.Reserved1) != fread(&ext.Reserved1, 1, sizeof(ext.Reserved1), fp))
348 return -1;
349 ext.Reserved2 = fget_le16(fp);
350 ext.DataSize = fget_le32(fp);
352 pos = ftell(fp);
353 off = 0;
354 while(off < ext.DataSize)
356 if(sizeof(asf_header_extension_t) + off > size)
357 break;
358 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
359 break;
360 if(off + tmp.Size > ext.DataSize)
361 break;
362 if(IsEqualGUID(&tmp.ID, &ASF_ExtendedStreamPropertiesObject))
363 _asf_read_extended_stream_object(fp, psong, tmp.Size);
365 off += tmp.Size;
366 fseek(fp, pos + off, SEEK_SET);
369 return 0;
372 static int
373 _asf_load_string(FILE *fp, int type, int size, char *buf, int len)
375 unsigned char data[2048];
376 uint16_t wc;
377 int i, j;
378 int16_t *wd16;
379 int32_t *wd32;
380 int64_t *wd64;
382 i = 0;
383 if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp)))
386 switch(type)
388 case ASF_VT_UNICODE:
389 for(j = 0; j < size; j += 2)
391 wd16 = (int16_t *) &data[j];
392 wc = (uint16_t)*wd16;
393 i += utf16le_to_utf8(&buf[i], len - i, wc);
395 break;
396 case ASF_VT_BYTEARRAY:
397 for(i = 0; i < size; i++)
399 if(i + 1 >= len)
400 break;
401 buf[i] = data[i];
403 break;
404 case ASF_VT_BOOL:
405 case ASF_VT_DWORD:
406 if(size >= 4)
408 wd32 = (int32_t *) &data[0];
409 i = snprintf(buf, len, "%d", le32_to_cpu(*wd32));
411 break;
412 case ASF_VT_QWORD:
413 if(size >= 8)
415 wd64 = (int64_t *) &data[0];
416 i = snprintf(buf, len, "%lld", (long long)le64_to_cpu(*wd64));
418 break;
419 case ASF_VT_WORD:
420 if(size >= 2)
422 wd16 = (int16_t *) &data[0];
423 i = snprintf(buf, len, "%d", le16_to_cpu(*wd16));
425 break;
428 size = 0;
430 else fseek(fp, size, SEEK_CUR);
432 buf[i] = 0;
433 return i;
436 static void *
437 _asf_load_picture(FILE *fp, int size, void *bm, int *bm_size)
439 int i;
440 char buf[256];
441 #if 0
443 // Picture type $xx
444 // Data length $xx $xx $xx $xx
445 // MIME type <text string> $00
446 // Description <text string> $00
447 // Picture data <binary data>
449 char pic_type;
450 long pic_size;
452 pic_type = fget_byte(fp); size -= 1;
453 pic_size = fget_le32(fp); size -= 4;
454 #else
455 fseek(fp, 5, SEEK_CUR);
456 size -= 5;
457 #endif
458 for(i = 0; i < sizeof(buf) - 1; i++)
460 buf[i] = fget_le16(fp); size -= 2;
461 if(!buf[i])
462 break;
464 buf[i] = '\0';
465 if(i == sizeof(buf) - 1)
467 while(fget_le16(fp))
468 size -= 2;
471 if(!strcasecmp(buf, "image/jpeg") ||
472 !strcasecmp(buf, "image/jpg") ||
473 !strcasecmp(buf, "image/peg"))
476 while(0 != fget_le16(fp))
477 size -= 2;
479 if(size > 0)
481 if(!(bm = malloc(size)))
483 DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size);
485 else
487 *bm_size = size;
488 if(size > *bm_size || fread(bm, 1, size, fp) != size)
490 DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size);
491 free(bm);
492 bm = NULL;
496 else
498 DPRINTF(E_ERROR, L_SCANNER, "No binary data\n");
499 size = 0;
500 bm = NULL;
503 else
505 DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf);
508 *bm_size = size;
509 return bm;
512 static int
513 _get_asffileinfo(char *file, struct song_metadata *psong)
515 FILE *fp;
516 asf_object_t hdr;
517 asf_object_t tmp;
518 unsigned long NumObjects;
519 unsigned short TitleLength;
520 unsigned short AuthorLength;
521 unsigned short CopyrightLength;
522 unsigned short DescriptionLength;
523 unsigned short RatingLength;
524 unsigned short NumEntries;
525 unsigned short NameLength;
526 unsigned short ValueType;
527 unsigned short ValueLength;
528 off_t pos;
529 char buf[2048];
530 asf_file_properties_t FileProperties;
532 psong->vbr_scale = -1;
534 if(!(fp = fopen(file, "rb")))
536 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file);
537 return -1;
540 if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp))
542 DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file);
543 fclose(fp);
544 return -1;
546 hdr.Size = le64_to_cpu(hdr.Size);
548 if(!IsEqualGUID(&hdr.ID, &ASF_HeaderObject))
550 DPRINTF(E_ERROR, L_SCANNER, "Not a valid header\n");
551 fclose(fp);
552 return -1;
554 NumObjects = fget_le32(fp);
555 fseek(fp, 2, SEEK_CUR); // Reserved le16
557 pos = ftell(fp);
558 while(NumObjects > 0)
560 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
561 break;
562 tmp.Size = le64_to_cpu(tmp.Size);
564 if(pos + tmp.Size > hdr.Size)
566 DPRINTF(E_ERROR, L_SCANNER, "Size overrun reading header object %llu\n",
567 (unsigned long long)tmp.Size);
568 break;
571 if(IsEqualGUID(&tmp.ID, &ASF_FileProperties))
573 _asf_read_file_properties(fp, &FileProperties, tmp.Size);
574 psong->song_length = le64_to_cpu(FileProperties.PlayDuration) / 10000;
575 psong->bitrate = le64_to_cpu(FileProperties.MaxBitrate);
576 psong->max_bitrate = psong->bitrate;
578 else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription))
580 TitleLength = fget_le16(fp);
581 AuthorLength = fget_le16(fp);
582 CopyrightLength = fget_le16(fp);
583 DescriptionLength = fget_le16(fp);
584 RatingLength = fget_le16(fp);
586 if(_asf_load_string(fp, ASF_VT_UNICODE, TitleLength, buf, sizeof(buf)))
588 if(buf[0])
589 psong->title = strdup(buf);
591 if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf)))
593 if(buf[0])
594 psong->contributor[ROLE_TRACKARTIST] = strdup(buf);
596 if(CopyrightLength)
597 fseek(fp, CopyrightLength, SEEK_CUR);
598 if(DescriptionLength)
599 fseek(fp, DescriptionLength, SEEK_CUR);
600 if(RatingLength)
601 fseek(fp, RatingLength, SEEK_CUR);
603 else if(IsEqualGUID(&tmp.ID, &ASF_ExtendedContentDescription))
605 NumEntries = fget_le16(fp);
606 while(NumEntries > 0)
608 NameLength = fget_le16(fp);
609 _asf_load_string(fp, ASF_VT_UNICODE, NameLength, buf, sizeof(buf));
610 ValueType = fget_le16(fp);
611 ValueLength = fget_le16(fp);
613 if(!strcasecmp(buf, "AlbumTitle") || !strcasecmp(buf, "WM/AlbumTitle"))
615 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
616 if(buf[0])
617 psong->album = strdup(buf);
619 else if(!strcasecmp(buf, "AlbumArtist") || !strcasecmp(buf, "WM/AlbumArtist"))
621 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
623 if(buf[0])
624 psong->contributor[ROLE_ALBUMARTIST] = strdup(buf);
627 else if(!strcasecmp(buf, "Description") || !strcasecmp(buf, "WM/Track"))
629 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
630 if(buf[0])
631 psong->track = atoi(buf);
633 else if(!strcasecmp(buf, "Genre") || !strcasecmp(buf, "WM/Genre"))
635 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
636 if(buf[0])
637 psong->genre = strdup(buf);
639 else if(!strcasecmp(buf, "Year") || !strcasecmp(buf, "WM/Year"))
641 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
642 if(buf[0])
643 psong->year = atoi(buf);
645 else if(!strcasecmp(buf, "WM/Director"))
647 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
648 if(buf[0])
649 psong->contributor[ROLE_CONDUCTOR] = strdup(buf);
651 else if(!strcasecmp(buf, "WM/Composer"))
653 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
654 if(buf[0])
655 psong->contributor[ROLE_COMPOSER] = strdup(buf);
657 else if(!strcasecmp(buf, "WM/Picture") && (ValueType == ASF_VT_BYTEARRAY))
659 psong->image = _asf_load_picture(fp, ValueLength, psong->image, &psong->image_size);
661 else if(!strcasecmp(buf, "TrackNumber") || !strcasecmp(buf, "WM/TrackNumber"))
663 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
664 if(buf[0])
665 psong->track = atoi(buf);
667 else if(!strcasecmp(buf, "isVBR"))
669 fseek(fp, ValueLength, SEEK_CUR);
670 psong->vbr_scale = 0;
672 else if(ValueLength)
674 fseek(fp, ValueLength, SEEK_CUR);
676 NumEntries--;
679 else if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader))
681 _asf_read_stream_object(fp, psong, tmp.Size);
683 else if(IsEqualGUID(&tmp.ID, &ASF_HeaderExtension))
685 _asf_read_header_extension(fp, psong, tmp.Size);
687 pos += tmp.Size;
688 fseek(fp, pos, SEEK_SET);
689 NumObjects--;
692 #if 0
693 if(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), fp) && IsEqualGUID(&hdr.ID, &ASF_DataObject))
695 if(psong->song_length)
697 psong->bitrate = (hdr.Size * 8000) / psong->song_length;
700 #endif
702 fclose(fp);
703 return 0;