Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 10개 있습니다.)
VC++: 121. DXGI를 이용한 윈도우 화면 캡처 소스 코드(Visual C++)
; https://www.sysnet.pe.kr/2/0/11385

.NET Framework: 705. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드
; https://www.sysnet.pe.kr/2/0/11400

.NET Framework: 706. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력
; https://www.sysnet.pe.kr/2/0/11401

.NET Framework: 712. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV
; https://www.sysnet.pe.kr/2/0/11407

.NET Framework: 713. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV (2)
; https://www.sysnet.pe.kr/2/0/11408

.NET Framework: 913. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 라이브러리
; https://www.sysnet.pe.kr/2/0/12238

.NET Framework: 1123. C# - (SharpDX + DXGI) 화면 캡처한 이미지를 빠르게 JPG로 변환하는 방법
; https://www.sysnet.pe.kr/2/0/12889

.NET Framework: 1126. C# - snagit처럼 화면 캡처를 연속으로 수행해 동영상 제작
; https://www.sysnet.pe.kr/2/0/12895

.NET Framework: 1128. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리
; https://www.sysnet.pe.kr/2/0/12897

.NET Framework: 1152. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리 (저해상도 현상 해결)
; https://www.sysnet.pe.kr/2/0/12963




C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리

지난 글에서는,

C# - snagit처럼 화면 캡처를 연속으로 수행해 동영상 제작
; https://www.sysnet.pe.kr/2/0/12895

동영상 처리를 OpenCvSharp.VideoWriter를 이용해 처리했는데요, 그 부분만 ffmpeg로 바꿔볼까요? ^^

자, 그럼 일단 지난 예제 코드 파일의 프로젝트를 로드하고 FFmpeg.AutoGen 패키지를 참조 추가한 후,

// Install-Package FFmpeg.AutoGen -Version 4.4.1.1

Install-Package FFmpeg.AutoGen

네이티브 DLL을 "FFmpeg" 디렉터리를 만들어 /bin/x64 디렉터리에 복사한 후 "Copy if newer" 빌드 설정을 합니다. 그다음, ffmpeg.RootPath를 /FFmpeg/bin/x64로 설정하면 이제 다음의 코드를 넣어 잘 동작할 수 있는 환경이 완료됩니다.

public Form1()
{
    // ...[생략]...

    FFmpegBinariesHelper.RegisterFFmpegBinaries();
}

이제부터는, 실제적인 ffmpeg 관련 코딩을 해야 하는데요, 바닥부터 하기보다는 FFmpeg.AutoGen의 예제에서 제공하던 H264VideoStreamEncoder.cs 파일을 그대로 복사해, 기존의 VideoWriter를 사용했던 코드를 최대한 그대로 재사용하기 위해 FFmpegVideoWriter를 만들어 H264VideoStreamEncoder에 연결하는 코드를 다음과 같이 작성했습니다.

public class FFmpegVideoWriter : IDisposable
{
    double _fps;
    public double Fps => _fps;

    OpenCvSharp.Size _frameSize;
    public OpenCvSharp.Size FrameSize => _frameSize;

    FileStream _outputStream;
    H264VideoStreamEncoder _streamEncoder;
    VideoFrameConverter _vfc;

    int _frameNumber = 0;

    public FFmpegVideoWriter(string filePath, FourCC type, double fps,
            OpenCvSharp.Size frameSize, bool color)
    {
        _frameSize = frameSize;
        _fps = fps;

        System.Drawing.Size imageSize = new System.Drawing.Size(frameSize.Width, frameSize.Height);
        AVPixelFormat sourcePixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24;
        AVPixelFormat destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_YUV420P;

        _outputStream = File.Open(filePath, FileMode.Create);
        _streamEncoder = new H264VideoStreamEncoder(_outputStream, AVCodecID.AV_CODEC_ID_H264, destinationPixelFormat, (int)fps, imageSize);

        _vfc = new VideoFrameConverter(imageSize, sourcePixelFormat, imageSize, destinationPixelFormat);
    }

    public unsafe void Write(Mat matFrame)
    {
        // ...[생략]...
    }

    public void Dispose()
    {
        _streamEncoder.Dispose();
        _outputStream.Dispose();
        _vfc.Dispose();
    }
}

이렇게 바꾸고 실행했더니, 일단 동작은 잘 하는데 AV_PIX_FMT_BGR24 포맷에서 AV_PIX_FMT_YUV420P 포맷으로 바뀌는 과정에 꽤나 데이터 손실이 발생해 이미지가 많이 뭉개집니다. (대신, 출력 파일의 크기는 예전에 비해 확실히 용량이 작아집니다.)

(2022-02-11 업데이트: bit_rate의 값을 높였더니 이미지가 뭉개지는 현상은 없어졌습니다.)





그나저나, ffmpeg에 대해 거의 모르니 저 데이터 손실을 없앨 방법을 모르겠군요. ^^; "AVPixelFormat destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_YUV420P"의 값을 AV_PIX_FMT_YUYV422로 바꿨더니 "ffmpeg.avcodec_open2(_pCodecContext, _pCodec, null).ThrowExceptionIfError();" 호출에서 "System.ApplicationException: 'Invalid argument'" 오류가 발생합니다.

H264 코덱이 좀 더 해상도가 높은 인코딩 방식을 지원하지 않는다는 것은 말이 안 되는 듯한데... 아마도 제가 모르는 다른 인자가 있겠지요. ^^ (혹시 이에 대해서 아시는 분은 덧글 부탁드립니다.)

