MatroskaKeyframeExtractor.cs 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions;
  5. using NEbml.Core;
  6. namespace Jellyfin.MediaEncoding.Keyframes.Matroska
  7. {
  8. /// <summary>
  9. /// The keyframe extractor for the matroska container.
  10. /// </summary>
  11. public static class MatroskaKeyframeExtractor
  12. {
  13. /// <summary>
  14. /// Extracts the keyframes in ticks (scaled using the container timestamp scale) from the matroska container.
  15. /// </summary>
  16. /// <param name="filePath">The file path.</param>
  17. /// <returns>An instance of <see cref="KeyframeData"/>.</returns>
  18. public static KeyframeData GetKeyframeData(string filePath)
  19. {
  20. using var stream = File.OpenRead(filePath);
  21. using var reader = new EbmlReader(stream);
  22. var seekHead = reader.ReadSeekHead();
  23. var info = reader.ReadInfo(seekHead.InfoPosition);
  24. var videoTrackNumber = reader.FindFirstTrackNumberByType(seekHead.TracksPosition, MatroskaConstants.TrackTypeVideo);
  25. var keyframes = new List<long>();
  26. reader.ReadAt(seekHead.CuesPosition);
  27. reader.EnterContainer();
  28. while (reader.FindElement(MatroskaConstants.CuePoint))
  29. {
  30. reader.EnterContainer();
  31. ulong? trackNumber = null;
  32. // Mandatory element
  33. reader.FindElement(MatroskaConstants.CueTime);
  34. var cueTime = reader.ReadUInt();
  35. // Mandatory element
  36. reader.FindElement(MatroskaConstants.CueTrackPositions);
  37. reader.EnterContainer();
  38. if (reader.FindElement(MatroskaConstants.CuePointTrackNumber))
  39. {
  40. trackNumber = reader.ReadUInt();
  41. }
  42. reader.LeaveContainer();
  43. if (trackNumber == videoTrackNumber)
  44. {
  45. keyframes.Add(ScaleToTicks(cueTime, info.TimestampScale));
  46. }
  47. reader.LeaveContainer();
  48. }
  49. reader.LeaveContainer();
  50. var result = new KeyframeData(ScaleToTicks(info.Duration ?? 0, info.TimestampScale), keyframes);
  51. return result;
  52. }
  53. private static long ScaleToTicks(ulong unscaledValue, long timestampScale)
  54. {
  55. // TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
  56. return (long)unscaledValue * timestampScale / 100;
  57. }
  58. private static long ScaleToTicks(double unscaledValue, long timestampScale)
  59. {
  60. // TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
  61. return Convert.ToInt64(unscaledValue * timestampScale / 100);
  62. }
  63. }
  64. }