Merge branch 'Teaman-ND' into Teaman-RT
[tomato.git] / release / src / router / minidlna / tagutils / tagutils-asf.c
blob11da1ae5301510d2a00d151bff7b6e10dfecaec7
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/>.
23 static int
24 _asf_read_file_properties(FILE *fp, asf_file_properties_t *p, __u32 size)
26 int len;
28 len = sizeof(*p) - offsetof(asf_file_properties_t, FileID);
29 if(size < len)
30 return -1;
32 memset(p, 0, sizeof(*p));
33 p->ID = ASF_FileProperties;
34 p->Size = size;
36 if(len != fread(&p->FileID, 1, len, fp))
37 return -1;
39 return 0;
42 static void
43 _pick_dlna_profile(struct song_metadata *psong, uint16_t format)
45 /* DLNA Profile Name */
46 switch( le16_to_cpu(format) )
48 case WMA:
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");
53 break;
54 case WMAPRO:
55 asprintf(&(psong->dlna_pn), "WMAPRO");
56 break;
57 case WMALSL:
58 asprintf(&(psong->dlna_pn), "WMALSL%s",
59 psong->channels > 2 ? "_MULT5" : "");
60 default:
61 break;
65 static int
66 _asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size)
68 asf_audio_stream_t s;
69 int len;
71 len = sizeof(s) - sizeof(s.Hdr);
72 if(len > size)
73 len = size;
75 if(len != fread(&s.wfx, 1, len, fp))
76 return -1;
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);
85 return 0;
88 static int
89 _asf_read_media_stream(FILE *fp, struct song_metadata *psong, __u32 size)
91 asf_media_stream_t s;
92 avi_audio_format_t wfx;
93 int len;
95 len = sizeof(s) - sizeof(s.Hdr);
96 if(len > size)
97 len = size;
99 if(len != fread(&s.MajorType, 1, len, fp))
100 return -1;
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))
107 return -1;
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);
117 return 0;
120 static int
121 _asf_read_stream_object(FILE *fp, struct song_metadata *psong, __u32 size)
123 asf_stream_object_t s;
124 int len;
126 len = sizeof(s) - sizeof(asf_object_t);
127 if(size < len)
128 return -1;
130 if(len != fread(&s.StreamType, 1, len, fp))
131 return -1;
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");
142 return 0;
145 static int
146 _asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, __u32 size)
148 int i, len;
149 long off;
150 asf_object_t tmp;
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))
156 return -1;
158 len = sizeof(xs) - offsetof(asf_extended_stream_object_t, StartTime);
159 if(len != fread(&xs.StartTime, 1, len, fp))
160 return -1;
161 off = sizeof(xs);
163 for(i = 0; i < xs.StreamNameCount; i++)
165 if(off + sizeof(nm) > size)
166 return -1;
167 if(sizeof(nm) != fread(&nm, 1, sizeof(nm), fp))
168 return -1;
169 off += sizeof(nm);
170 if(off + nm.Length > sizeof(asf_extended_stream_object_t))
171 return -1;
172 if(nm.Length > 0)
173 fseek(fp, nm.Length, SEEK_CUR);
174 off += nm.Length;
177 for(i = 0; i < xs.PayloadExtensionSystemCount; i++)
179 if(off + sizeof(pe) > size)
180 return -1;
181 if(sizeof(pe) != fread(&pe, 1, sizeof(pe), fp))
182 return -1;
183 off += sizeof(pe);
184 if(pe.InfoLength > 0)
185 fseek(fp, pe.InfoLength, SEEK_CUR);
186 off += pe.InfoLength;
189 if(off < size)
191 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
192 return -1;
193 if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader))
194 _asf_read_stream_object(fp, psong, tmp.Size);
197 return 0;
200 static int
201 _asf_read_header_extension(FILE *fp, struct song_metadata *psong, __u32 size)
203 off_t pos;
204 long off;
205 asf_header_extension_t ext;
206 asf_object_t tmp;
208 if(size < sizeof(asf_header_extension_t))
209 return -1;
211 fread(&ext.Reserved1, 1, sizeof(ext.Reserved1), fp);
212 ext.Reserved2 = fget_le16(fp);
213 ext.DataSize = fget_le32(fp);
215 pos = ftell(fp);
216 off = 0;
217 while(off < ext.DataSize)
219 if(sizeof(asf_header_extension_t) + off > size)
220 break;
221 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
222 break;
223 if(off + tmp.Size > ext.DataSize)
224 break;
225 if(IsEqualGUID(&tmp.ID, &ASF_ExtendedStreamPropertiesObject))
226 _asf_read_extended_stream_object(fp, psong, tmp.Size);
228 off += tmp.Size;
229 fseek(fp, pos + off, SEEK_SET);
232 return 0;
235 static int
236 _asf_load_string(FILE *fp, int type, int size, char *buf, int len)
238 unsigned char data[2048];
239 __u16 wc;
240 int i, j;
241 __s32 *wd32;
242 __s64 *wd64;
243 __s16 *wd16;
245 i = 0;
246 if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp)))
249 switch(type)
251 case ASF_VT_UNICODE:
252 for(j = 0; j < size; j += 2)
254 wc = *(__s16*)&data[j];
255 i += utf16le_to_utf8(&buf[i], len - i, wc);
257 break;
258 case ASF_VT_BYTEARRAY:
259 for(i = 0; i < size; i++)
261 if(i + 1 >= len)
262 break;
263 buf[i] = data[i];
265 break;
266 case ASF_VT_BOOL:
267 case ASF_VT_DWORD:
268 if(size >= 4)
270 wd32 = (__s32 *) &data[0];
271 i = snprintf(buf, len, "%d", le32_to_cpu(*wd32));
273 break;
274 case ASF_VT_QWORD:
275 if(size >= 8)
277 wd64 = (__s64 *) &data[0];
278 #if __WORDSIZE == 64
279 i = snprintf(buf, len, "%ld", le64_to_cpu(*wd64));
280 #else
281 i = snprintf(buf, len, "%lld", le64_to_cpu(*wd64));
282 #endif
284 break;
285 case ASF_VT_WORD:
286 if(size >= 2)
288 wd16 = (__s16 *) &data[0];
289 i = snprintf(buf, len, "%d", le16_to_cpu(*wd16));
291 break;
294 size = 0;
296 else fseek(fp, size, SEEK_CUR);
298 buf[i] = 0;
299 return i;
302 static void *
303 _asf_load_picture(FILE *fp, int size, void *bm, int *bm_size)
305 int i;
306 char buf[256];
307 #if 0
309 // Picture type $xx
310 // Data length $xx $xx $xx $xx
311 // MIME type <text string> $00
312 // Description <text string> $00
313 // Picture data <binary data>
315 char pic_type;
316 long pic_size;
318 pic_type = fget_byte(fp); size -= 1;
319 pic_size = fget_le32(fp); size -= 4;
320 #else
321 fseek(fp, 5, SEEK_CUR);
322 size -= 5;
323 #endif
324 for(i = 0; i < sizeof(buf) - 1; i++)
326 buf[i] = fget_le16(fp); size -= 2;
327 if(!buf[i])
328 break;
330 buf[i] = '\0';
331 if(i == sizeof(buf) - 1)
333 while(fget_le16(fp))
334 size -= 2;
337 if(!strcasecmp(buf, "image/jpeg") ||
338 !strcasecmp(buf, "image/jpg") ||
339 !strcasecmp(buf, "image/peg"))
342 while(0 != fget_le16(fp))
343 size -= 2;
345 if(size > 0)
347 if(!(bm = malloc(size)))
349 DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size);
351 else
353 *bm_size = size;
354 if(size <= *bm_size)
356 fread(bm, 1, size, fp);
358 else
360 DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size);
361 free(bm);
362 bm = NULL;
366 else
368 DPRINTF(E_ERROR, L_SCANNER, "No binary data\n");
369 size = 0;
370 bm = NULL;
373 else
375 DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf);
378 *bm_size = size;
379 return bm;
382 static int
383 _get_asffileinfo(char *file, struct song_metadata *psong)
385 FILE *fp;
386 asf_object_t hdr;
387 asf_object_t tmp;
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;
398 off_t pos;
399 char buf[2048];
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);
407 return -1;
410 if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp))
412 DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file);
413 fclose(fp);
414 return -1;
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");
421 fclose(fp);
422 return -1;
424 NumObjects = fget_le32(fp);
425 fseek(fp, 2, SEEK_CUR); // Reserved le16
427 pos = ftell(fp);
428 while(NumObjects > 0)
430 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
431 break;
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);
437 break;
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)))
457 if(buf[0])
458 psong->title = strdup(buf);
460 if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf)))
462 if(buf[0])
463 psong->contributor[ROLE_TRACKARTIST] = strdup(buf);
465 if(CopyrightLength)
466 fseek(fp, CopyrightLength, SEEK_CUR);
467 if(DescriptionLength)
468 fseek(fp, DescriptionLength, SEEK_CUR);
469 if(RatingLength)
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)))
485 if(buf[0])
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)))
492 if(buf[0])
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)))
499 if(buf[0])
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)))
505 if(buf[0])
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)))
511 if(buf[0])
512 psong->year = atoi(buf);
514 else if(!strcasecmp(buf, "WM/Director"))
516 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
517 if(buf[0])
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)))
523 if(buf[0])
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)))
533 if(buf[0])
534 psong->track = atoi(buf);
536 else if(!strcasecmp(buf, "isVBR"))
538 fseek(fp, ValueLength, SEEK_CUR);
539 psong->vbr_scale = 0;
541 else if(ValueLength)
543 fseek(fp, ValueLength, SEEK_CUR);
545 NumEntries--;
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);
556 pos += tmp.Size;
557 fseek(fp, pos, SEEK_SET);
558 NumObjects--;
561 #if 0
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;
569 #endif
571 fclose(fp);
572 return 0;