성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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)를 이용한 비디오 인코딩 예제(encode_video.c)</h1> <p> 제가 ffmpeg 라이브러리는 처음이라, 아직은 뭘 만들지는 못하고 ^^ 그저 만들어진 소스 코드가 있으면 따라 하는 수준으로 가능합니다.<br /> <br /> 그런데, ffmpeg 라이브러리의 하위 호환성이... 정말 좋지 않습니다. 실제로 웹에 퍼진 블로그의 자료들을 참고하는 경우 현재 FFmpeg.AutoGen이 지원하는 4.4.1 버전으로는 빌드되는 사례가 거의 없습니다. (그렇게 보면, 제가 지금 쓰는 글 역시 나중에는 deprecated 함수의 사용 예에 불과할 수 있다는 점!)<br /> <br /> 따라서, 블로그 등에서 ffmpeg 사용법을 찾기보다는 공식 사이트에서 제공하는 예제를 보는 것이 더 좋습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > FFmpeg - Examples ; <a target='tab' href='https://ffmpeg.org/doxygen/trunk/examples.html'>https://ffmpeg.org/doxygen/trunk/examples.html</a> </pre> <br /> 물론 ^^ 저 모든 예제를 포팅하진 못하고 그중에서 encode_video.c 파일만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > decoding_encoding.c ; <a target='tab' href='https://ffmpeg.org/doxygen/trunk/encode_video_8c-example.html'>https://ffmpeg.org/doxygen/trunk/encode_video_8c-example.html</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;' > using FFmpeg.AutoGen; using FFmpeg.AutoGen.Example; using System; using System.IO; namespace FFmpegApp1 { internal class Program { static void 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(); video_encode_example("C:\\temp\\test.h264", AVCodecID.AV_CODEC_ID_H264); } static unsafe void encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt, BinaryWriter output) { int ret; /* send the frame to the encoder */ if (frame != null) { Console.WriteLine($"Send frame {frame->pts}"); } ret = ffmpeg.avcodec_send_frame(enc_ctx, frame); if (ret < 0) { Console.WriteLine("Error sending a frame for encoding"); return; } while (ret >= 0) { ret = ffmpeg.avcodec_receive_packet(enc_ctx, pkt); if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF) { return; } else if (ret < 0) { Console.WriteLine("Error during encoding"); return; } Console.WriteLine($"Write packet {pkt->pts} (size={pkt->size})"); output.Write(new ReadOnlySpan<byte>(pkt->data, pkt->size)); ffmpeg.av_packet_unref(pkt); } } static unsafe void video_encode_example(string filename, AVCodecID codec_id) { AVCodec* _pCodec = null; AVCodecContext* _pCodecContext = null; AVFrame* frame = null; AVPacket* pkt = null; byte[] end_code = new byte[] { 0, 0, 1, 0xb7 }; using BinaryWriter output = new BinaryWriter(new FileStream(filename, FileMode.Create)); do { Console.WriteLine($"Encode video file {filename}"); _pCodec = ffmpeg.avcodec_find_encoder(codec_id); if (_pCodec == null) { Console.WriteLine($"Codec not found: {codec_id}"); break; } _pCodecContext = ffmpeg.avcodec_alloc_context3(_pCodec); if (_pCodecContext == null) { Console.WriteLine($"Could not allocate video codec context: {codec_id}"); break; } pkt = ffmpeg.av_packet_alloc(); if (pkt == null) { break; } _pCodecContext->bit_rate = 400000; _pCodecContext->width = 352; _pCodecContext->height = 288; /* frames per second */ _pCodecContext->time_base = new AVRational { num = 1, den = 25 }; _pCodecContext->framerate = new AVRational { num = 25, den = 1 }; _pCodecContext->gop_size = 10; /* emit one intra frame every ten frames */ _pCodecContext->max_b_frames = 1; _pCodecContext->pix_fmt = AVPixelFormat.AV_PIX_FMT_YUV420P; if (codec_id == AVCodecID.AV_CODEC_ID_H264) { ffmpeg.av_opt_set(_pCodecContext->priv_data, "preset", "slow", 0); } /* open it */ if (ffmpeg.avcodec_open2(_pCodecContext, _pCodec, null) < 0) { Console.WriteLine("Could not open codec"); break; } frame = ffmpeg.av_frame_alloc(); if (frame == null) { Console.WriteLine("Could not allocate video frame"); break; } frame->format = (int)_pCodecContext->pix_fmt; frame->width = _pCodecContext->width; frame->height = _pCodecContext->height; int ret = ffmpeg.av_frame_get_buffer(frame, 0); if (ret < 0) { Console.WriteLine("Could not allocate raw picture buffer"); break; } int x = 0; int y = 0; for (int i = 0; i < 25 * 10; i++) { ret = ffmpeg.av_frame_make_writable(frame); if (ret < 0) { break; } for (y = 0; y < _pCodecContext->height; y++) { for (x = 0; x < _pCodecContext->width; x++) { frame->data[0][y * frame->linesize[0] + x] = (byte)(x + y + i * 3); } } /* Cb and Cr */ for (y = 0; y < _pCodecContext->height / 2; y++) { for (x = 0; x < _pCodecContext->width / 2; x++) { frame->data[1][y * frame->linesize[1] + x] = (byte)(128 + y + i * 2); frame->data[2][y * frame->linesize[2] + x] = (byte)(64 + x + i * 5); } } frame->pts = i; encode(_pCodecContext, frame, pkt, output); } // flush the encoder encode(_pCodecContext, null, pkt, output); } while (false); if (_pCodec->id == AVCodecID.AV_CODEC_ID_MPEG1VIDEO || _pCodec->id == AVCodecID.AV_CODEC_ID_MPEG2VIDEO) { output.Write(end_code, 0, end_code.Length); } if (frame != null) { ffmpeg.av_frame_free(&frame); } if (pkt != null) { ffmpeg.av_packet_free(&pkt); } if (_pCodecContext != null) { ffmpeg.avcodec_free_context(&_pCodecContext); } } } } </pre> <br /> video_encode_example 메서드의 중간에 나오는 for 루프는 25fps * 10초만큼의 비디오 영상을 YUV420P 포맷으로 직접 생성해, encode 메서드에서 AV_CODEC_ID_H264 코덱으로 파일에 쓰고 있습니다.<br /> <br /> 마지막으로 위의 코드로 생성된 "C:\\temp\\test.h264" 파일을 재생하면 다음과 같은 식의 영상이 출력되는 것을 확인할 수 있습니다.<br /> <br /> <img alt='encode_video_sample_1.png' src='/SysWebRes/bbs/encode_video_sample_1.png' /><br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1881&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다. 단지, 압축 파일 크기를 줄이기 위해 ./FFmpeg/bin/x64 하위의 DLL은 제거했습니다.)<br /> (<a target='tab' href='https://github.com/stjeong/ffmpeg_autogen_cs/tree/master/encode_video'>이 글의 소스 코드는 github에 올려</a>져 있습니다.) </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
6066
(왼쪽의 숫자를 입력해야 합니다.)