/// <summary> /// Read the ID3 meta data extracting required tag values /// </summary> /// <param name="reader"></param> /// <param name="majorVersion"></param> /// <param name="maxPosition"></param> /// <param name="tags"></param> private static void ReadFrames(BinaryReader reader, int majorVersion, int maxPosition, MP3Tags tags) { // These are the tags that are currently being searched for HashSet <string> requiredTags = (majorVersion == 2) ? new HashSet <string>() { "TP1", "TP2", "TT2", "TAL", "TRK", "TYE", "TCO" } : new HashSet <string>() { "TPE1", "TPE2", "TIT2", "TALB", "TRCK", "TYER", "TCON" }; int nameSize = (majorVersion == 2) ? 3 : 4; // Put the tag contents here Dictionary <string, byte[]> frames = new Dictionary <string, byte[]>(); bool endOfTags = false; while ((reader.BaseStream.Position < maxPosition) && (endOfTags == false)) { Logger.Log(string.Format("Position at start of frame {0}", reader.BaseStream.Position)); // Check for end of the frames char frameNameFirstChar = reader.ReadChar(); if (frameNameFirstChar == '\0') { endOfTags = true; } else { // Get the rest of the tag name and its contents string frameName = frameNameFirstChar + new string( reader.ReadChars(nameSize - 1)); ulong frameSize = ReadFrameSize(reader, nameSize, majorVersion); Logger.Log(string.Format("Frame {0}, size {1}", frameName, frameSize)); // Skip a couple of bytes and then read the frame contents reader.ReadByte(); reader.ReadByte(); // Only store if it is one of the required tags if (requiredTags.Contains(frameName) == true) { frames[frameName] = (frameSize > 0) ? reader.ReadBytes(( int )frameSize) : new byte[0]; requiredTags.Remove(frameName); endOfTags = (requiredTags.Count == 0); } else { // Still need to skip over the frame contents if (frameSize > 0) { Logger.Log(string.Format("Position before skipping {0} bytes is {1}", frameSize, reader.BaseStream.Position)); reader.ReadBytes(( int )frameSize); Logger.Log(string.Format("Position after skipping bytes {0}", reader.BaseStream.Position)); } } } } // Now all the required frames have been obtained, extract the contents and assign to the tag instance if (majorVersion == 2) { tags.Artist = GetStringTag(frames, "TP1"); tags.AlbumArtist = GetStringTag(frames, "TP2"); tags.Title = GetStringTag(frames, "TT2"); tags.Album = GetStringTag(frames, "TAL"); tags.Track = GetStringTag(frames, "TRK"); tags.Year = GetStringTag(frames, "TYE"); tags.Genre = GetStringTag(frames, "TCO"); } else { tags.Artist = GetStringTag(frames, "TPE1"); tags.AlbumArtist = GetStringTag(frames, "TPE2"); tags.Title = GetStringTag(frames, "TIT2"); tags.Album = GetStringTag(frames, "TALB"); tags.Track = GetStringTag(frames, "TRCK"); tags.Year = GetStringTag(frames, "TYER"); tags.Genre = GetStringTag(frames, "TCON"); } // Log.WriteLine( LogPriority.Debug, "MobileApp:ReadFrames", string.Format( "Artist: {0} Title: {1} Album: {2} Track: {3}", tags.Artist, tags.Title, tags.Album, // tags.Track ) ); }
/// <summary> /// Extract MP3 tags and duration from the MP3 stream /// </summary> /// <param name="fileStream"></param> /// <returns></returns> public static MP3Tags GetFileTags(Stream fileStream) { MP3Tags tags = new MP3Tags(); using (BinaryReader reader = new BinaryReader(fileStream)) { try { // Make sure the file is at least 10 bytes long to determine the size of the ID3 header if (reader.BaseStream.Length >= 10) { // Look at the start of the file for the ID3 tags if (new string( reader.ReadChars(3)) == "ID3") { // Start extracting metadata // ID3 version byte majorVersion = reader.ReadByte(); // Skip the minor version reader.ReadByte(); // Skip next 'flags' byte reader.ReadByte(); // The size of the ID3 header ulong headerSize = ReadEncodedSize(reader, 4); if (headerSize > 0) { // Get all the required ID3 tags int id3EndPosition = ( int )headerSize + 10; ReadFrames(reader, majorVersion, id3EndPosition, tags); // Step on past the ID3 frames and attempt to read the first MP3 frame if ((reader.BaseStream.Position < id3EndPosition) && (reader.BaseStream.Length >= id3EndPosition)) { reader.ReadBytes(id3EndPosition - ( int )reader.BaseStream.Position); // The first MP3 frame does not always follow the end of the ID3 header, so go searching for the // MP3 synch pattern bool mp3HeaderFound = false; long readLimit = reader.BaseStream.Position + 0x1000; long searchStart = reader.BaseStream.Position; byte[] mp3Header = new byte[] { 0, 0, 0 }; while ((mp3HeaderFound == false) && (reader.BaseStream.Position < reader.BaseStream.Length) && (reader.BaseStream.Position < readLimit)) { mp3Header[1] = reader.ReadByte(); if ((mp3Header[0] == 0xFF) && ((mp3Header[1] & 0xE0) == 0xE0)) { // Synch bytes found mp3HeaderFound = true; // Get the last byte of the header mp3Header[2] = reader.ReadByte(); // Parse the MP3 frame and determine the duration of the file tags.Length = ParseMp3Frame(reader, mp3Header); } else { mp3Header[0] = mp3Header[1]; } } if (mp3HeaderFound == false) { Logger.Log(string.Format("MP3 frame not found between {0} and {1}", searchStart, reader.BaseStream.Position)); } } else { Logger.Log(string.Format("ID3 frame is truncated")); } } } else { Logger.Log(string.Format("Header is not ID3")); } } else { Logger.Log(string.Format("Could not read header")); } } catch (Exception tagsException) { Logger.Log(string.Format("Problem reading file: {0}", tagsException.Message)); } } return(tags); }