Skip to content

Commit

Permalink
* Add parameter change event to packet provider interface
Browse files Browse the repository at this point in the history
* Refactored decoder setup so parameter change can be handled
* Rewrote packet peek/get logic for simplicity
  • Loading branch information
ioctlLR committed Mar 5, 2014
1 parent e69e2d0 commit b554d59
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 135 deletions.
5 changes: 5 additions & 0 deletions NVorbis/IPacketProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,10 @@ public interface IPacketProvider : IDisposable
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0 or greater than the last available packet index.</exception>
/// <exception cref="InvalidOperationException"><see cref="CanSeek"/> is <c>False</c>.</exception>
void SeekToPacket(int index);

/// <summary>
/// Occurs when the stream is about to change parameters.
/// </summary>
event EventHandler<ParameterChangeEventArgs> ParameterChange;
}
}
1 change: 1 addition & 0 deletions NVorbis/NVorbis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<Compile Include="Ogg\OggPacket.cs" />
<Compile Include="Ogg\OggPacketReader.cs" />
<Compile Include="Ogg\OggPageFlags.cs" />
<Compile Include="ParameterChangeEventArgs.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RingBuffer.cs" />
<Compile Include="StreamReadBuffer.cs" />
Expand Down
121 changes: 49 additions & 72 deletions NVorbis/Ogg/OggPacketReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ public Packet[] Packets
}
}

// IPacketProvider requires this, but we aren't using it
#pragma warning disable 67 // disable the "unused" warning
public event EventHandler<ParameterChangeEventArgs> ParameterChange;
#pragma warning restore 67

ContainerReader _container;
int _streamSerial;
bool _eosFound;
Expand Down Expand Up @@ -170,97 +175,69 @@ public bool CanSeek
get { return _container.CanSeek; }
}

bool EnsurePackets()
{
do
{
lock (_packetLock)
{
// don't bother reading more packets unless we actually need them
if (_last != null && !_last.IsContinued && _current != _last) return true;
}

if (!_eosFound && !_container.GatherNextPage(_streamSerial))
{
// not technically true, but because the container doesn't have any more pages, it might as well be
_eosFound = true;
return false;
}

lock (_packetLock)
{
// if we've read the entire stream, do some further checking...
if (_eosFound)
{
// make sure the last packet read isn't continued... (per the spec, if the last packet is a partial, ignore it)
// if _last is null, something has gone horribly wrong (i.e., that shouldn't happen)
if (_last.IsContinued)
{
_last = _last.Prev;
_last.Next.Prev = null;
_last.Next = null;
}

// if our "current" packet is the same as the "last" packet, we're done
// _last won't be null here
if (_current == _last) return false;
}
}
} while (true);
}

// This is fast path... don't make the caller wait if we can help it...
public DataPacket GetNextPacket()
{
// make sure we have enough packets... if we're at the end of the stream, return null
if (!EnsurePackets()) return null;

Packet packet;
lock (_packetLock)
{
// "current" is always set to the packet previous to the one about to be returned...
if (_current == null)
{
packet = (_current = _first);
}
else
{
packet = (_current = _current.Next);
}
}
var packet = (_current = PeekNextPacketInternal());

if (packet.IsContinued) throw new InvalidDataException();

// make sure the packet is ready for "playback"
packet.Reset();

return packet;
}

public DataPacket PeekNextPacket()
{
return PeekNextPacketInternal();
}

Packet PeekNextPacketInternal()
{
// try to get the next packet in the sequence
Packet curPacket;
lock (_packetLock)
if (_current == null)
{
// get the current packet
curPacket = (_current ?? _first);
curPacket = _first;

// if we don't have one, we can't do anything...
if (curPacket == null) return null;

// if we have a next packet, go ahead and return it
if (curPacket.Next != null)
if (curPacket.IsContinued) throw new InvalidDataException("First packet cannot be split between pages!");
}
else
{
lock (_packetLock)
{
return curPacket.Next;
curPacket = _current.Next;

// keep going as long as we can possibly get a complete packet
while ((curPacket == null || curPacket.IsContinued) && !_eosFound)
{
// we need another packet and we've not found the end of the stream...
if (!_container.GatherNextPage(_streamSerial))
{
// we're at the end, so mark as much and move on
_eosFound = true;

// make sure we're handling the last packet correctly
if (_last.IsContinued)
{
// last packet was a partial... spec says dump it
_last = _last.Prev;
_last.Next.Prev = null;
_last.Next = null;
}
}

// make sure to get the correct value
curPacket = _current.Next;
}
}
}

// if we've hit the end of the stream, we're done
if (_eosFound) return null;
// if we're returning a packet, prep is for use
if (curPacket != null)
{
curPacket.Reset();
}

// finally, try to load more packets and just return the next one
EnsurePackets();
return curPacket.Next;
return curPacket;
}

public void SeekToPacket(int index)
Expand Down
28 changes: 28 additions & 0 deletions NVorbis/ParameterChangeEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NVorbis
{
/// <summary>
/// Event data for when a logical stream has a parameter change.
/// </summary>
[Serializable]
public class ParameterChangeEventArgs : EventArgs
{
/// <summary>
/// Creates a new instance of <see cref="ParameterChangeEventArgs"/>.
/// </summary>
/// <param name="firstPacket">The first packet after the parameter change.</param>
public ParameterChangeEventArgs(DataPacket firstPacket)
{
FirstPacket = firstPacket;
}

/// <summary>
/// Gets the first packet after the parameter change. This would typically be the parameters packet.
/// </summary>
public DataPacket FirstPacket { get; private set; }
}
}
18 changes: 15 additions & 3 deletions NVorbis/VorbisReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public VorbisReader(Stream stream, bool closeStreamOnDispose)
{
// oops, not Ogg!
// we don't support any other container types yet, so error out
// TODO: Add Matroska fallback
bufferedStream.Close();
throw new InvalidDataException("Could not determine container type!");
}
Expand Down Expand Up @@ -97,9 +98,7 @@ void NewStream(object sender, NewStreamEventArgs ea)
}
else
{
// NB: This could be an Ogg Skeleton stream... We should check that, just in case
// NB: This could be a RTP stream... We should check that, just in case

// This is almost certainly not a Vorbis stream
ea.IgnoreStream = true;
}
}
Expand Down Expand Up @@ -170,6 +169,11 @@ VorbisStreamDecoder ActiveDecoder
/// </summary>
public string[] Comments { get { return ActiveDecoder._comments; } }

/// <summary>
/// Gets whether the previous short sample count was due to a parameter change in the stream.
/// </summary>
public bool IsParameterChange { get { return ActiveDecoder.IsParameterChange; } }

/// <summary>
/// Gets the number of bits read that are related to framing and transport alone
/// </summary>
Expand Down Expand Up @@ -222,6 +226,14 @@ public int ReadSamples(float[] buffer, int offset, int count)
return count;
}

/// <summary>
/// Clears the parameter change flag so further samples can be requested.
/// </summary>
public void ClearParameterChange()
{
ActiveDecoder.IsParameterChange = false;
}

/// <summary>
/// Returns the number of logical streams found so far in the physical container
/// </summary>
Expand Down
Loading

0 comments on commit b554d59

Please sign in to comment.