/// <summary> /// Copy constructor /// </summary> /// <param name="reader"></param> public MXFKLV(MXFKLV klv) { this.Offset = klv.Offset; this.Key = klv.Key; this.Length = klv.Length; this.DataOffset = klv.DataOffset; this.Partition = klv.Partition; }
/// <summary> /// Copy constructor /// </summary> /// <param name="reader"></param> public MXFKLV(MXFKLV klv, string name, KeyType type) { this.Offset = klv.Offset; this.Key = klv.Key; this.Key.Name = name; this.Key.Type = type; this.Length = klv.Length; this.DataOffset = klv.DataOffset; this.Partition = klv.Partition; }
/// <summary> /// Check if the RIP is present and valid /// </summary> /// <param name="this.File"></param> /// <param name="results"></param> public override void OnExecuteTest(ref List <MXFValidationResult> results) { MXFValidationResult valResult = new MXFValidationResult("Random Index Pack Test"); results.Add(valResult); valResult.Category = "Random Index Pack"; Stopwatch sw = Stopwatch.StartNew(); if (this.File.RIP == null) { valResult.SetError(string.Format("Error! No RIP found.")); return; } if (this.File.RIPEntryCount != this.File.PartitionCount) { valResult.SetError(string.Format("Error! Number of RIP entries is not equal to the number of partitions ({0} vs {1}).", this.File.RIPEntryCount, this.File.PartitionCount)); return; } int ripErrorCount = 0; for (int n = 0; n < this.File.RIPEntryCount; n++) { MXFEntryRIP rip = this.File.RIP.Children[n] as MXFEntryRIP; if (rip != null) { MXFPartition part = this.File.Partitions.Where(a => (ulong)a.Offset == rip.PartitionOffset).FirstOrDefault(); if (part == null) { ripErrorCount++; valResult.AddError(string.Format("Error! RIP entry {0} not pointing to a valid partion.", n)); return; } } } if (ripErrorCount > 0) { valResult.SetError(string.Format("Error! {0} RIP entries are not pointing to a valid partion.", ripErrorCount)); return; } valResult.SetSuccess("Random Index Pack (RIP) is valid."); LogInfo("Validation completed in {0} msec", sw.ElapsedMilliseconds); }
/// <summary> /// Create a new MXF object based on the KLV key /// </summary> /// <param name="reader"></param> /// <param name="currentPartition"></param> /// <returns></returns> public MXFKLV CreateObject(MXFReader reader, MXFPartition currentPartition) { MXFKLV klv = new MXFKLV(reader); klv.Partition = currentPartition; // Pass the current partition through to the classes if (dict.TryGetValue(klv.Key, out Type foundType)) { return((MXFKLV)Activator.CreateInstance(foundType, reader, klv)); } else { // TODO what if the key cannot be found, i.e. it is not known? } return(klv); }
/// <summary> /// Create a new MXF object based on the KLV key /// </summary> /// <param name="reader"></param> /// <param name="currentPartition"></param> /// <returns></returns> public MXFKLV CreateObject(MXFReader reader, MXFPartition currentPartition) { MXFKLV klv = new MXFKLV(reader); klv.Partition = currentPartition; // Pass the current partition through to the classes foreach (MXFKey knownKey in MXFKLVFactory.m_allKeys) { if (klv.Key == knownKey) { if (knownKey.ObjectType != null) { return((MXFKLV)Activator.CreateInstance(knownKey.ObjectType, reader, klv)); } klv.Key.Name = knownKey.Name; klv.Key.Type = knownKey.Type; break; } } return(klv); }
public static bool IsPartitionClosedAndComplete(this MXFPartition p) { return(!(p.Closed && p.Complete)); }
public static bool IsPartitionDurationBetween(this MXFPartition partition, long min, long max) { return(partition.CountPictureEssences() >= min && partition.CountPictureEssences() <= max); }
public static long CountPictureEssences(this MXFPartition partition) { return(partition.Children.OfType <MXFEssenceElement>().Count(e => e.IsPicture)); }
/// <summary> /// Process a new KLV object /// </summary> /// <param name="klv"></param> /// <param name="partitions"></param> /// <param name="currentPartition"></param> /// <param name="partitionNumber"></param> /// <param name="allPrimerKeys"></param> private void ProcessKLVObject(MXFKLV klv, MXFObject partitions, ref MXFPartition currentPartition, ref int partitionNumber, ref Dictionary <UInt16, MXFEntryPrimer> allPrimerKeys) { // Is this a header, add to the partitions switch (klv.Key.Type) { case KeyType.Partition: currentPartition = klv as MXFPartition; currentPartition.File = this; currentPartition.PartitionNumber = partitionNumber; this.Partitions.Add(currentPartition); partitions.AddChild(currentPartition); partitionNumber++; break; case KeyType.PrimerPack: if (currentPartition != null) { MXFPrimerPack primer = klv as MXFPrimerPack; if (primer != null) // Just to be sure { // Let the partition know all primer keys allPrimerKeys = primer.AllKeys; currentPartition.PrimerKeys = primer.AllKeys; } currentPartition.AddChild(klv); // Add the primer } break; case KeyType.RIP: // Only add the RIP when not yet present if (this.RIP == null) { this.AddChild(klv); this.RIP = klv as MXFRIP; } break; case KeyType.SystemItem: if (currentPartition != null) { // Store the first system item for every partition // (required to calculate essence positions) if (currentPartition.FirstSystemItem == null) { currentPartition.FirstSystemItem = klv as MXFSystemItem; } currentPartition.AddChild(klv); } else { this.AddChild(klv); } // Store the first and the last system item if (this.FirstSystemItem == null) { this.FirstSystemItem = klv as MXFSystemItem; } this.LastSystemItem = klv as MXFSystemItem; break; case KeyType.Essence: if (currentPartition != null) { // Store the first system item for every partition // (required to calculate essence positions) MXFEssenceElement ee = klv as MXFEssenceElement; if (ee.IsPicture && currentPartition.FirstPictureEssenceElement == null) { currentPartition.FirstPictureEssenceElement = ee; } currentPartition.AddChild(klv); } else { this.AddChild(klv); } break; case KeyType.Preface: this.LogicalBase = new MXFLogicalObject(klv, klv.ToString()); // Normal if (currentPartition != null) { currentPartition.AddChild(klv); } else { this.AddChild(klv); } break; default: // Normal if (currentPartition != null) { currentPartition.AddChild(klv); } else { this.AddChild(klv); } break; } }
/// <summary> /// Partially Parse an MXF file, skip all data /// </summary> protected void ParsePartial(BackgroundWorker worker) { Stopwatch sw = Stopwatch.StartNew(); MXFKLVFactory klvFactory = new MXFKLVFactory(); MXFPartition currentPartition = null; int previousPercentage = 0; Dictionary <UInt16, MXFEntryPrimer> allPrimerKeys = null; int[] counters = new int[Enum.GetNames(typeof(KeyType)).Length]; PartialSeekMode seekMode = PartialSeekMode.Unknown; using (m_reader = new MXFReader(this.Filename)) { this.Filesize = m_reader.Size; MXFObject partitions = new MXFNamedObject("Partitions", 0); this.AddChild(partitions); // Start with trying to find the RIP bool ripFound = ReadRIP(klvFactory); if (ripFound) { seekMode = PartialSeekMode.UsingRIP; } m_reader.Seek(0); // Start at the beginning // Start by reading the first partition int partitionNumber = 0; // For easy partition identification while (!m_reader.EOF && seekMode != PartialSeekMode.Backwards) // Eof and NOT searching backwards { MXFKLV klv = klvFactory.CreateObject(m_reader, currentPartition); // Update overall counters if (klv.Key.Type == KeyType.None) { counters[(int)klv.Key.Type]++; } if (klv.Key.Type == KeyType.Partition && seekMode == PartialSeekMode.Backwards) { if (this.Partitions.Exists(a => a.Offset == klv.Offset)) { // A new partition has been found that we already found, quit the main loop break; } } // Process the new KLV ProcessKLVObject(klv, partitions, ref currentPartition, ref partitionNumber, ref allPrimerKeys); // If we found the second partition long nextSeekPosition = klv.DataOffset + klv.Length; if (partitionNumber >= 2) // Header fully read, now busy with the second partition { switch (seekMode) { case PartialSeekMode.UsingRIP: // And we already found the RIP if (currentPartition.FirstSystemItem != null) // And we found the first system item { MXFEntryRIP ripEntry = this.RIP.GetPartition(partitionNumber); if (ripEntry != null) { // Mark the current partition as not-completely read currentPartition.IsLoaded = false; // Start at the next partition nextSeekPosition = (long)ripEntry.PartitionOffset; } } break; case PartialSeekMode.Backwards: // NO RIP, searching backwards // Backwards, jump to the PREVIOUS partition if (currentPartition.FirstSystemItem != null) // And we found the first system item { // Jump to the previous partition if (currentPartition.PreviousPartition != 0) { // And we haven't found this partition yet if (!this.Partitions.Exists(a => a.ThisPartition == currentPartition.PreviousPartition)) { nextSeekPosition = (long)currentPartition.PreviousPartition; // Jump to previous } } } break; case PartialSeekMode.Unknown: // No RIP.... // Hmmm, RIP is not found, check if we have a footer partition somewhere MXFPartition part = this.Partitions.Where(a => a.FooterPartition != 0).FirstOrDefault(); if (part != null) { // If we are already at the footer, don't bother to seek if (currentPartition.Offset != (long)part.FooterPartition) { nextSeekPosition = (long)part.FooterPartition; // Start at the footer seekMode = PartialSeekMode.Backwards; } } break; } } // Next KLV please m_reader.Seek(nextSeekPosition); // Only report progress when the percentage has changed int currentPercentage = (int)((m_reader.Position * 90) / m_reader.Size); if (currentPercentage != previousPercentage) { worker.ReportProgress(currentPercentage, "Partial Parsing MXF file"); previousPercentage = currentPercentage; } } } // Progress should now be 90% // Update all type descriptions klvFactory.UpdateAllTypeDescriptions(allPrimerKeys); Debug.WriteLine("Finished parsing file '{0}' in {1} ms", this.Filename, sw.ElapsedMilliseconds); sw.Restart(); // Create a list with all UID keys worker.ReportProgress(91, "Creating key list"); Dictionary <string, MXFObject> allKeys = new Dictionary <string, MXFObject>(); CreateKeyList(allKeys, this); // Resolve the references worker.ReportProgress(92, "Resolving references"); ResolveReferences(allKeys, this); Debug.WriteLine("Finished resolving references in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); this.FlatList = new List <MXFObject>(); worker.ReportProgress(93, "Creating flat list"); this.AddToList(this.FlatList); Debug.WriteLine("Flatlist created in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); // Create the logical tree worker.ReportProgress(94, "Creating logical tree"); CreateLogicalTree(); Debug.WriteLine("Logical tree created in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); // And Execute FAST tests this.ExecuteValidationTest(worker, false); // Finished worker.ReportProgress(100, "Finished"); }
/// <summary> /// Fully Parse an MXF file /// </summary> protected void ParseFull(BackgroundWorker worker) { Stopwatch sw = Stopwatch.StartNew(); MXFKLVFactory klvFactory = new MXFKLVFactory(); MXFPartition currentPartition = null; int previousPercentage = 0; Dictionary <UInt16, MXFEntryPrimer> allPrimerKeys = null; int[] counters = new int[Enum.GetNames(typeof(KeyType)).Length]; using (m_reader = new MXFReader(this.Filename)) { this.Filesize = m_reader.Size; MXFObject partitions = new MXFNamedObject("Partitions", 0); this.AddChild(partitions); int partitionNumber = 0; // For easy partition identification while (!m_reader.EOF) { MXFKLV klv = klvFactory.CreateObject(m_reader, currentPartition); // Update overall counters if (klv.Key.Type == KeyType.None) { counters[(int)klv.Key.Type]++; } // Process the new KLV ProcessKLVObject(klv, partitions, ref currentPartition, ref partitionNumber, ref allPrimerKeys); // Next KLV please m_reader.Seek(klv.DataOffset + klv.Length); // Only report progress when the percentage has changed int currentPercentage = (int)((m_reader.Position * 90) / m_reader.Size); if (currentPercentage != previousPercentage) { worker.ReportProgress(currentPercentage, "Parsing MXF file"); previousPercentage = currentPercentage; } } } // Progress should now be 90% // Update all type descriptions klvFactory.UpdateAllTypeDescriptions(allPrimerKeys); worker.ReportProgress(91, "Creating key list"); Debug.WriteLine("Finished parsing file '{0}' in {1} ms", this.Filename, sw.ElapsedMilliseconds); sw.Restart(); // Create a list with all UID keys Dictionary <string, MXFObject> allKeys = new Dictionary <string, MXFObject>(); CreateKeyList(allKeys, this); worker.ReportProgress(92, "Creating resolving references"); // Resolve the references ResolveReferences(allKeys, this); Debug.WriteLine("Finished resolving references in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); worker.ReportProgress(93, "Resolving flatlist"); this.FlatList = new List <MXFObject>(); this.AddToList(this.FlatList); Debug.WriteLine("Flatlist created in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); worker.ReportProgress(94, "Creating Logical tree"); // Create the logical tree CreateLogicalTree(); Debug.WriteLine("Logical tree created in {0} ms", sw.ElapsedMilliseconds); sw.Restart(); // And Execute ALL test this.ExecuteValidationTest(worker, true); // Finished worker.ReportProgress(100, "Finished"); }
/// <summary> /// Check if all partitions are valid /// </summary> /// <param name="results"></param> public override void OnExecuteTest(ref List <MXFValidationResult> results) { this.Task = "Validating partitions"; MXFValidationResult valResult = new MXFValidationResult("Partition Test"); results.Add(valResult); valResult.Category = "Partitions"; Stopwatch sw = Stopwatch.StartNew(); if (this.File.Partitions == null || this.File.Partitions.Count == 0) { valResult.SetError(string.Format("Error! No partitions present.")); return; } // Check if only a single header is present if (this.File.Partitions.Where(a => a.PartitionType == PartitionType.Header).Count() > 1) { valResult.SetError(string.Format("Error! More then 1 header partion present!")); return; } MXFPartition header = this.File.Partitions.Where(a => a.PartitionType == PartitionType.Header).FirstOrDefault(); if (header == null) { valResult.SetError(string.Format("Error! No header partition present!")); return; } // Check if only a single footer is present if (this.File.Partitions.Where(a => a.PartitionType == PartitionType.Footer).Count() > 1) { valResult.SetError(string.Format("Error! More then 1 footer partion present!")); return; } MXFPartition footer = this.File.Partitions.Where(a => a.PartitionType == PartitionType.Footer).FirstOrDefault(); long footerExpected = 0; if (footer == null) { valResult.SetWarning(string.Format("Error! No footer partition present!")); footerExpected = 0; } else { footerExpected = footer.Offset; } // Check if all partitions point to the previous partition and check the this pointer // Note that this is more serious and less likely to go wrong then the footer check for (int n = 0; n < this.File.Partitions.Count(); n++) { if (this.File.Partitions[n].ThisPartition != (ulong)this.File.Partitions[n].Offset) { valResult.SetError(string.Format("Error! Partition[{0}] has an invalid 'ThisPartition' pointer.", n)); return; } if (n > 0) { if (this.File.Partitions[n].PreviousPartition != (ulong)this.File.Partitions[n - 1].Offset) { valResult.SetError(string.Format("Error! Partition[{0}] has no valid link to the previous partition.", n)); return; } } } // Check if all partitions point to the footer int errorCount = 0; for (int n = 0; n < this.File.Partitions.Count(); n++) { if (this.File.Partitions[n].Closed && this.File.Partitions[n].FooterPartition != (ulong)footerExpected) { errorCount++; } } if (errorCount > 0) { valResult.SetWarning(string.Format("There are {0} partitions that do not point to the footer partition.", errorCount)); return; } valResult.SetSuccess("Partition structure is valid."); LogInfo("Validation completed in {0} msec", sw.ElapsedMilliseconds); }
/// <summary> /// Fully Parse an MXF file /// </summary> protected void ParseFull(BackgroundWorker worker) { Stopwatch sw = Stopwatch.StartNew(); MXFKLVFactory klvFactory = new MXFKLVFactory(); MXFPartition currentPartition = null; int previousPercentage = 0; Dictionary <UInt16, MXFEntryPrimer> allPrimerKeys = null; //int[] counters = new int[Enum.GetNames(typeof(KeyType)).Length]; using (m_reader = new MXFReader(this.Filename)) { this.Filesize = m_reader.Size; MXFObject partitions = new MXFNamedObject("Partitions", 0); this.AddChild(partitions); int partitionNumber = 0; // For easy partition identification while (!m_reader.EOF) { try { MXFKLV klv = klvFactory.CreateObject(m_reader, currentPartition); //// Update overall counters //if (klv.Key.Type == KeyType.None) // counters[(int)klv.Key.Type]++; // Process the new KLV ProcessKLVObject(klv, partitions, ref currentPartition, ref partitionNumber, ref allPrimerKeys); // Next KLV please m_reader.Seek(klv.DataOffset + klv.Length); } catch (Exception e) { m_reader.SeekForNextPotentialKey(); } // Only report progress when the percentage has changed int currentPercentage = (int)((m_reader.Position * 90) / m_reader.Size); if (currentPercentage != previousPercentage) { worker.ReportProgress(currentPercentage, "Parsing MXF file"); previousPercentage = currentPercentage; } } } Debug.WriteLine("Finished parsing file '{0}' in {1} ms", this.Filename, sw.ElapsedMilliseconds); // Progress should now be 90% DoPostWork(worker, sw, allPrimerKeys); // And Execute ALL test sw.Restart(); this.ExecuteValidationTest(worker, true); Debug.WriteLine("Tests executed in {0} ms", sw.ElapsedMilliseconds); // Finished worker.ReportProgress(100, "Finished"); }