HI WELCOME TO KANSIRIS

true on Creating a Recorder object

Leave a Comment
This Code Uses NAudio by Mark Heath and SharpAvi by Vasilli Masilov available on GitHub.
If you want Compressed Audio, put the lameenc32.dll or lameenc64.dll (according to your os requrement) and pass EncodeAudio = true on Creating a Recorder object
Hope this much might help.
If You need a Complete Working App, Head On to http://github.com/MathewSachin/Captura
All the dependencies can be found at: https://github.com/MathewSachin/Captura/releases
using System;
using
System.Drawing;
using
System.Drawing.Imaging;
using
System.Linq;
using
System.Runtime.InteropServices;
using
System.Threading;
using
System.Threading.Tasks;
using
System.Windows;
using
System.Windows.Interop;
using
SharpAvi;
using
SharpAvi.Codecs;
using
SharpAvi.Output;
using
NAudio;
using
NAudio.Wave

namespace
Captura
{
// Used to Configure the Recorder
public class RecorderParams
{
public IntPtr hWnd;

static Recorder()
{
// Set LAME DLL path for MP3 encoder
Mp3AudioEncoderLame.SetLameDllLocation(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
string.Format("lameenc{0}.dll", Environment.Is64BitProcess ? "64" : "32")));
}

public RecorderParams(string filename, int FrameRate, FourCC Encoder, int Quality,
int AudioSourceId, bool UseStereo, bool EncodeAudio, int AudioQuality, bool IncludeCursor, IntPtr hWnd)
{
FileName = filename;
FramesPerSecond = FrameRate;
Codec = Encoder;
this.Quality = Quality;
this.AudioSourceId = AudioSourceId;
this.EncodeAudio = EncodeAudio;
AudioBitRate = Mp3AudioEncoderLame.SupportedBitRates.OrderBy(br => br).ElementAt(AudioQuality);
this.IncludeCursor = IncludeCursor;
this.hWnd = hWnd;

if (hWnd == IntPtr.Zero)
{
System.Windows.Media.Matrix toDevice;
using
(var source = new HwndSource(new HwndSourceParameters()))
toDevice
= source.CompositionTarget.TransformToDevice;

Height = (int)Math.Round(SystemParameters.PrimaryScreenHeight * toDevice.M22);
Width = (int)Math.Round(SystemParameters.PrimaryScreenWidth * toDevice.M11);
}
else
{
var rect = new RECT();
User32.GetWindowRect(hWnd, ref rect);

Width = rect.Right - rect.Left;
Height = rect.Bottom - rect.Top;
}

WaveFormat = new WaveFormat(44100, 16, UseStereo ? 2 : 1);
}

string FileName;
public int FramesPerSecond, Quality, AudioSourceId, AudioBitRate;
FourCC Codec;
public bool EncodeAudio, IncludeCursor;

public int Height { get; private set; }
public int Width { get; private set; }

public AviWriter CreateAviWriter()
{
return new AviWriter(FileName)
{
FramesPerSecond = FramesPerSecond,
EmitIndex1 = true,
};
}

public IAviVideoStream CreateVideoStream(AviWriter writer)
{
// Select encoder type based on FOURCC of codec
if (Codec == KnownFourCCs.Codecs.Uncompressed) return writer.AddUncompressedVideoStream(Width, Height);
else if (Codec == KnownFourCCs.Codecs.MotionJpeg) return writer.AddMotionJpegVideoStream(Width, Height, Quality);
else
{
return writer.AddMpeg4VideoStream(Width, Height, (double)writer.FramesPerSecond,
// It seems that all tested MPEG-4 VfW codecs ignore the quality affecting parameters passed through VfW API
// They only respect the settings from their own configuration dialogs, and Mpeg4VideoEncoder currently has no support for this
quality
: Quality,
codec
: Codec,
// Most of VfW codecs expect single-threaded use, so we wrap this encoder to special wrapper
// Thus all calls to the encoder (including its instantiation) will be invoked on a single thread although encoding (and writing) is performed asynchronously
forceSingleThreadedAccess
: true);
}
}

public IAviAudioStream CreateAudioStream(AviWriter writer)
{
// Create encoding or simple stream based on settings
if (EncodeAudio)
{
// LAME DLL path is set in App.OnStartup()
return writer.AddMp3AudioStream(WaveFormat.Channels, WaveFormat.SampleRate, AudioBitRate);
}
else return writer.AddAudioStream(WaveFormat.Channels, WaveFormat.SampleRate, WaveFormat.BitsPerSample);
}

public WaveFormat WaveFormat { get; private set; }
}

