/// <summary> /// Parses boxes for a specified range, looking for tags. /// </summary> /// <param name="start"> /// A <see cref="long" /> value specifying the seek position /// at which to start reading. /// </param> /// <param name="end"> /// A <see cref="long" /> value specifying the seek position /// at which to stop reading. /// </param> private void ParseTag(long start, long end, List <BoxHeader> parents) { BoxHeader header; for (long position = start; position < end; position += header.TotalBoxSize) { header = new BoxHeader(file, position); if (header.BoxType == BoxType.Moov) { ParseTag(header.HeaderSize + position, header.TotalBoxSize + position, AddParent(parents, header)); } else if (header.BoxType == BoxType.Mdia || header.BoxType == BoxType.Minf || header.BoxType == BoxType.Stbl || header.BoxType == BoxType.Trak) { ParseTag(header.HeaderSize + position, header.TotalBoxSize + position, AddParent(parents, header)); } else if (header.BoxType == BoxType.Udta) { IsoUserDataBox udtaBox = BoxFactory.CreateBox(file, header) as IsoUserDataBox; // Since we can have multiple udta boxes, save the parent for each one List <BoxHeader> new_parents = AddParent( parents, header); udtaBox.ParentTree = new_parents.ToArray(); udta_boxes.Add(udtaBox); } else if (header.BoxType == BoxType.Mdat) { mdat_start = position; mdat_end = position + header.TotalBoxSize; } if (header.TotalBoxSize == 0) { break; } } }
/// <summary> /// Gets a tag of a specified type from the current instance, /// optionally creating a new tag if possible. /// </summary> /// <param name="type"> /// A <see cref="TagLib.TagTypes" /> value indicating the /// type of tag to read. /// </param> /// <param name="create"> /// A <see cref="bool" /> value specifying whether or not to /// try and create the tag if one is not found. /// </param> /// <returns> /// A <see cref="Tag" /> object containing the tag that was /// found in or added to the current instance. If no /// matching tag was found and none was created, <see /// langword="null" /> is returned. /// </returns> /// <remarks> /// At the time of this writing, only <see cref="AppleTag" /> /// is supported. All other tag types will be ignored. /// </remarks> public override TagLib.Tag GetTag(TagTypes type, bool create) { if (type == TagTypes.Apple) { if (apple_tag == null && create) { IsoUserDataBox udtaBox = FindAppleTagUdta(); if (null == udtaBox) { udtaBox = new IsoUserDataBox(); } apple_tag = new AppleTag(udtaBox); tag.SetTags(apple_tag); } return(apple_tag); } return(null); }
/// <summary> /// Reads the file with a specified read style. /// </summary> /// <param name="propertiesStyle"> /// A <see cref="ReadStyle" /> value specifying at what level /// of accuracy to read the media properties, or <see /// cref="ReadStyle.None" /> to ignore the properties. /// </param> private void Read(ReadStyle propertiesStyle) { // TODO: Support Id3v2 boxes!!! tag = new CombinedTag(); Mode = AccessMode.Read; try { FileParser parser = new FileParser(this); if (propertiesStyle == ReadStyle.None) { parser.ParseTag(); } else { parser.ParseTagAndProperties(); } InvariantStartPosition = parser.MdatStartPosition; InvariantEndPosition = parser.MdatEndPosition; udta_boxes.AddRange(parser.UserDataBoxes); // Ensure our collection contains at least a single empty box if (udta_boxes.Count == 0) { IsoUserDataBox dummy = new IsoUserDataBox(); udta_boxes.Add(dummy); } // Check if a udta with ILST actually exists if (IsAppleTagUdtaPresent()) { TagTypesOnDisk |= TagTypes.Apple; //There is an udta present with ILST info } // Find the udta box with the Apple Tag ILST IsoUserDataBox udtaBox = FindAppleTagUdta(); if (null == udtaBox) { udtaBox = new IsoUserDataBox(); } apple_tag = new AppleTag(udtaBox); tag.SetTags(apple_tag); // If we're not reading properties, we're done. if (propertiesStyle == ReadStyle.None) { Mode = AccessMode.Closed; return; } // Get the movie header box. IsoMovieHeaderBox mvhd_box = parser.MovieHeaderBox; if (mvhd_box == null) { Mode = AccessMode.Closed; throw new CorruptFileException( "mvhd box not found."); } IsoAudioSampleEntry audio_sample_entry = parser.AudioSampleEntry; IsoVisualSampleEntry visual_sample_entry = parser.VisualSampleEntry; // Read the properties. properties = new Properties(mvhd_box.Duration, audio_sample_entry, visual_sample_entry); } finally { Mode = AccessMode.Closed; } }
/// <summary> /// Saves the changes made in the current instance to the /// file it represents. /// </summary> public override void Save() { if (udta_boxes.Count == 0) { IsoUserDataBox udtaBox = new IsoUserDataBox(); udta_boxes.Add(udtaBox); } // Try to get into write mode. Mode = File.AccessMode.Write; try { FileParser parser = new FileParser(this); parser.ParseBoxHeaders(); InvariantStartPosition = parser.MdatStartPosition; InvariantEndPosition = parser.MdatEndPosition; long size_change = 0; long write_position = 0; // To avoid rewriting udta blocks which might not have been modified, // the code here will work correctly if: // 1. There is a single udta for the entire file // - OR - // 2. There are multiple utdtas, but only 1 of them contains the Apple ILST box. // We should be OK in the vast majority of cases IsoUserDataBox udtaBox = FindAppleTagUdta(); if (null == udtaBox) { udtaBox = new IsoUserDataBox(); } ByteVector tag_data = udtaBox.Render(); // If we don't have a "udta" box to overwrite... if (udtaBox.ParentTree == null || udtaBox.ParentTree.Length == 0) { // Stick the box at the end of the moov box. BoxHeader moov_header = parser.MoovTree[ parser.MoovTree.Length - 1]; size_change = tag_data.Count; write_position = moov_header.Position + moov_header.TotalBoxSize; Insert(tag_data, write_position, 0); // Overwrite the parent box sizes. for (int i = parser.MoovTree.Length - 1; i >= 0; i--) { size_change = parser.MoovTree[i ].Overwrite(this, size_change); } } else { // Overwrite the old box. BoxHeader udta_header = udtaBox.ParentTree[udtaBox.ParentTree.Length - 1]; size_change = tag_data.Count - udta_header.TotalBoxSize; write_position = udta_header.Position; Insert(tag_data, write_position, udta_header.TotalBoxSize); // Overwrite the parent box sizes. for (int i = udtaBox.ParentTree.Length - 2; i >= 0; i--) { size_change = udtaBox.ParentTree[i ].Overwrite(this, size_change); } } // If we've had a size change, we may need to adjust // chunk offsets. if (size_change != 0) { // We may have moved the offset boxes, so we // need to reread. parser.ParseChunkOffsets(); InvariantStartPosition = parser.MdatStartPosition; InvariantEndPosition = parser.MdatEndPosition; foreach (Box box in parser.ChunkOffsetBoxes) { IsoChunkLargeOffsetBox co64 = box as IsoChunkLargeOffsetBox; if (co64 != null) { co64.Overwrite(this, size_change, write_position); continue; } IsoChunkOffsetBox stco = box as IsoChunkOffsetBox; if (stco != null) { stco.Overwrite(this, size_change, write_position); continue; } } } TagTypesOnDisk = TagTypes; } finally { Mode = File.AccessMode.Closed; } }
/// <summary> /// Parses boxes for a specified range, looking for tags and /// properties. /// </summary> /// <param name="start"> /// A <see cref="long" /> value specifying the seek position /// at which to start reading. /// </param> /// <param name="end"> /// A <see cref="long" /> value specifying the seek position /// at which to stop reading. /// </param> /// <param name="handler"> /// A <see cref="IsoHandlerBox" /> object that applied to the /// range being searched. /// </param> private void ParseTagAndProperties(long start, long end, IsoHandlerBox handler, List <BoxHeader> parents) { BoxHeader header; for (long position = start; position < end; position += header.TotalBoxSize) { header = new BoxHeader(file, position); ByteVector type = header.BoxType; if (type == BoxType.Moov) { ParseTagAndProperties(header.HeaderSize + position, header.TotalBoxSize + position, handler, AddParent(parents, header)); } else if (type == BoxType.Mdia || type == BoxType.Minf || type == BoxType.Stbl || type == BoxType.Trak) { ParseTagAndProperties( header.HeaderSize + position, header.TotalBoxSize + position, handler, AddParent(parents, header)); } else if (type == BoxType.Stsd) { stsd_boxes.Add(BoxFactory.CreateBox( file, header, handler)); } else if (type == BoxType.Hdlr) { handler = BoxFactory.CreateBox(file, header, handler) as IsoHandlerBox; } else if (mvhd_box == null && type == BoxType.Mvhd) { mvhd_box = BoxFactory.CreateBox(file, header, handler) as IsoMovieHeaderBox; } else if (type == BoxType.Udta) { IsoUserDataBox udtaBox = BoxFactory.CreateBox(file, header, handler) as IsoUserDataBox; // Since we can have multiple udta boxes, save the parent for each one List <BoxHeader> new_parents = AddParent( parents, header); udtaBox.ParentTree = new_parents.ToArray(); udta_boxes.Add(udtaBox); } else if (type == BoxType.Mdat) { mdat_start = position; mdat_end = position + header.TotalBoxSize; } if (header.TotalBoxSize == 0) { break; } } }