/// <summary> /// Locate all index table segments RECURSIVELY /// </summary> /// <param name="current"></param> public void FindIndexTableAndSystemItems(MXFObject current) { // LOAD the object (when not yet loaded) // This may take some time!!! if (current is ILazyLoadable loadable) { loadable.Load(); } MXFKLV klv = current as MXFKLV; if (klv != null) { if (klv.Key.Type == KeyType.IndexSegment) { MXFIndexTableSegment its = klv as MXFIndexTableSegment; if (its != null) { this.m_indexTables.Add(its); } } else if (klv.Key.Type == KeyType.SystemItem) { MXFSystemItem si = klv as MXFSystemItem; if (si != null) { si.Indexed = false; this.m_systemItems.Add(si); } } else if (klv.Key.Type == KeyType.Essence) { MXFEssenceElement ee = klv as MXFEssenceElement; if (ee != null) { if (ee.IsPicture) { ee.Indexed = false; this.m_pictureItems.Add(ee); } } } } if (!current.Children.Any()) { return; } foreach (MXFObject child in current.Children) { FindIndexTableAndSystemItems(child); } }
/// <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); }