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/>.
24 _asf_read_file_properties(FILE *fp
, asf_file_properties_t
*p
, __u32 size
)
28 len
= sizeof(*p
) - offsetof(asf_file_properties_t
, FileID
);
32 memset(p
, 0, sizeof(*p
));
33 p
->ID
= ASF_FileProperties
;
36 if(len
!= fread(&p
->FileID
, 1, len
, fp
))
43 _pick_dlna_profile(struct song_metadata
*psong
, uint16_t format
)
45 /* DLNA Profile Name */
46 switch( le16_to_cpu(format
) )
49 if( psong
->max_bitrate
< 193000 )
50 asprintf(&(psong
->dlna_pn
), "WMABASE");
51 else if( psong
->max_bitrate
< 385000 )
52 asprintf(&(psong
->dlna_pn
), "WMAFULL");
55 asprintf(&(psong
->dlna_pn
), "WMAPRO");
58 asprintf(&(psong
->dlna_pn
), "WMALSL%s",
59 psong
->channels
> 2 ? "_MULT5" : "");
66 _asf_read_audio_stream(FILE *fp
, struct song_metadata
*psong
, int size
)
71 len
= sizeof(s
) - sizeof(s
.Hdr
);
75 if(len
!= fread(&s
.wfx
, 1, len
, fp
))
78 psong
->channels
= le16_to_cpu(s
.wfx
.nChannels
);
79 psong
->bitrate
= le32_to_cpu(s
.wfx
.nAvgBytesPerSec
) * 8;
80 psong
->samplerate
= le32_to_cpu(s
.wfx
.nSamplesPerSec
);
81 if (!psong
->max_bitrate
)
82 psong
->max_bitrate
= psong
->bitrate
;
83 _pick_dlna_profile(psong
, s
.wfx
.wFormatTag
);
89 _asf_read_media_stream(FILE *fp
, struct song_metadata
*psong
, __u32 size
)
92 avi_audio_format_t wfx
;
95 len
= sizeof(s
) - sizeof(s
.Hdr
);
99 if(len
!= fread(&s
.MajorType
, 1, len
, fp
))
102 if(IsEqualGUID(&s
.MajorType
, &ASF_MediaTypeAudio
) &&
103 IsEqualGUID(&s
.FormatType
, &ASF_FormatTypeWave
) && s
.FormatSize
>= sizeof(wfx
))
106 if(sizeof(wfx
) != fread(&wfx
, 1, sizeof(wfx
), fp
))
109 psong
->channels
= le16_to_cpu(wfx
.nChannels
);
110 psong
->bitrate
= le32_to_cpu(wfx
.nAvgBytesPerSec
) * 8;
111 psong
->samplerate
= le32_to_cpu(wfx
.nSamplesPerSec
);
112 if (!psong
->max_bitrate
)
113 psong
->max_bitrate
= psong
->bitrate
;
114 _pick_dlna_profile(psong
, wfx
.wFormatTag
);
121 _asf_read_stream_object(FILE *fp
, struct song_metadata
*psong
, __u32 size
)
123 asf_stream_object_t s
;
126 len
= sizeof(s
) - sizeof(asf_object_t
);
130 if(len
!= fread(&s
.StreamType
, 1, len
, fp
))
133 if(IsEqualGUID(&s
.StreamType
, &ASF_AudioStream
))
134 _asf_read_audio_stream(fp
, psong
, s
.TypeSpecificSize
);
135 else if(IsEqualGUID(&s
.StreamType
, &ASF_StreamBufferStream
))
136 _asf_read_media_stream(fp
, psong
, s
.TypeSpecificSize
);
137 else if(!IsEqualGUID(&s
.StreamType
, &ASF_VideoStream
))
139 DPRINTF(E_ERROR
, L_SCANNER
, "Unknown asf stream type.\n");
146 _asf_read_extended_stream_object(FILE *fp
, struct song_metadata
*psong
, __u32 size
)
151 asf_extended_stream_object_t xs
;
152 asf_stream_name_t nm
;
153 asf_payload_extension_t pe
;
155 if(size
< sizeof(asf_extended_stream_object_t
))
158 len
= sizeof(xs
) - offsetof(asf_extended_stream_object_t
, StartTime
);
159 if(len
!= fread(&xs
.StartTime
, 1, len
, fp
))
163 for(i
= 0; i
< xs
.StreamNameCount
; i
++)
165 if(off
+ sizeof(nm
) > size
)
167 if(sizeof(nm
) != fread(&nm
, 1, sizeof(nm
), fp
))
170 if(off
+ nm
.Length
> sizeof(asf_extended_stream_object_t
))
173 fseek(fp
, nm
.Length
, SEEK_CUR
);
177 for(i
= 0; i
< xs
.PayloadExtensionSystemCount
; i
++)
179 if(off
+ sizeof(pe
) > size
)
181 if(sizeof(pe
) != fread(&pe
, 1, sizeof(pe
), fp
))
184 if(pe
.InfoLength
> 0)
185 fseek(fp
, pe
.InfoLength
, SEEK_CUR
);
186 off
+= pe
.InfoLength
;
191 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
193 if(IsEqualGUID(&tmp
.ID
, &ASF_StreamHeader
))
194 _asf_read_stream_object(fp
, psong
, tmp
.Size
);
201 _asf_read_header_extension(FILE *fp
, struct song_metadata
*psong
, __u32 size
)
205 asf_header_extension_t ext
;
208 if(size
< sizeof(asf_header_extension_t
))
211 fread(&ext
.Reserved1
, 1, sizeof(ext
.Reserved1
), fp
);
212 ext
.Reserved2
= fget_le16(fp
);
213 ext
.DataSize
= fget_le32(fp
);
217 while(off
< ext
.DataSize
)
219 if(sizeof(asf_header_extension_t
) + off
> size
)
221 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
223 if(off
+ tmp
.Size
> ext
.DataSize
)
225 if(IsEqualGUID(&tmp
.ID
, &ASF_ExtendedStreamPropertiesObject
))
226 _asf_read_extended_stream_object(fp
, psong
, tmp
.Size
);
229 fseek(fp
, pos
+ off
, SEEK_SET
);
236 _asf_load_string(FILE *fp
, int type
, int size
, char *buf
, int len
)
238 unsigned char data
[2048];
246 if(size
&& (size
<= sizeof(data
)) && (size
== fread(data
, 1, size
, fp
)))
252 for(j
= 0; j
< size
; j
+= 2)
254 wc
= *(__s16
*)&data
[j
];
255 i
+= utf16le_to_utf8(&buf
[i
], len
- i
, wc
);
258 case ASF_VT_BYTEARRAY
:
259 for(i
= 0; i
< size
; i
++)
270 wd32
= (__s32
*) &data
[0];
271 i
= snprintf(buf
, len
, "%d", le32_to_cpu(*wd32
));
277 wd64
= (__s64
*) &data
[0];
279 i
= snprintf(buf
, len
, "%ld", le64_to_cpu(*wd64
));
281 i
= snprintf(buf
, len
, "%lld", le64_to_cpu(*wd64
));
288 wd16
= (__s16
*) &data
[0];
289 i
= snprintf(buf
, len
, "%d", le16_to_cpu(*wd16
));
296 else fseek(fp
, size
, SEEK_CUR
);
303 _asf_load_picture(FILE *fp
, int size
, void *bm
, int *bm_size
)
310 // Data length $xx $xx $xx $xx
311 // MIME type <text string> $00
312 // Description <text string> $00
313 // Picture data <binary data>
318 pic_type
= fget_byte(fp
); size
-= 1;
319 pic_size
= fget_le32(fp
); size
-= 4;
321 fseek(fp
, 5, SEEK_CUR
);
324 for(i
= 0; i
< sizeof(buf
) - 1; i
++)
326 buf
[i
] = fget_le16(fp
); size
-= 2;
331 if(i
== sizeof(buf
) - 1)
337 if(!strcasecmp(buf
, "image/jpeg") ||
338 !strcasecmp(buf
, "image/jpg") ||
339 !strcasecmp(buf
, "image/peg"))
342 while(0 != fget_le16(fp
))
347 if(!(bm
= malloc(size
)))
349 DPRINTF(E_ERROR
, L_SCANNER
, "Couldn't allocate %d bytes\n", size
);
356 fread(bm
, 1, size
, fp
);
360 DPRINTF(E_ERROR
, L_SCANNER
, "Overrun %d bytes required\n", size
);
368 DPRINTF(E_ERROR
, L_SCANNER
, "No binary data\n");
375 DPRINTF(E_ERROR
, L_SCANNER
, "Invalid mime type %s\n", buf
);
383 _get_asffileinfo(char *file
, struct song_metadata
*psong
)
388 unsigned long NumObjects
;
389 unsigned short TitleLength
;
390 unsigned short AuthorLength
;
391 unsigned short CopyrightLength
;
392 unsigned short DescriptionLength
;
393 unsigned short RatingLength
;
394 unsigned short NumEntries
;
395 unsigned short NameLength
;
396 unsigned short ValueType
;
397 unsigned short ValueLength
;
400 asf_file_properties_t FileProperties
;
402 psong
->vbr_scale
= -1;
404 if(!(fp
= fopen(file
, "rb")))
406 DPRINTF(E_ERROR
, L_SCANNER
, "Could not open %s for reading\n", file
);
410 if(sizeof(hdr
) != fread(&hdr
, 1, sizeof(hdr
), fp
))
412 DPRINTF(E_ERROR
, L_SCANNER
, "Error reading %s\n", file
);
416 hdr
.Size
= le64_to_cpu(hdr
.Size
);
418 if(!IsEqualGUID(&hdr
.ID
, &ASF_HeaderObject
))
420 DPRINTF(E_ERROR
, L_SCANNER
, "Not a valid header\n");
424 NumObjects
= fget_le32(fp
);
425 fseek(fp
, 2, SEEK_CUR
); // Reserved le16
428 while(NumObjects
> 0)
430 if(sizeof(tmp
) != fread(&tmp
, 1, sizeof(tmp
), fp
))
432 tmp
.Size
= le64_to_cpu(tmp
.Size
);
434 if(pos
+ tmp
.Size
> hdr
.Size
)
436 DPRINTF(E_ERROR
, L_SCANNER
, "Size overrun reading header object %I64x\n", tmp
.Size
);
440 if(IsEqualGUID(&tmp
.ID
, &ASF_FileProperties
))
442 _asf_read_file_properties(fp
, &FileProperties
, tmp
.Size
);
443 psong
->song_length
= le64_to_cpu(FileProperties
.PlayDuration
) / 10000;
444 psong
->bitrate
= le64_to_cpu(FileProperties
.MaxBitrate
);
445 psong
->max_bitrate
= psong
->bitrate
;
447 else if(IsEqualGUID(&tmp
.ID
, &ASF_ContentDescription
))
449 TitleLength
= fget_le16(fp
);
450 AuthorLength
= fget_le16(fp
);
451 CopyrightLength
= fget_le16(fp
);
452 DescriptionLength
= fget_le16(fp
);
453 RatingLength
= fget_le16(fp
);
455 if(_asf_load_string(fp
, ASF_VT_UNICODE
, TitleLength
, buf
, sizeof(buf
)))
458 psong
->title
= strdup(buf
);
460 if(_asf_load_string(fp
, ASF_VT_UNICODE
, AuthorLength
, buf
, sizeof(buf
)))
463 psong
->contributor
[ROLE_TRACKARTIST
] = strdup(buf
);
466 fseek(fp
, CopyrightLength
, SEEK_CUR
);
467 if(DescriptionLength
)
468 fseek(fp
, DescriptionLength
, SEEK_CUR
);
470 fseek(fp
, RatingLength
, SEEK_CUR
);
472 else if(IsEqualGUID(&tmp
.ID
, &ASF_ExtendedContentDescription
))
474 NumEntries
= fget_le16(fp
);
475 while(NumEntries
> 0)
477 NameLength
= fget_le16(fp
);
478 _asf_load_string(fp
, ASF_VT_UNICODE
, NameLength
, buf
, sizeof(buf
));
479 ValueType
= fget_le16(fp
);
480 ValueLength
= fget_le16(fp
);
482 if(!strcasecmp(buf
, "AlbumTitle") || !strcasecmp(buf
, "WM/AlbumTitle"))
484 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
486 psong
->album
= strdup(buf
);
488 else if(!strcasecmp(buf
, "AlbumArtist") || !strcasecmp(buf
, "WM/AlbumArtist"))
490 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
493 psong
->contributor
[ROLE_ALBUMARTIST
] = strdup(buf
);
496 else if(!strcasecmp(buf
, "Description") || !strcasecmp(buf
, "WM/Track"))
498 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
500 psong
->track
= atoi(buf
);
502 else if(!strcasecmp(buf
, "Genre") || !strcasecmp(buf
, "WM/Genre"))
504 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
506 psong
->genre
= strdup(buf
);
508 else if(!strcasecmp(buf
, "Year") || !strcasecmp(buf
, "WM/Year"))
510 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
512 psong
->year
= atoi(buf
);
514 else if(!strcasecmp(buf
, "WM/Director"))
516 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
518 psong
->contributor
[ROLE_CONDUCTOR
] = strdup(buf
);
520 else if(!strcasecmp(buf
, "WM/Composer"))
522 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
524 psong
->contributor
[ROLE_COMPOSER
] = strdup(buf
);
526 else if(!strcasecmp(buf
, "WM/Picture") && (ValueType
== ASF_VT_BYTEARRAY
))
528 psong
->image
= _asf_load_picture(fp
, ValueLength
, psong
->image
, &psong
->image_size
);
530 else if(!strcasecmp(buf
, "TrackNumber") || !strcasecmp(buf
, "WM/TrackNumber"))
532 if(_asf_load_string(fp
, ValueType
, ValueLength
, buf
, sizeof(buf
)))
534 psong
->track
= atoi(buf
);
536 else if(!strcasecmp(buf
, "isVBR"))
538 fseek(fp
, ValueLength
, SEEK_CUR
);
539 psong
->vbr_scale
= 0;
543 fseek(fp
, ValueLength
, SEEK_CUR
);
548 else if(IsEqualGUID(&tmp
.ID
, &ASF_StreamHeader
))
550 _asf_read_stream_object(fp
, psong
, tmp
.Size
);
552 else if(IsEqualGUID(&tmp
.ID
, &ASF_HeaderExtension
))
554 _asf_read_header_extension(fp
, psong
, tmp
.Size
);
557 fseek(fp
, pos
, SEEK_SET
);
562 if(sizeof(hdr
) == fread(&hdr
, 1, sizeof(hdr
), fp
) && IsEqualGUID(&hdr
.ID
, &ASF_DataObject
))
564 if(psong
->song_length
)
566 psong
->bitrate
= (hdr
.Size
* 8000) / psong
->song_length
;