Skip to content

Commit

Permalink
Seek Improvements and Indexing (#280)
Browse files Browse the repository at this point in the history
* starting with seek indexing. Related to issue #139

* continued work on seek indexing.

* Generalizing sorted binary search algorithm. Continued work on seek indices. See issues #250 and #139

* improved and simplified seek logic.

* minor renaming and added seek error checking.

* logging when using a video seek index entry.

* fixing frame start offset by offseting them by main component.
  • Loading branch information
mariodivece committed Sep 24, 2018
1 parent 4c65e60 commit 8f0a59f
Show file tree
Hide file tree
Showing 21 changed files with 792 additions and 360 deletions.
12 changes: 6 additions & 6 deletions Unosquare.FFME.Common/Commands/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal sealed class CommandManager : IDisposable, ILoggingSource
private bool m_IsDisposed;

private DirectCommandBase CurrentDirectCommand;
private CommandBase CurrentQueueCommand;
private CommandBase ExecutingQueueCommand;

#endregion

Expand Down Expand Up @@ -302,7 +302,7 @@ public CommandType ExecuteNextQueuedCommand()
{
command = CommandQueue[0];
CommandQueue.RemoveAt(0);
CurrentQueueCommand = command;
ExecutingQueueCommand = command;
}
}

Expand Down Expand Up @@ -339,7 +339,7 @@ public CommandType ExecuteNextQueuedCommand()
DecrementPendingSeeks();
}

CurrentQueueCommand = null;
ExecutingQueueCommand = null;
}
}

Expand Down Expand Up @@ -497,10 +497,10 @@ private async Task<bool> ExecutePriorityCommand(CommandType commandType)
CommandBase currentCommand = null;
lock (QueueLock)
{
if (CurrentQueueCommand != null &&
CurrentQueueCommand.CommandType == commandType)
if (ExecutingQueueCommand != null &&
ExecutingQueueCommand.CommandType == commandType)
{
currentCommand = CurrentQueueCommand;
currentCommand = ExecutingQueueCommand;
}

if (currentCommand == null)
Expand Down
72 changes: 41 additions & 31 deletions Unosquare.FFME.Common/Commands/SeekCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,18 @@ protected override void PerformActions()
// Mark for debugger output
hasDecoderSeeked = true;

// Signal the starting state clearing the packet buffer cache
m.Container.Components.ClearQueuedPackets(flushBuffers: true);

// wait for the current reading and decoding cycles
// to finish. We don't want to interfere with reading in progress
// or decoding in progress. For decoding we already know we are not
// in a cycle because the decoding worker called this logic.
m.PacketReadingCycle.Wait();

// Signal the starting state clearing the packet buffer cache
m.Container.Components.ClearQueuedPackets(flushBuffers: true);

// Capture seek target adjustment
var adjustedSeekTarget = TargetPosition;
if (mainBlocks.IsMonotonic)
if (TargetPosition != TimeSpan.Zero && mainBlocks.IsMonotonic)
{
var targetSkewTicks = Convert.ToInt64(
mainBlocks.MonotonicDuration.Ticks * (mainBlocks.Capacity / 2d));
Expand All @@ -112,38 +112,48 @@ protected override void PerformActions()
adjustedSeekTarget = TimeSpan.FromTicks(adjustedSeekTarget.Ticks - targetSkewTicks);
}

// Clear Blocks and frames, reset the render times
foreach (var mt in all)
{
m.Blocks[mt].Clear();
m.InvalidateRenderer(mt);
}

// Populate frame queues with after-seek operation
var frames = m.Container.Seek(adjustedSeekTarget);
m.State.UpdateMediaEnded(false, TimeSpan.Zero);

// Clear all the blocks. We don't need them
foreach (var kvp in m.Blocks)
kvp.Value.Clear();

// Create the blocks from the obtained seek frames
foreach (var frame in frames)
m.Blocks[frame.MediaType]?.Add(frame, m.Container);

// Now read blocks until we have reached at least the Target Position
// TODO: This might not be entirely right
while (m.ShouldReadMorePackets
&& mainBlocks.IsFull == false
&& mainBlocks.IsInRange(TargetPosition) == false)
var firstFrame = m.Container.Seek(adjustedSeekTarget);
if (firstFrame != null)
{
// Read the next packet
m.Container.Read();
// Ensure we signal media has not ended
m.State.UpdateMediaEnded(false, TimeSpan.Zero);

// Clear Blocks and frames, reset the render times
foreach (var mt in all)
{
if (m.Blocks[mt].IsFull == false)
m.Blocks[mt].Add(m.Container.Components[mt].ReceiveNextFrame(), m.Container);
m.Blocks[mt].Clear();
m.InvalidateRenderer(mt);
}

// Create the blocks from the obtained seek frames
m.Blocks[firstFrame.MediaType]?.Add(firstFrame, m.Container);

// Decode all available queued packets into the media component blocks
foreach (var mt in all)
{
while (m.Blocks[mt].IsFull == false)
{
var frame = m.Container.Components[mt].ReceiveNextFrame();
if (frame == null) break;
m.Blocks[mt].Add(frame, m.Container);
}
}

// Align to the exact requested position on the main component
while (m.ShouldReadMorePackets)
{
// Check if we are already in range
if (mainBlocks.IsInRange(TargetPosition)) break;

// Read the next packet
var packetType = m.Container.Read();
var blocks = m.Blocks[packetType];
if (blocks == null) continue;

// Get the next frame
if (blocks.RangeEndTime.Ticks < TargetPosition.Ticks || blocks.IsFull == false)
blocks.Add(m.Container.Components[packetType].ReceiveNextFrame(), m.Container);
}
}

Expand Down
5 changes: 3 additions & 2 deletions Unosquare.FFME.Common/Decoding/AudioFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ internal AudioFrame(AVFrame* frame, MediaComponent component)
: base(frame, component, MediaType.Audio)
{
// Compute the start time.
var mainOffset = component.Container.Components.Main.StartTime;
frame->pts = frame->best_effort_timestamp;
HasValidStartTime = frame->pts != ffmpeg.AV_NOPTS_VALUE;
StartTime = frame->pts == ffmpeg.AV_NOPTS_VALUE ?
TimeSpan.FromTicks(0) :
TimeSpan.FromTicks(frame->pts.ToTimeSpan(StreamTimeBase).Ticks - component.Container.MediaStartTime.Ticks);
TimeSpan.FromTicks(frame->pts.ToTimeSpan(StreamTimeBase).Ticks - mainOffset.Ticks);

// Compute the audio frame duration
Duration = frame->pkt_duration != 0 ?
Duration = frame->pkt_duration > 0 ?
frame->pkt_duration.ToTimeSpan(StreamTimeBase) :
TimeSpan.FromTicks(Convert.ToInt64(TimeSpan.TicksPerMillisecond * 1000d * frame->nb_samples / frame->sample_rate));

Expand Down

0 comments on commit 8f0a59f

Please sign in to comment.