성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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)를 이용한 비디오 프레임의 크기 및 포맷 변경 예제(scaling_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)를 이용한 오디오 필터 예제(filter_audio.c) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12952'>https://www.sysnet.pe.kr/2/0/12952</a> </pre> <a name='src'></a> <br /> 이번에는 <a target='tab' href='https://ffmpeg.org/doxygen/trunk/examples.html'>ffmpeg 예제</a> 중 "<a target='tab' href='https://ffmpeg.org/doxygen/trunk/scaling_video_8c-example.html'>scaling_video.c</a>" 파일을 FFmpeg.AutoGen으로 포팅하겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using FFmpeg.AutoGen; using FFmpeg.AutoGen.Example; using System; using System.IO; namespace FFmpegApp1 { internal unsafe class Program { static unsafe int Main(string[] args) { FFmpegBinariesHelper.RegisterFFmpegBinaries(); #if DEBUG Console.WriteLine("Current directory: " + Environment.CurrentDirectory); Console.WriteLine("Running in {0}-bit mode.", Environment.Is64BitProcess ? "64" : "32"); Console.WriteLine($"FFmpeg version info: {ffmpeg.av_version_info()}"); #endif Console.WriteLine(); byte_ptrArray4 src_data = new byte_ptrArray4(); byte_ptrArray4 dst_data = new byte_ptrArray4(); int_array4 src_linesize = new int_array4(); int_array4 dst_linesize = new int_array4(); int src_w = 320, src_h = 240, dst_w, dst_h; AVPixelFormat src_pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P, dst_pix_fmt = AVPixelFormat.AV_PIX_FMT_RGB24; string dst_size = null; string dst_filename = null; int dst_bufsize = 0; SwsContext* sws_ctx = null; int i, ret; dst_filename = @"c:\temp\output\test.mp4"; dst_size = "cif"; // 352x288 // dst_size = "hd1080"; // 1920x1080 if (VideoSizeAbbr.av_parse_video_size(&dst_w, &dst_h, dst_size) < 0) { Console.WriteLine($"Invalid size {dst_size}, must be in the form WxH or a valid abbreviation"); return 1; } using FileStream fs = File.Create(dst_filename); do { sws_ctx = ffmpeg.sws_getContext(src_w, src_h, src_pix_fmt, dst_w, dst_h, dst_pix_fmt, ffmpeg.SWS_BILINEAR, null, null, null); if (sws_ctx == null) { Console.WriteLine($"Impossible to create scale context for the conversion fmt:{ffmpeg.av_get_pix_fmt_name(src_pix_fmt)} s:{src_w}x{src_h} -> fmt:{ffmpeg.av_get_pix_fmt_name(dst_pix_fmt)} s:{dst_w}x{dst_h}"); ret = ffmpeg.AVERROR(ffmpeg.EINVAL); break; } if ((ret = ffmpeg.av_image_alloc(ref src_data, ref src_linesize, src_w, src_h, src_pix_fmt, 16)) < 0) { Console.WriteLine("Could not allocate source image"); break; } if ((ret = ffmpeg.av_image_alloc(ref dst_data, ref dst_linesize, dst_w, dst_h, dst_pix_fmt, 1)) < 0) { Console.WriteLine("Could not allocate destination image"); break; } dst_bufsize = ret; for (i = 0; i < 100; i ++) { fill_yuv_image(src_data, src_linesize, src_w, src_h, i); ffmpeg.sws_scale(sws_ctx, src_data, src_linesize, 0, src_h, dst_data, dst_linesize); ReadOnlySpan<byte> buffer = new ReadOnlySpan<byte>(dst_data[0], dst_bufsize); fs.Write(buffer); } Console.WriteLine("Scaling succeeded. Play the output file with the command:"); Console.WriteLine($"ffplay -f rawvideo -pix_fmt {ffmpeg.av_get_pix_fmt_name(dst_pix_fmt)} -video_size {dst_w}x{dst_h} {dst_filename}"); } while (false); if (src_data[0] != null) { ffmpeg.av_freep(&src_data); } if (dst_data[0] != null) { ffmpeg.av_freep(&dst_data); } if (sws_ctx != null) { ffmpeg.sws_freeContext(sws_ctx); } return 0; } static unsafe void fill_yuv_image(byte*[] data, int[] linesize, int width, int height, int frame_index) { int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { data[0][y * linesize[0] + x] = (byte)(x + y + frame_index * 3); } } for (y = 0; y < height / 2; y++) { for (x = 0; x < width / 2; x++) { data[1][y * linesize[1] + x] = (byte)(128 + y + frame_index * 2); data[2][y * linesize[2] + x] = (byte)(64 + x + frame_index * 5); } } } public class VideoSizeAbbr { string _abbr; public string Abbr => _abbr; int _width; public int Width => _width; int _height; public int Height => _height; public VideoSizeAbbr(string abbr, int width, int height) { _abbr = abbr; _width = width; _height = height; } public static VideoSizeAbbr[] Video_size_abbrs => _video_size_abbrs; static VideoSizeAbbr[] _video_size_abbrs = new VideoSizeAbbr[] { new ("ntsc", 720, 480), new ("pal", 720, 576 ), new ("qntsc", 352, 240 ), /* VCD compliant NTSC */ new ("qpal", 352, 288 ), /* VCD compliant PAL */ new ("sntsc", 640, 480 ), /* square pixel NTSC */ new ("spal", 768, 576 ), /* square pixel PAL */ new ("film", 352, 240 ), new ("ntsc-film", 352, 240 ), new ("sqcif", 128, 96 ), new ("qcif", 176, 144 ), new ("cif", 352, 288 ), new ("4cif", 704, 576 ), new ("16cif", 1408,1152 ), new ("qqvga", 160, 120 ), new ("qvga", 320, 240 ), new ("vga", 640, 480 ), new ("svga", 800, 600 ), new ("xga", 1024, 768 ), new ("uxga", 1600,1200 ), new ("qxga", 2048,1536 ), new ("sxga", 1280,1024 ), new ("qsxga", 2560,2048 ), new ("hsxga", 5120,4096 ), new ("wvga", 852, 480 ), new ("wxga", 1366, 768 ), new ("wsxga", 1600,1024 ), new ("wuxga", 1920,1200 ), new ("woxga", 2560,1600 ), new ("wqhd", 2560,1440 ), new ("wqsxga", 3200,2048 ), new ("wquxga", 3840,2400 ), new ("whsxga", 6400,4096 ), new ("whuxga", 7680,4800 ), new ("cga", 320, 200 ), new ("ega", 640, 350 ), new ("hd480", 852, 480 ), new ("hd720", 1280, 720 ), new ("hd1080", 1920,1080 ), new ("quadhd", 2560,1440 ), new ("2k", 2048,1080 ), /* Digital Cinema System Specification */ new ("2kdci", 2048,1080 ), new ("2kflat", 1998,1080 ), new ("2kscope", 2048, 858 ), new ("4k", 4096,2160 ), /* Digital Cinema System Specification */ new ("4kdci", 4096,2160 ), new ("4kflat", 3996,2160 ), new ("4kscope", 4096,1716 ), new ("nhd", 640,360 ), new ("hqvga", 240,160 ), new ("wqvga", 400,240 ), new ("fwqvga", 432,240 ), new ("hvga", 480,320 ), new ("qhd", 960,540 ), new ("uhd2160", 3840,2160 ), new ("uhd4320", 7680,4320 ), }; public static unsafe int av_parse_video_size(int* width_ptr, int* height_ptr, string str) { int i; int n = VideoSizeAbbr.Video_size_abbrs.Length; int width = 0; int height = 0; for (i = 0; i < n; i++) { if (VideoSizeAbbr.Video_size_abbrs[i].Abbr == str) { width = VideoSizeAbbr.Video_size_abbrs[i].Width; height = VideoSizeAbbr.Video_size_abbrs[i].Height; break; } } if (i == n) { string[] size = str.Split('x'); int.TryParse(size[0], out width); int.TryParse(size[1], out height); } if (width <= 0 || height <= 0) { return ffmpeg.AVERROR(ffmpeg.EINVAL); } *width_ptr = width; *height_ptr = height; return 0; } } } } </pre> <br /> 이번 포팅 예제는 이미 지난 글들에서 모두 다뤘기 때문에 그래도 눈에 잘 들어옵니다. ^^<br /> <br /> av_image_alloc으로 AVPixelFormat과 width, height에 따른 1개 프레임만큼의 메모리를 할당할 수 있습니다. 해당 영역에 fill_yuv_image를 이용해, YUV420P 포맷의 색을 채워 넣고 다시 sws_scale로 RGB24 포맷의 프레임으로 변환합니다.<br /> <br /> 그런 다음, 해당 RGB 데이터 이미지를 하나의 파일에 연속으로 쓰고 마지막에 어떻게 ffplay로 볼 수 있는지 방법을 알려줍니다. 가령, 352x288 크기의 이미지였다면 다음과 같이 재생할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>ffplay -f rawvideo -pix_fmt rgb24 -video_size 352x288 c:\temp\output\test.mp4</span> </pre> <br /> <img alt='scaling_video_1.png' src='/SysWebRes/bbs/scaling_video_1.png' /><br /> <br /> 마지막으로 아래의 예제에서는 YUV420P 포맷을 AV_PIX_FMT_GRAY8 포맷으로 변환했었습니다.<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/12959#gray8'>https://www.sysnet.pe.kr/2/0/12959#gray8</a> </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1899&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> (<a target='tab' href='https://github.com/stjeong/ffmpeg_autogen_cs/tree/master/scaling_video'>이 글의 소스 코드는 github에 올려</a>져 있습니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2104
(왼쪽의 숫자를 입력해야 합니다.)