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)를 이용한 오디오 필터 예제(filter_audio.c)

지난 예제에 이어,

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

이번에는 ffmpeg 예제 중 "filter_audio.c" 파일을 FFmpeg.AutoGen으로 포팅하겠습니다.

using FFmpeg.AutoGen;
using FFmpeg.AutoGen.Example;
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

namespace FFmpegApp1
{
    internal unsafe class Program
    {
        const int INPUT_SAMPLERATE = 48000;
        const AVSampleFormat INPUT_FORMAT = AVSampleFormat.AV_SAMPLE_FMT_FLTP;
        const int INPUT_CHANNEL_LAYOUT = ffmpeg.AV_CH_LAYOUT_5POINT0;
        const float VOLUME_VAL = 0.90f;
        const int FRAME_SIZE = 1024;

        static 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();

            AVFilterGraph* graph;
            AVFilterContext* src;
            AVFilterContext* sink;
            AVFrame* frame;
            byte* errstr = stackalloc byte[1024];
            float duration;
            int err, nb_frames, i;

            duration = 20.0f;

            nb_frames = (int)(duration * INPUT_SAMPLERATE / FRAME_SIZE);
            if (nb_frames <= 0)
            {
                Console.WriteLine($"Invalid duration: {duration}");
                return 1;
            }

            frame = ffmpeg.av_frame_alloc();
            if (frame == null)
            {
                Console.WriteLine("Error allocating the frame");
                return 1;
            }

            do
            {
                err = init_filter_graph(&graph, &src, &sink);
                if (err < 0)
                {
                    Console.WriteLine("Unable to init filter graph:");
                    break;
                }

                for (i = 0; i < nb_frames; i++)
                {
                    err = get_input(frame, i);
                    if (err < 0)
                    {
                        Console.WriteLine("Error generating input frame:");
                        break;
                    }

                    err = ffmpeg.av_buffersrc_add_frame(src, frame);
                    if (err < 0)
                    {
                        ffmpeg.av_frame_unref(frame);
                        Console.WriteLine("Error submitting the frame to the filtergraph:");
                        break;
                    }

                    MD5 md5 = MD5.Create();

                    while ((err = ffmpeg.av_buffersink_get_frame(sink, frame)) >= 0)
                    {
                        err = process_output(md5, frame);
                        if (err < 0)
                        {
                            Console.WriteLine("Error processing the filtered frame:");
                            break;
                        }

                        ffmpeg.av_frame_unref(frame);
                    }

                    if (err == ffmpeg.AVERROR(ffmpeg.EAGAIN))
                    {
                        continue;
                    }
                    else if (err == ffmpeg.AVERROR_EOF)
                    {
                        break;
                    }
                    else if (err < 0)
                    {
                        Console.WriteLine("Error filtering the data:");
                        break;
                    }
                }

            } while (false);

            if (graph != null)
            {
                ffmpeg.avfilter_graph_free(&graph);
            }

            if (frame != null)
            {
                ffmpeg.av_frame_free(&frame);
            }

            if (err < 0 && err != ffmpeg.AVERROR(ffmpeg.EAGAIN))
            {
                ffmpeg.av_strerror(err, errstr, 1024);

                string result = Marshal.PtrToStringAnsi(new IntPtr(errstr), getStringSize(errstr, 1024));
                Console.WriteLine(result);
                return 1;
            }

            return 0;
        }


