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