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)
1277정성태5/8/201231234오류 유형: 152. cmd.exe - The system cannot write to the specified device. [2]
1276정성태4/28/201223092Phone: 5. 모든 Marketplace에 윈폰 앱을 등록하는 방법 [1]
1275정성태4/28/201226939개발 환경 구성: 150. 프로세스 실행으로 잠긴 파일이지만, 이름은 변경가능하다는 사실! 아셨나요? [7]
1274정성태4/17/201221482Phone: 4. "Holiday Calendar" 윈폰 응용 프로그램 등록
1273정성태4/6/201224696Phone: 3. 윈도우 폰을 위한 Holiyday Calendar 앱 개발파일 다운로드1
1272정성태4/5/201226252오류 유형: 151. ASP.NET - EcbGetUnicodeServerVariables 코드에서 System.AccessViolationException 예외 발생
1271정성태4/3/201228936Math: 6. 동전을 여러 더미로 나누는 경우의 수 세기 [1]
1270정성태3/29/201222865오류 유형: 150. Visual Studio 2010 원격 디버깅 오류 - Kerberos authentication failed
1269정성태3/27/201236702오류 유형: 149. ODP.NET 오류 - The provider is not compatible with the version of Oracle client
1268정성태3/27/201233207오류 유형: 148. WCF svc 호출 시 HTTP Error 404.17 - Not Found [1]
1267정성태3/16/201231169.NET Framework: 314. C++의 inline asm 사용을 .NET으로 포팅하는 방법 [1]파일 다운로드1
1266정성태3/14/201234390개발 환경 구성: 149. RAID 1 구성 시 하드 디스크 장애 발생 해결에 대한 경험담
1265정성태3/13/201224691VC++: 61. 아이태니엄(IA64: Itanium) 에서 겪은 C++ 포인터 연산 문제 [2]
1264정성태3/10/201244095.NET Framework: 313. WELL512 난수 발생 알고리즘 - C# [5]파일 다운로드1
1263정성태3/9/201222886개발 환경 구성: 148. tinyget 사용법
1262정성태3/8/201243759개발 환경 구성: 147. .keystore 파일에 저장된 개인키 추출 방법과 인증기관으로부터 온 공개키를 합친 pfx 파일 만드는 방법 [1]
1261정성태3/7/201224467Phone: 2. 개발자용 윈도우 폰 7 기기 등록하는 방법
1260정성태3/6/201224304Phone: 1. 윈도폰 7 개발자 (회사) 등록하는 방법 [3]
1259정성태3/4/201235814Windows: 57. 새로 추가된 네트워크 커널 디버깅 및 PowerShell 3.0 [1]
1258정성태3/3/201237438개발 환경 구성: 146. SQL Server 2012에 포함된 LocalDB 기능 소개 [3]파일 다운로드1
1257정성태3/3/201225637.NET Framework: 312. Native 스레드와 Managed 스레드 개체의 상태 관계 [1]파일 다운로드1
1256정성태3/3/201229207Math: 5. Euler's totient function - C#파일 다운로드1
1255정성태3/3/201231563Math: 4. 소수 판정 및 소인수 분해 소스 코드 - C# [1]파일 다운로드1
1254정성태3/1/201226530Windows: 56. Windows 8 Consumer Preview를 사용해 보고... [1]
1253정성태3/1/201227993VS.NET IDE: 71. Visual Studio 11 Ultimate 베타 설치 [3]
1252정성태3/1/201225344Windows: 55. 윈도우 8 베타 설치 과정 [1]
... [151]  152  153  154  155  156  157  158  159  160  161  162  163  164  165  ...