/// <summary> /// Execute all validation tests /// </summary> public void ExecuteValidationTest(BackgroundWorker worker, bool extendedTest) { // Reset results this.m_results.Clear(); // Execute validation tests List <MXFValidator> allTest = new List <MXFValidator>(); allTest.Add(new MXFValidatorInfo()); allTest.Add(new MXFValidatorPartitions()); allTest.Add(new MXFValidatorRIP()); if (extendedTest) { allTest.Add(new MXFValidatorIndex()); } foreach (MXFValidator mxfTest in allTest) { mxfTest.Initialize(this, worker); mxfTest.ExecuteTest(ref m_results); } if (!extendedTest) { MXFValidationResult valResult = new MXFValidationResult("Index Table"); this.m_results.Add(valResult); valResult.SetWarning("Index table test not executed in partial loading mode (to execute test press the execute all test button)."); //MXFValidationResult valResult = new MXFValidationResult("Index Tables"); //this.Results.Add(valResult); // And directly add the results } }
public override void OnExecuteTest(ref List <MXFValidationResult> results) { this.Task = "Track Info"; int not = this.File.NumberOfTracks; for (int n = 1; n <= not; n++) { ReportProgress(n * 100 / not); MXFValidationResult valResult = new MXFValidationResult(string.Format("Track {0}", n)); valResult.Category = "Track Info"; results.Add(valResult); // And directly add the results valResult.SetInfo(this.File.GetTrackInfo(n)); } }
public override void OnExecuteTest(ref List <MXFValidationResult> results) { this.Task = "Track Info"; MXFMaterialPackage mp = this.File.GetContentStorage()?.GetFirstMaterialPackage(); List <MXFGenericTrack> tracks = mp.GetGenericTracks().ToList(); foreach (var t in tracks) { int n = tracks.IndexOf(t); ReportProgress(n * 100 / tracks.Count); MXFValidationResult valResult = new MXFValidationResult(string.Format("Track {0}", n)); valResult.Category = "Track Info"; valResult.SetInfo(this.File.GetTrackInfo(t)); results.Add(valResult); // And directly add the results } }
/// <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); }
public override void OnExecuteTest(ref List <MXFValidationResult> results) { this.Task = "Checking MXF Keys"; Stopwatch sw = Stopwatch.StartNew(); var klvWithUnknownKeys = this.File.Descendants() .OfType <MXFKLV>() .Where(klv => string.IsNullOrEmpty(klv.Key.Name)) .OrderBy(klv => klv.Offset); foreach (var unkownKLV in klvWithUnknownKeys) { MXFValidationResult valResult = new MXFValidationResult("Keys"); valResult.SetWarning(string.Format("Unknown key {0} @ {1}.", unkownKLV.Key, unkownKLV.Offset)); results.Add(valResult); // And directly add the results } LogInfo("MXF Key check completed in {0} msec", sw.ElapsedMilliseconds); }
/// <summary> /// Check the essence range /// </summary> /// <param name="file"></param> /// <param name="results"></param> protected void CheckContinuityCounter(MXFFile file, List <MXFValidationResult> results) { MXFValidationResult valResult = new MXFValidationResult("System Items"); results.Add(valResult); // And directly add the results // Check for continous range int cc = -1; int errorCount = 0; foreach (MXFSystemItem si in this.m_systemItems) { if (si.ContinuityCount - cc != 1) { errorCount++; //valResult.SetError(string.Format("Invalid continuity count for system item at offset {0}. CC should be {1} but is {2}!", si.Offset, cc + 1, si.ContinuityCount)); //return; } cc = si.ContinuityCount; } if (errorCount > 0) { if (errorCount >= this.m_systemItems.Count() - 1) { valResult.SetWarning(string.Format("All continuity counter values are not set!")); } else { valResult.SetError(string.Format("Found {0} invalid continuity counter values (total system items {1})!", errorCount, this.m_systemItems.Count())); } } else { valResult.SetSuccess(string.Format("Continuity counter values are correct!")); } }
/// <summary> /// Check the essence range /// </summary> /// <param name="file"></param> /// <param name="results"></param> protected void CheckUserDates(MXFFile file, List <MXFValidationResult> results) { List <MXFSystemItem> items = this.m_systemItems.OrderBy(a => a.ContinuityCount).ToList(); if (items.Count > 1) { MXFTimeStamp ts = new MXFTimeStamp(items.First().UserDate); if (ts != null) { MXFValidationResult valResult = new MXFValidationResult("System Items"); results.Add(valResult); // And directly add the results MXFTimeStamp tsLast = null; for (int n = 1; n < items.Count() - 1; n++) // Skip last one (always invalid??) { ts.Increase(); if (!items[n].UserDate.IsEmpty()) { if (!items[n].UserDate.IsSame(ts)) { valResult.SetError(string.Format("Invalid user date at offset {0} (was {1}, expected {2})!", items[n].Offset, items[n].UserDate, ts)); return; } tsLast = items[n].UserDate; } } if (tsLast != null) { valResult.SetSuccess(string.Format("UserDates are continious from {0} to {1}, at {2} fps!", items.First().UserDate, tsLast, ts.FrameRate)); } else { valResult.SetSuccess(string.Format("UserDates are continious!")); } } } }
/// <summary> /// Check all index tables /// </summary> /// <param name="file"></param> /// <param name="results"></param> public override void OnExecuteTest(ref List <MXFValidationResult> results) { MXFValidationResult valResult = new MXFValidationResult("Index Tables"); results.Add(valResult); // And directly add the results Stopwatch sw = Stopwatch.StartNew(); // Clear list m_indexTables = new List <MXFIndexTableSegment>(); m_systemItems = new List <MXFSystemItem>(); // Load all partitions this.Task = "Loading partitions"; List <MXFPartition> partitionsToLoad = this.File.Partitions.Where(a => !a.IsLoaded).ToList(); for (int n = 0; n < partitionsToLoad.Count(); n++) { int progress = (n * 50) / partitionsToLoad.Count(); ReportProgress(progress); partitionsToLoad[n].Load(); } // Find all index tables (and the first system index) this.Task = "Locating index tables"; FindIndexTableAndSystemItems(this.File); ReportProgress(55); LogInfo("Found {0} index table segments in {1} ms", this.m_indexTables.Count, sw.ElapsedMilliseconds); sw.Restart(); // Check if first index table is CBE if (this.m_indexTables.Count == 0) { valResult.SetError(string.Format("No index tables found!", m_indexTables.Count)); return; } MXFIndexTableSegment firstTable = this.m_indexTables.FirstOrDefault(); if (firstTable != null) { if (firstTable.EditUnitByteCount > 0) { // Check other tables for (int n = 1; n < this.m_indexTables.Count; n++) { if (this.m_indexTables[n].EditUnitByteCount != firstTable.EditUnitByteCount && this.m_indexTables[n].BodySID == firstTable.BodySID) { valResult.SetError(string.Format("Constant Bytes per Element ({0} bytes) but index table {1} has other CBE: {2} (both have BodySID {3}).", firstTable.EditUnitByteCount, n, this.m_indexTables[n].EditUnitByteCount, firstTable.BodySID)); return; } } // CBE for this BodySID if (firstTable.IndexDuration == 0 && firstTable.IndexStartPosition == 0) { valResult.SetSuccess(string.Format("Constant Bytes per Element ({0} bytes) for the whole duration of BodySID {1}", firstTable.EditUnitByteCount, firstTable.BodySID)); // TODO Check if the size of all compounds is the same?? return; } else { // Complicated, mixed file valResult.SetWarning(string.Format("Constant Bytes per Element ({0} bytes) but not for the whole file for BodySID {1}. Duration: {2}, StartOffset: {3}", firstTable.EditUnitByteCount, firstTable.BodySID, firstTable.IndexDuration, firstTable.IndexStartPosition)); return; } } } // Check if there are multiple index table segments pointing to the same data int invalidCt = 0; int validCt = 0; int totalSystemItems = 0; int counter = 0; this.Task = "Checking for duplicates"; foreach (MXFIndexTableSegment ids in this.m_indexTables) { ReportProgress(55 + (counter * 20) / this.m_indexTables.Count()); // And all index entries if (ids.IndexEntries != null) { List <MXFIndexTableSegment> sameStuff = this.m_indexTables.Where(a => a != ids && a.IndexSID == ids.IndexSID && a.IndexStartPosition == ids.IndexStartPosition).ToList(); foreach (MXFIndexTableSegment sameIds in sameStuff) { if (sameIds.IndexEntries.Count != ids.IndexEntries.Count) { valResult.AddError(string.Format("Index {0} in partition {1} is not the same length as in partition {2}!", sameIds.IndexSID, sameIds.Partition.ToString(), ids.Partition.ToString())); invalidCt++; } else { for (int n = 0; n < sameIds.IndexEntries.Count; n++) { if (ids.IndexEntries[n].KeyFrameOffset != sameIds.IndexEntries[n].KeyFrameOffset || ids.IndexEntries[n].StreamOffset != sameIds.IndexEntries[n].StreamOffset || ids.IndexEntries[n].TemporalOffset != sameIds.IndexEntries[n].TemporalOffset) { valResult.AddError(string.Format("The indexentry {0} of Index {1} in partition {2} is not the same data as in partition {3}!", n, sameIds.IndexSID, sameIds.Partition.ToString(), ids.Partition.ToString())); invalidCt++; break; } } } } } counter++; } // First try to match system items if (m_systemItems.Count != 0) { // For each index table segment totalSystemItems = m_systemItems.Count(); counter = 0; this.Task = "Checking essence offsets"; foreach (MXFIndexTableSegment ids in this.m_indexTables) { ReportProgress(75 + (counter * 20) / this.m_indexTables.Count()); // And all index entries if (ids.IndexEntries != null) { foreach (MXFEntryIndex index in ids.IndexEntries) { // Check if there is a system item at this offset long searchIndex = (long)(index.StreamOffset); MXFSystemItem si = this.m_systemItems.Where(a => a.EssenceOffset == searchIndex).FirstOrDefault(); if (si != null) { // Yes, found validCt++; si.Indexed = true; } else { // Not found valResult.AddError(string.Format("Index {0} not pointing to valid essence!", index.Index)); invalidCt++; } } } counter++; } } else if (m_pictureItems.Count != 0) { // Now try to match the picture essences // For each index table segment counter = 0; totalSystemItems = m_pictureItems.Count(); this.Task = "Checking picture offsets"; foreach (MXFIndexTableSegment ids in this.m_indexTables) { ReportProgress(75 + (counter * 20) / this.m_indexTables.Count()); // And all index entries if (ids.IndexEntries != null) { foreach (MXFEntryIndex index in ids.IndexEntries) { // Check if there is a system item at this offset long searchIndex = (long)(index.StreamOffset); MXFEssenceElement ee = this.m_pictureItems.Where(a => a.EssenceOffset == searchIndex).FirstOrDefault(); if (ee != null) { // Yes, found validCt++; ee.Indexed = true; } else { // Not found valResult.AddError(string.Format("Index {0} not pointing to a valid picture essence!", index.Index)); invalidCt++; } } } } } else { valResult.SetError(string.Format("No system items and/or picture essences found (found {0} index table segments)", m_indexTables.Count)); return; } bool fError = false; if (this.m_systemItems.Count() > 0) { if (this.m_systemItems.Count(a => !a.Indexed) != 0) { // Hmm still some items left in the array valResult.SetError(string.Format("There are {0} essence elements (of the total {1}) that are not referenced in an index table!", this.m_systemItems.Count, totalSystemItems)); fError = true; } } else if (this.m_pictureItems.Count() > 0) { if (this.m_pictureItems.Count(a => !a.Indexed) != 0) { // Hmm still some items left in the array valResult.SetError(string.Format("There are {0} essence elements (of the total {1}) that are not referenced in an index table!", this.m_pictureItems.Count, totalSystemItems)); fError = true; } } if (!fError) { if (invalidCt > 0) { valResult.SetError(string.Format("Found {0} index errors! There are {0} indices that are NOT pointing to valid essence data (valid {1})!", invalidCt, validCt)); } else if (validCt > 0) { valResult.SetSuccess(string.Format("Index table is valid! All {0} index entries point to valid essences!", validCt)); } if (validCt == 0 && invalidCt == 0) { valResult.SetError(string.Format("No valid indexes found in this file!")); } } LogInfo("Validation completed in {0} msec", sw.ElapsedMilliseconds); // Check system item range if (this.m_systemItems.Count() > 0) { CheckUserDates(this.File, results); CheckContinuityCounter(this.File, results); } ReportProgress(100); }
/// <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); }