< Summary - Jellyfin

Information
Class: Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions
Assembly: Jellyfin.MediaEncoding.Keyframes
File(s): /srv/git/jellyfin/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Extensions/EbmlReaderExtensions.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 88
Coverable lines: 88
Total lines: 177
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 38
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
FindElement(...)0%2040%
ReadUIntFromBinary(...)100%210%
ReadSeekHead(...)0%812280%
ReadInfo(...)0%620%
FindFirstTrackNumberByType(...)0%2040%

File(s)

/srv/git/jellyfin/src/Jellyfin.MediaEncoding.Keyframes/Matroska/Extensions/EbmlReaderExtensions.cs

#LineLine coverage
 1using System;
 2using System.Buffers.Binary;
 3using Jellyfin.MediaEncoding.Keyframes.Matroska.Models;
 4using NEbml.Core;
 5
 6namespace Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions;
 7
 8/// <summary>
 9/// Extension methods for the <see cref="EbmlReader"/> class.
 10/// </summary>
 11internal static class EbmlReaderExtensions
 12{
 13    /// <summary>
 14    /// Traverses the current container to find the element with <paramref name="identifier"/> identifier.
 15    /// </summary>
 16    /// <param name="reader">An instance of <see cref="EbmlReader"/>.</param>
 17    /// <param name="identifier">The element identifier.</param>
 18    /// <returns>A value indicating whether the element was found.</returns>
 19    internal static bool FindElement(this EbmlReader reader, ulong identifier)
 020    {
 021        while (reader.ReadNext())
 022        {
 023            if (reader.ElementId.EncodedValue == identifier)
 024            {
 025                return true;
 26            }
 027        }
 28
 029        return false;
 030    }
 31
 32    /// <summary>
 33    /// Reads the current position in the file as an unsigned integer converted from binary.
 34    /// </summary>
 35    /// <param name="reader">An instance of <see cref="EbmlReader"/>.</param>
 36    /// <returns>The unsigned integer.</returns>
 37    internal static uint ReadUIntFromBinary(this EbmlReader reader)
 038    {
 039        var buffer = new byte[4];
 040        reader.ReadBinary(buffer, 0, 4);
 041        return BinaryPrimitives.ReadUInt32BigEndian(buffer);
 042    }
 43
 44    /// <summary>
 45    /// Reads from the start of the file to retrieve the SeekHead segment.
 46    /// </summary>
 47    /// <param name="reader">An instance of <see cref="EbmlReader"/>.</param>
 48    /// <returns>Instance of <see cref="SeekHead"/>.</returns>
 49    internal static SeekHead ReadSeekHead(this EbmlReader reader)
 050    {
 051        reader = reader ?? throw new ArgumentNullException(nameof(reader));
 52
 053        if (reader.ElementPosition != 0)
 054        {
 055            throw new InvalidOperationException("File position must be at 0");
 56        }
 57
 58        // Skip the header
 059        if (!reader.FindElement(MatroskaConstants.SegmentContainer))
 060        {
 061            throw new InvalidOperationException("Expected a segment container");
 62        }
 63
 064        reader.EnterContainer();
 65
 066        long? tracksPosition = null;
 067        long? cuesPosition = null;
 068        long? infoPosition = null;
 69        // The first element should be a SeekHead otherwise we'll have to search manually
 070        if (!reader.FindElement(MatroskaConstants.SeekHead))
 071        {
 072            throw new InvalidOperationException("Expected a SeekHead");
 73        }
 74
 075        reader.EnterContainer();
 076        while (reader.FindElement(MatroskaConstants.Seek))
 077        {
 078            reader.EnterContainer();
 079            reader.ReadNext();
 080            var type = (ulong)reader.ReadUIntFromBinary();
 081            switch (type)
 82            {
 83                case MatroskaConstants.Tracks:
 084                    reader.ReadNext();
 085                    tracksPosition = (long)reader.ReadUInt();
 086                    break;
 87                case MatroskaConstants.Cues:
 088                    reader.ReadNext();
 089                    cuesPosition = (long)reader.ReadUInt();
 090                    break;
 91                case MatroskaConstants.Info:
 092                    reader.ReadNext();
 093                    infoPosition = (long)reader.ReadUInt();
 094                    break;
 95            }
 96
 097            reader.LeaveContainer();
 98
 099            if (tracksPosition.HasValue && cuesPosition.HasValue && infoPosition.HasValue)
 0100            {
 0101                break;
 102            }
 0103        }
 104
 0105        reader.LeaveContainer();
 106
 0107        if (!tracksPosition.HasValue || !cuesPosition.HasValue || !infoPosition.HasValue)
 0108        {
 0109            throw new InvalidOperationException("SeekHead is missing or does not contain Info, Tracks and Cues positions
 110        }
 111
 0112        return new SeekHead(infoPosition.Value, tracksPosition.Value, cuesPosition.Value);
 0113    }
 114
 115    /// <summary>
 116    /// Reads from SegmentContainer to retrieve the Info segment.
 117    /// </summary>
 118    /// <param name="reader">An instance of <see cref="EbmlReader"/>.</param>
 119    /// <param name="position">The position of the info segment relative to the Segment container.</param>
 120    /// <returns>Instance of <see cref="Info"/>.</returns>
 121    internal static Info ReadInfo(this EbmlReader reader, long position)
 0122    {
 0123        reader.ReadAt(position);
 124
 0125        double? duration = null;
 0126        reader.EnterContainer();
 127        // Mandatory element
 0128        reader.FindElement(MatroskaConstants.TimestampScale);
 0129        var timestampScale = reader.ReadUInt();
 130
 0131        if (reader.FindElement(MatroskaConstants.Duration))
 0132        {
 0133            duration = reader.ReadFloat();
 0134        }
 135
 0136        reader.LeaveContainer();
 137
 0138        return new Info((long)timestampScale, duration);
 0139    }
 140
 141    /// <summary>
 142    /// Enters the Tracks segment and reads all tracks to find the specified type.
 143    /// </summary>
 144    /// <param name="reader">Instance of <see cref="EbmlReader"/>.</param>
 145    /// <param name="tracksPosition">The relative position of the tracks segment.</param>
 146    /// <param name="type">The track type identifier.</param>
 147    /// <returns>The first track number with the specified type.</returns>
 148    /// <exception cref="InvalidOperationException">Stream type is not found.</exception>
 149    internal static ulong FindFirstTrackNumberByType(this EbmlReader reader, long tracksPosition, ulong type)
 0150    {
 0151        reader.ReadAt(tracksPosition);
 152
 0153        reader.EnterContainer();
 0154        while (reader.FindElement(MatroskaConstants.TrackEntry))
 0155        {
 0156            reader.EnterContainer();
 157            // Mandatory element
 0158            reader.FindElement(MatroskaConstants.TrackNumber);
 0159            var trackNumber = reader.ReadUInt();
 160
 161            // Mandatory element
 0162            reader.FindElement(MatroskaConstants.TrackType);
 0163            var trackType = reader.ReadUInt();
 164
 0165            reader.LeaveContainer();
 0166            if (trackType == MatroskaConstants.TrackTypeVideo)
 0167            {
 0168                reader.LeaveContainer();
 0169                return trackNumber;
 170            }
 0171        }
 172
 0173        reader.LeaveContainer();
 174
 0175        throw new InvalidOperationException($"No stream with type {type} found");
 0176    }
 177}