/// <summary>Load a cluster by 0-based index</summary> public ReusableCluster load(int idx) { Entry entry; if (dict.TryGetValue(idx, out entry)) { list.Remove(entry.node); list.AddLast(entry.node); return(entry.value); } ReusableCluster cluster = null; LinkedListNode <int> node; if (dict.Count >= capacity) { // Evicting a cluster from the cache node = list.First; if (dict.TryGetValue(node.Value, out entry)) { cluster = entry.value; dict.Remove(node.Value); } list.RemoveFirst(); } cluster = file.segment.cluster[idx].load(file.stream, file.segment.position, cluster); node = list.AddLast(idx); dict.Add(idx, new Entry(node, cluster)); return(cluster); }
public void loadCluster(int idx, ReusableCluster dest) { if (idx < 0 || idx >= segment.cluster.Length) { throw new IndexOutOfRangeException(); } segment.cluster[idx].load(stream, segment.position, dest); }
bool loadCluster(int i, bool unpackFrames = true) { // Stopwatch sw = Stopwatch.StartNew(); ReusableCluster cluster = clusters.load(i); clusterTimestamp = (long)cluster.timestamp; clusterIndex = i; blobs.Clear(); if (null != cluster.simpleBlock) { foreach (var b in cluster.simpleBlock) { if (b.trackNumber != trackNumber) { continue; } blobs.Add(b); } } if (null != cluster.blockGroup) { foreach (var b in cluster.blockGroup) { if (b.block.trackNumber != trackNumber) { continue; } blobs.Add(b.block); } } if (blobs.Count > 0) { if (!unpackFrames) { return(true); } // Initialize the first block of the now current cluster var firstBlock = blobs[0]; lock (clusters.syncRoot) stream.unpackFrames(ref firstBlock, ref laced); updateTimestamp(firstBlock.timestamp); // Logger.logVerbose( "{0}.loadCluster #{1}, {2}", this.GetType().Name, i, timestamp ); // Logger.logVerbose( "{0}.loadCluster #{1}, {2}; took {3} ms", this.GetType().Name, i, timestamp, sw.Elapsed.TotalMilliseconds ); return(true); } // Sometimes a cluster has a single blob of only 1 stream. // Probably caused by the fact that unlike Mpeg4, for some reason MKV developers decided to use the same time scale for all tracks. // Despite they're completely unrelated, audio comes at 48 KHz, video at 24 or 29.97 or 59.94 FPS. return(false); }
internal ReusableCluster load(Stream stream, long segmentOffset, ReusableCluster cluster) { if (null == cluster) { cluster = new ReusableCluster(); } stream.Seek(segmentOffset + segmentPosition, SeekOrigin.Begin); var id = stream.readElementId(); if (id != eElement.Cluster) { throw new ApplicationException($"Expected a cluster, found { id }"); } cluster.read(stream); return(cluster); }
public static MkvSeekPosition find(ClustersCache cache, TimeSpan where, ulong trackNumber) { ulong searchingTime = cache.timeScaler.convertBack(where); int idx = Array.BinarySearch(cache.file.segment.cluster, new ClusterPlaceholder(searchingTime), clusterCompare); if (idx < 0) { idx = (~idx) - 1; } if (idx < 0) { return(new MkvSeekPosition(TimeSpan.Zero, 0, 0)); } if (null == tempCluster) { tempCluster = new ReusableCluster(); } Logger.logDebug("FindSeekPosition.find, looking for {0}, scaled {1}, starting from {2}", where, searchingTime, idx); // Create a new reusable one ReusableCluster rc = new ReusableCluster(); sSeekPos? blob = listBlobsInClusters(cache, trackNumber, idx) .Where(sp => sp.time <= searchingTime) .Select(nullable) .LastOrDefault(); if (!blob.HasValue) { throw new ApplicationException("Seek failed, might be trying to seek past the end"); } TimeSpan time = cache.timeScaler.convert((long)blob.Value.time); return(new MkvSeekPosition(time, blob.Value.cluster, blob.Value.blob)); }
/// <summary>Sequence of blobs in the cluster, with sequence numbers</summary> static IEnumerable <sSeekPos> listBlobsInCluster(ReusableCluster cluster, ulong trackNumber, int clusterIdx) { sSeekPos result = default; result.cluster = clusterIdx; result.blob = 0; long time = (long)cluster.timestamp; // The filtering code needs to match what's in ReaderBase.loadCluster or this gonna fail, miserably. if (null != cluster.simpleBlock) { foreach (var b in cluster.simpleBlock) { if (b.trackNumber != trackNumber) { continue; } result.time = checked ((ulong)(time + b.timestamp)); yield return(result); result.blob++; } } if (null != cluster.blockGroup) { foreach (var b in cluster.blockGroup) { if (b.block.trackNumber != trackNumber) { continue; } result.time = checked ((ulong)(time + b.block.timestamp)); result.blob++; } } }
public Entry(LinkedListNode <int> node, ReusableCluster value) { this.node = node; this.value = value; }