Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 24개 있습니다.)
.NET Framework: 1129. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 인코딩 예제(encode_video.c)
; https://www.sysnet.pe.kr/2/0/12898

.NET Framework: 1134. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c)
; https://www.sysnet.pe.kr/2/0/12924

.NET Framework: 1135. C# - ffmpeg(FFmpeg.AutoGen)로 하드웨어 가속기를 이용한 비디오 디코딩 예제(hw_decode.c)
; https://www.sysnet.pe.kr/2/0/12932

.NET Framework: 1136. C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP2 오디오 파일 디코딩 예제(decode_audio.c)
; https://www.sysnet.pe.kr/2/0/12933

.NET Framework: 1137. ffmpeg의 파일 해시 예제(ffhash.c)를 C#으로 포팅
; https://www.sysnet.pe.kr/2/0/12935

.NET Framework: 1138. C# - ffmpeg(FFmpeg.AutoGen)를 이용해 멀티미디어 파일의 메타데이터를 보여주는 예제(metadata.c)
; https://www.sysnet.pe.kr/2/0/12936

.NET Framework: 1139. C# - ffmpeg(FFmpeg.AutoGen)를 이용해 오디오(mp2) 인코딩하는 예제(encode_audio.c)
; https://www.sysnet.pe.kr/2/0/12937

.NET Framework: 1147. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 오디오 필터링 예제(filtering_audio.c)
; https://www.sysnet.pe.kr/2/0/12951

.NET Framework: 1148. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 오디오 필터 예제(filter_audio.c)
; https://www.sysnet.pe.kr/2/0/12952

.NET Framework: 1150. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/12959

개발 환경 구성: 637. ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 세 번째 이야기
; https://www.sysnet.pe.kr/2/0/12960

.NET Framework: 1151. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 프레임의 크기 및 포맷 변경 예제(scaling_video.c)
; https://www.sysnet.pe.kr/2/0/12961

.NET Framework: 1153. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 avio_reading.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12964

.NET Framework: 1157. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 muxing.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12971

.NET Framework: 1159. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 qsvdec.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12975

.NET Framework: 1161. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 resampling_audio.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12978

.NET Framework: 1166. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 filtering_video.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12984

.NET Framework: 1169. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 demuxing_decoding.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12987

.NET Framework: 1170. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 transcode_aac.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12991

.NET Framework: 1178. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 http_multiclient.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/13002

.NET Framework: 1180. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 remuxing.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/13006

.NET Framework: 1188. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 transcoding.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/13023

.NET Framework: 1190. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 vaapi_encode.c, vaapi_transcode.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/13025

.NET Framework: 1191. C 언어로 작성된 FFmpeg Examples의 C# 포팅 전체 소스 코드
; https://www.sysnet.pe.kr/2/0/13026




C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 두 번째 이야기

예전 글에서,

C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c)
; https://www.sysnet.pe.kr/2/0/12924

비디오 프레임을 디코딩 후 PGM 포맷으로 저장한 데이터가 정상적으로 보이지 않는다고 했는데요, 저도 이젠 나름 ^^ 지식이 쌓이다 보니 해당 문제를 다시 살펴볼 여유가 생겼습니다.

일단, 디코딩이 잘 되는지 "C# - ffmpeg(FFmpeg.AutoGen) - 비디오 프레임 디코딩" 글에서처럼 YUV 데이터를 RGB로 변환해 저장해 보았는데요,

