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

비밀번호

댓글 작성자
 




1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13527정성태1/14/20241960오류 유형: 892. Visual Studio - Failed to launch debug adapter. Additional information may be available in the output window.
13526정성태1/14/20242051닷넷: 2201. C# - Facebook 연동 / 사용자 탈퇴 처리 방법
13525정성태1/13/20242020오류 유형: 891. Visual Studio - Web Application을 실행하지 못하는 IISExpress
13524정성태1/12/20242066오류 유형: 890. 한국투자증권 KIS Developers OpenAPI - GW라우팅 중 오류가 발생했습니다.
13523정성태1/12/20241888오류 유형: 889. Visual Studio - error : A project with that name is already opened in the solution.
13522정성태1/11/20242028닷넷: 2200. C# - HttpClient.PostAsJsonAsync 호출 시 "Transfer-Encoding: chunked" 대신 "Content-Length" 헤더 처리
13521정성태1/11/20242110닷넷: 2199. C# - 한국투자증권 KIS Developers OpenAPI의 WebSocket Ping, Pong 처리
13520정성태1/10/20241858오류 유형: 888. C# - Unable to resolve service for type 'Microsoft.Extensions.ObjectPool.ObjectPool`....'
13519정성태1/10/20241937닷넷: 2198. C# - Reflection을 이용한 ClientWebSocket의 Ping 호출파일 다운로드1
13518정성태1/9/20242174닷넷: 2197. C# - ClientWebSocket의 Ping, Pong 처리
13517정성태1/8/20242017스크립트: 63. Python - 공개 패키지를 이용한 위성 이미지 생성 (pystac_client, odc.stac)
13516정성태1/7/20242103닷넷: 2196. IIS - AppPool의 "Disable Overlapped Recycle" 옵션의 부작용
13515정성태1/6/20242376닷넷: 2195. async 메서드 내에서 C# 7의 discard 구문 활용 사례 [1]
13514정성태1/5/20242065개발 환경 구성: 702. IIS - AppPool의 "Disable Overlapped Recycle" 옵션
13513정성태1/5/20242005닷넷: 2194. C# - WebActivatorEx / System.Web의 PreApplicationStartMethod 특성
13512정성태1/4/20241979개발 환경 구성: 701. IIS - w3wp.exe 프로세스의 ASP.NET 런타임을 항상 Warmup 모드로 유지하는 preload Enabled 설정
13511정성태1/4/20241995닷넷: 2193. C# - ASP.NET Web Application + OpenAPI(Swashbuckle) 스펙 제공
13510정성태1/3/20241937닷넷: 2192. C# - 특정 실행 파일이 있는지 확인하는 방법 (Linux)
13509정성태1/3/20241966오류 유형: 887. .NET Core 2 이하의 프로젝트에서 System.Runtime.CompilerServices.Unsafe doesn't support netcoreapp2.0.
13508정성태1/3/20242016오류 유형: 886. ORA-28000: the account is locked
13507정성태1/2/20242700닷넷: 2191. C# - IPGlobalProperties를 이용해 netstat처럼 사용 중인 Socket 목록 구하는 방법파일 다운로드1
13506정성태12/29/20232191닷넷: 2190. C# - 닷넷 코어/5+에서 달라지는 System.Text.Encoding 지원
13505정성태12/27/20232704닷넷: 2189. C# - WebSocket 클라이언트를 닷넷으로 구현하는 예제 (System.Net.WebSockets)파일 다운로드1
13504정성태12/27/20232322닷넷: 2188. C# - ASP.NET Core SignalR로 구현하는 채팅 서비스 예제파일 다운로드1
13503정성태12/27/20232191Linux: 67. WSL 환경 + mlocate(locate) 도구의 /mnt 디렉터리 검색 문제
13502정성태12/26/20232298닷넷: 2187. C# - 다른 프로세스의 환경변수 읽는 예제파일 다운로드1
1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...