public class Recorder : IDisposable
{
#region Fields
AviWriter writer;
RecorderParams Params;
IAviVideoStream videoStream;
IAviAudioStream audioStream;
WaveInEvent audioSource;
Thread screenThread;
ManualResetEvent stopThread = new ManualResetEvent(false);
AutoResetEvent videoFrameWritten = new AutoResetEvent(false),
audioBlockWritten
= new AutoResetEvent(false);
public bool IsPaused = false;
#endregion

public Recorder(RecorderParams Params)
{
this.Params = Params;

// Create AVI writer and specify FPS
writer
= Params.CreateAviWriter();

// Create video stream
videoStream
= Params.CreateVideoStream(writer);
// Set only name. Other properties were when creating stream,
// either explicitly by arguments or implicitly by the encoder used
videoStream
.Name = "Captura";

if (Params.AudioSourceId != -1)
{
try
{
var waveFormat = Params.WaveFormat;

audioStream
= Params.CreateAudioStream(writer);
audioStream
.Name = "Voice";

audioSource
= new WaveInEvent
{
DeviceNumber = Params.AudioSourceId,
WaveFormat = waveFormat,
// Buffer size to store duration of 1 frame
BufferMilliseconds = (int)Math.Ceiling(1000 / writer.FramesPerSecond),
NumberOfBuffers = 3,
};

audioSource
.DataAvailable += AudioDataAvailable;
}
catch { }
}

screenThread
= new Thread(RecordScreen)
{
Name = typeof(Recorder).Name + ".RecordScreen",
IsBackground = true
};

if (audioSource != null)
{
videoFrameWritten
.Set();
audioBlockWritten
.Reset();
audioSource
.StartRecording();
}
screenThread
.Start();
}

public void Dispose()
{
if (IsPaused) Resume();

stopThread
.Set();
screenThread
.Join();

if (audioSource != null)
{
audioSource
.StopRecording();
audioSource
.DataAvailable -= AudioDataAvailable;
}

// Close writer: the remaining data is written to a file and file is closed
writer
.Close();

stopThread
.Close();
}

public void Pause()
{
if (!IsPaused)
{
screenThread
.Suspend();

if (audioSource != null) audioSource.StopRecording();

IsPaused = true;
}
}

public void Resume()
{
if (IsPaused)
{
screenThread
.Resume();

if (audioSource != null)
{
videoFrameWritten
.Set();
audioBlockWritten
.Reset();
audioSource
.StartRecording();
}

IsPaused = false;
}
}

void RecordScreen()
{
var frameInterval = TimeSpan.FromSeconds(1 / (double)writer.FramesPerSecond);
var buffer = new byte[Params.Width * Params.Height * 4];
Task videoWriteTask = null;
var isFirstFrame = true;
var timeTillNextFrame = TimeSpan.Zero;
while (!stopThread.WaitOne(timeTillNextFrame))
{
var timestamp = DateTime.Now;

Screenshot(buffer);

// Wait for the previous frame is written
if (!isFirstFrame)
{
videoWriteTask
.Wait();
videoFrameWritten
.Set();
}

if (audioStream != null)
{
var signalled = WaitHandle.WaitAny(new WaitHandle[] { audioBlockWritten, stopThread });
if (signalled == 1) break;
}

// Start asynchronous (encoding and) writing of the new frame
videoWriteTask
= videoStream.WriteFrameAsync(true, buffer, 0, buffer.Length);

timeTillNextFrame
= timestamp + frameInterval - DateTime.Now;
if (timeTillNextFrame < TimeSpan.Zero) timeTillNextFrame = TimeSpan.Zero;

isFirstFrame
= false;
}

// Wait for the last frame is written
if (!isFirstFrame) videoWriteTask.Wait();
}

public void Screenshot(byte[] Buffer)
{
IntPtr hDC = User32.GetWindowDC(IntPtr.Zero),
hMemDC
= Gdi32.CreateCompatibleDC(hDC),
hIcon
= IntPtr.Zero;

int CursorX = 0, CursorY = 0;

IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hDC, Params.Width, Params.Height);

if (hBitmap != IntPtr.Zero)
{
IntPtr hOld = Gdi32.SelectObject(hMemDC, hBitmap);

Gdi32.BitBlt(hMemDC, 0, 0, Params.Width, Params.Height, hDC, 0, 0, PatBltTypes.SRCCOPY);

Gdi32.SelectObject(hMemDC, hOld);
}

#region Include Cursor
if (Params.hWnd == IntPtr.Zero && Params.IncludeCursor)
{
CursorInfo ci = new CursorInfo() { cbSize = Marshal.SizeOf(typeof(CursorInfo)) };

IconInfo icInfo;

if (User32.GetCursorInfo(out ci))
{
if (ci.flags == User32.CURSOR_SHOWING)
{
hIcon
= User32.CopyIcon(ci.hCursor);
if (User32.GetIconInfo(hIcon, out icInfo))
{
CursorX = ci.ptScreenPos.X - ((int)icInfo.xHotspot);
CursorY = ci.ptScreenPos.Y - ((int)icInfo.yHotspot);
}
}
}
}
#endregion

using
(var BMP = new Bitmap(Params.Width, Params.Height))
{
using
(var g = Graphics.FromImage(BMP))
{
g
.DrawImage(Bitmap.FromHbitmap(hBitmap), 0, 0);

if (hIcon != IntPtr.Zero)
{
Bitmap CursorBMP = Icon.FromHandle(hIcon).ToBitmap();
g
.DrawImage(CursorBMP, CursorX, CursorY, CursorBMP.Width, CursorBMP.Height);
}

g
.Flush();

var bits = BMP.LockBits(new Rectangle(0, 0, Params.Width, Params.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
Marshal.Copy(bits.Scan0, Buffer, 0, Buffer.Length);
BMP
.UnlockBits(bits);
}
}

Gdi32.DeleteObject(hBitmap);
Gdi32.DeleteDC(hMemDC);
User32.ReleaseDC(Params.hWnd, hDC);
}

void AudioDataAvailable(object sender, WaveInEventArgs e)
{
var signalled = WaitHandle.WaitAny(new WaitHandle[] { videoFrameWritten, stopThread });
if (signalled == 0)
{
audioStream
.WriteBlock(e.Buffer, 0, e.BytesRecorded);
audioBlockWritten
.Set();
}
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
public struct IconInfo
{
public bool fIcon; // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies
public int xHotspot; // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot
public int yHotspot; // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot
public IntPtr hbmMask; // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon,
public IntPtr hbmColor; // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this
}

[StructLayout(LayoutKind.Sequential)]
public struct CursorInfo
{
public int cbSize; // Specifies the size, in bytes, of the structure.
public int flags; // Specifies the cursor state. This parameter can be one of the following values:
public IntPtr hCursor; // Handle to the cursor.
public POINT ptScreenPos; // A POINT structure that receives the screen coordinates of the cursor.
}

public static class User32
{
public const int CURSOR_SHOWING = 0x00000001;

[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);

[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);

[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out CursorInfo pci);

[DllImport("user32.dll")]
public static extern IntPtr CopyIcon(IntPtr hIcon);

[DllImport("user32.dll")]
public static extern bool GetIconInfo(IntPtr hIcon, out IconInfo piconinfo);

[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
}

public static class Gdi32
{
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);

[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);

[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, PatBltTypes dwRop);

[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);

[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
}

public enum PatBltTypes
{
SRCCOPY
= 0x00CC0020,
SRCPAINT
= 0x00EE0086,
SRCAND
= 0x008800C6,
SRCINVERT
= 0x00660046,
SRCERASE
= 0x00440328,
NOTSRCCOPY
= 0x00330008,
NOTSRCERASE
= 0x001100A6,
MERGECOPY
= 0x00C000CA,
MERGEPAINT
= 0x00BB0226,
PATCOPY
= 0x00F00021,
PATPAINT
= 0x00FB0A09,
PATINVERT
= 0x005A0049,
DSTINVERT
= 0x00550009,
BLACKNESS
= 0x00000042,
WHITENESS
= 0x00FF0062
}
Visit GitHub for the Complete App - http://github.com/MathewSachin/Captura

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.