while (ret >= 0)
{
    ret = ffmpeg.avcodec_receive_frame(pCodecContext, frame);
    if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
    {
        return true;
    }
    else if (ret < 0)
    {
        Console.WriteLine("Error during decoding");
        return false;
    }

    AVPixelFormat format = (AVPixelFormat)frame->format;
    if (format == AVPixelFormat.AV_PIX_FMT_YUV420P)
    {
        byte* yData = frame->data[0];
        byte* uData = frame->data[1];
        byte* vData = frame->data[2];

        int yStride = frame->linesize[0];
        int uStride = frame->linesize[1];
        int vStride = frame->linesize[2];

        {
            byte Y, U, V;
            int r, g, b;
            Bitmap bitmap = new Bitmap(frame->width, frame->height, PixelFormat.Format24bppRgb);

            for (int y = 0; y < frame->height; y++)
            {
                for (int x = 0; x < frame->width; x++)
                {
                    Y = yData[yStride * y + x];
                    U = uData[uStride * (y / 2) + x / 2];
                    V = vData[vStride * (y / 2) + x / 2];

                    YUV2RGB_ByMS(Y, U, V, out r, out g, out b);
                    Color pixel = Color.FromArgb(0, r, g, b);
                    bitmap.SetPixel(x, y, pixel);
                }
            }

            bitmap.Save(Path.Combine(outfileDirPath, "yuv422p_" + pCodecContext->frame_number + ".bmp"));
        }
    }
}

위의 소스 코드를 실행해 보면, BMP 파일의 출력 결과가 깨져 나옵니다.

mepg1video_yuv_img_1.png

즉, 애당초 해당 코드는 정상적으로 video frame을 생성하지 못 하고 있었던 것입니다.




그래서 일단 decode_video.c가 av_parser_parse2를 이용해 디코딩하는 것을 "C# - ffmpeg(FFmpeg.AutoGen) - decoding 과정" 글에서처럼 av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로 바꿨습니다.

동일하게 mpeg1video 코덱이 선택되었고,

{
    videoStream = av_context->streams[videoStreamIndex];
    videoContext = ffmpeg.avcodec_alloc_context3(videoDecoder);
    ret = ffmpeg.avcodec_parameters_to_context(videoContext, videoStream->codecpar);
    ret = ffmpeg.avcodec_open2(videoContext, videoDecoder, null);
}

string codecName = Marshal.PtrToStringAnsi(new IntPtr(videoDecoder->name));
Console.WriteLine(codecName); // mpeg1video

YUV420P 프레임을 정상적으로 해석해 BMP 파일로도 저장이 되었습니다. 그렇기 때문에 Y(Luma channel) 값을 pgm 파일로 저장하는 코드 역시 정상적으로 gray 포맷으로 출력이 되었습니다.

PGM File Viewer (browser-based)
; https://smallpond.ca/jim/photomicrography/pgmViewer/index.html

(사진은 유튜브 영상 "디에이드"의 "안다은" 님이고 사용을 허락받고 올립니다.)
mepg1video_yuv_img_2.png

그러니까 애당초 decode_video.c가 정상적으로 동작하지 않았던 코드였습니다. ^^;




참고로, 프레임을 명시적으로 AV_PIX_FMT_GRAY8로 변환해도 됩니다. 이를 위해 YUV to GRAY 변경을 SwsContext를 이용해 다음과 같이 수행할 수 있습니다.

SwsContext* sws_ctx = ffmpeg.sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format,
    frame->width, frame->height, AVPixelFormat.AV_PIX_FMT_GRAY8, ffmpeg.SWS_BICUBIC, null, null, null);

byte* pgray8 = (byte*)ffmpeg.av_calloc((ulong)(frame->width * frame->height), 1);
byte*[] ppgray8 = new byte*[1];
ppgray8[0] = pgray8;

int[] gray_stride = new int[1];
gray_stride[0] = frame->width;

int result = ffmpeg.sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, ppgray8, gray_stride);
// result == 1080 (1920 x 1080 인 경우)

Console.WriteLine($"saving frame {frame->coded_picture_number}");

string outputFile = Path.Combine(@"C:\temp\output", "noname_" + frame->coded_picture_number + ".pgm");
pgm_save(pgray8, frame->width, frame->width, frame->height, outputFile);

ffmpeg.av_free(pgray8);

ffmpeg.sws_freeContext(sws_ctx);