        private static unsafe int init_filter_graph(AVFilterGraph** graph, AVFilterContext** src, AVFilterContext** sink)
        {
            AVFilterGraph* filter_graph;
            AVFilterContext* abuffer_ctx = null;
            AVFilter* abuffer = null;
            AVFilterContext* volume_ctx = null;
            AVFilter* volume = null;
            AVFilterContext* aformat_ctx = null;
            AVFilter* aformat = null;
            AVFilterContext* abuffersink_ctx = null;
            AVFilter* abuffersink = null;

            AVDictionary* options_dict = null;
            byte* ch_layout = stackalloc byte[64];

            int err = 0;

            filter_graph = ffmpeg.avfilter_graph_alloc();
            if (filter_graph == null)
            {
                Console.WriteLine("Unable to create filter graph.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }


            abuffer = ffmpeg.avfilter_get_by_name("abuffer");
            if (abuffer == null)
            {
                Console.WriteLine("Could not find the abuffer filter.");
                return ffmpeg.AVERROR_FILTER_NOT_FOUND;
            }

            abuffer_ctx = ffmpeg.avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
            if (abuffer_ctx == null)
            {
                Console.WriteLine("Could not allocate the abuffer instance.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            ffmpeg.av_get_channel_layout_string(ch_layout, 64, 0, INPUT_CHANNEL_LAYOUT);

            string ch_layout_value = Marshal.PtrToStringAnsi(new IntPtr(ch_layout), getStringSize(ch_layout, 64));
            err = ffmpeg.av_opt_set(abuffer_ctx, "channel_layout", ch_layout_value, ffmpeg.AV_OPT_SEARCH_CHILDREN);
            err = ffmpeg.av_opt_set(abuffer_ctx, "sample_fmt", ffmpeg.av_get_sample_fmt_name(INPUT_FORMAT), ffmpeg.AV_OPT_SEARCH_CHILDREN);
            err = ffmpeg.av_opt_set_q(abuffer_ctx, "time_base", new AVRational { num = 1, den = INPUT_SAMPLERATE }, ffmpeg.AV_OPT_SEARCH_CHILDREN);
            err = ffmpeg.av_opt_set_int(abuffer_ctx, "sample_rate", INPUT_SAMPLERATE, ffmpeg.AV_OPT_SEARCH_CHILDREN);

            err = ffmpeg.avfilter_init_str(abuffer_ctx, null);
            if (err < 0)
            {
                Console.WriteLine("Could not initialze the abuffer filter.");
                return err;
            }

            volume = ffmpeg.avfilter_get_by_name("volume");
            if (volume == null)
            {
                Console.WriteLine("Could not find the volume filter.");
                return ffmpeg.AVERROR_FILTER_NOT_FOUND;
            }

            volume_ctx = ffmpeg.avfilter_graph_alloc_filter(filter_graph, volume, "volume");
            if (volume_ctx == null)
            {
                Console.WriteLine("Could not allocate the volume instance.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            ffmpeg.av_dict_set(&options_dict, "volume", $"{VOLUME_VAL:F}", 0);
            err = ffmpeg.avfilter_init_dict(volume_ctx, &options_dict);
            ffmpeg.av_dict_free(&options_dict);
            if (err < 0)
            {
                Console.WriteLine("Could not initialze the volume filter.");
                return err;
            }

            aformat = ffmpeg.avfilter_get_by_name("aformat");
            if (aformat == null)
            {
                Console.WriteLine("Could not find the aformat filter.");
                return ffmpeg.AVERROR_FILTER_NOT_FOUND;
            }

            aformat_ctx = ffmpeg.avfilter_graph_alloc_filter(filter_graph, aformat, "aformat");
            if (aformat_ctx == null)
            {
                Console.WriteLine("Could not allocate the aformat instance.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            string options_str = $"sample_fmts={ffmpeg.av_get_sample_fmt_name(AVSampleFormat.AV_SAMPLE_FMT_S16)}:sample_rates=44100:channel_layouts=0x{ffmpeg.AV_CH_LAYOUT_STEREO:x}";
            err = ffmpeg.avfilter_init_str(aformat_ctx, options_str);
            if (err < 0)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Could not initialize the aformat filter.");
                return err;
            }

            abuffersink = ffmpeg.avfilter_get_by_name("abuffersink");
            if (abuffersink == null)
            {
                Console.WriteLine("Could not allocate the abuffersink instance.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            abuffersink_ctx = ffmpeg.avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
            if (abuffersink_ctx == null)
            {
                Console.WriteLine("Could not initialize the abuffersink instance.");
                return err;
            }

            err = ffmpeg.avfilter_init_str(abuffersink_ctx, null);
            if (err < 0)
            {
                Console.WriteLine("Could not initialize the abuffersink instance.");
                return err;
            }

            err = ffmpeg.avfilter_link(abuffer_ctx, 0, volume_ctx, 0);
            if (err >= 0)
            {
                err = ffmpeg.avfilter_link(volume_ctx, 0, aformat_ctx, 0);
            }

            if (err >= 0)
            {
                err = ffmpeg.avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0);
            }

            if (err < 0)
            {
                Console.WriteLine("Error connecting filters.");
                return err;
            }

            err = ffmpeg.avfilter_graph_config(filter_graph, null);
            if (err < 0)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Error configuring the filter graph");
                return err;
            }

            *graph = filter_graph;
            *src = abuffer_ctx;
            *sink = abuffersink_ctx;

            return 0;
        }

        private static int getStringSize(byte* buffer, int length)
        {
            for (int i = 0; i < length; i++)
            {
                if (buffer[i] == 0)
                {
                    return i;
                }
            }

            return 0;
        }

        static unsafe int process_output(MD5 md5, AVFrame* frame)
        {
            int planar = ffmpeg.av_sample_fmt_is_planar((AVSampleFormat)frame->format);
            int channels = ffmpeg.av_get_channel_layout_nb_channels(frame->channel_layout);
            int planes = planar != 0 ? channels : 1;
            int bps = ffmpeg.av_get_bytes_per_sample((AVSampleFormat)frame->format);
            int plane_size = bps * frame->nb_samples * (planar != 0 ? 1 : channels);
            int i;

            for (i = 0; i < planes; i++)
            {
                Span<byte> buffer = new Span<byte>(frame->extended_data[i], plane_size);
                byte[] computed = md5.ComputeHash(buffer.ToArray()); // ToArray: copy overhead

                Console.WriteLine(BitConverter.ToString(computed).Replace("-", ""));
            }
            
            return 0;
        }

        static unsafe int get_input(AVFrame* frame, int frame_num)
        {
            int err, i, j;

            frame->sample_rate = INPUT_SAMPLERATE;
            frame->format = (int)INPUT_FORMAT;
            frame->channel_layout = INPUT_CHANNEL_LAYOUT;
            frame->nb_samples = FRAME_SIZE;
            frame->pts = frame_num * FRAME_SIZE;

            err = ffmpeg.av_frame_get_buffer(frame, 0);
            if (err < 0)
            {
                return err;
            }

            for (i = 0; i < 5; i++)
            {
                float* data = (float*)frame->extended_data[i];

                for (j = 0; j < frame->nb_samples; j++)
                {
                    data[j] = (float)Math.Sin(2 * Math.PI * (frame_num + j) * (i + 1) / FRAME_SIZE);
                }
            }

            return 0;
        }
    }
}

위의 코드를 실행하면 get_input으로 사인파 데이터를 생성해 필터를 통과하면서 process_output에 의해 md5 checksum 데이터를 출력합니다.

아직 제 수준에서는 이런 예제까지는 필요 없는 것 같습니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)
(이 글의 소스 코드는 github에 올려져 있습니다.)




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







[최초 등록일: ]
[최종 수정일: 3/13/2022]

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

비밀번호

댓글 작성자
 




... 151  152  153  154  155  156  157  158  159  160  161  162  163  [164]  165  ...
NoWriterDateCnt.TitleFile(s)
945정성태11/11/201025110VC++: 44. C++/CLI 컴파일 오류 - error C4368: mixed types are not supported
944정성태11/11/201031655VC++: 43. C++/CLI 컴파일 오류 - error C2872: 'IServiceProvider' : ambiguous symbol could be ...
943정성태11/8/201030765디버깅 기술: 30. windbg ".loadby sos" 명령어 [2]
942정성태11/7/201042348.NET Framework: 187. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 [7]파일 다운로드3
941정성태11/6/201025256.NET Framework: 186. windbg로 확인하는 .NET CLR LCG 메서드(DynamicMethod) [1]파일 다운로드1
940정성태11/6/201026134.NET Framework: 185. windbg로 확인하는 .NET CLR 메서드파일 다운로드1
939정성태10/24/201033080Windows: 51. RDP로 접속한 Windows Server 2008 R2 환경에서 Aero Glass 활성화 [1]
938정성태10/23/201024830디버깅 기술: 29. Windbg - Hyper-V 윈도우 7 원격 디버깅 구성 [1]
937정성태10/22/201030969DDK: 5. NT Legacy 드라이버: 프로세스(EXE) 생성/제거 모니터링 [3]파일 다운로드1
936정성태10/21/201029999DDK: 4. Device Driver 응용 프로그램의 빌드 스크립트 - 두 번째 이야기 [2]파일 다운로드1
935정성태10/17/201030618디버깅 기술: 28. Windbg - 윈도우 핸들 테이블 [3]
934정성태10/11/201033618디버깅 기술: 27. Windbg - Local Kernel Debug 모드 [2]
933정성태10/10/201025807.NET Framework: 184. 닷넷에서 호출 스택의 메서드에 대한 인자 값 확인이 가능할까? [2]파일 다운로드1
932정성태10/10/201029037DDK: 3. NT Legacy 드라이버를 이용하여 C#에서 Port 입출력파일 다운로드1
931정성태9/30/201023615오류 유형: 108. Net.Tcp Listener Adapter 서비스 시작 실패
930정성태9/30/201022923웹: 16. 윈도우 미디어 플레이어 - 일시 정지/시작을 스크립트에서 감지
929정성태9/17/201024143웹: 15. IE 9 - 작업 표시줄의 웹 사이트 바로가기 사용자 정의 - JumpLists [3]파일 다운로드1
928정성태9/16/201028316VC++: 42. 쓰기 전용 파일(예: 로그 파일)의 동기화 방법파일 다운로드1
927정성태9/14/201038189VC++: 41. UTF-8 포맷의 INI 파일에 대한 GetPrivateProfile... API 사용 불가 [2]
926정성태9/9/201027453Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [1]파일 다운로드1
925정성태9/8/201031356Team Foundation Server: 40. Visual Studio 2010 - Code Coverage 결과를 외부 XML 파일로 출력하는 명령행 도구 제작 [1]파일 다운로드1
924정성태9/6/201021608개발 환경 구성: 88. SCVMM이 설치된 도메인에 참여하지 않은 Hyper-V 호스트 추가
923정성태9/5/201027599오류 유형: 107. SCVMM Agent 설치 오류 - Failed to configure the WS-Management service.
922정성태9/4/201036276오류 유형: 106. Hyper-V 가상 머신의 네트워크 끊김 현상
921정성태9/2/201030294DDK: 2. Device Driver 응용 프로그램의 빌드 스크립트 [2]파일 다운로드1
920정성태9/1/201034748오류 유형: 105. WMI - The RPC server is unavailable [2]
... 151  152  153  154  155  156  157  158  159  160  161  162  163  [164]  165  ...