using System; using System.Collections.Generic; using System.IO; using Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions; using NEbml.Core; namespace Jellyfin.MediaEncoding.Keyframes.Matroska { /// /// The keyframe extractor for the matroska container. /// public static class MatroskaKeyframeExtractor { /// /// Extracts the keyframes in ticks (scaled using the container timestamp scale) from the matroska container. /// /// The file path. /// An instance of . public static KeyframeData GetKeyframeData(string filePath) { using var stream = File.OpenRead(filePath); using var reader = new EbmlReader(stream); var seekHead = reader.ReadSeekHead(); var info = reader.ReadInfo(seekHead.InfoPosition); var videoTrackNumber = reader.FindFirstTrackNumberByType(seekHead.TracksPosition, MatroskaConstants.TrackTypeVideo); var keyframes = new List(); reader.ReadAt(seekHead.CuesPosition); reader.EnterContainer(); while (reader.FindElement(MatroskaConstants.CuePoint)) { reader.EnterContainer(); ulong? trackNumber = null; // Mandatory element reader.FindElement(MatroskaConstants.CueTime); var cueTime = reader.ReadUInt(); // Mandatory element reader.FindElement(MatroskaConstants.CueTrackPositions); reader.EnterContainer(); if (reader.FindElement(MatroskaConstants.CuePointTrackNumber)) { trackNumber = reader.ReadUInt(); } reader.LeaveContainer(); if (trackNumber == videoTrackNumber) { keyframes.Add(ScaleToNanoseconds(cueTime, info.TimestampScale)); } reader.LeaveContainer(); } reader.LeaveContainer(); var result = new KeyframeData(ScaleToNanoseconds(info.Duration ?? 0, info.TimestampScale), keyframes); return result; } private static long ScaleToNanoseconds(ulong unscaledValue, long timestampScale) { // TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns return (long)unscaledValue * timestampScale / 100; } private static long ScaleToNanoseconds(double unscaledValue, long timestampScale) { // TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns return Convert.ToInt64(unscaledValue * timestampScale / 100); } } }