위에서는 pgm_save에 frame->data[0]을 전달하지 않고, sws_scale로 변환된 pgray8 포인터를 전달하고 있습니다. 당연히 저렇게 출력한 pgm 파일도 Y 채널만 출력한 pgm 파일과 동일한 흑백 영상이 나옵니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로는 비디오가 잘 해석이 되는데, av_parser_parse2를 이용한 버전은 왜 안 되는 것일까요? 음... 이 정도까지는 아직 제 수준에서 이해할 수가 없군요. ^^

decode_video.c 파일의 버그인지, 아니면 원래 저렇게는 안 되는 것인지 알 수가 없습니다.

av_parser_parse2가 잘 동작하는 코드가 있긴 했습니다. 바로 오디오 파일을 디코딩하는 경우입니다.

C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP2 오디오 파일 디코딩 예제(decode_audio.c)
; https://www.sysnet.pe.kr/2/0/12933

C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP3 오디오 파일 인코딩/디코딩하는 예제
; https://www.sysnet.pe.kr/2/0/12939

하지만 비디오 디코딩에도 사용하는 소스 코드가 있는 걸로 봐서는,

MediaPlayer/MediaPlayer/FFmpegDecoder.cpp
; https://github.com/xc724655471/MediaPlayer/blob/13f0f518fd4e9e0ecb763ca1b23bf12bbd1ab249/MediaPlayer/FFmpegDecoder.cpp

분명히 디코딩 과정은 잘 동작했을 것으로 추정은 됩니다.




혹시나 싶어서, gray 포맷의 (mpeg1video는 오직 yuv420p만 지원하므로) h264 동영상을 전달하면 잘 동작할까요?

D:\media_sample> ffmpeg -i mp4video_sample2.mp4 -pix_fmt gray mp4video_sample2_gray.mp4

gray mp4 파일을 decode_video.c의 입력으로 전달하고 codec id만 AVCodecID.AV_CODEC_ID_H264로 바꿔보았는데요, 그래도 여전히 정상적인 비디오 프레임이 나오지는 않았습니다. 어쩌면 저 작업이 굳이 필요하지도 않았는데요, 왜냐하면 pix_fmt을 gray로 지정하긴 했지만 ffprobe로 확인해 보면 mp4video_sample2_gray.mp4는 여전히 yuv420p로 나오기 때문입니다. 즉, 영상만 흑백이고 pix_fmt은 변함없이 YUV420P입니다.

비록 pgm 저장은 우회해서 성공했지만, decode_vide.c 소스 코드는 끝내 미스터리로 남는군요. ^^




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







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

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

비밀번호

댓글 작성자
 



2022-02-08 12시45분
[이승준] 저도 av_parser_parse2 이건 사용해 보지 않았어서 뭔가 뭔가 싶었는데요.
찾아보니 기본적으로 컨테이너를 지원하지 않는 api로 보입니다. 즉 비디오/오디오 압축데이타만 지원한다는거죠.
그래서 오디오에서는 정상 동작한것으로 보입니다. 오디오 압축 파일은 컨테이너가 없거든요.
https://titanwolf.org/Network/Articles/Article?AID=d650b07d-cc80-4c8b-be0c-cf04e0da6435
참고링크 입니다.
아마도 비디오만 추출해서 테스트 해 보시면 정상동작할거라고 보여집니다.
[guest]
2022-02-08 01시42분
퇴근 후에 한번 해보겠습니다. ^^ 이제서야 decode_video.c의 수수께끼가 풀리는군요. 게다가 av_parser_parse2와 av_read_frame의 차이점도 자연스럽게 이해되고. ^^
정성태

