성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] A graphical depiction of the steps ...
[정성태] 질문을 주셔서 출판사 측에 문의를 했습니다. 약 한 달 정도 후...
[Thorondor
] @정성태 개인 블로그인데도 거의 커뮤니티 급 인 것 같아요. 요...
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Video Capture 장치(Camera) 열거 및 지원 포맷 조회</h1> <p> 기왕에 Microsoft Media Foundation의 PInvoke 호출까지 알아봤으니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13607'>https://www.sysnet.pe.kr/2/0/13607</a> </pre> <br /> 아래 문서에 나오는 예제를 C#으로 완성해 보겠습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Enumerating Video Capture Devices ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices'>https://learn.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices</a> How to Set the Video Capture Format ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format'>https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format</a> </pre> <br /> 우선, Video 장치의 수를 먼저 알아볼 텐데요, 관련 COM 인터페이스만 적절하게 정의를 맞춰주면 다음과 같이 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static unsafe uint GetVideoDeviceCount() { uint pcSourceActivate = 0; IMFAttributes? pAttributes = null; IMFActivate[]? ppDevices = null; // 1. IMFAttributes 개체를 구해, <span style='color: blue; font-weight: bold'>NativeMethods.MFCreateAttributes(out pAttributes, 1);</span> try { // 2. Video 장치로 대상을 설정하고, pAttributes.SetGuid(MFGuid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_GUID, MFGuid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); // 3. 장치에 대응하는 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices'>IMFActivate 인스턴스 배열을 구함</a> (pcSourceActivate가 장치의 개수를 의미) NativeMethods.<a target='tab' href='https://www.sysnet.pe.kr/2/0/13607#MFEnumDeviceSources'>MFEnumDeviceSources</a>(pAttributes, out ppDevices, out pcSourceActivate); } finally { // 자원 해제 if (ppDevices != null) { for (uint i = 0; i < pcSourceActivate; i++) { Marshal.ReleaseComObject(ppDevices[i]); } } if (pAttributes != null) { Marshal.ReleaseComObject(pAttributes); } } return pcSourceActivate; } </pre> <br /> 설치한 비디오 Camera 수를 알았으면 이제 인덱스 별로 장치에 대한 IMFMediaSource 인터페이스를 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Enumerating Video Capture Devices // ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices'>https://learn.microsoft.com/en-us/windows/win32/medfound/enumerating-video-capture-devices</a> uint captureDeviceCount = GetVideoDeviceCount(); for (int i = 0; i < captureDeviceCount; i++) { IMFMediaSource mediaSource = <span style='color: blue; font-weight: bold'>GetMediaSource(i, out string symbolicLink, out string deviceName);</span> // ... Video Camera에 대한 mediaSource 인터페이스 사용 } public static IMFMediaSource GetMediaSource(int deviceIndex, out string symbolicLink, out string deviceName) { IMFAttributes? pAttributes = null; IMFActivate[]? ppDevices = null; IMFMediaSource? pSource = null; uint pcSourceActivate = 0; symbolicLink = string.Empty; // 1. GetVideoDeviceCount 메서드에서 썼던 코드를 재사용, 즉, 연결 중인 Video 장치를 모두 가져와서, NativeMethods.MFCreateAttributes(out pAttributes, 1); try { pAttributes.SetGuid(MFGuid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_GUID, MFGuid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); NativeMethods.MFEnumDeviceSources(pAttributes, out ppDevices, out pcSourceActivate); if (deviceIndex >= pcSourceActivate) { throw new ArgumentOutOfRangeException($"MediaDevices[{nameof(deviceIndex)}]"); } // 2. 그 장치 목록 배열에서 deviceIndex를 지정한 장치의 정보를 조회 <span style='color: blue; font-weight: bold'>ppDevices[deviceIndex]</span>.GetAllocatedString(MFGuid.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK_GUID, <span style='color: blue; font-weight: bold'>out symbolicLink</span>, out _); <span style='color: blue; font-weight: bold'>ppDevices[deviceIndex]</span>.GetAllocatedString(MFGuid.MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, <span style='color: blue; font-weight: bold'>out deviceName</span>, out _); // 3. 해당 장치의 IMFMediaSource 인터페이스를 반환 ppDevices[deviceIndex].<span style='color: blue; font-weight: bold'>ActivateObject</span>(typeof(IMFMediaSource).GUID, <span style='color: blue; font-weight: bold'>out pSource</span>); } finally { if (ppDevices != null) { for (uint i = 0; i < pcSourceActivate; i++) { Marshal.ReleaseComObject(ppDevices[i]); } } if (pAttributes != null) { Marshal.ReleaseComObject(pAttributes); } } return pSource; } </pre> <br /> 참고로, 위에서 구한 symbolicLink, deviceName을 출력해 보면 이런 식으로 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > symbolicLink: \\?\USB#VID_046D&...[생략]...\{bbefb6c7-...[생략]...724083}: deviceName: HD Pro Webcam C920 </pre> <br /> 그러고 보니 언젠가 이와 유사한 정보를 구했던 것 같습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 데스크톱 윈도우 응용 프로그램에서 UWP 라이브러리를 이용한 비디오 장치 열람하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11284'>https://www.sysnet.pe.kr/2/0/11284</a> </pre> <br /> 위의 글에서 "DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);" 코드로 구했던 <a target='tab' href='https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceinformation'>DeviceInformation</a> 타입의 Id와 Name이 정확히 그 값에 해당합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, IMFMediaSource를 구했으면 이제 그 장치에 대한 정보를 조회할 수 있습니다. 일례로 해당 장치가 지원하고 있는 Capture Format을 열거하는 것이 가능한데요, 대충 다음과 같은 코드로 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // How to Set the Video Capture Format // ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format'>https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format</a> internal static void GetCaptureFormats(<span style='color: blue; font-weight: bold'>IMFMediaSource pSource</span>) { IMFPresentationDescriptor? pPD = null; IMFStreamDescriptor? pSD = null; IMFMediaTypeHandler? pHandler = null; try { pSource.CreatePresentationDescriptor(out pPD); pPD.GetStreamDescriptorByIndex(0, out bool pfSelected, out pSD); pSD.GetMediaTypeHandler(out pHandler); // 1. 장치에서 지원하는 Capture Format 수를 구하고, pHandler.GetMediaTypeCount(out uint cTypes); // 2. 해당 Capture Format을 구성하는 속성들을 조회할 수 있는 IMFMediaType을 가져와, for (uint i = 0; i < cTypes; i++) { pHandler.GetMediaTypeByIndex(i, out IMFMediaType ppType); try { // 3. Capture Format을 설명하는 각각의 값을 출력 <span style='color: blue; font-weight: bold'>DebugLogMediaType(ppType);</span> } finally { if (ppType != null) { Marshal.ReleaseComObject(ppType); } } } } finally { if (pPD != null) { Marshal.ReleaseComObject(pPD); } if (pSD != null) { Marshal.ReleaseComObject(pSD); } if (pHandler != null) { Marshal.ReleaseComObject(pHandler); } } } </pre> <br /> 여기서 좀 난관이 있는데요, IMFMediaType으로 조회하는 필드의 값 타입이 PropVariant이므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PROPVARIANT structure (propidlbase.h) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/propidlbase/ns-propidlbase-propvariant'>https://learn.microsoft.com/en-us/windows/win32/api/propidlbase/ns-propidlbase-propvariant</a> </pre> <br /> 직접 만들기 귀찮으니 PropVariant를 감싼 적절한 소스코드를 찾아,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Programming/C#/Windows API Code Pack 1.1/source/WindowsAPICodePack/Core/PropertySystem ; <a target='tab' href='https://github.com/jlnewton87/Programming/blob/master/C%23/Windows%20API%20Code%20Pack%201.1/source/WindowsAPICodePack/Core/PropertySystem/PropVariant.cs'>https://github.com/jlnewton87/Programming/blob/master/C%23/Windows%20API%20Code%20Pack%201.1/source/WindowsAPICodePack/Core/PropertySystem/PropVariant.cs</a> </pre> <br /> 활용하면 이런 식으로 DebugLogMediaType 코드를 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static void DebugLogMediaType(IMFMediaType pType) { uint count = 0; // 1. Capture Format을 구성하는 필드의 수를 반환 pType.GetCount(out count); // 2. 개별 필드에 대한 값을 구해 출력 for (uint i = 0; i < count; i ++) { IMFAttributes pAttr = pType as IMFAttributes; DebugLogAttributeValueByIndex(pAttr, i); } } private static void DebugLogAttributeValueByIndex(IMFAttributes pAttr, uint i) { PropVariant var = new PropVariant(); pAttr.GetItemByIndex(i, out Guid guid, var); string guidName = MFGuid.GetName(guid); Console.Write($"{guidName}: "); SpecialCaseAttributeValue(guid, var); if (hr == HRESULT.S_FALSE) { switch (var.VarType) { case VarEnum.VT_UI4: case VarEnum.VT_R8: Console.Write(var.Value); break; case VarEnum.VT_CLSID: Console.Write(MFGuid.GetName((Guid)var.Value)); break; case VarEnum.VT_LPWSTR: Console.Write(var.Value); break; case VarEnum.VT_VECTOR | VarEnum.VT_UI1: Console.Write($"<<byte array>>"); break; case VarEnum.VT_UNKNOWN: Console.Write($"IUnknown"); break; } } Console.WriteLine(); } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 테스트해보면, Logitech HD Pro Webcam C920 제품의 경우 573개의 Capture Foramt을 지원하는데요, 개별 포맷에 대한 필드를 보면 이런 출력 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Format: #1 MF_MT_MAJOR_TYPE MFMediaType_Video MF_MT_VIDEO_LIGHTING 3 MF_MT_DEFAULT_STRIDE 1600 MF_MT_VIDEO_CHROMA_SITING 6 MF_MT_AM_FORMAT_TYPE {F72A76A0-EB0A-11D0-ACE4-0000C0CC16BA} MF_MT_FIXED_SIZE_SAMPLES 1 MF_MT_VIDEO_NOMINAL_RANGE 2 MF_MT_FRAME_RATE 30 x 1 MF_MT_PIXEL_ASPECT_RATIO 1 x 1 MF_MT_ALL_SAMPLES_INDEPENDENT 1 MF_MT_FRAME_RATE_RANGE_MIN 30 x 1 MF_MT_SAMPLE_SIZE 716800 MF_MT_VIDEO_PRIMARIES 2 MF_MT_INTERLACE_MODE 2 MF_MT_FRAME_RATE_RANGE_MAX 30 x 1 MF_MT_SUBTYPE MFVideoFormat_YUY2 ...[생략]... Format: #573 MF_MT_FRAME_SIZE: 1920 x 1080 MF_MT_AVG_BITRATE: 995328000 MF_MT_YUV_MATRIX: 2 MF_MT_MAJOR_TYPE: MFMediaType_Video MF_MT_VIDEO_LIGHTING: 3 MF_MT_CHROMA_SITING: 6 MF_MT_AM_FORMAT_TYPE: WAVEFORMAT_VIDEOINFOHEADER2 MF_MT_FIXED_SIZE_SAMPLES: 1 MF_MT_VIDEO_NOMINAL_RANGE: 2 MF_MT_FRAME_RATE: 5 x 1 MF_MT_PIXEL_ASPECT_RATIO: 1 x 1 MF_MT_ALL_SAMPLES_INDEPENDENT: 1 MF_MT_FRAME_RATE_RANGE_MIN: 5 x 1 MF_MT_SAMPLE_SIZE: 4147200 MF_MT_VIDEO_PRIMARIES: 2 MF_MT_INTERLACE_MODE: 2 MF_MT_FRAME_RATE_RANGE_MAX: 5 x 1 MF_MT_SUBTYPE: MFVideoFormat_H264 </pre> <br /> 암튼, 오디오와 비디오 세계는 그동안 쌓인 역사가 오래돼서 그런지 꽤나 복잡한 듯합니다. ^^<br /> <br /> <a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2171&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>하지만, 기본적인 Interop만 포함하고 있으므로 활용 수준으로는 적합하지 않습니다. 그런 목적으로는 Nuget에 있는 MediaFoundation 패키지가 도움이 될 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MediaFoundation ; <a target='tab' href='https://www.nuget.org/packages/MediaFoundation'>https://www.nuget.org/packages/MediaFoundation</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 아래의 질문이 갑자기 떠오르는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > OpenCV 이용 해상도 설정 질문 입니다.. ; <a target='tab' href='https://www.sysnet.pe.kr/3/0/5317'>https://www.sysnet.pe.kr/3/0/5317</a> </pre> <br /> 아마도 윈도우 10의 기본 앱에서는 "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format'>How to Set the Video Capture Format</a>" 글에 나온 "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfmediatypehandler-setcurrentmediatype'>IMFMediaTypeHandler.SetCurrentMediaType</a>" 함수를 호출해 2560x1440 모드로 설정을 한 것이 아닐까... 하는 예상을 해봅니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8955
(왼쪽의 숫자를 입력해야 합니다.)