1//========================================================================= 2// FILENAME : tagutils-asf.c 3// DESCRIPTION : ASF (wma/wmv) metadata reader 4//========================================================================= 5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved. 6//========================================================================= 7 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. 13 * 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. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24static int 25_asf_read_file_properties(FILE *fp, asf_file_properties_t *p, __u32 size) 26{ 27 int len; 28 29 len = sizeof(*p) - offsetof(asf_file_properties_t, FileID); 30 if(size < len) 31 return -1; 32 33 memset(p, 0, sizeof(*p)); 34 p->ID = ASF_FileProperties; 35 p->Size = size; 36 37 if(len != fread(&p->FileID, 1, len, fp)) 38 return -1; 39 40 return 0; 41} 42 43static int 44_asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size) 45{ 46 asf_audio_stream_t s; 47 int len; 48 49 len = sizeof(s) - sizeof(s.Hdr); 50 if(len > size) 51 len = size; 52 53 if(len != fread(&s.wfx, 1, len, fp)) 54 return -1; 55 56 psong->channels = le16_to_cpu(s.wfx.nChannels); 57 psong->bitrate = le32_to_cpu(s.wfx.nAvgBytesPerSec) * 8; 58 psong->samplerate = le32_to_cpu(s.wfx.nSamplesPerSec); 59 /* DLNA Profile Name */ 60 switch( le16_to_cpu(s.wfx.wFormatTag) ) 61 { 62 case WMAV1: 63 case WMAV2: 64 if( (psong->bitrate/1000+1) >= 385 || psong->samplerate > 48000 ) 65 asprintf(&(psong->dlna_pn), "WMAPRO"); 66 else if( (psong->bitrate / 1000)+1 < 192 ) 67 asprintf(&(psong->dlna_pn), "WMABASE"); 68 else 69 asprintf(&(psong->dlna_pn), "WMAFULL"); 70 break; 71 case WMAPRO: 72 asprintf(&(psong->dlna_pn), "WMAPRO"); 73 break; 74 default: 75 break; 76 } 77 78 return 0; 79} 80 81static int 82_asf_read_media_stream(FILE *fp, struct song_metadata *psong, __u32 size) 83{ 84 asf_media_stream_t s; 85 avi_audio_format_t wfx; 86 int len; 87 88 len = sizeof(s) - sizeof(s.Hdr); 89 if(len > size) 90 len = size; 91 92 if(len != fread(&s.MajorType, 1, len, fp)) 93 return -1; 94 95 if(IsEqualGUID(&s.MajorType, &ASF_MediaTypeAudio) && 96 IsEqualGUID(&s.FormatType, &ASF_FormatTypeWave) && s.FormatSize >= sizeof(wfx)) 97 { 98 99 if(sizeof(wfx) != fread(&wfx, 1, sizeof(wfx), fp)) 100 return -1; 101 102 psong->channels = le16_to_cpu(wfx.nChannels); 103 psong->bitrate = le32_to_cpu(wfx.nAvgBytesPerSec) * 8; 104 psong->samplerate = le32_to_cpu(wfx.nSamplesPerSec); 105 /* DLNA Profile Name */ 106 switch( le16_to_cpu(wfx.wFormatTag) ) 107 { 108 case WMAV1: 109 case WMAV2: 110 if( (psong->bitrate/1000+1) >= 385 || psong->samplerate > 48000 ) 111 asprintf(&(psong->dlna_pn), "WMAPRO"); 112 else if( (psong->bitrate / 1000)+1 < 192 ) 113 asprintf(&(psong->dlna_pn), "WMABASE"); 114 else 115 asprintf(&(psong->dlna_pn), "WMAFULL"); 116 break; 117 case WMAPRO: 118 asprintf(&(psong->dlna_pn), "WMAPRO"); 119 break; 120 default: 121 break; 122 } 123 } 124 return 0; 125} 126 127static int 128_asf_read_stream_object(FILE *fp, struct song_metadata *psong, __u32 size) 129{ 130 asf_stream_object_t s; 131 int len; 132 133 len = sizeof(s) - sizeof(asf_object_t); 134 if(size < len) 135 return -1; 136 137 if(len != fread(&s.StreamType, 1, len, fp)) 138 return -1; 139 140 if(IsEqualGUID(&s.StreamType, &ASF_AudioStream)) 141 _asf_read_audio_stream(fp, psong, s.TypeSpecificSize); 142 else if(IsEqualGUID(&s.StreamType, &ASF_StreamBufferStream)) 143 _asf_read_media_stream(fp, psong, s.TypeSpecificSize); 144 else 145 { 146 DPRINTF(E_ERROR, L_SCANNER, "Unknown asf stream type.\n"); 147 } 148 149 return 0; 150} 151 152static int 153_asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, __u32 size) 154{ 155 int i, len; 156 long off; 157 asf_object_t tmp; 158 asf_extended_stream_object_t xs; 159 asf_stream_name_t nm; 160 asf_payload_extension_t pe; 161 162 if(size < sizeof(asf_extended_stream_object_t)) 163 return -1; 164 165 len = sizeof(xs) - offsetof(asf_extended_stream_object_t, StartTime); 166 if(len != fread(&xs.StartTime, 1, len, fp)) 167 return -1; 168 off = sizeof(xs); 169 170 for(i = 0; i < xs.StreamNameCount; i++) 171 { 172 if(off + sizeof(nm) > size) 173 return -1; 174 if(sizeof(nm) != fread(&nm, 1, sizeof(nm), fp)) 175 return -1; 176 off += sizeof(nm); 177 if(off + nm.Length > sizeof(asf_extended_stream_object_t)) 178 return -1; 179 if(nm.Length > 0) 180 fseek(fp, nm.Length, SEEK_CUR); 181 off += nm.Length; 182 } 183 184 for(i = 0; i < xs.PayloadExtensionSystemCount; i++) 185 { 186 if(off + sizeof(pe) > size) 187 return -1; 188 if(sizeof(pe) != fread(&pe, 1, sizeof(pe), fp)) 189 return -1; 190 off += sizeof(pe); 191 if(pe.InfoLength > 0) 192 fseek(fp, pe.InfoLength, SEEK_CUR); 193 off += pe.InfoLength; 194 } 195 196 if(off < size) 197 { 198 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) 199 return -1; 200 if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader)) 201 _asf_read_stream_object(fp, psong, tmp.Size); 202 } 203 204 return 0; 205} 206 207static int 208_asf_read_header_extension(FILE *fp, struct song_metadata *psong, __u32 size) 209{ 210 off_t pos; 211 long off; 212 asf_header_extension_t ext; 213 asf_object_t tmp; 214 215 if(size < sizeof(asf_header_extension_t)) 216 return -1; 217 218 fread(&ext.Reserved1, 1, sizeof(ext.Reserved1), fp); 219 ext.Reserved2 = fget_le16(fp); 220 ext.DataSize = fget_le32(fp); 221 222 pos = ftell(fp); 223 off = 0; 224 while(off < ext.DataSize) 225 { 226 if(sizeof(asf_header_extension_t) + off > size) 227 break; 228 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) 229 break; 230 if(off + tmp.Size > ext.DataSize) 231 break; 232 if(IsEqualGUID(&tmp.ID, &ASF_ExtendedStreamPropertiesObject)) 233 _asf_read_extended_stream_object(fp, psong, tmp.Size); 234 235 off += tmp.Size; 236 fseek(fp, pos + off, SEEK_SET); 237 } 238 239 return 0; 240} 241 242static int 243_asf_load_string(FILE *fp, int type, int size, char *buf, int len) 244{ 245 unsigned char data[2048]; 246 __u16 wc; 247 int i, j; 248 249 i = 0; 250 if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp))) 251 { 252 253 switch(type) 254 { 255 case ASF_VT_UNICODE: 256 for(j = 0; j < size; j += 2) 257 { 258 wc = *(__s16*)&data[j]; 259 i += utf16le_to_utf8(&buf[i], len - i, wc); 260 } 261 break; 262 case ASF_VT_BYTEARRAY: 263 for(i = 0; i < size; i++) 264 { 265 if(i + 1 >= len) 266 break; 267 buf[i] = data[i]; 268 } 269 break; 270 case ASF_VT_BOOL: 271 case ASF_VT_DWORD: 272 if(size >= 4) 273 i = snprintf(buf, len, "%d", le32_to_cpu(*(__s32*)&data[0])); 274 break; 275 case ASF_VT_QWORD: 276 if(size >= 8) 277 { 278#if __WORDSIZE == 64 279 i = snprintf(buf, len, "%ld", le64_to_cpu(*(__s64*)&data[0])); 280#else 281 i = snprintf(buf, len, "%lld", le64_to_cpu(*(__s64*)&data[0])); 282#endif 283 } 284 break; 285 case ASF_VT_WORD: 286 if(size >= 2) 287 i = snprintf(buf, len, "%d", le16_to_cpu(*(__s16*)&data[0])); 288 break; 289 } 290 291 size = 0; 292 } 293 else fseek(fp, size, SEEK_CUR); 294 295 buf[i] = 0; 296 return i; 297} 298 299static void * 300_asf_load_picture(FILE *fp, int size, void *bm, int *bm_size) 301{ 302 int i; 303 char buf[256]; 304 char pic_type; 305 long pic_size; 306 307 // 308 // Picture type $xx 309 // Data length $xx $xx $xx $xx 310 // MIME type <text string> $00 311 // Description <text string> $00 312 // Picture data <binary data> 313 314 pic_type = fget_byte(fp); size -= 1; 315 pic_size = fget_le32(fp); size -= 4; 316 317 for(i = 0; i < sizeof(buf) - 1; i++) 318 { 319 buf[i] = fget_le16(fp); size -= 2; 320 if(!buf[i]) 321 break; 322 } 323 buf[i] = '\0'; 324 if(i == sizeof(buf) - 1) 325 { 326 while(fget_le16(fp)) 327 size -= 2; 328 } 329 330 if(!strcasecmp(buf, "image/jpeg") || 331 !strcasecmp(buf, "image/jpg") || 332 !strcasecmp(buf, "image/peg")) 333 { 334 335 while(0 != fget_le16(fp)) 336 size -= 2; 337 338 if(size > 0) 339 { 340 if(!(bm = malloc(size))) 341 { 342 DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size); 343 } 344 else 345 { 346 *bm_size = size; 347 if(size <= *bm_size) 348 { 349 fread(bm, 1, size, fp); 350 } 351 else 352 { 353 DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size); 354 free(bm); 355 bm = NULL; 356 } 357 } 358 } 359 else 360 { 361 DPRINTF(E_ERROR, L_SCANNER, "No binary data\n"); 362 size = 0; 363 bm = NULL; 364 } 365 } 366 else 367 { 368 DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf); 369 } 370 371 *bm_size = size; 372 return bm; 373} 374 375static int 376_get_asffileinfo(char *file, struct song_metadata *psong) 377{ 378 FILE *fp; 379 asf_object_t hdr; 380 asf_object_t tmp; 381 unsigned long NumObjects; 382 unsigned short Reserved; 383 unsigned short TitleLength; 384 unsigned short AuthorLength; 385 unsigned short CopyrightLength; 386 unsigned short DescriptionLength; 387 unsigned short RatingLength; 388 unsigned short NumEntries; 389 unsigned short NameLength; 390 unsigned short ValueType; 391 unsigned short ValueLength; 392 off_t pos; 393 char buf[2048]; 394 int mask; 395 asf_file_properties_t FileProperties; 396 397 psong->vbr_scale = -1; 398 399 if(!(fp = fopen(file, "rb"))) 400 { 401 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file); 402 return -1; 403 } 404 405 if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp)) 406 { 407 DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file); 408 fclose(fp); 409 return -1; 410 } 411 hdr.Size = le64_to_cpu(hdr.Size); 412 413 if(!IsEqualGUID(&hdr.ID, &ASF_HeaderObject)) 414 { 415 DPRINTF(E_ERROR, L_SCANNER, "Not a valid header\n"); 416 fclose(fp); 417 return -1; 418 } 419 NumObjects = fget_le32(fp); 420 Reserved = fget_le16(fp); 421 422 pos = ftell(fp); 423 mask = 0; 424 while(NumObjects > 0) 425 { 426 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) 427 break; 428 tmp.Size = le64_to_cpu(tmp.Size); 429 430 if(pos + tmp.Size > hdr.Size) 431 { 432 DPRINTF(E_ERROR, L_SCANNER, "Size overrun reading header object %I64x\n", tmp.Size); 433 break; 434 } 435 436 if(IsEqualGUID(&tmp.ID, &ASF_FileProperties)) 437 { 438 _asf_read_file_properties(fp, &FileProperties, tmp.Size); 439 psong->song_length = le64_to_cpu(FileProperties.PlayDuration) / 10000; 440 psong->bitrate = le64_to_cpu(FileProperties.MaxBitrate); 441 } 442 else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription)) 443 { 444 TitleLength = fget_le16(fp); 445 AuthorLength = fget_le16(fp); 446 CopyrightLength = fget_le16(fp); 447 DescriptionLength = fget_le16(fp); 448 RatingLength = fget_le16(fp); 449 450 if(_asf_load_string(fp, ASF_VT_UNICODE, TitleLength, buf, sizeof(buf))) 451 { 452 if(buf[0]) 453 psong->title = strdup(buf); 454 } 455 if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf))) 456 { 457 if(buf[0]) 458 psong->contributor[ROLE_TRACKARTIST] = strdup(buf); 459 } 460 if(CopyrightLength) 461 fseek(fp, CopyrightLength, SEEK_CUR); 462 if(DescriptionLength) 463 fseek(fp, DescriptionLength, SEEK_CUR); 464 if(RatingLength) 465 fseek(fp, RatingLength, SEEK_CUR); 466 } 467 else if(IsEqualGUID(&tmp.ID, &ASF_ExtendedContentDescription)) 468 { 469 NumEntries = fget_le16(fp); 470 while(NumEntries > 0) 471 { 472 NameLength = fget_le16(fp); 473 _asf_load_string(fp, ASF_VT_UNICODE, NameLength, buf, sizeof(buf)); 474 ValueType = fget_le16(fp); 475 ValueLength = fget_le16(fp); 476 477 if(!strcasecmp(buf, "AlbumTitle") || !strcasecmp(buf, "WM/AlbumTitle")) 478 { 479 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 480 if(buf[0]) 481 psong->album = strdup(buf); 482 } 483 else if(!strcasecmp(buf, "AlbumArtist") || !strcasecmp(buf, "WM/AlbumArtist")) 484 { 485 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 486 { 487 if(buf[0]) 488 psong->contributor[ROLE_ALBUMARTIST] = strdup(buf); 489 } 490 } 491 else if(!strcasecmp(buf, "Description") || !strcasecmp(buf, "WM/Track")) 492 { 493 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 494 if(buf[0]) 495 psong->track = atoi(buf); 496 } 497 else if(!strcasecmp(buf, "Genre") || !strcasecmp(buf, "WM/Genre")) 498 { 499 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 500 if(buf[0]) 501 psong->genre = strdup(buf); 502 } 503 else if(!strcasecmp(buf, "Year") || !strcasecmp(buf, "WM/Year")) 504 { 505 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 506 if(buf[0]) 507 psong->year = atoi(buf); 508 } 509 else if(!strcasecmp(buf, "WM/Director")) 510 { 511 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 512 if(buf[0]) 513 psong->contributor[ROLE_CONDUCTOR] = strdup(buf); 514 } 515 else if(!strcasecmp(buf, "WM/Picture") && (ValueType == ASF_VT_BYTEARRAY)) 516 { 517 psong->image = _asf_load_picture(fp, ValueLength, psong->image, &psong->image_size); 518 } 519 else if(!strcasecmp(buf, "TrackNumber") || !strcasecmp(buf, "WM/TrackNumber")) 520 { 521 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 522 if(buf[0]) 523 psong->track = atoi(buf); 524 } 525 else if(!strcasecmp(buf, "isVBR")) 526 { 527 fseek(fp, ValueLength, SEEK_CUR); 528 psong->vbr_scale = 0; 529 } 530 else if(ValueLength) 531 { 532 fseek(fp, ValueLength, SEEK_CUR); 533 } 534 NumEntries--; 535 } 536 } 537 else if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader)) 538 { 539 _asf_read_stream_object(fp, psong, tmp.Size); 540 } 541 else if(IsEqualGUID(&tmp.ID, &ASF_HeaderExtension)) 542 { 543 _asf_read_header_extension(fp, psong, tmp.Size); 544 } 545 pos += tmp.Size; 546 fseek(fp, pos, SEEK_SET); 547 NumObjects--; 548 } 549 550#if 0 551 if(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), fp) && IsEqualGUID(&hdr.ID, &ASF_DataObject)) 552 { 553 if(psong->song_length) 554 { 555 psong->bitrate = (hdr.Size * 8000) / psong->song_length; 556 } 557 } 558#endif 559 560 fclose(fp); 561 return 0; 562} 563