Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Avoid flickering when using SharpAvi #568

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Captura.Console/CmdOptions/StartCmdOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class StartCmdOptions : CommonCmdOptions, ICmdlineVerb
[Option("settings", HelpText = "Settings file to use for overlay settings, ffmpeg path and output path")]
public string Settings { get; set; }

[Option("stop", HelpText = "Token, such as a GUID, to use to stop recording. Can be used with or without length")]
public string StopToken { get; set; }

[Usage]
public static IEnumerable<Example> Examples
{
Expand Down
16 changes: 16 additions & 0 deletions src/Captura.Console/ConsoleManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,22 @@ void HandleWebcam(StartCmdOptions StartOptions)

void Loop(StartCmdOptions StartOptions)
{
if (!string.IsNullOrEmpty(StartOptions.StopToken))
{
var waitForSignal = new EventWaitHandle(
false, EventResetMode.ManualReset, StartOptions.StopToken);

if (StartOptions.Length > 0)
{
waitForSignal.WaitOne(StartOptions.Length * 1000);
}
else
{
waitForSignal.WaitOne();
}
return;
}

if (StartOptions.Length > 0)
{
var elapsed = 0;
Expand Down
74 changes: 74 additions & 0 deletions src/Captura.SharpAvi/AviWriter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Captura.Audio;
using Captura.Video;
using Captura.Windows.Native;
using SharpAvi.Codecs;
using SharpAvi.Output;
using AviInternalWriter = SharpAvi.Output.AviWriter;
Expand All @@ -17,6 +18,9 @@ class AviWriter : IVideoFileWriter
IAviVideoStream _videoStream;
IAviAudioStream _audioStream;
byte[] _videoBuffer;
byte[] _prevVideoBuffer;
readonly byte[] _zeroBuffer;
bool _hasOneGoodFrame = false;
readonly AviCodec _codec;
readonly object _syncLock = new object();

Expand All @@ -39,6 +43,12 @@ public AviWriter(string FileName, AviCodec Codec, IImageProvider ImageProvider,
_codec = Codec;

_videoBuffer = new byte[ImageProvider.Width * ImageProvider.Height * 4];
_prevVideoBuffer = new byte[_videoBuffer.Length];
_zeroBuffer = new byte[_videoBuffer.Length];
for (int i = 0; i < _zeroBuffer.Length; i++)
{
_zeroBuffer[i] = 0;
}

_writer = new AviInternalWriter(FileName)
{
Expand Down Expand Up @@ -66,7 +76,71 @@ public void WriteFrame(IBitmapFrame Frame)
}

lock (_syncLock)
{
if (IsTransparentOrTruncatedFrame(_videoBuffer))
{
// To avoid dropped frames, just repeat the previous one

if (_hasOneGoodFrame)
{
// Use previous frame instead

_videoStream.WriteFrame(true, _prevVideoBuffer, 0, _prevVideoBuffer.Length);
}
else
{
// Just need to make do with what we have

_videoStream.WriteFrame(true, _videoBuffer, 0, _videoBuffer.Length);
}

return;
}

if (!_hasOneGoodFrame)
{
_hasOneGoodFrame = true;
}

_videoStream.WriteFrame(true, _videoBuffer, 0, _videoBuffer.Length);

// Save frame in case we need it as stand-in for next one

Buffer.BlockCopy(_videoBuffer, 0, _prevVideoBuffer, 0, _videoBuffer.Length);
}
}

bool IsTransparentOrTruncatedFrame(byte[] buffer)
{
if (_videoBuffer.Length != _zeroBuffer.Length)
{
return true;
}

// First check first 100 bytes - assuming that in most cases they will not be transparent
// and therefore this will avoid having to check the entire frame
if (IsStartTransparent(buffer, 100))
{
// If start is transparent compare the whole frame using P/invoke for performance
// We don't check the length as we already know that it is equal
return Msvcrt.memcmp(buffer, _zeroBuffer, buffer.Length) == 0;
}
else
{
return false;
}
}

bool IsStartTransparent(byte[] buffer, int bytesToCheck)
{
for (int i = 0; i < bytesToCheck; i++)
{
if (buffer[i] != _zeroBuffer[i])
{
return false;
}
}
return true;
}

void CreateVideoStream(int Width, int Height)
Expand Down
1 change: 1 addition & 0 deletions src/Captura.SharpAvi/Captura.SharpAvi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Captura.Base\Captura.Base.csproj" />
<ProjectReference Include="..\Captura.Windows\Captura.Windows.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SharpAvi" Version="2.1.2" />
Expand Down
15 changes: 15 additions & 0 deletions src/Captura.Windows/Native/Msvcrt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Captura.Windows.Native
{
public static class Msvcrt
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int memcmp(byte[] b1, byte[] b2, long count);
}
}