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, see <http://www.gnu.org/licenses/>. 21 */ 22#ifdef HAVE_MACHINE_ENDIAN_H 23#include <machine/endian.h> 24#else 25#include <endian.h> 26#endif 27 28static inline uint16_t 29le16_to_cpu(uint16_t le16) 30{ 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 37} 38 39static inline uint32_t 40le32_to_cpu(uint32_t le32) 41{ 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 52} 53 54static inline uint64_t 55le64_to_cpu(uint64_t le64) 56{ 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 73} 74 75static inline uint32_t 76cpu_to_be32(uint32_t cpu32) 77{ 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 88} 89 90static inline uint8_t 91fget_byte(FILE *fp) 92{ 93 uint8_t d; 94 95 if (fread(&d, sizeof(d), 1, fp) != 1) 96 return 0; 97 return d; 98} 99 100static inline uint16_t 101fget_le16(FILE *fp) 102{ 103 uint16_t d; 104 105 if (fread(&d, sizeof(d), 1, fp) != 1) 106 return 0; 107 d = le16_to_cpu(d); 108 return d; 109} 110 111static inline uint32_t 112fget_le32(FILE *fp) 113{ 114 uint32_t d; 115 116 if (fread(&d, sizeof(d), 1, fp) != 1) 117 return 0; 118 d = le32_to_cpu(d); 119 return d; 120} 121 122// NOTE: support U+0000 ~ U+FFFF only. 123static int 124utf16le_to_utf8(char *dst, int n, uint16_t utf16le) 125{ 126 uint16_t wc = le16_to_cpu(utf16le); 127 if (wc < 0x80) 128 { 129 if (n < 1) 130 return 0; 131 *dst++ = wc & 0xff; 132 return 1; 133 } 134 else if (wc < 0x800) 135 { 136 if (n < 2) 137 return 0; 138 *dst++ = 0xc0 | (wc>>6); 139 *dst++ = 0x80 | (wc & 0x3f); 140 return 2; 141 } 142 else 143 { 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; 150 } 151} 152 153static int 154_asf_read_file_properties(FILE *fp, asf_file_properties_t *p, uint32_t size) 155{ 156 int len; 157 158 len = sizeof(*p) - offsetof(asf_file_properties_t, FileID); 159 if(size < len) 160 return -1; 161 162 memset(p, 0, sizeof(*p)); 163 p->ID = ASF_FileProperties; 164 p->Size = size; 165 166 if(len != fread(&p->FileID, 1, len, fp)) 167 return -1; 168 169 return 0; 170} 171 172static void 173_pick_dlna_profile(struct song_metadata *psong, uint16_t format) 174{ 175 /* DLNA Profile Name */ 176 switch( le16_to_cpu(format) ) 177 { 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; 192 } 193} 194 195static int 196_asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size) 197{ 198 asf_audio_stream_t s; 199 int len; 200 201 len = sizeof(s) - sizeof(s.Hdr); 202 if(len > size) 203 len = size; 204 205 if(len != fread(&s.wfx, 1, len, fp)) 206 return -1; 207 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); 214 215 return 0; 216} 217 218static int 219_asf_read_media_stream(FILE *fp, struct song_metadata *psong, uint32_t size) 220{ 221 asf_media_stream_t s; 222 avi_audio_format_t wfx; 223 int len; 224 225 len = sizeof(s) - sizeof(s.Hdr); 226 if(len > size) 227 len = size; 228 229 memset(&s, 0, sizeof(s)); 230 231 if(len != fread(&s.MajorType, 1, len, fp)) 232 return -1; 233 234 if(IsEqualGUID(&s.MajorType, &ASF_MediaTypeAudio) && 235 IsEqualGUID(&s.FormatType, &ASF_FormatTypeWave) && s.FormatSize >= sizeof(wfx)) 236 { 237 238 if(sizeof(wfx) != fread(&wfx, 1, sizeof(wfx), fp)) 239 return -1; 240 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); 247 } 248 249 return 0; 250} 251 252static int 253_asf_read_stream_object(FILE *fp, struct song_metadata *psong, uint32_t size) 254{ 255 asf_stream_object_t s; 256 int len; 257 258 len = sizeof(s) - sizeof(asf_object_t); 259 if(size < len) 260 return -1; 261 262 memset(&s, 0, sizeof(s)); 263 264 if(len != fread(&s.StreamType, 1, len, fp)) 265 return -1; 266 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)) 272 { 273 DPRINTF(E_ERROR, L_SCANNER, "Unknown asf stream type.\n"); 274 } 275 276 return 0; 277} 278 279static int 280_asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, uint32_t size) 281{ 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; 288 289 if(size < sizeof(asf_extended_stream_object_t)) 290 return -1; 291 292 memset(&xs, 0, sizeof(xs)); 293 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); 298 299 for(i = 0; i < xs.StreamNameCount; i++) 300 { 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; 311 } 312 313 for(i = 0; i < xs.PayloadExtensionSystemCount; i++) 314 { 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; 323 } 324 325 if(off < size) 326 { 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); 331 } 332 333 return 0; 334} 335 336static int 337_asf_read_header_extension(FILE *fp, struct song_metadata *psong, uint32_t size) 338{ 339 off_t pos; 340 long off; 341 asf_header_extension_t ext; 342 asf_object_t tmp; 343 344 if(size < sizeof(asf_header_extension_t)) 345 return -1; 346 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); 351 352 pos = ftell(fp); 353 off = 0; 354 while(off < ext.DataSize) 355 { 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); 364 365 off += tmp.Size; 366 fseek(fp, pos + off, SEEK_SET); 367 } 368 369 return 0; 370} 371 372static int 373_asf_load_string(FILE *fp, int type, int size, char *buf, int len) 374{ 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; 381 382 i = 0; 383 if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp))) 384 { 385 386 switch(type) 387 { 388 case ASF_VT_UNICODE: 389 for(j = 0; j < size; j += 2) 390 { 391 wd16 = (int16_t *) &data[j]; 392 wc = (uint16_t)*wd16; 393 i += utf16le_to_utf8(&buf[i], len - i, wc); 394 } 395 break; 396 case ASF_VT_BYTEARRAY: 397 for(i = 0; i < size; i++) 398 { 399 if(i + 1 >= len) 400 break; 401 buf[i] = data[i]; 402 } 403 break; 404 case ASF_VT_BOOL: 405 case ASF_VT_DWORD: 406 if(size >= 4) 407 { 408 wd32 = (int32_t *) &data[0]; 409 i = snprintf(buf, len, "%d", le32_to_cpu(*wd32)); 410 } 411 break; 412 case ASF_VT_QWORD: 413 if(size >= 8) 414 { 415 wd64 = (int64_t *) &data[0]; 416 i = snprintf(buf, len, "%lld", (long long)le64_to_cpu(*wd64)); 417 } 418 break; 419 case ASF_VT_WORD: 420 if(size >= 2) 421 { 422 wd16 = (int16_t *) &data[0]; 423 i = snprintf(buf, len, "%d", le16_to_cpu(*wd16)); 424 } 425 break; 426 } 427 428 size = 0; 429 } 430 else fseek(fp, size, SEEK_CUR); 431 432 buf[i] = 0; 433 return i; 434} 435 436static void * 437_asf_load_picture(FILE *fp, int size, void *bm, int *bm_size) 438{ 439 int i; 440 char buf[256]; 441#if 0 442 // 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> 448 449 char pic_type; 450 long pic_size; 451 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++) 459 { 460 buf[i] = fget_le16(fp); size -= 2; 461 if(!buf[i]) 462 break; 463 } 464 buf[i] = '\0'; 465 if(i == sizeof(buf) - 1) 466 { 467 while(fget_le16(fp)) 468 size -= 2; 469 } 470 471 if(!strcasecmp(buf, "image/jpeg") || 472 !strcasecmp(buf, "image/jpg") || 473 !strcasecmp(buf, "image/peg")) 474 { 475 476 while(0 != fget_le16(fp)) 477 size -= 2; 478 479 if(size > 0) 480 { 481 if(!(bm = malloc(size))) 482 { 483 DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size); 484 } 485 else 486 { 487 *bm_size = size; 488 if(size > *bm_size || fread(bm, 1, size, fp) != size) 489 { 490 DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size); 491 free(bm); 492 bm = NULL; 493 } 494 } 495 } 496 else 497 { 498 DPRINTF(E_ERROR, L_SCANNER, "No binary data\n"); 499 size = 0; 500 bm = NULL; 501 } 502 } 503 else 504 { 505 DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf); 506 } 507 508 *bm_size = size; 509 return bm; 510} 511 512static int 513_get_asffileinfo(char *file, struct song_metadata *psong) 514{ 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; 531 532 psong->vbr_scale = -1; 533 534 if(!(fp = fopen(file, "rb"))) 535 { 536 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file); 537 return -1; 538 } 539 540 if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp)) 541 { 542 DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file); 543 fclose(fp); 544 return -1; 545 } 546 hdr.Size = le64_to_cpu(hdr.Size); 547 548 if(!IsEqualGUID(&hdr.ID, &ASF_HeaderObject)) 549 { 550 DPRINTF(E_ERROR, L_SCANNER, "Not a valid header\n"); 551 fclose(fp); 552 return -1; 553 } 554 NumObjects = fget_le32(fp); 555 fseek(fp, 2, SEEK_CUR); // Reserved le16 556 557 pos = ftell(fp); 558 while(NumObjects > 0) 559 { 560 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) 561 break; 562 tmp.Size = le64_to_cpu(tmp.Size); 563 564 if(pos + tmp.Size > hdr.Size) 565 { 566 DPRINTF(E_ERROR, L_SCANNER, "Size overrun reading header object %llu\n", 567 (unsigned long long)tmp.Size); 568 break; 569 } 570 571 if(IsEqualGUID(&tmp.ID, &ASF_FileProperties)) 572 { 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; 577 } 578 else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription)) 579 { 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); 585 586 if(_asf_load_string(fp, ASF_VT_UNICODE, TitleLength, buf, sizeof(buf))) 587 { 588 if(buf[0]) 589 psong->title = strdup(buf); 590 } 591 if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf))) 592 { 593 if(buf[0]) 594 psong->contributor[ROLE_TRACKARTIST] = strdup(buf); 595 } 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); 602 } 603 else if(IsEqualGUID(&tmp.ID, &ASF_ExtendedContentDescription)) 604 { 605 NumEntries = fget_le16(fp); 606 while(NumEntries > 0) 607 { 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); 612 613 if(!strcasecmp(buf, "AlbumTitle") || !strcasecmp(buf, "WM/AlbumTitle")) 614 { 615 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 616 if(buf[0]) 617 psong->album = strdup(buf); 618 } 619 else if(!strcasecmp(buf, "AlbumArtist") || !strcasecmp(buf, "WM/AlbumArtist")) 620 { 621 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 622 { 623 if(buf[0]) 624 psong->contributor[ROLE_ALBUMARTIST] = strdup(buf); 625 } 626 } 627 else if(!strcasecmp(buf, "Description") || !strcasecmp(buf, "WM/Track")) 628 { 629 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 630 if(buf[0]) 631 psong->track = atoi(buf); 632 } 633 else if(!strcasecmp(buf, "Genre") || !strcasecmp(buf, "WM/Genre")) 634 { 635 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 636 if(buf[0]) 637 psong->genre = strdup(buf); 638 } 639 else if(!strcasecmp(buf, "Year") || !strcasecmp(buf, "WM/Year")) 640 { 641 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 642 if(buf[0]) 643 psong->year = atoi(buf); 644 } 645 else if(!strcasecmp(buf, "WM/Director")) 646 { 647 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 648 if(buf[0]) 649 psong->contributor[ROLE_CONDUCTOR] = strdup(buf); 650 } 651 else if(!strcasecmp(buf, "WM/Composer")) 652 { 653 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 654 if(buf[0]) 655 psong->contributor[ROLE_COMPOSER] = strdup(buf); 656 } 657 else if(!strcasecmp(buf, "WM/Picture") && (ValueType == ASF_VT_BYTEARRAY)) 658 { 659 psong->image = _asf_load_picture(fp, ValueLength, psong->image, &psong->image_size); 660 } 661 else if(!strcasecmp(buf, "TrackNumber") || !strcasecmp(buf, "WM/TrackNumber")) 662 { 663 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 664 if(buf[0]) 665 psong->track = atoi(buf); 666 } 667 else if(!strcasecmp(buf, "isVBR")) 668 { 669 fseek(fp, ValueLength, SEEK_CUR); 670 psong->vbr_scale = 0; 671 } 672 else if(ValueLength) 673 { 674 fseek(fp, ValueLength, SEEK_CUR); 675 } 676 NumEntries--; 677 } 678 } 679 else if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader)) 680 { 681 _asf_read_stream_object(fp, psong, tmp.Size); 682 } 683 else if(IsEqualGUID(&tmp.ID, &ASF_HeaderExtension)) 684 { 685 _asf_read_header_extension(fp, psong, tmp.Size); 686 } 687 pos += tmp.Size; 688 fseek(fp, pos, SEEK_SET); 689 NumObjects--; 690 } 691 692#if 0 693 if(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), fp) && IsEqualGUID(&hdr.ID, &ASF_DataObject)) 694 { 695 if(psong->song_length) 696 { 697 psong->bitrate = (hdr.Size * 8000) / psong->song_length; 698 } 699 } 700#endif 701 702 fclose(fp); 703 return 0; 704} 705