728x90
728x170
TestSolution.zip
다운로드
TestSolution.z01
다운로드
TestSolution.z02
다운로드
TestSolution.z03
다운로드
TestSolution.z04
다운로드
[TestLibrary 프로젝트]
▶ BITMAPINFOHEADER.cs
using System.Runtime.InteropServices;
namespace TestLibrary
{
/// <summary>
/// 비트맵 정보 헤더
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 크기
/// </summary>
public uint Size;
/// <summary>
/// 너비
/// </summary>
public int Width;
/// <summary>
/// 높이
/// </summary>
public int Height;
/// <summary>
/// 플레인 수
/// </summary>
public ushort PlaneCount;
/// <summary>
/// 비트 수
/// </summary>
public ushort BitCount;
/// <summary>
/// 압축 방법
/// </summary>
public uint Compression;
/// <summary>
/// 이미지 크기
/// </summary>
public uint ImageSize;
/// <summary>
/// 미터당 X 픽셀 수
/// </summary>
public int XPixelCountPerMeter;
/// <summary>
/// 미터당 Y 픽셀 수
/// </summary>
public int YPixelCountPerMeter;
/// <summary>
/// 사용 색상 인덱스 수
/// </summary>
public uint UsedColorCount;
/// <summary>
/// 중료 색상 인덱스 수
/// </summary>
public uint ImportantColorIndexCount;
#endregion
}
}
728x90
▶ DirectShowException.cs
using System;
using System.Runtime.InteropServices;
namespace TestLibrary
{
/// <summary>
/// DirectShow 예외
/// </summary>
public sealed class DirectShowException : Exception
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - DirectShowException(message, resultHandle)
/// <summary>
/// 생성자
/// </summary>
/// <param name="message">메시지</param>
/// <param name="resultHandle">결과 핸들</param>
public DirectShowException(string message, int resultHandle) : base(message, Marshal.GetExceptionForHR(resultHandle))
{
}
#endregion
}
}
300x250
▶ VideoCaptureDevice.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 비디오 캡처 장치
/// </summary>
public sealed class VideoCaptureDevice : IEquatable<VideoCaptureDevice>
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 명칭
/// </summary>
private readonly String name;
/// <summary>
/// 장치 경로
/// </summary>
private readonly String devicePath;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 명칭 - Name
/// <summary>
/// 명칭
/// </summary>
public string Name
{
get
{
return this.name;
}
}
#endregion
#region 장치 경로 - DevicePath
/// <summary>
/// 장치 경로
/// </summary>
internal string DevicePath
{
get
{
return this.devicePath;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - VideoCaptureDevice(info)
/// <summary>
/// 생성자
/// </summary>
public VideoCaptureDevice(VideoInputDeviceInfo info)
{
this.name = info.DeviceName;
devicePath = info.DevicePath;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 연산자 == 재정의하기 - ==(left, right)
/// <summary>
/// 연산자 == 재정의하기
/// </summary>
/// <param name="left">왼쪽 객체</param>
/// <param name="right">오른쪽 객체</param>
/// <returns>처리 결과</returns>
public static bool operator ==(VideoCaptureDevice left, VideoCaptureDevice right)
{
return Equals(left, right);
}
#endregion
#region 연산자 != 재정의하기 - !=(left, right)
/// <summary>
/// 연산자 != 재정의하기
/// </summary>
/// <param name="left">왼쪽 객체</param>
/// <param name="right">오른쪽 객체</param>
/// <returns>처리 결과</returns>
public static bool operator !=(VideoCaptureDevice left, VideoCaptureDevice right)
{
return !Equals(left, right);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 동일 여부 구하기 - Equals(sourceObject)
/// <summary>
/// 동일 여부 구하기
/// </summary>
/// <param name="sourceObject">소스 객체</param>
/// <returns>동일 여부</returns>
public override bool Equals(object sourceObject)
{
if(ReferenceEquals(null, sourceObject))
{
return false;
}
if(ReferenceEquals(this, sourceObject))
{
return true;
}
if(sourceObject.GetType() != typeof(VideoCaptureDevice))
{
return false;
}
return Equals((VideoCaptureDevice)sourceObject);
}
#endregion
#region 동일 여부 구하기 - Equals(source)
/// <summary>
/// 동일 여부 구하기
/// </summary>
/// <param name="source">소스 객체</param>
/// <returns>동일 여부</returns>
public bool Equals(VideoCaptureDevice source)
{
if(ReferenceEquals(null, source))
{
return false;
}
if(ReferenceEquals(this, source))
{
return true;
}
return Equals(source.name, this.name) && Equals(source.devicePath, this.devicePath);
}
#endregion
#region 해시 코드 구하기 - GetHashCode()
/// <summary>
/// 해시 코드 구하기
/// </summary>
/// <returns>해시 코드</returns>
public override int GetHashCode()
{
unchecked
{
return (this.name.GetHashCode() * 397) ^ this.devicePath.GetHashCode();
}
}
#endregion
}
}
▶ VideoInputDeviceInfo.cs
using System.Runtime.InteropServices;
namespace TestLibrary
{
/// <summary>
/// 비디오 입력 장치 정보
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct VideoInputDeviceInfo
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 장치명
/// </summary>
[MarshalAs(UnmanagedType.BStr)]
public string DeviceName;
/// <summary>
/// 장치 경로
/// </summary>
[MarshalAs(UnmanagedType.BStr)]
public string DevicePath;
#endregion
}
}
▶ DirectShowHelper.cs
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace TestLibrary
{
/// <summary>
/// DirectShow 헬퍼
/// </summary>
public sealed class DirectShowHelper : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 라이브러리 로드하기 - LoadLibrary(filePath)
/// <summary>
/// 라이브러리 로드하기
/// </summary>
/// <param name="filePath">파일 경로</param>
/// <returns>처리 결과</returns>
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string filePath);
#endregion
#region 프로시저 주소 구하기 - GetProcAddress(moduleHandle, procedureName)
/// <summary>
/// 프로시저 주소 구하기
/// </summary>
/// <param name="moduleHandle">모듈 핸들</param>
/// <param name="procedureName">프로시저명</param>
/// <returns>프로세스 핸들</returns>
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procedureName);
#endregion
#region 라이브러리 해제하기 - FreeLibrary(moduleHandle)
/// <summary>
/// 라이브러리 해제하기
/// </summary>
/// <param name="moduleHandle">모듈 핸들</param>
/// <returns>처리 결과</returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeLibrary(IntPtr moduleHandle);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 비디오 입력 장치 열거 콜백 처리하기 대리자 - EnumerateVideoInputDeviceCallbackDelegate(info)
/// <summary>
/// 비디오 입력 장치 열거 콜백 처리하기 대리자
/// </summary>
/// <param name="info">비디오 입력 장치 정보</param>
public delegate void EnumerateVideoInputDeviceCallbackDelegate(ref VideoInputDeviceInfo info);
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 비디오 입력 장치 리스트 열거하기 대리자 - EnumerateVideoInputDeviceListDelegate(callback)
/// <summary>
/// 비디오 입력 장치 리스트 열거하기 대리자
/// </summary>
/// <param name="callback">비디오 입력 장치 열거 콜백 대리자</param>
private delegate void EnumerateVideoInputDeviceListDelegate(EnumerateVideoInputDeviceCallbackDelegate callback);
#endregion
#region 캡처 그래프 구축하기 대리자 - BuildCaptureGraphDelegate()
/// <summary>
/// 캡처 그래프 구축하기 대리자
/// </summary>
/// <returns>처리 결과</returns>
private delegate int BuildCaptureGraphDelegate();
#endregion
#region 렌더 필터 추가하기 대리자 - AddRenderFilterDelegate(windowHandle)
/// <summary>
/// 렌더 필터 추가하기 대리자
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <returns>처리 결과</returns>
private delegate int AddRenderFilterDelegate(IntPtr windowHandle);
#endregion
#region 캡처 필터 추가하기 대리자 - AddCaptureFilterDelegate(devicePath)
/// <summary>
/// 캡처 필터 추가하기 대리자
/// </summary>
/// <param name="devicePath">장치 경로</param>
/// <returns>처리 결과</returns>
private delegate int AddCaptureFilterDelegate([MarshalAs(UnmanagedType.BStr)]string devicePath);
#endregion
#region 캡처 그래프 리셋하기 대리자 - ResetCaptureGraphDelegate()
/// <summary>
/// 캡처 그래프 리셋하기 대리자
/// </summary>
/// <returns>처리 결과</returns>
private delegate int ResetCaptureGraphDelegate();
#endregion
#region 시작하기 대리자 - StartDelegate()
/// <summary>
/// 시작하기 대리자
/// </summary>
/// <returns>처리 결과</returns>
private delegate int StartDelegate();
#endregion
#region 현재 이미지 구하기 대리자 - GetCurrentImageDelegate(dibHandle)
/// <summary>
/// 현재 이미지 구하기 대리자
/// </summary>
/// <param name="dibHandle">DIB 핸들</param>
/// <returns>처리 결과</returns>
private delegate int GetCurrentImageDelegate([Out] out IntPtr dibHandle);
#endregion
#region 비디오 크기 구하기 대리자 - GetVideoSizeDelegate(width, height)
/// <summary>
/// 비디오 크기 구하기 대리자
/// </summary>
/// <param name="width">너비</param>
/// <param name="height">높이</param>
/// <returns>처리 결과</returns>
private delegate int GetVideoSizeDelegate(out int width, out int height);
#endregion
#region 중단하기 대리자 - StopDelegate()
/// <summary>
/// 중단하기 대리자
/// </summary>
/// <returns>처리 결과</returns>
private delegate int StopDelegate();
#endregion
#region 캡처 그래프 제거하기 대리자 - DestroyCaptureGraphDelegate()
/// <summary>
/// 캡처 그래프 제거하기 대리자
/// </summary>
private delegate void DestroyCaptureGraphDelegate();
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 비디오 입력 장치 리스트 열거하기 대리자
/// </summary>
private EnumerateVideoInputDeviceListDelegate enumerateVideoInputDeviceListDelegate;
/// <summary>
/// 캡처 그래프 구축하기 대리자
/// </summary>
private BuildCaptureGraphDelegate buildCaptureGraphDelegate;
/// <summary>
/// 렌더 필터 추가하기 대리자
/// </summary>
private AddRenderFilterDelegate addRenderFilterDelegate;
/// <summary>
/// 캡처 필터 추가하기 대리자
/// </summary>
private AddCaptureFilterDelegate addCaptureFilterDelegate;
/// <summary>
/// 캡처 그래프 리셋하기 대리자
/// </summary>
private ResetCaptureGraphDelegate resetCaptureGraphDelegate;
/// <summary>
/// 시작하기 대리자
/// </summary>
private StartDelegate startDelegate;
/// <summary>
/// 현재 이미지 구하기 대리자
/// </summary>
private GetCurrentImageDelegate getCurrentImageDelegate;
/// <summary>
/// 비디오 크기 구하기 대리자
/// </summary>
private GetVideoSizeDelegate getVideoSizeDelegate;
/// <summary>
/// 중단하기 대리자
/// </summary>
private StopDelegate stopDelegate;
/// <summary>
/// 캡처 그래프 제거하기 대리자
/// </summary>
private DestroyCaptureGraphDelegate destroyCaptureGraphDelegate;
/// <summary>
/// 모듈 파일 경로
/// </summary>
private string moduleFilePath = string.Empty;
/// <summary>
/// 모듈 핸들
/// </summary>
private IntPtr moduleHandle = IntPtr.Zero;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 32비트 플랫폼 여부 - IsX86Platform
/// <summary>
/// 32비트 플랫폼 여부
/// </summary>
private bool IsX86Platform
{
get
{
return IntPtr.Size == 4;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - DirectShowHelper()
/// <summary>
/// 생성자
/// </summary>
public DirectShowHelper()
{
LoadModule();
SetDelegate(this.moduleHandle);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 비디오 입력 장치 리스트 열거하기 - EnumerateVideoInputDeviceList(enumerateVideoInputDeviceCallbackDelegate)
/// <summary>
/// 비디오 입력 장치 리스트 열거하기
/// </summary>
/// <param name="enumerateVideoInputDeviceCallbackDelegate">비디오 입력 장치 열거 콜백 처리하기 대리자</param>
public void EnumerateVideoInputDeviceList(EnumerateVideoInputDeviceCallbackDelegate enumerateVideoInputDeviceCallbackDelegate)
{
this.enumerateVideoInputDeviceListDelegate(enumerateVideoInputDeviceCallbackDelegate);
}
#endregion
#region 캡처 그래프 구축하기 - BuildCaptureGraph()
/// <summary>
/// 캡처 그래프 구축하기
/// </summary>
public void BuildCaptureGraph()
{
ThrowExceptionForResult(this.buildCaptureGraphDelegate(), "Failed to build a video capture graph.");
}
#endregion
#region 렌더 필터 추가하기 - AddRenderFilter(windowHandle)
/// <summary>
/// 렌더 필터 추가하기
/// </summary>
/// <param name="windowHandle"></param>
public void AddRenderFilter(IntPtr windowHandle)
{
ThrowExceptionForResult(this.addRenderFilterDelegate(windowHandle), "Failed to setup a render filter.");
}
#endregion
#region 캡처 필터 추가하기 - AddCaptureFilter(devicePath)
/// <summary>
/// 캡처 필터 추가하기
/// </summary>
/// <param name="devicePath">장치 경로</param>
public void AddCaptureFilter(string devicePath)
{
ThrowExceptionForResult(this.addCaptureFilterDelegate(devicePath), "Failed to add a video capture filter.");
}
#endregion
#region 캡처 그래프 리셋하기 - ResetCaptureGraph()
/// <summary>
/// 캡처 그래프 리셋하기
/// </summary>
public void ResetCaptureGraph()
{
ThrowExceptionForResult(this.resetCaptureGraphDelegate(), "Failed to reset a video capture graph.");
}
#endregion
#region 시작하기 - Start()
/// <summary>
/// 시작하기
/// </summary>
public void Start()
{
ThrowExceptionForResult(this.startDelegate(), "Failed to run a capture graph.");
}
#endregion
#region 현재 이미지 구하기 - GetCurrentImage()
/// <summary>
/// 현재 이미지 구하기
/// </summary>
/// <returns>현재 이미지</returns>
public Bitmap GetCurrentImage()
{
IntPtr dibHandle;
ThrowExceptionForResult(this.getCurrentImageDelegate(out dibHandle), "Failed to get the current image.");
try
{
BITMAPINFOHEADER bitmapInfoHeader = (BITMAPINFOHEADER)Marshal.PtrToStructure(dibHandle, typeof(BITMAPINFOHEADER));
int stride = bitmapInfoHeader.Width * (bitmapInfoHeader.BitCount / 8);
int padding = stride % 4 > 0 ? 4 - stride % 4 : 0;
stride += padding;
PixelFormat pixelFormat = PixelFormat.Undefined;
switch(bitmapInfoHeader.BitCount)
{
case 1 : pixelFormat = PixelFormat.Format1bppIndexed; break;
case 4 : pixelFormat = PixelFormat.Format4bppIndexed; break;
case 8 : pixelFormat = PixelFormat.Format8bppIndexed; break;
case 16 : pixelFormat = PixelFormat.Format16bppRgb555; break;
case 24 : pixelFormat = PixelFormat.Format24bppRgb; break;
case 32 : pixelFormat = PixelFormat.Format32bppRgb; break;
}
Bitmap bitmap = new Bitmap
(
bitmapInfoHeader.Width,
bitmapInfoHeader.Height,
stride,
pixelFormat,
(IntPtr)(dibHandle.ToInt64() + Marshal.SizeOf(bitmapInfoHeader))
);
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bitmap;
}
finally
{
if(dibHandle != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(dibHandle);
}
}
}
#endregion
#region 비디오 크기 구하기 - GetVideoSize()
/// <summary>
/// 비디오 크기 구하기
/// </summary>
/// <returns>비디오 크기</returns>
public Size GetVideoSize()
{
int width;
int height;
ThrowExceptionForResult(this.getVideoSizeDelegate(out width, out height), "Failed to get the video size.");
return new Size(width, height);
}
#endregion
#region 중단하기 - Stop()
/// <summary>
/// 중단하기
/// </summary>
public void Stop()
{
ThrowExceptionForResult(this.stopDelegate(), "Failed to stop a video capture graph.");
}
#endregion
#region 캡처 그래프 제거하기 - DestroyCaptureGraph()
/// <summary>
/// 캡처 그래프 제거하기
/// </summary>
public void DestroyCaptureGraph()
{
this.destroyCaptureGraphDelegate();
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
if(this.moduleHandle != IntPtr.Zero)
{
FreeLibrary(this.moduleHandle);
this.moduleHandle = IntPtr.Zero;
}
if(File.Exists(this.moduleFilePath))
{
File.Delete(this.moduleFilePath);
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 모듈 로드하기 - LoadModule()
/// <summary>
/// 모듈 로드하기
/// </summary>
private void LoadModule()
{
string currentDirectoryPath = Environment.CurrentDirectory;
this.moduleFilePath = Path.Combine
(
currentDirectoryPath,
Environment.Is64BitProcess ? "X64\\DirectShowFacade64.dll" : "X86\\DirectShowFacade.dll"
);
this.moduleHandle = LoadLibrary(this.moduleFilePath);
if(this.moduleHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
#endregion
#region 대리자 설정하기 - SetDelegate(modlueHandle)
/// <summary>
/// 대리자 설정하기
/// </summary>
/// <param name="modlueHandle">모듈 핸들</param>
private void SetDelegate(IntPtr modlueHandle)
{
IntPtr methodHandle = GetProcAddress(modlueHandle, "EnumVideoInputDevices");
this.enumerateVideoInputDeviceListDelegate = (EnumerateVideoInputDeviceListDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(EnumerateVideoInputDeviceListDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "BuildCaptureGraph");
this.buildCaptureGraphDelegate = (BuildCaptureGraphDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(BuildCaptureGraphDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "AddRenderFilter");
this.addRenderFilterDelegate = (AddRenderFilterDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(AddRenderFilterDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "AddCaptureFilter");
this.addCaptureFilterDelegate = (AddCaptureFilterDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(AddCaptureFilterDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "ResetCaptureGraph");
this.resetCaptureGraphDelegate = (ResetCaptureGraphDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(ResetCaptureGraphDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "Start");
this.startDelegate = (StartDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(StartDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "GetCurrentImage");
this.getCurrentImageDelegate = (GetCurrentImageDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(GetCurrentImageDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "GetVideoSize");
this.getVideoSizeDelegate = (GetVideoSizeDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(GetVideoSizeDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "Stop");
this.stopDelegate = (StopDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(StopDelegate)
);
methodHandle = GetProcAddress(modlueHandle, "DestroyCaptureGraph");
this.destroyCaptureGraphDelegate = (DestroyCaptureGraphDelegate)Marshal.GetDelegateForFunctionPointer
(
methodHandle,
typeof(DestroyCaptureGraphDelegate)
);
}
#endregion
#region 결과용 예외 던지기 - ThrowExceptionForResult(resultHandle, message)
/// <summary>
/// 결과용 예외 던지기
/// </summary>
/// <param name="resultHandle">결과 핸들</param>
/// <param name="message">메시지</param>
private static void ThrowExceptionForResult(int resultHandle, string message)
{
if(resultHandle < 0)
{
throw new DirectShowException(message, resultHandle);
}
}
#endregion
}
}
▶ FFMpegHelper.cs
using System;
using System.IO;
using System.Runtime.InteropServices;
using FFmpeg.AutoGen;
namespace TestLibrary
{
/// <summary>
/// FFMPEG 헬퍼
/// </summary>
public static class FFMpegHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region DLL 디렉토리 설정하기 - SetDllDirectory(directoryPath)
/// <summary>
/// DLL 디렉토리 설정하기
/// </summary>
/// <param name="directoryPath">디렉토리 경로</param>
/// <returns>처리 결과</returns>
[DllImport("kernel32", SetLastError = true)]
private static extern bool SetDllDirectory(string directoryPath);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// LD_LIBRARY_PATH
/// </summary>
private const string LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 등록하기 - Register()
/// <summary>
/// 등록하기
/// </summary>
public static void Register()
{
switch(Environment.OSVersion.Platform)
{
case PlatformID.Win32NT :
case PlatformID.Win32S :
case PlatformID.Win32Windows :
{
string currentDirectoryPath = Environment.CurrentDirectory;
while(currentDirectoryPath != null)
{
string dllDirectoryPath = Path.Combine(currentDirectoryPath, Environment.Is64BitProcess ? "X64" : "X86");
if(Directory.Exists(dllDirectoryPath))
{
Register(dllDirectoryPath);
return;
}
currentDirectoryPath = Directory.GetParent(currentDirectoryPath)?.FullName;
}
break;
}
case PlatformID.Unix :
case PlatformID.MacOSX :
{
string dllDirectoryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
Register(dllDirectoryPath);
break;
}
}
}
#endregion
#region 에러 메시지 구하기 - GetErrorMessage(errorCode)
/// <summary>
/// 에러 메시지 구하기
/// </summary>
/// <param name="errorCode">에러 코드</param>
/// <returns>에러 메시지</returns>
public static unsafe string GetErrorMessage(int errorCode)
{
int bufferSize = 1024;
byte* buffer = stackalloc byte[bufferSize];
ffmpeg.av_strerror(errorCode, buffer, (ulong)bufferSize);
string message = Marshal.PtrToStringAnsi((IntPtr)buffer);
return message;
}
#endregion
#region 에러시 예외 던지기 - ThrowExceptionIfError(error)
/// <summary>
/// 에러시 예외 던지기
/// </summary>
/// <param name="errorCode">에러 코드</param>
/// <returns>에러 코드</returns>
public static int ThrowExceptionIfError(this int errorCode)
{
if(errorCode < 0)
{
throw new ApplicationException(GetErrorMessage(errorCode));
}
return errorCode;
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 등록하기 - Register(dllDirectoryPath)
/// <summary>
/// 등록하기
/// </summary>
/// <param name="dllDirectoryPath">DLL 디렉토리 경로</param>
private static void Register(string dllDirectoryPath)
{
switch(Environment.OSVersion.Platform)
{
case PlatformID.Win32NT :
case PlatformID.Win32S :
case PlatformID.Win32Windows :
SetDllDirectory(dllDirectoryPath);
break;
case PlatformID.Unix :
case PlatformID.MacOSX :
string currentValue = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
if(string.IsNullOrWhiteSpace(currentValue) == false && currentValue.Contains(dllDirectoryPath) == false)
{
string newValue = currentValue + Path.PathSeparator + dllDirectoryPath;
Environment.SetEnvironmentVariable(LD_LIBRARY_PATH, newValue);
}
break;
}
}
#endregion
}
}
▶ VideoStreamDecoder.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using FFmpeg.AutoGen;
namespace TestLibrary
{
/// <summary>
/// 비디오 스트림 디코더
/// </summary>
public sealed unsafe class VideoStreamDecoder : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 포맷 컨텍스트
/// </summary>
private readonly AVFormatContext* formatContext;
/// <summary>
/// 스트림 인덱스
/// </summary>
private readonly int streamIndex;
/// <summary>
/// 코덱 컨텍스트
/// </summary>
private readonly AVCodecContext* codecContext;
/// <summary>
/// 패킷
/// </summary>
private readonly AVPacket* packet;
/// <summary>
/// 프레임
/// </summary>
private readonly AVFrame* frame;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 코덱명 - CodecName
/// <summary>
/// 코덱명
/// </summary>
public string CodecName { get; }
#endregion
#region 프레임 크기 - FrameSize
/// <summary>
/// 프레임 크기
/// </summary>
public Size FrameSize { get; }
#endregion
#region 픽셀 포맷 - PixelFormat
/// <summary>
/// 픽셀 포맷
/// </summary>
public AVPixelFormat PixelFormat { get; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - VideoStreamDecoder(device)
/// <summary>
/// 생성자
/// </summary>
/// <param name="device">장치</param>
public VideoStreamDecoder(string device)
{
this.formatContext = ffmpeg.avformat_alloc_context();
AVFormatContext* formatContext = this.formatContext;
ffmpeg.avdevice_register_all();
AVInputFormat* inputFormat = ffmpeg.av_find_input_format("dshow");
ffmpeg.avformat_open_input(&formatContext, device, inputFormat, null).ThrowExceptionIfError();
ffmpeg.avformat_find_stream_info(this.formatContext, null).ThrowExceptionIfError();
AVStream* stream = null;
for(var i = 0; i < this.formatContext->nb_streams; i++)
{
if(this.formatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
{
stream = this.formatContext->streams[i];
break;
}
}
if(stream == null)
{
throw new InvalidOperationException("Could not found video stream.");
}
this.streamIndex = stream->index;
this.codecContext = stream->codec;
AVCodecID codecID = this.codecContext->codec_id;
AVCodec* codec = ffmpeg.avcodec_find_decoder(codecID);
if(codec == null)
{
throw new InvalidOperationException("Unsupported codec.");
}
ffmpeg.avcodec_open2(this.codecContext, codec, null).ThrowExceptionIfError();
CodecName = ffmpeg.avcodec_get_name(codecID);
FrameSize = new Size(this.codecContext->width, this.codecContext->height);
PixelFormat = this.codecContext->pix_fmt;
this.packet = ffmpeg.av_packet_alloc();
this.frame = ffmpeg.av_frame_alloc();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 컨텍스트 정보 딕셔너리 구하기 - GetContextInfoDictionary()
/// <summary>
/// 컨텍스트 정보 딕셔너리 구하기
/// </summary>
/// <returns>컨텍스트 정보 딕셔너리</returns>
public IReadOnlyDictionary<string, string> GetContextInfoDictionary()
{
AVDictionaryEntry* dictionaryEntry = null;
Dictionary<string, string> resultDictionary = new Dictionary<string, string>();
while((dictionaryEntry = ffmpeg.av_dict_get(this.formatContext->metadata, "", dictionaryEntry, ffmpeg.AV_DICT_IGNORE_SUFFIX)) != null)
{
string key = Marshal.PtrToStringAnsi((IntPtr)dictionaryEntry->key );
string value = Marshal.PtrToStringAnsi((IntPtr)dictionaryEntry->value);
resultDictionary.Add(key, value);
}
return resultDictionary;
}
#endregion
#region 다음 프레임 디코드 시도하기 - TryDecodeNextFrame(frame)
/// <summary>
/// 다음 프레임 디코드 시도하기
/// </summary>
/// <param name="frame">프레임</param>
/// <returns>처리 결과</returns>
public bool TryDecodeNextFrame(out AVFrame frame)
{
ffmpeg.av_frame_unref(this.frame);
int errorCode;
do
{
try
{
do
{
errorCode = ffmpeg.av_read_frame(this.formatContext, this.packet);
if(errorCode == ffmpeg.AVERROR_EOF)
{
frame = *this.frame;
return false;
}
errorCode.ThrowExceptionIfError();
}
while(this.packet->stream_index != this.streamIndex);
ffmpeg.avcodec_send_packet(this.codecContext, this.packet).ThrowExceptionIfError();
}
finally
{
ffmpeg.av_packet_unref(this.packet);
}
errorCode = ffmpeg.avcodec_receive_frame(this.codecContext, this.frame);
}
while(errorCode == ffmpeg.AVERROR(ffmpeg.EAGAIN));
errorCode.ThrowExceptionIfError();
frame = *this.frame;
return true;
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
ffmpeg.av_frame_unref(this.frame);
ffmpeg.av_free(this.frame);
ffmpeg.av_packet_unref(this.packet);
ffmpeg.av_free(this.packet);
ffmpeg.avcodec_close(this.codecContext);
AVFormatContext* formatContext = this.formatContext;
ffmpeg.avformat_close_input(&formatContext);
}
#endregion
}
}
▶ VideoFrameConverter.cs
using System;
using System.Runtime.InteropServices;
using System.Windows;
using FFmpeg.AutoGen;
namespace TestLibrary
{
/// <summary>
/// 비디오 프레임 컨버터
/// </summary>
public sealed unsafe class VideoFrameConverter : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 타겟 크기
/// </summary>
private readonly Size targetSize;
/// <summary>
/// 컨텍스트
/// </summary>
private readonly SwsContext* context;
/// <summary>
/// 버퍼 핸들
/// </summary>
private readonly IntPtr buferHandle;
/// <summary>
/// 임시 프레임 데이터
/// </summary>
private readonly byte_ptrArray4 temporaryFrameData;
/// <summary>
/// 임시 프레임 라인 크기
/// </summary>
private readonly int_array4 temporaryFrameLineSize;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - VideoFrameConverter(sourceSize, sourcePixelFormat, targetSize, targetPixelFormat)
/// <summary>
/// 생성자
/// </summary>
/// <param name="sourceSize">소스 크기</param>
/// <param name="sourcePixelFormat">소스 픽셀 포맷</param>
/// <param name="targetSize">타겟 크기</param>
/// <param name="targetPixelFormat">타겟 픽셀 포맷</param>
public VideoFrameConverter(Size sourceSize, AVPixelFormat sourcePixelFormat, Size targetSize, AVPixelFormat targetPixelFormat)
{
this.targetSize = targetSize;
this.context = ffmpeg.sws_getContext
(
(int)sourceSize.Width,
(int)sourceSize.Height,
sourcePixelFormat,
(int)targetSize.Width,
(int)targetSize.Height,
targetPixelFormat,
ffmpeg.SWS_FAST_BILINEAR,
null,
null,
null
);
if(this.context == null)
{
throw new ApplicationException("Could not initialize the conversion context.");
}
int bufferSize = ffmpeg.av_image_get_buffer_size(targetPixelFormat, (int)targetSize.Width, (int)targetSize.Height, 1);
this.buferHandle = Marshal.AllocHGlobal(bufferSize);
this.temporaryFrameData = new byte_ptrArray4();
this.temporaryFrameLineSize = new int_array4();
ffmpeg.av_image_fill_arrays
(
ref this.temporaryFrameData,
ref this.temporaryFrameLineSize,
(byte*)this.buferHandle,
targetPixelFormat,
(int)targetSize.Width,
(int)targetSize.Height,
1
);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 변환하기 - Convert(sourceFrame)
/// <summary>
/// 변환하기
/// </summary>
/// <param name="sourceFrame">소스 프레임</param>
/// <returns>프레임</returns>
public AVFrame Convert(AVFrame sourceFrame)
{
ffmpeg.sws_scale
(
this.context,
sourceFrame.data,
sourceFrame.linesize,
0,
sourceFrame.height,
this.temporaryFrameData,
this.temporaryFrameLineSize
);
byte_ptrArray8 targetFrameData = new byte_ptrArray8();
targetFrameData.UpdateFrom(this.temporaryFrameData);
int_array8 targetFrameLineSize = new int_array8();
targetFrameLineSize.UpdateFrom(this.temporaryFrameLineSize);
return new AVFrame
{
data = targetFrameData,
linesize = targetFrameLineSize,
width = (int)this.targetSize.Width,
height = (int)this.targetSize.Height
};
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
Marshal.FreeHGlobal(this.buferHandle);
ffmpeg.sws_freeContext(this.context);
}
#endregion
}
}
▶ CameraControl.xaml
<UserControl x:Class="TestLibrary.CameraControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Image Name="image"
Stretch="Fill"
SnapsToDevicePixels="True" />
</UserControl>
▶ CameraControl.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using FFmpeg.AutoGen;
namespace TestLibrary
{
/// <summary>
/// 카메라 컨트롤
/// </summary>
public partial class CameraControl : UserControl
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// DirectShow 헬퍼
/// </summary>
private static DirectShowHelper _directShowHelper = new DirectShowHelper();
/// <summary>
/// 장치명 리스트
/// </summary>
private static readonly List<string> _deviceNameList = new List<string>();
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 디스패처
/// </summary>
private Dispatcher dispatcher = Application.Current.Dispatcher;
/// <summary>
/// 장치명
/// </summary>
private string deviceName = null;
/// <summary>
/// 스레드
/// </summary>
private Thread thread;
/// <summary>
/// 스레드 실행 여부
/// </summary>
private bool isThreadRunning;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 장치명 리스트 - DeviceNameList
/// <summary>
/// 장치명 리스트
/// </summary>
public static List<string> DeviceNameList
{
get
{
return _deviceNameList;
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 장치명 - DeviceName
/// <summary>
/// 장치명
/// </summary>
public string DeviceName
{
get
{
return this.deviceName;
}
set
{
this.deviceName = value;
}
}
#endregion
#region 캡처 여부 - IsCapturing
/// <summary>
/// 캡처 여부
/// </summary>
public bool IsCapturing
{
get
{
return this.isThreadRunning;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Static
#region 생성자 - CameraControl()
/// <summary>
/// 생성자
/// </summary>
static CameraControl()
{
FFMpegHelper.Register();
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - CameraControl()
/// <summary>
/// 생성자
/// </summary>
public CameraControl()
{
InitializeComponent();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 장치명 리스트 구하기 - GetDeviceNameList()
/// <summary>
/// 장치명 리스트 구하기
/// </summary>
/// <returns>장치명 리스트</returns>
public static List<string> GetDeviceNameList()
{
_deviceNameList.Clear();
_directShowHelper.EnumerateVideoInputDeviceList(AddVideoCaptureDeviceList);
return _deviceNameList;
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 비디오 캡처 장치 리스트 추가하기 - AddVideoCaptureDeviceList(info)
/// <summary>
/// 비디오 캡처 장치 리스트 추가하기
/// </summary>
/// <param name="info">비디오 입력 장치 정보</param>
private static void AddVideoCaptureDeviceList(ref VideoInputDeviceInfo info)
{
if(!string.IsNullOrEmpty(info.DevicePath))
{
VideoCaptureDevice device = new VideoCaptureDevice(info);
_deviceNameList.Add(device.Name);
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 시작하기 - Start()
/// <summary>
/// 시작하기
/// </summary>
public void Start()
{
if(this.isThreadRunning == true)
{
return;
}
if(this.deviceName == null || !_deviceNameList.Contains(this.deviceName))
{
return;
}
this.isThreadRunning = true;
this.thread = new Thread(ProcessThread);
this.thread.Start();
}
#endregion
#region 중단하기 - Stop()
/// <summary>
/// 중단하기
/// </summary>
public void Stop()
{
this.isThreadRunning = false;
if(this.thread != null && this.thread.IsAlive)
{
this.isThreadRunning = false;
this.thread.Join();
}
}
#endregion
#region 이미지 구하기 - GetImage()
/// <summary>
/// 이미지 구하기
/// </summary>
/// <returns>이미지</returns>
public ImageSource GetImage()
{
BitmapSource bitmapSource = this.image.Source as BitmapSource;
return bitmapSource.Clone() as BitmapSource;
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 로그 콜백 설정하기 - SetLogCallback()
/// <summary>
/// 로그 콜백 설정하기
/// </summary>
private static unsafe void SetLogCallback()
{
ffmpeg.av_log_set_level(ffmpeg.AV_LOG_VERBOSE);
av_log_set_callback_callback callback = (p0, level, format, vl) =>
{
if(level > ffmpeg.av_log_get_level())
{
return;
}
int lineSize = 1024;
byte* lineBuffer = stackalloc byte[lineSize];
int printPrefix = 1;
ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer);
};
ffmpeg.av_log_set_callback(callback);
}
#endregion
#region 이미지 소스 설정하기 - SetImageSource(bitmap)
/// <summary>
/// 이미지 소스 설정하기
/// </summary>
/// <param name="bitmap">비트맵</param>
private void SetImageSource(System.Drawing.Bitmap bitmap)
{
this.dispatcher.BeginInvoke((Action)(() =>
{
using(MemoryStream memoryStream = new MemoryStream())
{
if(this.thread.IsAlive)
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
memoryStream.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.StreamSource = memoryStream;
bitmapimage.EndInit();
this.image.Source = bitmapimage;
}
}
}));
}
#endregion
#region 스레드 처리하기 - ProcessThread()
/// <summary>
/// 스레드 처리하기
/// </summary>
private unsafe void ProcessThread()
{
using(VideoStreamDecoder decoder = new VideoStreamDecoder($"video={this.deviceName}"))
{
IReadOnlyDictionary<string, string> contextInfoDictionary = decoder.GetContextInfoDictionary();
contextInfoDictionary.ToList().ForEach(x => Console.WriteLine($"{x.Key} = {x.Value}"));
Size sourceSize = decoder.FrameSize;
AVPixelFormat sourcePixelFormat = decoder.PixelFormat;
Size targetSize = sourceSize;
AVPixelFormat targetPixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24;
using(VideoFrameConverter converter = new VideoFrameConverter(sourceSize, sourcePixelFormat, targetSize, targetPixelFormat))
{
int frameNumber = 0;
while(decoder.TryDecodeNextFrame(out AVFrame sourceFrame) && this.isThreadRunning)
{
AVFrame targetFrame = converter.Convert(sourceFrame);
System.Drawing.Bitmap bitmap;
bitmap = new System.Drawing.Bitmap
(
targetFrame.width,
targetFrame.height,
targetFrame.linesize[0],
System.Drawing.Imaging.PixelFormat.Format24bppRgb,
(IntPtr)targetFrame.data[0]
);
SetImageSource(bitmap);
frameNumber++;
}
}
}
}
#endregion
}
}
[TestProject 프로젝트]
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:library="clr-namespace:TestLibrary;assembly=TestLibrary"
Width="800"
Height="600"
Title="FFMpeg을 사용해 웹 카메라 사용하기 (기능 개선)"
FontFamily="나눔고딕코딩"
FontSize="16">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderThickness="1"
BorderBrush="Black">
<library:CameraControl Name="cameraControl"
Width="600"
Height="480" />
</Border>
<StackPanel Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button Name="startButton"
Width="100"
Height="30"
Content="시작" />
<Button Name="imageButton"
Margin="10 0 0 0"
Width="100"
Height="30"
Content="이미지" />
</StackPanel>
</Grid>
</Window>
▶ MainWindow.xaml.cs
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using TestLibrary;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
Closing += Window_Closing;
this.startButton.Click += startButton_Click;
this.imageButton.Click += imageButton_Click;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 윈도우 닫을 경우 처리하기 - Window_Closing(sender, e)
/// <summary>
/// 윈도우 닫을 경우 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Window_Closing(object sender, CancelEventArgs e)
{
this.cameraControl.Stop();
}
#endregion
#region 시작 버튼 클릭시 처리하기 - startButton_Click(sender, e)
/// <summary>
/// 시작 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void startButton_Click(object sender, RoutedEventArgs e)
{
if(this.startButton.Content.ToString() == "시작")
{
this.cameraControl.DeviceName = CameraControl.GetDeviceNameList().First();
this.cameraControl.Start();
this.startButton.Content = "중단";
}
else
{
this.cameraControl.Stop();
this.startButton.Content = "시작";
}
}
#endregion
#region 이미지 버튼 클릭시 처리하기 - imageButton_Click(sender, e)
/// <summary>
/// 이미지 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void imageButton_Click(object sender, RoutedEventArgs e)
{
ImageSource imageSource = this.cameraControl.GetImage();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(imageSource as BitmapImage));
using(FileStream stream = new FileStream("d:\\sample.png", FileMode.Create, FileAccess.Write, FileShare.None))
{
encoder.Save(stream);
}
}
#endregion
}
}
728x90
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] Canvas 클래스 : 선 도형 추가하기 (0) | 2020.12.26 |
---|---|
[C#/WPF] 타원 상에서 호(Arc) 시작점/종료점 구하기 (0) | 2020.12.26 |
[C#/WPF] 타원과 직선 교차점 배열 찾기 (0) | 2020.12.26 |
[C#/WPF] Canvas 클래스 : 타원 도형 추가하기 (0) | 2020.12.26 |
[C#/WPF] Canvas 클래스 : 사각형 도형 추가하기 (0) | 2020.12.26 |
[C#/WPF] ItemsControl 클래스 : 리스트 컨트롤 사용하기 (드래그 기능 개선) (0) | 2020.12.19 |
[C#/WPF] MarkupExtension 클래스 : 마크업 확장 사용하기 (0) | 2020.12.15 |
[C#/WPF] Image 클래스 : LayoutUpdated 이벤트를 사용해 이미지 픽셀 보정하기 (0) | 2020.12.13 |
[C#/WPF] FrameworkElement 클래스 : 장평을 설정하는 커스텀 텍스트 블럭 사용하기 (0) | 2020.12.13 |
[C#/WPF] Grid 클래스 : 환형 패널 사용하기 (0) | 2020.12.12 |