... 181  182  183  184  185  186  187  188  189  190  191  192  193  [194]  195  ...
NoWriterDateCnt.TitleFile(s)
92정성태1/29/200518248.NET Framework: 23. Unmanaged 환경에서 Managed DLL에 정의된 메서드 호출 시 오류 확인하는 방법
91정성태11/14/200518848VC++: 12. VS.NET 2005 VC++ Debug: Expression: ( (state != ST_INVALID ) )
90정성태1/27/200519629.NET Framework: 22. Debug: The underlying connection was closed: Unable to connect to the remote server.
89정성태1/26/200524168VC++: 11. Delay Loaded DLL
87정성태1/23/200517768VS.NET IDE: 18. VS.NET 2005 Beta 1 - VC++ 프로젝트에서 Connection Point 구현시 버그
88정성태1/23/200517474    답변글 VS.NET IDE: 18.1. VS.NET 2003 : VC++ 프로젝트에서 Connection Point 추가시에도 버그
86정성태1/23/200523193.NET Framework: 21. Code Snippet - Enum과 관련된 다양한 형변환 [1]
85정성태1/23/200521321스크립트: 4. Windows 2003에서 BHO(Browser Helper Objects) 동작 안하는 현상 [1]
83정성태1/18/200526461.NET Framework: 20. System.AccessViolationException 예외가 발생한 한 예.
82정성태1/3/200519926VS.NET IDE: 17. Windows 운영 - 특정 사용자 또는 그룹에 대해서 파일 공유 접근 금지
79정성태1/20/200527867기타: 8. DELL Latitude D800 노트북 컴퓨터의 PC Beep 소음(!) 문제.
78정성태12/27/200420241VS.NET IDE: 16. MS 제품 관련 사용되는 TCP/IP 포트 열거파일 다운로드1
77정성태12/27/200420491VS.NET IDE: 15. Virtual CD-ROM Control Panel - ISO 이미지를 CD-ROM 드라이브처럼 접근하게 해주는 EXE 프로그램 [1]파일 다운로드1
76정성태12/27/200421545VS.NET IDE: 14. VPN 접속시 IP를 고정적으로 할당받는 방법 [1]
75정성태12/27/200417785VS.NET IDE: 13. VS.NET 2005 Beta 1 - Portfolio Explorer 에 등록된 Team Server 항목 삭제 방법
84정성태1/19/200518669    답변글 VS.NET IDE: 13.1. VS.NET 2005 Beta 1 : Team Server 에 등록된 포트폴리오 프로젝트 삭제 방법
74정성태12/26/200419290VS.NET IDE: 12. [시나리오] VS.NET 2005 Team Foundation Server을 Virtual Server에 설치 [1]
80정성태12/31/200418567    답변글 VS.NET IDE: 12.1. Client Tier, 즉 VS.NET 2005가 설치된 컴퓨터도 ActiveDirectory에 참여를 해야 합니다.
81정성태12/31/200420489    답변글 VS.NET IDE: 12.2. Tier 컴퓨터를 모두 영문으로 재구성
109정성태3/4/200515686    답변글 VS.NET IDE: 12.3. [보완] MS 공식 아티클 - Installing the December CTP Release of Visual Studio Team System
73정성태11/14/200517517.NET Framework: 19. VS.NET 2005 Team Foundation Server 설치오류 - 26204 예외
72정성태12/26/200418960.NET Framework: 18. .NET Framework 2.0 Beta 설치 후에 Windows SharePoint Service 오류 [1]
136정성태3/31/200518839    답변글 .NET Framework: 18.1. Windows Sharepoint Services 를 설치한 이후 ASP.NET 오류 문제
71정성태12/26/200417183VS.NET IDE: 11. SQL Server 2005 Beta 2 를 네트워크 드라이브로부터 설치시 오류
70정성태12/26/200420018VS.NET IDE: 10. WSS 설치 후 localhost 접근 보안 오류
69정성태12/5/200417100VS.NET IDE: 9. 다른 컴퓨터(방화벽 설치)에 설치된 SQL Server에 통합 인증을 할 때 필요한 포트
... 181  182  183  184  185  186  187  188  189  190  191  192  193  [194]  195  ...