public void generateThumbnail(MediaProbe mediaProbe, AudioMetadata audio,
            CancellationToken token, int timeoutSeconds, int nrThumbnails)
        {
          
            // get attached images
            List<MediaThumb> thumbBitmaps = mediaProbe.grabAttachedImages(Constants.MAX_THUMBNAIL_WIDTH,
                Constants.MAX_THUMBNAIL_HEIGHT, token, timeoutSeconds);

            if (thumbBitmaps.Count > 0)
            {
                audio.Thumbnail = new Thumbnail(thumbBitmaps[0].Thumb); 
            }
            
        }
        void getAudioProperties(ObservableCollection<Tuple<String, String>> p, AudioMetadata audio)
        {
            p.Add(new Tuple<string, string>("", "AUDIO"));
            p.Add(new Tuple<string, string>("Audio Container", audio.AudioContainer));                               
            p.Add(new Tuple<string, string>("Duration", MiscUtils.formatTimeSeconds(audio.DurationSeconds)));           
            p.Add(new Tuple<string, string>("Audio Codec", audio.AudioCodec));
            p.Add(new Tuple<string, string>("Bits Per Sample", audio.BitsPerSample.ToString()));
            p.Add(new Tuple<string, string>("Samples Per Second", audio.SamplesPerSecond.ToString()));
            p.Add(new Tuple<string, string>("Nr Channels", audio.NrChannels.ToString()));

            if (audio.BitRate.HasValue)
            {
                p.Add(new Tuple<string, string>("Rate", MiscUtils.formatSizeBytes(audio.BitRate.Value / 8) + "/s"));
            }
        }
        void parseFFMpegMetaData(List<string> fsMetaData, AudioMetadata audio)
        {
            if (fsMetaData == null) return;

            foreach (String info in fsMetaData)
            {
                string[] temp = info.Split(new char[] { ':' }, 2);

                if (temp != null)
                {
                    String param = temp[0].ToLower();
                    String value = temp[1].Trim();

                    if (String.IsNullOrEmpty(value) || String.IsNullOrWhiteSpace(value)) continue;

                    // Note that when setting the title like this, if the user clears the (XMP) title it will 
                    // revert to the title stored in the ffmpeg metadata. This will be confusing for the user
                    // and should probably be fixed.
                    if (audio.Title == null && param.Equals("title"))
                    {
                        audio.Title = value;
                    }
                    else if (audio.Description == null && descriptionMatch.Any(s => s.Equals(param)))
                    {
                        audio.Description = value;
                    }
                    else if (audio.Author == null && authorMatch.Any(s => s.Equals(param)))
                    {
                        audio.Author = value;
                    }
                    else if (audio.Copyright == null && param.Equals("copyright"))
                    {
                        audio.Copyright = value;
                    }
                    else if (audio.Software == null && encoderMatch.Any(s => s.Equals(param)))
                    {
                        audio.Software = value;
                    }
                    else if (audio.Genre == null && param.Equals("genre"))
                    {
                        audio.Genre = value;
                    }
                    else if (audio.Album == null && param.Equals("album"))
                    {
                        audio.Album = value;
                    }
                    else if (audio.TrackNr == null && param.Equals("track"))
                    {
                        int seperator = value.IndexOf('/');

                        int trackNr, totalTracks;
                        bool success;

                        if (seperator != -1)
                        {
                            string[] trackInfo = value.Split(new char[] { '/' }, 2);
                            value = trackInfo[0].Trim();

                            success = Int32.TryParse(trackInfo[1].Trim(), out totalTracks);
                            if (success)
                            {
                                audio.TotalTracks = totalTracks;
                            }                            
                        }
                        
                        success = Int32.TryParse(value, out trackNr);
                        if (success)
                        {
                            audio.TrackNr = trackNr;
                        }
                    }
                    else if (audio.TotalTracks == null && param.Equals("tracktotal"))
                    {
                        int totalTracks;
                        bool success = Int32.TryParse(value, out totalTracks);
                        if (success)
                        {
                            audio.TotalTracks = totalTracks;
                        }
                    }
                    else if (audio.DiscNr == null && param.Equals("disc"))
                    {
                        int seperator = value.IndexOf('/');

                        int discNr, totalDiscs;
                        bool success;

                        if (seperator != -1)
                        {
                            string[] discInfo = value.Split(new char[] { '/' }, 2);
                            value = discInfo[0].Trim();

                            success = Int32.TryParse(discInfo[1].Trim(), out totalDiscs);
                            if (success)
                            {
                                audio.TotalDiscs = totalDiscs;
                            }
                        }

                        success = Int32.TryParse(value, out discNr);
                        if (success)
                        {
                            audio.DiscNr = discNr;
                        }
                    }
                    else if (audio.TotalTracks == null && param.Equals("disctotal"))
                    {
                        int totalDiscs;
                        bool success = Int32.TryParse(value, out totalDiscs);
                        if (success)
                        {
                            audio.TotalDiscs = totalDiscs;
                        }
                    }
                                                      
                }
            }
        }
        public static BaseMetadata read(String location, MetadataFactory.ReadOptions options, CancellationToken token, int timeoutSeconds)
        {
            BaseMetadata metadata = new UnknownMetadata(FileUtils.getPathWithoutFileName(location));
            metadata.Name = Path.GetFileName(location);

            Logger.Log.Info("Reading metadata for: " + location);

            int timeoutMs = timeoutSeconds * 1000;

            Stream data = FileUtils.waitForFileAccess(location, FileAccess.Read, timeoutMs, token);

            MediaProbe mediaProbe = new MediaProbe();

            try
            {
                mediaProbe.open(location, token);

                switch (mediaProbe.MediaType)
                {
                    case MediaType.AUDIO_MEDIA:
                        {
                            metadata = new AudioMetadata(location, data);                          
                            AudioFileMetadataReader reader = new AudioFileMetadataReader();
                            reader.readMetadata(mediaProbe, data, options, metadata, token, timeoutSeconds);
                            break;
                        }
                    case MediaType.IMAGE_MEDIA:
                        {
                            metadata = new ImageMetadata(location, data);                        
                            ImageFileMetadataReader reader = new ImageFileMetadataReader();
                            reader.readMetadata(mediaProbe, data, options, metadata, token, timeoutSeconds);
                            break;
                        }                
                    case MediaType.VIDEO_MEDIA:
                        {
                            metadata = new VideoMetadata(location, data);                       
                            VideoFileMetadataReader reader = new VideoFileMetadataReader();
                            reader.readMetadata(mediaProbe, data, options, metadata, token, timeoutSeconds);
                            break;
                        }
                    default:
                        break;
                }                

                FileInfo info = new FileInfo(location);
                info.Refresh();

                if (info.Attributes.HasFlag(FileAttributes.ReadOnly))
                {
                    metadata.IsReadOnly = true;
                }

                if (!options.HasFlag(MetadataFactory.ReadOptions.LEAVE_STREAM_OPENED_AFTER_READ))
                {
                    metadata.close();
                }

            }
            catch (Exception e)
            {
                metadata.MetadataReadError = e;                
            }
            finally
            {                
                mediaProbe.close();
                mediaProbe.Dispose();                
            }
                        
            return metadata;
        }