암튼, 기초는 끝냈으니... 조금씩 예제 코드를 확장해 보겠습니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다. 단지, 압축 파일 크기를 줄이기 위해 ./FFmpeg/bin/x64 하위의 DLL은 제거했습니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/11/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2022-01-03 10시54분
[이승준] AV_PIX_FMT_YUYV422 이것의 변경에서 에러가 나는것은 두가지 경우를 생각해 볼 수 있습니다.

1. 인코더가 해당 컬러포맷을 지원하지 않는 경우.
2. 컬러포맷 컨버터를 지원하지 않도록 만든 경우.

1번의 경우는 인코더마다 지원하는 컬러포맷이 다릅니다.
문제는 위의 코드로는 어떤 코덱이 선택되었는지 조차도 알수가 없고,
에러처리를 코덱 선택 부분에서 실제 지원 컬러포맷을 확인해서 처리했는지?
아니면 그냥 범용적으로 사용되는 컬러포맷이 아닐 경우 전부 오류 처리를 했는지 알수가 없다는거죠.

2번의 경우는 가능성이 거의 없습니다.
ffmpeg 포맷 컨버터는 특수한 컬러포맷이 아니면 거의 변환을 합니다.
당연히 YUYV422은 문제가 없죠.
[guest]
2022-01-03 11시03분
@이승준 덧글 감사합니다. 1번의 경우를 언급하면서 본문의 코드에서 어떤 코덱이 선택되었는지 알 수가 없다고 했는데, 그렇다면 "AVCodecID.AV_CODEC_ID_H264"라는 식별자도 H264를 지원하는 것들의 다양한 코덱 종류에 대한 대표값에 불과하다는 거죠?
정성태
2022-01-03 12시54분
[이승준] 정성태님 맞습니다.
다만 보통 기본 인코더가 선택이 되는데. 이게 라이브러리 빌드에 따라서 달라집니다.
ffmpeg.org에서 다운로드 받는것 중 gpl3기준일 경우 libx264가 올라오고요.
lgpl일 경우 아주 기본적인 소프트웨어 인코더가 올라오거나 아니면 gpu에 따라 gpu인코더가 올라오기도 합니다.
vcpkg 로 직접 빌드할 경우에는 딱 기본적인 라이브러리만 추가되서 빌드될겁니다.

https://github.com/m-ab-s/media-autobuild_suite
요건 윈도우즈에서 최신버전의 ffmpeg을 본인 입맛대로 빌드할 수 있는 스크립트가 제공되는 깃허브 입니다.
단점은 간헐적으로 버그가 발생하면 빌드가 안되는 경우도 있습니다. 그리고 ffmpeg을 그때그때 다운로드 받기 때문에 소스 수정은 불가능 합니다.
스크립트 자체를 수정하면 물론 가능하고요.
[guest]
2022-01-03 01시48분
@이승준 답변 정말 감사드립니다. ^^ 생각했던 것보다 이쪽 분야가 엄청 복잡다단하군요. ^^; 말씀해 주신 링크도 참고해서 다양한 빌드 버전을 얻어 테스트를 좀 더 해보겠습니다. (알려주시지 않았다면 그런게 있었는지 평생 몰랐을 듯싶군요. ^^)
정성태

... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13147정성태10/25/20224761오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/20225593.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
13145정성태10/21/20225858오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/20225715.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/20226226오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/20224953도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/20226710.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/20226092C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/20225925.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/20227211.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/20225601.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
13136정성태10/7/20226175.NET Framework: 2054. .NET Core/5+ SDK 설치 없이 dotnet-dump 사용하는 방법
13135정성태10/5/20226405.NET Framework: 2053. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프를 분석하는 방법 - 두 번째 이야기
13134정성태10/4/20225135오류 유형: 820. There is a problem with AMD Radeon RX 5600 XT device. For more information, search for 'graphics device driver error code 31'
13133정성태10/4/20225453Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법 [1]
13132정성태10/3/20225324스크립트: 42. 파이썬 - latexify-py 패키지 소개 - 함수를 mathjax 식으로 표현
13131정성태10/3/20227987.NET Framework: 2052. C# - Windows Forms의 데이터 바인딩 지원(DataBinding, DataSource) [2]파일 다운로드1
13130정성태9/28/20225092.NET Framework: 2051. .NET Core/5+ - 에러 로깅을 위한 Middleware가 동작하지 않는 경우파일 다운로드1
13129정성태9/27/20225390.NET Framework: 2050. .NET Core를 IIS에서 호스팅하는 경우 .NET Framework CLR이 함께 로드되는 환경
13128정성태9/23/20227965C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import파일 다운로드1
13127정성태9/22/20226416Windows: 210. WSL에 systemd 도입
13126정성태9/15/20227030.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
13125정성태9/14/20227236.NET Framework: 2048. C# 11 - 구조체 필드의 자동 초기화(auto-default structs)
13124정성태9/13/20226977.NET Framework: 2047. Golang, Python, C#에서의 CRC32 사용
13123정성태9/8/20227415.NET Framework: 2046. C# 11 - 멤버(속성/필드)에 지정할 수 있는 required 예약어 추가
13122정성태8/26/20227416.NET Framework: 2045. C# 11 - 메서드 매개 변수에 대한 nameof 지원
... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...