1 //=========================================================================
2 // FILENAME : tagutils-asf.c
3 // DESCRIPTION : ASF (wma/wmv) metadata reader
4 //=========================================================================
5 // Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6 //=========================================================================
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>
28 static inline uint16_t
29 le16_to_cpu(uint16_t le16
)
31 #if __BYTE_ORDER == __LITTLE_ENDIAN
34 uint16_t be16
= ((le16
<< 8) & 0xff00) | ((le16
>> 8) & 0x00ff);
39 static inline uint32_t
40 le32_to_cpu(uint32_t le32
)
42 #if __BYTE_ORDER == __LITTLE_ENDIAN
46 ((le32
<< 24) & 0xff000000) |
47 ((le32
<< 8) & 0x00ff0000) |
48 ((le32
>> 8) & 0x0000ff00) |
49 ((le32
>> 24) & 0x000000ff);
54 static inline uint64_t
55 le64_to_cpu(uint64_t le64
)
57 #if __BYTE_ORDER == __LITTLE_ENDIAN
61 uint8_t *le64p
= (uint8_t*)&le64
;
62 uint8_t *be64p
= (uint8_t*)&be64
;
75 static inline uint32_t
76 cpu_to_be32(uint32_t cpu32
)
78 #if __BYTE_ORDER == __LITTLE_ENDIAN
80 ((cpu32
<< 24) & 0xff000000) |
81 ((cpu32
<< 8) & 0x00ff0000) |
82 ((cpu32
>> 8) & 0x0000ff00) |
83 ((cpu32
>> 24) & 0x000000ff);
95 if (fread(&d
, sizeof(d
), 1, fp
) != 1)
100 static inline uint16_t
105 if (fread(&d
, sizeof(d
), 1, fp
) != 1)
111 static inline uint32_t
116 if (fread(&d
, sizeof(d
), 1, fp
) != 1)
122 // NOTE: support U+0000 ~ U+FFFF only.
124 utf16le_to_utf8(char *dst
, int n
, uint16_t utf16le
)
126 uint16_t wc
= le16_to_cpu(utf16le
);
138 *dst
++ = 0xc0 | (wc
>>6);
139 *dst
++ = 0x80 | (wc
& 0x3f);
146 *dst
++ = 0xe0 | (wc
>>12);
147 *dst
++ = 0x80 | ((wc
>>6) & 0x3f);
148 *dst
++ = 0x80 | (wc
& 0x3f);
154 _asf_read_file_properties(FILE *fp
, asf_file_properties_t
*p
, uint32_t size
)
158 len
= sizeof(*p
) - offsetof(asf_file_properties_t
, FileID
);
162 memset(p
, 0, sizeof(*p
));
163 p
->ID
= ASF_FileProperties
;
166 if(len
!= fread(&p
->FileID
, 1, len
, fp
))
173 _pick_dlna_profile(struct song_metadata
*psong
, uint16_t format
)
175 /* DLNA Profile Name */
176 switch( le16_to_cpu(format
) )
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");
185 xasprintf(&(psong
->dlna_pn
), "WMAPRO");
188 xasprintf(&(psong
->dlna_pn
), "WMALSL%s",
189 psong
->channels
> 2 ? "_MULT5" : "");
196 _asf_read_audio_stream(FILE *fp
, struct song_metadata
*psong
, int size
)
198 asf_audio_stream_t s
;
201 len
= sizeof(s
) - sizeof(s
.Hdr
);
205 if(len
!= fread(&s
.wfx
, 1, len
, fp
))
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
);
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
;
225 len
= sizeof(s
) - sizeof(s
.Hdr
);
229 memset(&s
, 0, sizeof(s
));
231 if(len
!= fread(&s
.MajorType
, 1, len
, fp
))
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
))
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
);
253 _asf_read_stream_object(FILE *fp
, struct song_metadata
*psong
, uint32_t size
)
255 asf_stream_object_t s
;
258 len
= sizeof(s
) - sizeof(asf_object_t
);
262 memset(&s
, 0, sizeof(s
));
264 if(len
!= fread(&s
.StreamType
, 1, len
, fp
))
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");
280 _asf_read_extended_stream_object(FILE *fp
, struct song_metadata
*psong
, uint32_t size
)
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
))
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
))
299 for(i
= 0; i
< xs
.StreamNameCount
; i
++)
301 if(off
+ sizeof(nm
) > size
)
303 if(sizeof(nm
) != fread(&nm
, 1, sizeof(nm
), fp
))
306 if(off
+ nm
.Length
> sizeof(asf_extended_stream_object_t
))
309 fseek(fp
, nm
.Length
, SEEK_CUR
);
313 for(i
= 0; i
< xs
.PayloadExtensionSystemCount
; i
++)
315 if(off
+ sizeof(pe
) > size
)
317 if(sizeof(pe
) != fread(&pe
, 1, sizeof(pe
), fp
))
320 if(pe
.InfoLength
> 0)
321 fseek(fp
, pe
.InfoLength
, SEEK_CUR
);
322 off
+= pe
.InfoLength
;
327 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
329 if(IsEqualGUID(&tmp
.ID
, &ASF_StreamHeader
))
330 _asf_read_stream_object(fp
, psong
, tmp
.Size
);
337 _asf_read_header_extension(FILE *fp
, struct song_metadata
*psong
, uint32_t size
)
341 asf_header_extension_t ext
;
344 if(size
< sizeof(asf_header_extension_t
))
347 if(sizeof(ext
.Reserved1
) != fread(&ext
.Reserved1
, 1, sizeof(ext
.Reserved1
), fp
))
349 ext
.Reserved2
= fget_le16(fp
);
350 ext
.DataSize
= fget_le32(fp
);
354 while(off
< ext
.DataSize
)
356 if(sizeof(asf_header_extension_t
) + off
> size
)
358 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
360 if(off
+ tmp
.Size
> ext
.DataSize
)
362 if(IsEqualGUID(&tmp
.ID
, &ASF_ExtendedStreamPropertiesObject
))
363 _asf_read_extended_stream_object(fp
, psong
, tmp
.Size
);
366 fseek(fp
, pos
+ off
, SEEK_SET
);
373 _asf_load_string(FILE *fp
, int type
, int size
, char *buf
, int len
)
375 unsigned char data
[2048];
383 if(size
&& (size
<= sizeof(data
)) && (size
== fread(data
, 1, size
, fp
)))
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
);
396 case ASF_VT_BYTEARRAY
:
397 for(i
= 0; i
< size
; i
++)
408 wd32
= (int32_t *) &data
[0];
409 i
= snprintf(buf
, len
, "%d", le32_to_cpu(*wd32
));
415 wd64
= (int64_t *) &data
[0];
416 i
= snprintf(buf
, len
, "%lld", (long long)le64_to_cpu(*wd64
));
422 wd16
= (int16_t *) &data
[0];
423 i
= snprintf(buf
, len
, "%d", le16_to_cpu(*wd16
));
430 else fseek(fp
, size
, SEEK_CUR
);
437 _asf_load_picture(FILE *fp
, int size
, void *bm
, int *bm_size
)
444 // Data length $xx $xx $xx $xx
445 // MIME type <text string> $00
446 // Description <text string> $00
447 // Picture data <binary data>
452 pic_type
= fget_byte(fp
); size
-= 1;
453 pic_size
= fget_le32(fp
); size
-= 4;
455 fseek(fp
, 5, SEEK_CUR
);
458 for(i
= 0; i
< sizeof(buf
) - 1; i
++)
460 buf
[i
] = fget_le16(fp
); size
-= 2;
465 if(i
== sizeof(buf
) - 1)
471 if(!strcasecmp(buf
, "image/jpeg") ||
472 !strcasecmp(buf
, "image/jpg") ||
473 !strcasecmp(buf
, "image/peg"))
476 while(0 != fget_le16(fp
))
481 if(!(bm
= malloc(size
)))
483 DPRINTF(E_ERROR
, L_SCANNER
, "Couldn't allocate %d bytes\n", size
);
488 if(size
> *bm_size
|| fread(bm
, 1, size
, fp
) != size
)
490 DPRINTF(E_ERROR
, L_SCANNER
, "Overrun %d bytes required\n", size
);
498 DPRINTF(E_ERROR
, L_SCANNER
, "No binary data\n");
505 DPRINTF(E_ERROR
, L_SCANNER
, "Invalid mime type %s\n", buf
);
513 _get_asffileinfo(char *file
, struct song_metadata
*psong
)
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
;
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
);
540 if(sizeof(hdr
) != fread(&hdr
, 1, sizeof(hdr
), fp
))
542 DPRINTF(E_ERROR
, L_SCANNER
, "Error reading %s\n", file
);
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");
554 NumObjects
= fget_le32(fp
);
555 fseek(fp
, 2, SEEK_CUR
); // Reserved le16
558 while(NumObjects
> 0)
560 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
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
);
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
)))
589 psong
->title
= strdup(buf
);
591 if(_asf_load_string(fp
, ASF_VT_UNICODE
, AuthorLength
, buf
, sizeof(buf
)))
594 psong
->contributor
[ROLE_TRACKARTIST
] = strdup(buf
);
597 fseek(fp
, CopyrightLength
, SEEK_CUR
);
598 if(DescriptionLength
)
599 fseek(fp
, DescriptionLength
, SEEK_CUR
);
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
)))
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
)))
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
)))
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
)))
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
)))
643 psong
->year
= atoi(buf
);
645 else if(!strcasecmp(buf
, "WM/Director"))
647 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
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
)))
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
)))
665 psong
->track
= atoi(buf
);
667 else if(!strcasecmp(buf
, "isVBR"))
669 fseek(fp
, ValueLength
, SEEK_CUR
);
670 psong
->vbr_scale
= 0;
674 fseek(fp
, ValueLength
, SEEK_CUR
);
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
);
688 fseek(fp
, pos
, SEEK_SET
);
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
;