성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
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# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) - 두 번째 이야기</h1> <p> 예전 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12924'>https://www.sysnet.pe.kr/2/0/12924</a> </pre> <br /> 비디오 프레임을 디코딩 후 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12912'>PGM 포맷</a>으로 저장한 데이터가 정상적으로 보이지 않는다고 했는데요, 저도 이젠 나름 ^^ 지식이 쌓이다 보니 해당 문제를 다시 살펴볼 여유가 생겼습니다. <br /> <br /> 일단, 디코딩이 잘 되는지 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/12958'>C# - ffmpeg(FFmpeg.AutoGen) - 비디오 프레임 디코딩</a>" 글에서처럼 YUV 데이터를 RGB로 변환해 저장해 보았는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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")); } } } </pre> <br /> 위의 소스 코드를 실행해 보면, BMP 파일의 출력 결과가 깨져 나옵니다.<br /> <br /> <img alt='mepg1video_yuv_img_1.png' src='/SysWebRes/bbs/mepg1video_yuv_img_1.png' /><br /> <br /> 즉, 애당초 해당 코드는 정상적으로 video frame을 생성하지 못 하고 있었던 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그래서 일단 decode_video.c가 av_parser_parse2를 이용해 디코딩하는 것을 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/12956'>C# - ffmpeg(FFmpeg.AutoGen) - decoding 과정</a>" 글에서처럼 av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로 바꿨습니다.<br /> <br /> 동일하게 mpeg1video 코덱이 선택되었고, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { 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)); <span style='color: blue; font-weight: bold'>Console.WriteLine(codecName); // mpeg1video</span> </pre> <br /> YUV420P 프레임을 정상적으로 해석해 BMP 파일로도 저장이 되었습니다. 그렇기 때문에 Y(Luma channel) 값을 pgm 파일로 저장하는 코드 역시 정상적으로 gray 포맷으로 출력이 되었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PGM File Viewer (browser-based) ; <a target='tab' href='https://smallpond.ca/jim/photomicrography/pgmViewer/index.html'>https://smallpond.ca/jim/photomicrography/pgmViewer/index.html</a> </pre> <br /> (사진은 <a target='tab' href='https://www.youtube.com/watch?v=xjWYhCac0XU'>유튜브 영상 "디에이드"의 "안다은" 님</a>이고 사용을 허락받고 올립니다.)<br /> <img onclick='toggle_img(this)' class='imgView' alt='mepg1video_yuv_img_2.png' src='/SysWebRes/bbs/mepg1video_yuv_img_2.png' /><br /> <br /> 그러니까 애당초 decode_video.c가 정상적으로 동작하지 않았던 코드였습니다. ^^;<br /> <br /> <hr style='width: 50%' /><br /> <a name='gray8'></a> <br /> 참고로, 프레임을 명시적으로 AV_PIX_FMT_GRAY8로 변환해도 됩니다. 이를 위해 YUV to GRAY 변경을 SwsContext를 이용해 다음과 같이 수행할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 = <span style='color: blue; font-weight: bold'>ffmpeg.sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, ppgray8, gray_stride);</span> // 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"); <span style='color: blue; font-weight: bold'>pgm_save(pgray8, frame->width, frame->width, frame->height, outputFile);</span> ffmpeg.av_free(pgray8); ffmpeg.sws_freeContext(sws_ctx); </pre> <br /> 위에서는 pgm_save에 frame->data[0]을 전달하지 않고, sws_scale로 변환된 pgray8 포인터를 전달하고 있습니다. 당연히 저렇게 출력한 pgm 파일도 Y 채널만 출력한 pgm 파일과 동일한 흑백 영상이 나옵니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1898&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로는 비디오가 잘 해석이 되는데, av_parser_parse2를 이용한 버전은 왜 안 되는 것일까요? 음... 이 정도까지는 아직 제 수준에서 이해할 수가 없군요. ^^<br /> <br /> decode_video.c 파일의 버그인지, 아니면 원래 저렇게는 안 되는 것인지 알 수가 없습니다.<br /> <br /> av_parser_parse2가 잘 동작하는 코드가 있긴 했습니다. 바로 오디오 파일을 디코딩하는 경우입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP2 오디오 파일 디코딩 예제(decode_audio.c) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12933'>https://www.sysnet.pe.kr/2/0/12933</a> C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP3 오디오 파일 인코딩/디코딩하는 예제 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12939'>https://www.sysnet.pe.kr/2/0/12939</a> </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;' > MediaPlayer/MediaPlayer/FFmpegDecoder.cpp ; <a target='tab' href='https://github.com/xc724655471/MediaPlayer/blob/13f0f518fd4e9e0ecb763ca1b23bf12bbd1ab249/MediaPlayer/FFmpegDecoder.cpp'>https://github.com/xc724655471/MediaPlayer/blob/13f0f518fd4e9e0ecb763ca1b23bf12bbd1ab249/MediaPlayer/FFmpegDecoder.cpp</a> </pre> <br /> 분명히 디코딩 과정은 잘 동작했을 것으로 추정은 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 혹시나 싶어서, gray 포맷의 (<a target='tab' href='https://www.sysnet.pe.kr/2/0/12957#mpeg1video'>mpeg1video는 오직 yuv420p만 지원하므로</a>) h264 동영상을 전달하면 잘 동작할까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > D:\media_sample> <span style='color: blue; font-weight: bold'>ffmpeg -i mp4video_sample2.mp4 -pix_fmt gray mp4video_sample2_gray.mp4</span> </pre> <br /> gray mp4 파일을 decode_video.c의 입력으로 전달하고 codec id만 AVCodecID.AV_CODEC_ID_H264로 바꿔보았는데요, 그래도 여전히 정상적인 비디오 프레임이 나오지는 않았습니다. 어쩌면 저 작업이 굳이 필요하지도 않았는데요, 왜냐하면 pix_fmt을 gray로 지정하긴 했지만 ffprobe로 확인해 보면 mp4video_sample2_gray.mp4는 여전히 yuv420p로 나오기 때문입니다. 즉, 영상만 흑백이고 pix_fmt은 변함없이 YUV420P입니다.<br /> <br /> 비록 pgm 저장은 우회해서 성공했지만, decode_vide.c 소스 코드는 끝내 미스터리로 남는군요. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1824
(왼쪽의 숫자를 입력해야 합니다.)