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 23static int 24_asf_read_file_properties(FILE *fp, asf_file_properties_t *p, __u32 size) 25{ 26 int len; 27 28 len = sizeof(*p) - offsetof(asf_file_properties_t, FileID); 29 if(size < len) 30 return -1; 31 32 memset(p, 0, sizeof(*p)); 33 p->ID = ASF_FileProperties; 34 p->Size = size; 35 36 if(len != fread(&p->FileID, 1, len, fp)) 37 return -1; 38 39 return 0; 40} 41 42static void 43_pick_dlna_profile(struct song_metadata *psong, uint16_t format) 44{ 45 /* DLNA Profile Name */ 46 switch( le16_to_cpu(format) ) 47 { 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; 62 } 63} 64 65static int 66_asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size) 67{ 68 asf_audio_stream_t s; 69 int len; 70 71 len = sizeof(s) - sizeof(s.Hdr); 72 if(len > size) 73 len = size; 74 75 if(len != fread(&s.wfx, 1, len, fp)) 76 return -1; 77 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); 84 85 return 0; 86} 87 88static int 89_asf_read_media_stream(FILE *fp, struct song_metadata *psong, __u32 size) 90{ 91 asf_media_stream_t s; 92 avi_audio_format_t wfx; 93 int len; 94 95 len = sizeof(s) - sizeof(s.Hdr); 96 if(len > size) 97 len = size; 98 99 if(len != fread(&s.MajorType, 1, len, fp)) 100 return -1; 101 102 if(IsEqualGUID(&s.MajorType, &ASF_MediaTypeAudio) && 103 IsEqualGUID(&s.FormatType, &ASF_FormatTypeWave) && s.FormatSize >= sizeof(wfx)) 104 { 105 106 if(sizeof(wfx) != fread(&wfx, 1, sizeof(wfx), fp)) 107 return -1; 108 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); 115 } 116 117 return 0; 118} 119 120static int 121_asf_read_stream_object(FILE *fp, struct song_metadata *psong, __u32 size) 122{ 123 asf_stream_object_t s; 124 int len; 125 126 len = sizeof(s) - sizeof(asf_object_t); 127 if(size < len) 128 return -1; 129 130 if(len != fread(&s.StreamType, 1, len, fp)) 131 return -1; 132 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)) 138 { 139 DPRINTF(E_ERROR, L_SCANNER, "Unknown asf stream type.\n"); 140 } 141 142 return 0; 143} 144 145static int 146_asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, __u32 size) 147{ 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; 154 155 if(size < sizeof(asf_extended_stream_object_t)) 156 return -1; 157 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); 162 163 for(i = 0; i < xs.StreamNameCount; i++) 164 { 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; 175 } 176 177 for(i = 0; i < xs.PayloadExtensionSystemCount; i++) 178 { 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; 187 } 188 189 if(off < size) 190 { 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); 195 } 196 197 return 0; 198} 199 200static int 201_asf_read_header_extension(FILE *fp, struct song_metadata *psong, __u32 size) 202{ 203 off_t pos; 204 long off; 205 asf_header_extension_t ext; 206 asf_object_t tmp; 207 208 if(size < sizeof(asf_header_extension_t)) 209 return -1; 210 211 fread(&ext.Reserved1, 1, sizeof(ext.Reserved1), fp); 212 ext.Reserved2 = fget_le16(fp); 213 ext.DataSize = fget_le32(fp); 214 215 pos = ftell(fp); 216 off = 0; 217 while(off < ext.DataSize) 218 { 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); 227 228 off += tmp.Size; 229 fseek(fp, pos + off, SEEK_SET); 230 } 231 232 return 0; 233} 234 235static int 236_asf_load_string(FILE *fp, int type, int size, char *buf, int len) 237{ 238 unsigned char data[2048]; 239 __u16 wc; 240 int i, j; 241 __s32 *wd32; 242 __s64 *wd64; 243 __s16 *wd16; 244 245 i = 0; 246 if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp))) 247 { 248 249 switch(type) 250 { 251 case ASF_VT_UNICODE: 252 for(j = 0; j < size; j += 2) 253 { 254 wc = *(__s16*)&data[j]; 255 i += utf16le_to_utf8(&buf[i], len - i, wc); 256 } 257 break; 258 case ASF_VT_BYTEARRAY: 259 for(i = 0; i < size; i++) 260 { 261 if(i + 1 >= len) 262 break; 263 buf[i] = data[i]; 264 } 265 break; 266 case ASF_VT_BOOL: 267 case ASF_VT_DWORD: 268 if(size >= 4) 269 { 270 wd32 = (__s32 *) &data[0]; 271 i = snprintf(buf, len, "%d", le32_to_cpu(*wd32)); 272 } 273 break; 274 case ASF_VT_QWORD: 275 if(size >= 8) 276 { 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 283 } 284 break; 285 case ASF_VT_WORD: 286 if(size >= 2) 287 { 288 wd16 = (__s16 *) &data[0]; 289 i = snprintf(buf, len, "%d", le16_to_cpu(*wd16)); 290 } 291 break; 292 } 293 294 size = 0; 295 } 296 else fseek(fp, size, SEEK_CUR); 297 298 buf[i] = 0; 299 return i; 300} 301 302static void * 303_asf_load_picture(FILE *fp, int size, void *bm, int *bm_size) 304{ 305 int i; 306 char buf[256]; 307#if 0 308 // 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> 314 315 char pic_type; 316 long pic_size; 317 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++) 325 { 326 buf[i] = fget_le16(fp); size -= 2; 327 if(!buf[i]) 328 break; 329 } 330 buf[i] = '\0'; 331 if(i == sizeof(buf) - 1) 332 { 333 while(fget_le16(fp)) 334 size -= 2; 335 } 336 337 if(!strcasecmp(buf, "image/jpeg") || 338 !strcasecmp(buf, "image/jpg") || 339 !strcasecmp(buf, "image/peg")) 340 { 341 342 while(0 != fget_le16(fp)) 343 size -= 2; 344 345 if(size > 0) 346 { 347 if(!(bm = malloc(size))) 348 { 349 DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size); 350 } 351 else 352 { 353 *bm_size = size; 354 if(size <= *bm_size) 355 { 356 fread(bm, 1, size, fp); 357 } 358 else 359 { 360 DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size); 361 free(bm); 362 bm = NULL; 363 } 364 } 365 } 366 else 367 { 368 DPRINTF(E_ERROR, L_SCANNER, "No binary data\n"); 369 size = 0; 370 bm = NULL; 371 } 372 } 373 else 374 { 375 DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf); 376 } 377 378 *bm_size = size; 379 return bm; 380} 381 382static int 383_get_asffileinfo(char *file, struct song_metadata *psong) 384{ 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; 401 402 psong->vbr_scale = -1; 403 404 if(!(fp = fopen(file, "rb"))) 405 { 406 DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file); 407 return -1; 408 } 409 410 if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp)) 411 { 412 DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file); 413 fclose(fp); 414 return -1; 415 } 416 hdr.Size = le64_to_cpu(hdr.Size); 417 418 if(!IsEqualGUID(&hdr.ID, &ASF_HeaderObject)) 419 { 420 DPRINTF(E_ERROR, L_SCANNER, "Not a valid header\n"); 421 fclose(fp); 422 return -1; 423 } 424 NumObjects = fget_le32(fp); 425 fseek(fp, 2, SEEK_CUR); // Reserved le16 426 427 pos = ftell(fp); 428 while(NumObjects > 0) 429 { 430 if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) 431 break; 432 tmp.Size = le64_to_cpu(tmp.Size); 433 434 if(pos + tmp.Size > hdr.Size) 435 { 436 DPRINTF(E_ERROR, L_SCANNER, "Size overrun reading header object %I64x\n", tmp.Size); 437 break; 438 } 439 440 if(IsEqualGUID(&tmp.ID, &ASF_FileProperties)) 441 { 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; 446 } 447 else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription)) 448 { 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); 454 455 if(_asf_load_string(fp, ASF_VT_UNICODE, TitleLength, buf, sizeof(buf))) 456 { 457 if(buf[0]) 458 psong->title = strdup(buf); 459 } 460 if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf))) 461 { 462 if(buf[0]) 463 psong->contributor[ROLE_TRACKARTIST] = strdup(buf); 464 } 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); 471 } 472 else if(IsEqualGUID(&tmp.ID, &ASF_ExtendedContentDescription)) 473 { 474 NumEntries = fget_le16(fp); 475 while(NumEntries > 0) 476 { 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); 481 482 if(!strcasecmp(buf, "AlbumTitle") || !strcasecmp(buf, "WM/AlbumTitle")) 483 { 484 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 485 if(buf[0]) 486 psong->album = strdup(buf); 487 } 488 else if(!strcasecmp(buf, "AlbumArtist") || !strcasecmp(buf, "WM/AlbumArtist")) 489 { 490 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 491 { 492 if(buf[0]) 493 psong->contributor[ROLE_ALBUMARTIST] = strdup(buf); 494 } 495 } 496 else if(!strcasecmp(buf, "Description") || !strcasecmp(buf, "WM/Track")) 497 { 498 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 499 if(buf[0]) 500 psong->track = atoi(buf); 501 } 502 else if(!strcasecmp(buf, "Genre") || !strcasecmp(buf, "WM/Genre")) 503 { 504 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 505 if(buf[0]) 506 psong->genre = strdup(buf); 507 } 508 else if(!strcasecmp(buf, "Year") || !strcasecmp(buf, "WM/Year")) 509 { 510 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 511 if(buf[0]) 512 psong->year = atoi(buf); 513 } 514 else if(!strcasecmp(buf, "WM/Director")) 515 { 516 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 517 if(buf[0]) 518 psong->contributor[ROLE_CONDUCTOR] = strdup(buf); 519 } 520 else if(!strcasecmp(buf, "WM/Composer")) 521 { 522 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 523 if(buf[0]) 524 psong->contributor[ROLE_COMPOSER] = strdup(buf); 525 } 526 else if(!strcasecmp(buf, "WM/Picture") && (ValueType == ASF_VT_BYTEARRAY)) 527 { 528 psong->image = _asf_load_picture(fp, ValueLength, psong->image, &psong->image_size); 529 } 530 else if(!strcasecmp(buf, "TrackNumber") || !strcasecmp(buf, "WM/TrackNumber")) 531 { 532 if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf))) 533 if(buf[0]) 534 psong->track = atoi(buf); 535 } 536 else if(!strcasecmp(buf, "isVBR")) 537 { 538 fseek(fp, ValueLength, SEEK_CUR); 539 psong->vbr_scale = 0; 540 } 541 else if(ValueLength) 542 { 543 fseek(fp, ValueLength, SEEK_CUR); 544 } 545 NumEntries--; 546 } 547 } 548 else if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader)) 549 { 550 _asf_read_stream_object(fp, psong, tmp.Size); 551 } 552 else if(IsEqualGUID(&tmp.ID, &ASF_HeaderExtension)) 553 { 554 _asf_read_header_extension(fp, psong, tmp.Size); 555 } 556 pos += tmp.Size; 557 fseek(fp, pos, SEEK_SET); 558 NumObjects--; 559 } 560 561#if 0 562 if(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), fp) && IsEqualGUID(&hdr.ID, &ASF_DataObject)) 563 { 564 if(psong->song_length) 565 { 566 psong->bitrate = (hdr.Size * 8000) / psong->song_length; 567 } 568 } 569#endif 570 571 fclose(fp); 572 return 0; 573} 574