public long SeekTo(long granulePos, int preRoll, GetPacketGranuleCount getPacketGranuleCount) { if (_reader == null) { throw new ObjectDisposedException(nameof(PacketProvider)); } var pageIndex = _reader.FindPage(granulePos); int packetIndex; if (granulePos == 0) { // for this, we can generically say the first packet on the first page having a non-zero granule packetIndex = 0; } else { packetIndex = FindPacket(pageIndex, ref granulePos, getPacketGranuleCount); packetIndex -= preRoll; } if (!NormalizePacketIndex(ref pageIndex, ref packetIndex)) { throw new ArgumentOutOfRangeException(nameof(granulePos)); } _lastPacket = null; _pageIndex = pageIndex; _packetIndex = packetIndex; return(granulePos); }
private int FindPacket(int pageIndex, ref long granulePos, GetPacketGranuleCount getPacketGranuleCount, bool isRecursed = false) { // pageIndex is the correct page; we just need to figure out which packet // first let's get the starting granule of the page long startGP; bool isContinued; if (pageIndex == 0) { startGP = 0; } else if (_reader.GetPage(pageIndex - 1, out startGP, out _, out _, out isContinued, out _, out _)) { if (isContinued) { // we have a continued packet, so we need to look at the previous page return(FindPacket(pageIndex - 1, ref granulePos, getPacketGranuleCount, true)); } } else { throw new System.IO.InvalidDataException("Could not get page?!"); } // now get the ending granule of the page if (!_reader.GetPage(pageIndex, out var endGP, out var isResync, out _, out isContinued, out var packetCount, out _)) { throw new System.IO.InvalidDataException("Could not get found page?!"); } // increment through the packets until we would go past granulePos var packetIndex = -1; var isFirst = pageIndex == _reader.FirstDataPageIndex; while (++packetIndex < packetCount) { var packet = CreatePacket(ref pageIndex, ref packetIndex, false, endGP, isFirst && isResync, isContinued, packetCount, 0); if (packet == null) { throw new System.IO.InvalidDataException("Could not find end of continuation!"); } var granules = getPacketGranuleCount(packet, isFirst); if (startGP + granules > granulePos) { granulePos = startGP; return(packetIndex); } startGP += granules; isFirst = false; } if (isContinued && isRecursed) { granulePos = endGP; return(packetCount - 1); } // we ran out of packets?! throw new System.IO.InvalidDataException("Ran out of packets?!"); }
public long SeekTo(long granulePos, int preRoll, GetPacketGranuleCount getPacketGranuleCount) { if (_reader == null) { throw new ObjectDisposedException(nameof(PacketProvider)); } int pageIndex; int packetIndex; if (granulePos == 0) { // for this, we can generically say the first packet on the first page having a non-zero granule pageIndex = _reader.FirstDataPageIndex; packetIndex = 0; } else { pageIndex = _reader.FindPage(granulePos); if (_reader.HasAllPages && _reader.MaxGranulePosition == granulePos) { // allow seek to the offset immediatly after the last available (for what good it'll do) _lastPacket = null; _pageIndex = pageIndex; _packetIndex = 0; return(granulePos); } else { packetIndex = FindPacket(pageIndex, ref granulePos, getPacketGranuleCount); } packetIndex -= preRoll; } if (!NormalizePacketIndex(ref pageIndex, ref packetIndex)) { throw new ArgumentOutOfRangeException(nameof(granulePos)); } if (pageIndex < _reader.FirstDataPageIndex) { pageIndex = _reader.FirstDataPageIndex; packetIndex = 0; } _lastPacket = null; _pageIndex = pageIndex; _packetIndex = packetIndex; return(granulePos); }
private int FindPacket(int pageIndex, ref long granulePos, GetPacketGranuleCount getPacketGranuleCount) { // we will only look through the current page... // if the granule isn't on the page, behavior is: // - if before: return 0 // - if after: return packetCount - (isContinued ? 2 : 1) // neither should happen because we're already looking at the correct page if (_reader.GetPage(pageIndex, out var pageGranulePos, out var isResync, out _, out var isContinued, out var packetCount, out var pageOverhead)) { var gp = pageGranulePos; var packetIndex = packetCount - (isContinued ? 2 : 1); while (packetIndex > 0) { if (gp >= granulePos) { var packet = CreatePacket(ref pageIndex, ref packetIndex, false, pageGranulePos, isResync, isContinued, packetCount, pageOverhead); if (packet == null) { break; } gp -= getPacketGranuleCount(packet, false); } if (gp < granulePos) { granulePos = gp; return(packetIndex); } --packetIndex; } if (packetIndex == 0) { // we're likely in the right spot for the granulePos... if (pageIndex > 0 && _reader.GetPage(pageIndex - 1, out var prevGranulePos, out _, out _, out _, out _, out _)) { granulePos = prevGranulePos; return(0); } } } return(-1); }
private int FindPacket(int pageIndex, ref long granulePos, GetPacketGranuleCount getPacketGranuleCount) { // pageIndex is the correct page; we just need to figure out which packet bool isContinued; int firstRealPacket = 0; if (_reader.GetPage(pageIndex - 1, out _, out _, out _, out isContinued, out _, out _)) { if (isContinued) { firstRealPacket = 1; } } else { throw new System.IO.InvalidDataException("Could not get page?!"); } // now get the ending granule of the page if (!_reader.GetPage(pageIndex, out var pageGranulePos, out var isResync, out var isContinuation, out isContinued, out var packetCount, out _)) { throw new System.IO.InvalidDataException("Could not get found page?!"); } if (isContinued) { // if continued, the last packet index doesn't apply packetCount--; } var packetIndex = packetCount; var isLastInPage = !isContinued; var endGP = pageGranulePos; while (endGP > granulePos && --packetIndex >= firstRealPacket) { // it would be nice to pass false instead of isContinued, but (hypothetically) we don't know if getPacketGranuleCount(...) needs the whole thing... // Vorbis doesn't, but someone might decide to try to use us for another purpose so we'll be good here. var packet = CreatePacket(ref pageIndex, ref packetIndex, false, pageGranulePos, packetIndex == 0 && isResync, isContinued, packetCount, 0); if (packet == null) { throw new System.IO.InvalidDataException("Could not find end of continuation!"); } endGP -= getPacketGranuleCount(packet, isLastInPage); isLastInPage = false; } if (packetIndex < firstRealPacket) { // either it's a continued packet OR we've hit the "long->short over page boundary" bug. // in both cases we'll just return the last packet of the previous page. var prevPageIndex = pageIndex; var prevPacketIndex = -1; if (!NormalizePacketIndex(ref prevPageIndex, ref prevPacketIndex)) { throw new System.IO.InvalidDataException("Failed to normalize packet index?"); } var packet = CreatePacket(ref prevPageIndex, ref prevPacketIndex, false, endGP, false, isContinuation, prevPacketIndex + 1, 0); if (packet == null) { throw new System.IO.InvalidDataException("Could not load previous packet!"); } granulePos = endGP - getPacketGranuleCount(packet, false); return(-1); } // normal seek; that wasn't so hard, right? granulePos = endGP; return(packetIndex); }
/// <summary> /// Not supported /// </summary> /// <param name="granulePos"></param> /// <param name="preRoll"></param> /// <param name="getPacketGranuleCount"></param> /// <returns></returns> public long SeekTo(long granulePos, int preRoll, GetPacketGranuleCount getPacketGranuleCount) => throw new NotSupportedException();