1 /*
2 *
3 * File: metatag_info.c
4 *
5 * Copyright (C) 2009-2013 Darran Kartaschew
6 *
7 * This file is part of the gMTP package.
8 *
9 * gMTP is free software; you can redistribute it and/or modify
10 * it under the terms of the BSD License as included within the
11 * file 'COPYING' located in the root directory
12 *
13 */
14
15 #include "metatag_info.h"
16
17 #include <sys/stat.h>
18 #include <string.h>
19 #include <id3tag.h>
20 #include <stdio.h>
21 #include <FLAC/all.h>
22 #include <vorbis/vorbisfile.h>
23
24 // Constants needed for MP3 frame calculations.
25 int mp3_samplerate[3][4] = {
26 {22050, 24000, 16000, 50000}, // MPEG 2.0
27 {44100, 48000, 32000, 50000}, // MPEG 1.0
28 {11025, 12000, 8000, 50000} // MPEG 2.5
29 };
30
31 int mp3_bitrate[2][3][15] = {
32 { // MPEG 2.0
33 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer 3
34 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, // Layer 2
35 {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256} // Layer 1
36 },
37 { // MPEG 1.0
38 {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}, // Layer 3
39 {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, // Layer 2
40 {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448} // Layer 1
41 }
42 };
43
44 int mp3_sampleperframe[3][4] = {
45 // MP2.5 , res , MP2, MP1
46 {576, 0, 576, 1152}, // Layer 3
47 {1152, 0, 1152, 1152}, // Layer 2
48 {384, 0, 384, 384} // Layer 1
49
50 };
51
52 // ************************************************************************************************
53
54 /**
55 * Returns the Frame Text for a given tag.
56 * @param tag
57 * @param frame_name
58 * @return
59 */
ID3_getFrameText(struct id3_tag * tag,char * frame_name)60 gchar * ID3_getFrameText(struct id3_tag *tag, char *frame_name) {
61 const id3_ucs4_t *id3_string;
62 struct id3_frame *id3_frame;
63 union id3_field *id3_field;
64 gchar *rtn_string = NULL;
65 enum id3_field_textencoding id3_field_encoding = ID3_FIELD_TEXTENCODING_ISO_8859_1;
66
67 id3_frame = id3_tag_findframe(tag, frame_name, 0);
68 if (id3_frame == NULL)
69 return NULL;
70
71 id3_field = id3_frame_field(id3_frame, 0);
72 if (id3_field && (id3_field_type(id3_field) == ID3_FIELD_TYPE_TEXTENCODING)) {
73 id3_field_encoding = id3_field->number.value;
74 }
75 //if (frame_name == ID3_FRAME_COMMENT) {
76 if (g_ascii_strcasecmp(frame_name, ID3_FRAME_COMMENT) == 0) {
77 id3_field = id3_frame_field(id3_frame, 3);
78 } else {
79 id3_field = id3_frame_field(id3_frame, 1);
80 }
81 if (id3_field == NULL)
82 return NULL;
83
84 if (g_ascii_strcasecmp(frame_name, ID3_FRAME_COMMENT) == 0) {
85 id3_string = id3_field_getfullstring(id3_field);
86 } else {
87 id3_string = id3_field_getstrings(id3_field, 0);
88 }
89 if (id3_string == NULL)
90 return NULL;
91
92 if (g_ascii_strcasecmp(frame_name, ID3_FRAME_GENRE) == 0)
93 id3_string = id3_genre_name(id3_string);
94
95 if (id3_field_encoding == ID3_FIELD_TEXTENCODING_ISO_8859_1) {
96 rtn_string = (gchar *) id3_ucs4_latin1duplicate(id3_string);
97 } else {
98 rtn_string = (gchar *) id3_ucs4_utf8duplicate(id3_string);
99 }
100 return rtn_string;
101 }
102
103 // ************************************************************************************************
104
105 /**
106 * Returns header infomation from a MP3 file.
107 * @param mp3_file File handle to an open MP3 file.
108 * @param header_info Variable to hold all the MP3 header information for this MP3 frame.
109 * @return TRUE if successful, otherwise FALSE.
110 */
get_mp3_header(FILE * mp3_file,MP3_header * header_info)111 gboolean get_mp3_header(FILE * mp3_file, MP3_header * header_info) {
112 uint8_t raw_header[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
113 uint32_t framesize = 0;
114 uint32_t id3_footer = 0;
115 uint32_t id3_size = 0;
116
117 resync:
118 if (fread(&raw_header, sizeof (uint8_t)*10, 1, mp3_file) < 1) {
119 header_info->header_sync = 0;
120 return FALSE;
121 }
122 /*
123 // Print header info for debugging
124 printf("Offset: %llx : ", ftell(mp3_file) - 10);
125 for (int i = 0; i < 10; i++) {
126 printf("0x%x ", raw_header[i]);
127 }
128 printf("\n");
129 */
130
131 header_info->header_sync = ((raw_header[0] << 4) | ((raw_header[1]&0xE0) >> 4));
132
133 // Check for ID3 Tags
134 while (header_info->header_sync != 0xFFE) {
135 // We may have ID3 Tag.
136 if (raw_header[0] == 'I' && raw_header[1] == 'D' && raw_header[2] == '3') {
137 // We have a ID3 header...
138 // Now skip it.
139 id3_footer = (raw_header[5] >> 4) & 0x1;
140 id3_size = ((uint32_t) (raw_header[6] << 21) +
141 (uint32_t) (raw_header[7] << 14) +
142 (uint32_t) (raw_header[8] << 7) +
143 (uint32_t) (raw_header[9]));
144 if (id3_footer == 1) {
145 fseek(mp3_file, (id3_size + 10), SEEK_CUR);
146 } else {
147 fseek(mp3_file, (id3_size), SEEK_CUR);
148 }
149 if (fread(&raw_header, sizeof (uint8_t)*10, 1, mp3_file) < 1) {
150 header_info->header_sync = 0;
151 return FALSE;
152 }
153 /*
154 // Print header info for debugging
155 printf("Offset: %llx : ", ftell(mp3_file) - 10);
156 for (int i = 0; i < 10; i++) {
157 printf("0x%x ", raw_header[i]);
158 }
159 printf("\n");
160 */
161 header_info->header_sync = ((raw_header[0] << 4) | ((raw_header[1]&0xE0) >> 4));
162 } else {
163 // Not a valid frame, nor an ID3 frame?
164 // Attempt resync;
165 header_info->header_sync = 0;
166 fseek(mp3_file, -9, SEEK_CUR);
167 goto resync;
168 //return FALSE;
169 }
170 }
171
172 header_info->version = (raw_header[1] >> 3) & 0x3;
173 header_info->layer = (raw_header[1] >> 1) & 0x3;
174 header_info->crc = raw_header[1] & 0x1;
175 header_info->bitrate = (raw_header[2] >> 4) & 0xF;
176 header_info->samplerate = (raw_header[2] >> 2) & 0x3;
177 header_info->padding = (raw_header[2] >> 1) & 0x1;
178 header_info->private_bit = (raw_header[2]) & 0x1;
179 header_info->channel_mode = (raw_header[3] >> 6) & 0x3;
180 header_info->mode_extension = (raw_header[3] >> 4) & 0x3;
181 header_info->copyright = (raw_header[3] >> 3) & 0x1;
182 header_info->original = (raw_header[3] >> 2) & 0x1;
183 header_info->emphasis = (raw_header[3]) & 0x3;
184
185 // Sanity checks --
186 if ((header_info->header_sync != 0xFFE)
187 || (header_info->version == 0x1) // Reserved value
188 //|| (header_info->layer == 0x1) // We only care about layer 3 data
189 || (header_info->bitrate == 0xF) // Bad value
190 || (header_info->samplerate == 0x3)) { // Reserved value
191 header_info->header_sync = 0;
192 // Attempt resync;
193 header_info->header_sync = 0;
194 fseek(mp3_file, -9, SEEK_CUR);
195 goto resync;
196 //return FALSE;
197 }
198 // We have a valid header, so forward to next possible frame.
199 // FrameSize = (samples per sec / 8) * BitRate / (SampleRate + Padding).
200
201 // Layer I
202 if (header_info->layer == 3) {
203 framesize = ((12 * mp3_bitrate[header_info->version & 0x1][header_info->layer - 1][header_info->bitrate] * 1000
204 / mp3_samplerate[header_info->version & 0x1][header_info->samplerate]
205 ) + header_info->padding) * 4;
206 } else {
207 // Layer 2 and Layer 3
208 framesize = (mp3_sampleperframe[header_info->layer - 1][header_info->version] / 8) // Samples per frame.
209 * mp3_bitrate[header_info->version & 0x1][header_info->layer - 1][header_info->bitrate] * 1000 // Bitrate
210 / (mp3_samplerate[header_info->version & 0x1][header_info->samplerate] // Sample Rate
211 ) + header_info->padding; // Padding bit
212 }
213 fseek(mp3_file, (framesize - 10), SEEK_CUR);
214 return TRUE;
215 }
216
217 // ************************************************************************************************
218
219 /**
220 * Get our file information for an MP3 based file.
221 * @param filename
222 * @param mp3_struct
223 */
get_mp3_info(gchar * filename,MP3_Info * mp3_struct)224 void get_mp3_info(gchar *filename, MP3_Info *mp3_struct) {
225
226 FILE * mp3_file = NULL;
227 struct stat sb;
228 MP3_header header_info;
229 //uint32_t initial_bitrate = 0;
230 //uint32_t new_bitrate = 0;
231 //uint64_t total_bitrate = 0;
232 //uint32_t frames_sampled = 0;
233 uint64_t filesize = 0;
234
235 // Init our struct that has been passed to us, and return defaults even if
236 // things go wrong.
237 mp3_struct->VBR = 1;
238 mp3_struct->bitrate = 0;
239 mp3_struct->channels = 0;
240 mp3_struct->duration = 0;
241
242 mp3_file = fopen(filename, "r");
243 if (mp3_file == NULL)
244 return;
245
246 if (stat(filename, &sb) == -1) {
247 perror("stat");
248 fclose(mp3_file);
249 return;
250 }
251
252 filesize = sb.st_size;
253
254 if (get_mp3_header(mp3_file, &header_info) == TRUE) {
255 uint32_t initial_bitrate = mp3_bitrate[header_info.version & 0x1][header_info.layer - 1][header_info.bitrate];
256 uint64_t total_bitrate = initial_bitrate;
257 uint32_t frames_sampled = 1;
258 if (header_info.channel_mode == 0x3) {
259 mp3_struct->channels = 1;
260 } else {
261 mp3_struct->channels = 2;
262 }
263 // Scan the full file for all frames.
264 while ((ftell(mp3_file) < (int64_t) (filesize - 128)) && (get_mp3_header(mp3_file, &header_info) == TRUE)) {
265 uint32_t new_bitrate = mp3_bitrate[header_info.version & 0x1][header_info.layer - 1][header_info.bitrate];
266 total_bitrate += new_bitrate;
267 frames_sampled++;
268 if (new_bitrate != initial_bitrate)
269 mp3_struct->VBR = 2;
270 }
271 if (mp3_struct->VBR != 2) {
272 mp3_struct->bitrate = initial_bitrate * 1000;
273 } else {
274 mp3_struct->bitrate = (total_bitrate / frames_sampled) * 1000;
275 }
276 //mp3_struct->duration = frames_sampled * 26;
277 mp3_struct->duration = (double) frames_sampled * 26.00;
278 // Each frame lasts for 26ms, so just multiple the number of frames by 26 to get our duration
279 }
280 }
281
282 // ************************************************************************************************
283
get_id3_tags(gchar * filename,LIBMTP_track_t * trackinformation)284 void get_id3_tags(gchar *filename, LIBMTP_track_t *trackinformation) {
285
286 MP3_Info mp3_information;
287 struct id3_file * id3_file_id = id3_file_open(filename, ID3_FILE_MODE_READONLY);
288
289 if (id3_file_id != NULL) {
290 // We have a valid file, so lets get some data.
291 struct id3_tag* id3_tag_id = id3_file_tag(id3_file_id);
292 // We have our tag data, so now cycle through the fields.
293 trackinformation->album = ID3_getFrameText(id3_tag_id, ID3_FRAME_ALBUM);
294 trackinformation->title = ID3_getFrameText(id3_tag_id, ID3_FRAME_TITLE);
295 trackinformation->artist = ID3_getFrameText(id3_tag_id, ID3_FRAME_ARTIST);
296 trackinformation->date = ID3_getFrameText(id3_tag_id, ID3_FRAME_YEAR);
297 trackinformation->genre = ID3_getFrameText(id3_tag_id, ID3_FRAME_GENRE);
298
299 gchar * tracknumber = ID3_getFrameText(id3_tag_id, ID3_FRAME_TRACK);
300 if (tracknumber != 0) {
301 trackinformation->tracknumber = atoi(tracknumber);
302 } else {
303 trackinformation->tracknumber = 0;
304 }
305
306 // Need below if the default artist field is NULL
307 if (trackinformation->artist == NULL)
308 trackinformation->artist = ID3_getFrameText(id3_tag_id, "TPE2");
309 if (trackinformation->artist == NULL)
310 trackinformation->artist = ID3_getFrameText(id3_tag_id, "TPE3");
311 if (trackinformation->artist == NULL)
312 trackinformation->artist = ID3_getFrameText(id3_tag_id, "TPE4");
313 if (trackinformation->artist == NULL)
314 trackinformation->artist = ID3_getFrameText(id3_tag_id, "TCOM");
315 // Need this if using different Year field.
316 if (trackinformation->date == NULL)
317 trackinformation->date = ID3_getFrameText(id3_tag_id, "TDRC");
318
319 // Get our track duration via ID3 Tag.
320 gchar * trackduration = ID3_getFrameText(id3_tag_id, "TLEN");
321 if (trackduration != 0) {
322 trackinformation->duration = atoi(trackduration);
323 } else {
324 trackinformation->duration = 0;
325 }
326 // Close our file for reading the fields.
327 id3_file_close(id3_file_id);
328 }
329
330 // Duration, bitrate and other information
331 // This information must be derived by manually decoding the MP3 file.
332 get_mp3_info(filename, &mp3_information);
333 trackinformation->duration = mp3_information.duration;
334 trackinformation->bitrate = mp3_information.bitrate;
335 trackinformation->bitratetype = mp3_information.VBR;
336 trackinformation->nochannels = mp3_information.channels;
337 }
338
339 // ************************************************************************************************
340
341 /**
342 * Return the OGG Comment for the given tag name
343 * @param comments The OGG Comments data field.
344 * @param name The tag name to return
345 * @return gchar* to string with tag contents
346 */
OGG_getFieldText(const vorbis_comment * comments,const char * name)347 gchar * OGG_getFieldText(const vorbis_comment *comments, const char *name) {
348
349 // We simple cycle through our comments, looking for our name, and return it's value;
350 if (comments->comments > 0) {
351 gchar ** file_comments = comments->user_comments;
352 gint file_comments_count = comments->comments;
353 while (file_comments_count--) {
354 // We have our comment, now see if it is what we are after?
355 gchar ** comments_split = g_strsplit(*file_comments, "=", 2);
356 if (*comments_split != NULL) {
357 if (g_ascii_strcasecmp(name, *comments_split) == 0) {
358 // We have our desrired tag, so return it to the user.
359 comments_split++;
360 return g_strdup(*comments_split);
361 }
362 }
363 // Increment our pointers accordingly.
364 file_comments++;
365 }
366 } else {
367 // No comments, so return a NULL value;
368 return NULL;
369 }
370 // We didn't find our key, so return NULL
371 return NULL;
372 }
373
374 // ************************************************************************************************
375
get_ogg_tags(gchar * filename,LIBMTP_track_t * trackinformation)376 void get_ogg_tags(gchar *filename, LIBMTP_track_t *trackinformation) {
377 OggVorbis_File *mov_file = NULL;
378 vorbis_info * mov_info = NULL;
379 FILE *mfile;
380 vorbis_comment *mov_file_comment = NULL;
381 gchar * tracknumber = NULL;
382
383 // Attempt to open the file, and init the OggVorbis_File struct for our file.
384 // Yes I know about ov_fopen(), but Solaris 10 ships with vorbis 1.0.1 which
385 // doesn't have this function.
386 mfile = fopen(filename, "r");
387 if (mfile == NULL)
388 return;
389
390 // Allocate memory to hold the OV file information.
391 mov_file = g_malloc0(sizeof (OggVorbis_File));
392
393 if (ov_open(mfile, mov_file, NULL, 0) != 0) {
394 fclose(mfile);
395 return;
396 }
397
398 // Get or comment data;
399 mov_file_comment = ov_comment(mov_file, -1);
400 mov_info = ov_info(mov_file, -1);
401
402 trackinformation->album = OGG_getFieldText(mov_file_comment, "ALBUM");
403 trackinformation->title = OGG_getFieldText(mov_file_comment, "TITLE");
404 trackinformation->artist = OGG_getFieldText(mov_file_comment, "ARTIST");
405 trackinformation->date = OGG_getFieldText(mov_file_comment, "DATE");
406 trackinformation->genre = OGG_getFieldText(mov_file_comment, "GENRE");
407 tracknumber = OGG_getFieldText(mov_file_comment, "TRACKNUMBER");
408 if (tracknumber != NULL) {
409 trackinformation->tracknumber = atoi(tracknumber);
410 } else {
411 trackinformation->tracknumber = 0;
412 }
413 // Duration, bitrate and other information
414 trackinformation->duration = (int) ov_time_total(mov_file, -1) * 1000;
415 trackinformation->bitrate = ov_bitrate(mov_file, -1);
416 trackinformation->bitratetype = 2; // VBR
417 trackinformation->nochannels = mov_info->channels;
418 // Clean up our data structures.
419 ov_clear(mov_file);
420 g_free(mov_file);
421 return;
422 }
423
424 // ************************************************************************************************
425
426 /**
427 * Get our FLAC Comment for the given tag name
428 * @param tags The FLAC Comments
429 * @param name The tag name.
430 * @return
431 */
FLAC_getFieldText(const FLAC__StreamMetadata * tags,const char * name)432 gchar *FLAC_getFieldText(const FLAC__StreamMetadata *tags, const char *name) {
433 int index = FLAC__metadata_object_vorbiscomment_find_entry_from(tags, 0, name);
434 if (index < 0) {
435 return NULL;
436 } else {
437 return strchr((const char *) tags->data.vorbis_comment.comments[index].entry, '=') + 1;
438 }
439 }
440
441 // ************************************************************************************************
442
get_flac_tags(gchar * filename,LIBMTP_track_t * trackinformation)443 void get_flac_tags(gchar *filename, LIBMTP_track_t *trackinformation) {
444 FLAC__StreamMetadata *tags = NULL;
445 FLAC__StreamMetadata streaminfo;
446 gchar * tracknumber = 0;
447
448 // Load in our tag information stream
449 if (!FLAC__metadata_get_tags(filename, &tags))
450 return;
451 if (!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
452 return;
453 }
454 // We have our tag data, get the individual fields.
455 trackinformation->album = g_strdup(FLAC_getFieldText(tags, "ALBUM"));
456 trackinformation->title = g_strdup(FLAC_getFieldText(tags, "TITLE"));
457 trackinformation->artist = g_strdup(FLAC_getFieldText(tags, "ARTIST"));
458 trackinformation->date = g_strdup(FLAC_getFieldText(tags, "DATE"));
459 trackinformation->genre = g_strdup(FLAC_getFieldText(tags, "GENRE"));
460
461 tracknumber = FLAC_getFieldText(tags, "TRACKNUMBER");
462 if (tracknumber != 0) {
463 trackinformation->tracknumber = atoi(tracknumber);
464 } else {
465 trackinformation->tracknumber = 0;
466 }
467
468 // Duration, bitrate and other information
469 if ((streaminfo.data.stream_info.sample_rate != 0)&&(streaminfo.data.stream_info.total_samples != 0)) {
470 trackinformation->duration = (streaminfo.data.stream_info.total_samples /
471 streaminfo.data.stream_info.sample_rate) * 1000;
472 trackinformation->bitrate = 8.0 * (float) (trackinformation->filesize) /
473 (1000.0 * (float) streaminfo.data.stream_info.total_samples
474 / (float) streaminfo.data.stream_info.sample_rate);
475 } else {
476 trackinformation->duration = 0;
477 trackinformation->bitrate = 0;
478 }
479 trackinformation->bitratetype = 0; // Not used
480 trackinformation->nochannels = streaminfo.data.stream_info.channels;
481
482 //trackinformation->tracknumber = atoi(FLAC_getFieldText(tags, "TRACKNUMBER"));
483 FLAC__metadata_object_delete(tags);
484 //FLAC__metadata_object_delete(&streaminfo);
485 return;
486 }
487
488 // ************************************************************************************************
489
get_asf_tags(gchar * filename,LIBMTP_track_t * trackinformation)490 void get_asf_tags(gchar *filename, LIBMTP_track_t *trackinformation) {
491 FILE *ASF_File;
492 GUID Header_GUID;
493 GUID Stream_GUID;
494 uint32_t Header_Blocks;
495 uint64_t Object_Size;
496 long ASF_File_Position;
497
498 // Content Object
499 uint16_t Title_Length = 0;
500 uint16_t Author_Length = 0;
501 uint16_t Copyright_Length = 0;
502 uint16_t Description_Length = 0;
503 uint16_t Rating_Length = 0;
504
505 gchar *Title = NULL;
506 gchar *Author = NULL;
507
508 // Extended Content Object
509 uint16_t Content_Descriptors_Count = 0;
510 uint16_t Descriptor_Name_Length = 0;
511 gchar *Descriptor_Name = NULL;
512 gchar *Descriptor_Name_UTF16 = NULL;
513 uint16_t Descriptor_Value_Type = 0;
514 uint16_t Descriptor_Value_Length = 0;
515 uint64_t Descriptor_Value = 0;
516 gchar *Descriptor_Value_Str = NULL;
517 gchar *Descriptor_Value_Str_UTF16 = NULL;
518
519 // Audio Object
520 uint16_t Stream_Channels;
521 uint32_t Stream_Bitrate;
522
523 // File Object
524 uint64_t Stream_Duration;
525
526
527 ASF_File = fopen(filename, "r");
528 if (ASF_File == NULL)
529 return;
530
531 // Get our header GUID and make sure this is it.
532 size_t i = fread(&Header_GUID, sizeof (GUID), 1, ASF_File);
533 if (!memcmp(&Header_GUID, &ASF_header, sizeof (GUID))) {
534 // If not exit.
535 fclose(ASF_File);
536 return;
537 }
538 if (i != 1) {
539 // If not exit.
540 fclose(ASF_File);
541 return;
542 }
543 // Skip the rest of the header area;
544 fseek(ASF_File, 8, SEEK_CUR);
545 fread(&Header_Blocks, sizeof (uint32_t), 1, ASF_File);
546 fseek(ASF_File, 2, SEEK_CUR);
547
548 // We should be at the start of the header blocks;
549 // Header_blocks has the number of header objects that we can test.
550 while (Header_Blocks--) {
551 fread(&Header_GUID, sizeof (GUID), 1, ASF_File);
552 if (memcmp(&Header_GUID, &ASF_comment_header, sizeof (GUID)) == 0) {
553 // We have our standard comment header block;
554
555 // Get the size of the object, and the current file position.
556 fread(&Object_Size, sizeof (uint64_t), 1, ASF_File);
557 ASF_File_Position = ftell(ASF_File);
558 // Get our field lengths.
559 fread(&Title_Length, sizeof (uint16_t), 1, ASF_File);
560 fread(&Author_Length, sizeof (uint16_t), 1, ASF_File);
561 fread(&Copyright_Length, sizeof (uint16_t), 1, ASF_File);
562 fread(&Description_Length, sizeof (uint16_t), 1, ASF_File);
563 fread(&Rating_Length, sizeof (uint16_t), 1, ASF_File);
564 // Since we only need Title and Author, we only need to alloc memory for those two.
565 Title = g_malloc0(Title_Length + 0x10);
566 Author = g_malloc0(Author_Length + 0x10);
567 fread(Title, Title_Length, 1, ASF_File);
568 fread(Author, Author_Length, 1, ASF_File);
569 // Set our track information
570 trackinformation->title = g_utf16_to_utf8((const gunichar2 *) Title, Title_Length, NULL, NULL, NULL);
571 trackinformation->artist = g_utf16_to_utf8((const gunichar2 *) Author, Author_Length, NULL, NULL, NULL);
572 // Free our memory that we used to load in the fields.
573 g_free(Title);
574 g_free(Author);
575 Title = NULL;
576 Author = NULL;
577 // Set our file position so it's ready to read in the next GUID Header.
578 fseek(ASF_File, ASF_File_Position, SEEK_SET);
579 fseek(ASF_File, (Object_Size - sizeof (uint64_t) - sizeof (GUID)), SEEK_CUR);
580 } else {
581 if (memcmp(&Header_GUID, &ASF_extended_content_header, sizeof (GUID)) == 0) {
582 // We have our standard comment header block;
583 //g_printf("WMA: Found our extended comment block\n");
584 // Get the size of the object, and the current file position.
585 fread(&Object_Size, sizeof (uint64_t), 1, ASF_File);
586 ASF_File_Position = ftell(ASF_File);
587 // Get the number of Descripions field we have, as we will need to cycle through them all.
588 fread(&Content_Descriptors_Count, sizeof (uint16_t), 1, ASF_File);
589 while (Content_Descriptors_Count--) {
590 // These themselves are Objects within the main extended content header, which we need to handle.
591 // Format is:
592 // Descriptor Name Length (word)
593 // Descriptor Name (varies)
594 // Descriptor Value Type (word)
595 // Descriptor Value Length (word)
596 // Descriptor Value (varies - depend on Value Type).
597 Descriptor_Name_Length = 0;
598 //Descriptor_Name = NULL;
599 //Descriptor_Name_UTF16 = NULL;
600 Descriptor_Value_Type = 0;
601 Descriptor_Value_Length = 0;
602 Descriptor_Value = 0;
603 Descriptor_Value_Str = NULL;
604 Descriptor_Value_Str_UTF16 = NULL;
605 // Get our Descriptor Name.
606 fread(&Descriptor_Name_Length, sizeof (uint16_t), 1, ASF_File);
607 Descriptor_Name_UTF16 = g_malloc0(Descriptor_Name_Length + 0x10);
608 fread(Descriptor_Name_UTF16, Descriptor_Name_Length, 1, ASF_File);
609 Descriptor_Name = g_utf16_to_utf8((const gunichar2 *) Descriptor_Name_UTF16,
610 Descriptor_Name_Length, NULL, NULL, NULL);
611 // Get our Value Type and Value Length
612 fread(&Descriptor_Value_Type, sizeof (uint16_t), 1, ASF_File);
613 fread(&Descriptor_Value_Length, sizeof (uint16_t), 1, ASF_File);
614 switch (Descriptor_Value_Type) {
615 case 0: // String;
616 case 1: // Binary;
617 Descriptor_Value_Str_UTF16 = g_malloc0(Descriptor_Value_Length + 0x10);
618 fread(Descriptor_Value_Str_UTF16, Descriptor_Value_Length, 1, ASF_File);
619 Descriptor_Value_Str = g_utf16_to_utf8((const gunichar2 *) Descriptor_Value_Str_UTF16,
620 Descriptor_Value_Length, NULL, NULL, NULL);
621 // We have out key=value pair so lets look for our desired keys 'WM/AlbumTitle', 'WM/Genre' and 'WM/Year'
622 if (g_ascii_strcasecmp(Descriptor_Name, "WM/AlbumTitle\0") == 0) {
623 // We have the album Title;
624 trackinformation->album = g_strdup(Descriptor_Value_Str);
625 } else {
626 if (g_ascii_strcasecmp(Descriptor_Name, "WM/Genre\0") == 0) {
627 // We have the album Genre;
628 trackinformation->genre = g_strdup(Descriptor_Value_Str);
629 } else {
630 if (g_ascii_strcasecmp(Descriptor_Name, "WM/Year\0") == 0) {
631 // We have the album Year;
632 trackinformation->date = g_strdup(Descriptor_Value_Str);
633 }
634 }
635 }
636 break;
637 case 2: // Boolean (DWORD)
638 case 3: // DWORD
639 case 4: // QWORD
640 case 5: // WORD
641 if (Descriptor_Value_Length > sizeof (Descriptor_Value))
642 Descriptor_Value_Length = sizeof (Descriptor_Value);
643 fread(&Descriptor_Value, Descriptor_Value_Length, 1, ASF_File);
644 if ((g_ascii_strcasecmp(Descriptor_Name, "WM/Track\0") == 0)) {
645 trackinformation->tracknumber = Descriptor_Value + 1;
646 } else {
647 if (g_ascii_strcasecmp(Descriptor_Name, "WM/TrackNumber\0") == 0)
648 trackinformation->tracknumber = Descriptor_Value;
649 }
650 break;
651 default: // Unknown so skip it.
652 fseek(ASF_File, Descriptor_Value_Length, SEEK_CUR);
653 break;
654 }
655
656 // Free up our allocated memory;
657 g_free(Descriptor_Name);
658 g_free(Descriptor_Name_UTF16);
659 g_free(Descriptor_Value_Str);
660 g_free(Descriptor_Value_Str_UTF16);
661 }
662
663 // Set our file position so it's ready to read in the next GUID Header.
664 fseek(ASF_File, ASF_File_Position, SEEK_SET);
665 fseek(ASF_File, (Object_Size - sizeof (uint64_t) - sizeof (GUID)), SEEK_CUR);
666 } else {
667 if (memcmp(&Header_GUID, &ASF_Stream_header, sizeof (GUID)) == 0) {
668 // We have an audio header for the track information.
669 fread(&Object_Size, sizeof (uint64_t), 1, ASF_File);
670 ASF_File_Position = ftell(ASF_File);
671
672 // Read in the stream type GUID
673 fread(&Stream_GUID, sizeof (GUID), 1, ASF_File);
674 if (memcmp(&Stream_GUID, &ASF_Audio_Media_header, sizeof (GUID)) == 0) {
675 // We have an audio header.
676 fseek(ASF_File, 38, SEEK_CUR);
677 // We should be pointing at our audio stream data block.
678 fseek(ASF_File, sizeof (uint16_t), SEEK_CUR); // Skip CODEC ID
679 fread(&Stream_Channels, sizeof (uint16_t), 1, ASF_File);
680 fseek(ASF_File, 4, SEEK_CUR); // Skip Samples per second
681 fread(&Stream_Bitrate, sizeof (uint32_t), 1, ASF_File);
682
683 trackinformation->nochannels = Stream_Channels;
684 trackinformation->bitrate = Stream_Bitrate * 8; // This value is in BYTES
685 trackinformation->bitratetype = 0; // Not used
686 }
687 // Set our file position so it's ready to read in the next GUID Header.
688 fseek(ASF_File, ASF_File_Position, SEEK_SET);
689 fseek(ASF_File, (Object_Size - sizeof (uint64_t) - sizeof (GUID)), SEEK_CUR);
690 } else {
691 if (memcmp(&Header_GUID, &ASF_File_Properties_header, sizeof (GUID)) == 0) {
692 // We have a file header for the track information.
693 fread(&Object_Size, sizeof (uint64_t), 1, ASF_File);
694 ASF_File_Position = ftell(ASF_File);
695 // Skip File ID, Filesize, Creation Date and Data Packets Count
696 fseek(ASF_File, (sizeof (GUID) + (sizeof (uint64_t) * 3)), SEEK_CUR);
697 fread(&Stream_Duration, sizeof (uint64_t), 1, ASF_File);
698 // Convert from 1/100ths nano sec to millisec.
699 trackinformation->duration = Stream_Duration / 10000;
700
701 fseek(ASF_File, ASF_File_Position, SEEK_SET);
702 fseek(ASF_File, (Object_Size - sizeof (uint64_t) - sizeof (GUID)), SEEK_CUR);
703 } else {
704 // Skip this header;
705 fread(&Object_Size, sizeof (uint64_t), 1, ASF_File);
706 fseek(ASF_File, (Object_Size - sizeof (uint64_t) - sizeof (GUID)), SEEK_CUR);
707 }
708 }
709 }
710 }
711 }
712 fclose(ASF_File);
713 return;
714 }
715