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)를 이용한 demuxing_decoding.c 예제 포팅

지난 예제에 이어,

C# - ffmpeg(FFmpeg.AutoGen)를 이용한 filtering_video.c 예제 포팅
; https://www.sysnet.pe.kr/2/0/12984

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

using FFmpeg.AutoGen;
using FFmpeg.AutoGen.Example;
using System;
using System.IO;

namespace encode_video
{
    internal unsafe class Program
    {
        static AVFormatContext* fmt_ctx = null;
        static AVCodecContext* video_dec_ctx = null;
        static AVCodecContext* audio_dec_ctx = null;
        static int width, height;
        static AVPixelFormat pix_fmt;
        static AVStream* video_stream;
        static AVStream* audio_stream;
        static string src_filename = "";
        static string video_dst_filename = "";
        static string audio_dst_filename = "";
        static FileStream? video_dst_file;
        static FileStream? audio_dst_file;

        static byte_ptrArray4 video_dst_data;
        static int_array4 video_dst_linesize;
        static int video_dst_bufsize;

        static int video_stream_idx = -1;
        static int audio_stream_idx = -1;
        static AVFrame* frame = null;
        static AVPacket* pkt = null;
        static int video_Frame_count = 0;
        static int audio_frame_count = 0;

        static unsafe int output_video_frame(AVFrame* frame)
        {
            if (frame->width != width || frame->height != height || frame->format != (int)pix_fmt)
            {
                Console.WriteLine("Error: Width, height and pixel format have to be constant in an rawvideo file, but the width, height or pixel format of the input video changed:\n" +
                    $"old: width = {width}, height = {height}, format = {ffmpeg.av_get_pix_fmt_name(pix_fmt)}\n" +
                    $"new: width = {frame->width}, height = {frame->height}, format = {ffmpeg.av_get_pix_fmt_name((AVPixelFormat)frame->format)}");
                return -1;
            }

            Console.WriteLine($"video_frame n: {video_Frame_count++} coded: {frame->coded_picture_number}\n");

            byte_ptrArray4 tempData = new byte_ptrArray4();
            tempData.UpdateFrom(frame->data);

            int_array4 tempLinesize = new int_array4();
            tempLinesize.UpdateFrom(frame->linesize);
            ffmpeg.av_image_copy(ref video_dst_data, ref video_dst_linesize, ref tempData, tempLinesize, pix_fmt, width, height);

            video_dst_file?.Write(new ReadOnlySpan<byte>(video_dst_data[0], video_dst_bufsize));
            return 0;
        }

        static int output_audio_frame(AVFrame* frame)
        {
            int unpadded_linesize = frame->nb_samples * ffmpeg.av_get_bytes_per_sample((AVSampleFormat)frame->format);
            Console.WriteLine($"audio_frame n: {audio_frame_count++} nb_samples: {frame->nb_samples} pts: {FFmpegHelper.av_ts2timestr(frame->pts, in audio_dec_ctx->time_base)}");

            audio_dst_file?.Write(new ReadOnlySpan<byte>(frame->extended_data[0], unpadded_linesize));

            return 0;
        }

        static unsafe int decode_packet(AVCodecContext* dec, AVPacket* pkt)
        {
            int ret = ffmpeg.avcodec_send_packet(dec, pkt);
            if (ret < 0)
            {
                Console.WriteLine($"Error submitting a packet for decoding {ret}");
                return ret;
            }

            while (ret >= 0)
            {
                ret = ffmpeg.avcodec_receive_frame(dec, frame);
                if (ret < 0)
                {
                    if (ret == ffmpeg.AVERROR(ffmpeg.AVERROR_EOF) || ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
                    {
                        return 0;
                    }

                    Console.WriteLine($"Error during decoding ({FFmpegHelper.av_err2str(ret)})");
                    return ret;
                }

                if (dec->codec->type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                {
                    ret = output_video_frame(frame);
                }
                else
                {
                    ret = output_audio_frame(frame);
                }

                ffmpeg.av_frame_unref(frame);

                if (ret < 0)
                {
                    return ret;
                }
            }

            return 0;
        }

        static unsafe int open_codec_context(int* stream_idx, AVCodecContext** dec_ctx, AVFormatContext* fmt_ctx, AVMediaType type)
        {
            int ret, stream_index;
            AVStream* st;
            AVCodec* dec = null;

            ret = ffmpeg.av_find_best_stream(fmt_ctx, type, -1, -1, null, 0);
            if (ret < 0)
            {
                Console.WriteLine($"Could not find {ffmpeg.av_get_media_type_string(type)} stream in input file '{src_filename}'");
                return ret;
            }
            else
            {
                stream_index = ret;
                st = fmt_ctx->streams[stream_index];

                dec = ffmpeg.avcodec_find_decoder(st->codecpar->codec_id);
                if (dec == null)
                {
                    Console.WriteLine($"Failed to find {ffmpeg.av_get_media_type_string(type)} codec");
                    return ffmpeg.AVERROR(ffmpeg.EINVAL);
                }

                *dec_ctx = ffmpeg.avcodec_alloc_context3(dec);
                if (*dec_ctx == null)
                {
                    Console.WriteLine($"Failed to allocate the {ffmpeg.av_get_media_type_string(type)} codec context");
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }

                if ((ret = ffmpeg.avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0)
                {
                    Console.WriteLine($"Failed to copy {ffmpeg.av_get_media_type_string(type)} codec parameters to decoder context");
                    return ret;
                }

                if ((ret = ffmpeg.avcodec_open2(*dec_ctx, dec, null)) < 0)
                {
                    Console.WriteLine($"Failed to open {ffmpeg.av_get_media_type_string(type)} codec");
                    return ret;
                }

                *stream_idx = stream_index;
            }

            return 0;
        }

        static unsafe int Main(string[] args)
        {
            FFmpegBinariesHelper.RegisterFFmpegBinaries();

            int ret = 0;

            // https://samplelib.com/sample-mp4.html
            string dirPath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? "";
            src_filename = Path.Combine(dirPath, "..", "..", "..", "Samples", "sample-10s.mp4");

            video_dst_filename = Path.Combine(dirPath, "video.out");
            audio_dst_filename = Path.Combine(dirPath, "audio.out");

            fixed (AVFormatContext** pfmt_ctx = &fmt_ctx)
            fixed (AVCodecContext** pvideo_dec_ctx = &video_dec_ctx)
            fixed (AVCodecContext** paudio_dec_ctx = &audio_dec_ctx)
            fixed (int* pvideo_stream_index = &video_stream_idx)
            fixed (int* paudio_stream_index = &audio_stream_idx)
            fixed (AVPacket** ppkt = &pkt)
            fixed (AVFrame** pframe = &frame)
            {
                if (ffmpeg.avformat_open_input(pfmt_ctx, src_filename, null, null) < 0)
                {
                    Console.WriteLine($"Could not open source file {src_filename}");
                    return 1;
                }

                if (ffmpeg.avformat_find_stream_info(fmt_ctx, null) < 0)
                {
                    Console.WriteLine($"Could not find stream information");
                    return 1;
                }

                if (open_codec_context(pvideo_stream_index, pvideo_dec_ctx, fmt_ctx, AVMediaType.AVMEDIA_TYPE_VIDEO) >= 0)
                {
                    video_stream = fmt_ctx->streams[video_stream_idx];

                    video_dst_file = File.OpenWrite(video_dst_filename);
                    if (video_dst_file == null)
                    {
                        Console.WriteLine($"Could not open destination file {video_dst_filename}");
                        ret = 1;
                        goto end;
                    }

                    width = video_dec_ctx->width;
                    height = video_dec_ctx->height;
                    pix_fmt = video_dec_ctx->pix_fmt;
                    ret = ffmpeg.av_image_alloc(ref video_dst_data, ref video_dst_linesize, width, height, pix_fmt, 1);
                    if (ret < 0)
                    {
                        Console.WriteLine("Could not allocate raw video buffer");
                        goto end;
                    }

                    video_dst_bufsize = ret;
                }

                if (open_codec_context(paudio_stream_index, paudio_dec_ctx, fmt_ctx, AVMediaType.AVMEDIA_TYPE_AUDIO) >= 0)
                {
                    audio_stream = fmt_ctx->streams[audio_stream_idx];
                    audio_dst_file = File.OpenWrite(audio_dst_filename);
                    if (audio_dst_file == null)
                    {
                        Console.WriteLine($"Could not open destination file {audio_dst_filename}");
                        ret = 1;
                        goto end;
                    }
                }

                ffmpeg.av_dump_format(fmt_ctx, 0, src_filename, 0);

                if (audio_stream == null && video_stream == null)
                {
                    Console.WriteLine("Could not find audio or video stream in the input, aborting");
                    ret = 1;
                    goto end;
                }

                frame = ffmpeg.av_frame_alloc();
                if (frame == null)
                {
                    Console.WriteLine("Could not allocate frame");
                    ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto end;
                }

                pkt = ffmpeg.av_packet_alloc();
                if (pkt == null)
                {
                    Console.WriteLine("Could not allocate packet");
                    ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto end;
                }

                if (video_stream != null)
                {
                    Console.WriteLine($"Demuxing video from file '{src_filename}' into '{video_dst_filename}'");
                }

                if (audio_stream != null)
                {
                    Console.WriteLine($"Demuxing audio from file '{src_filename}' into '{audio_dst_filename}'");
                }

                while (ffmpeg.av_read_frame(fmt_ctx, pkt) >= 0)
                {
                    if (pkt->stream_index == video_stream_idx)
                    {
                        ret = decode_packet(video_dec_ctx, pkt);
                    }
                    else if (pkt->stream_index == audio_stream_idx)
                    {
                        ret = decode_packet(audio_dec_ctx, pkt);
                    }

                    ffmpeg.av_packet_unref(pkt);
                    if (ret < 0)
                    {
                        break;
                    }
                }

                if (video_dec_ctx != null)
                {
                    decode_packet(video_dec_ctx, null);
                }

                if (audio_dec_ctx != null)
                {
                    decode_packet(audio_dec_ctx, null);
                }

                Console.WriteLine("Demuxing succeeded.");

                if (video_stream != null)
                {
                    Console.WriteLine("Play the output video file with the command: \n" +
                        $"ffplay -autoexit -f rawvideo -pix_fmt {ffmpeg.av_get_pix_fmt_name(pix_fmt)} -video_size {width}x{height} {video_dst_filename}");
                }

                if (audio_stream != null)
                {
                    AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;
                    int n_channels = audio_dec_ctx->channels;
                    string fmt;

                    if (ffmpeg.av_sample_fmt_is_planar(sfmt) == 1)
                    {
                        string packed = ffmpeg.av_get_sample_fmt_name(sfmt);
                        Console.WriteLine("Warning: the sample format the decoder produced is planar " +
                            $"({(packed != null ? packed : "?")}). This example will output the first channel only.");
                        sfmt = ffmpeg.av_get_packed_sample_fmt(sfmt);
                        n_channels = 1;
                    }

                    if ((ret = FFmpegHelper.get_format_from_sample_fmt(out fmt, sfmt)) < 0)
                    {
                        goto end;
                    }

                    Console.WriteLine("Play the output audio file with the command:\n" +
                        $"ffplay -autoexit -f {fmt} -ac {n_channels} -ar {audio_dec_ctx->sample_rate} {audio_dst_filename}");
                }

            end:

                ffmpeg.avcodec_free_context(pvideo_dec_ctx);
                ffmpeg.avcodec_free_context(paudio_dec_ctx);
                ffmpeg.avformat_close_input(pfmt_ctx);

                if (video_dst_file != null)
                {
                    video_dst_file.Close();
                }

                if (audio_dst_file != null)
                {
                    audio_dst_file.Close();
                }

                ffmpeg.av_packet_free(ppkt);
                ffmpeg.av_frame_free(pframe);
                ffmpeg.av_free(video_dst_data[0]);

            }

            return 0;
        }
    }
}

실행하면,

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '...[생략]...\demuxing_decoding\bin\Debug\..\..\..\Samples\sample-10s.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.44.100
  Duration: 00:00:10.24, start: 0.000000, bitrate: 4285 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 4207 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
  Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 121 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]
Demuxing video from file 'E:\git_clone\ffmpeg_autogen_cs\ffmpeg_autogen_cs\demuxing_decoding\bin\Debug\..\..\..\Samples\sample-10s.mp4' into '...[생략]...\demuxing_decoding\bin\Debug\video.out'
Demuxing audio from file 'E:\git_clone\ffmpeg_autogen_cs\ffmpeg_autogen_cs\demuxing_decoding\bin\Debug\..\..\..\Samples\sample-10s.mp4' into '...[생략]...\demuxing_decoding\bin\Debug\audio.out'
video_frame n: 0 coded: 0

audio_frame n: 0 nb_samples: 1024 pts: 0
audio_frame n: 1 nb_samples: 1024 pts: 0.023219954648526078
video_frame n: 1 coded: 3

audio_frame n: 2 nb_samples: 1024 pts: 0.046439909297052155
video_frame n: 2 coded: 2

...[생략]...
audio_frame n: 437 nb_samples: 1024 pts: 10.147120181405896
audio_frame n: 438 nb_samples: 1024 pts: 10.170340136054422
audio_frame n: 439 nb_samples: 1024 pts: 10.193560090702949
video_frame n: 301 coded: 302

video_frame n: 302 coded: 299

Error during decoding (End of file)
Error during decoding (End of file)
Demuxing succeeded.
Play the output video file with the command:
ffplay -autoexit -f rawvideo -pix_fmt yuv420p -video_size 1920x1080 ...[생략]...\demuxing_decoding\bin\Debug\video.out
Warning: the sample format the decoder produced is planar (fltp). This example will output the first channel only.
Play the output audio file with the command:
ffplay -autoexit -f f32le -ac 1 -ar 44100 ...[생략]...\demuxing_decoding\bin\Debug\audio.out

입력 파일의 동영상 파일을 각각 영상과 음성 파일로 분리해 별도의 파일로 저장합니다. 그리고 마지막 콘솔 출력 부분을 보면, 해당 파일들을 ffplay로 어떻게 재생할 수 있는지 명령행 옵션과 함께 보여줍니다.

(이 글의 소스 코드는 github에 올려져 있습니다.)




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







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

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

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  [53]  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12612정성태4/23/202116569.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/202115451오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/202115381.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/202117882.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/202118422.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [9]파일 다운로드1
12607정성태4/21/202115655.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/202121531.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/202116879.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/202114784VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/202116444VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/202117509VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 [1]
12601정성태4/15/202116529.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/202116770.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
12599정성태4/14/202117550.NET Framework: 1038. C# - 인증서 및 키 파일로부터 pfx/p12 파일을 생성하는 방법파일 다운로드1
12598정성태4/14/202118120.NET Framework: 1037. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 (2)파일 다운로드1
12597정성태4/13/202117717개발 환경 구성: 569. csproj의 내용을 공통 설정할 수 있는 Directory.Build.targets / Directory.Build.props 파일
12596정성태4/12/202117051개발 환경 구성: 568. Windows의 80 포트 점유를 해제하는 방법
12595정성태4/12/202116797.NET Framework: 1036. SQL 서버 - varbinary 타입에 대한 문자열의 CAST, CONVERT 변환을 C# 코드로 구현
12594정성태4/11/202116253.NET Framework: 1035. C# - kubectl 명령어 또는 REST API 대신 Kubernetes 클라이언트 라이브러리를 통해 프로그래밍으로 접근 [1]파일 다운로드1
12593정성태4/10/202117288개발 환경 구성: 567. Docker Desktop for Windows - kubectl proxy 없이 k8s 대시보드 접근 방법
12592정성태4/10/202116800개발 환경 구성: 566. Docker Desktop for Windows - k8s dashboard의 Kubeconfig 로그인 및 Skip 방법
12591정성태4/9/202120707.NET Framework: 1034. C# - byte 배열을 Hex(16진수) 문자열로 고속 변환하는 방법 [2]파일 다운로드1
12590정성태4/9/202116845.NET Framework: 1033. C# - .NET 4.0 이하에서 Console.IsInputRedirected 구현 [1]
12589정성태4/8/202118087.NET Framework: 1032. C# - Environment.OSVersion의 문제점 및 윈도우 운영체제의 버전을 구하는 다양한 방법 [1]
12588정성태4/7/202119814개발 환경 구성: 565. PowerShell - New-SelfSignedCertificate를 사용해 CA 인증서 생성 및 인증서 서명 방법
12587정성태4/6/202121088개발 환경 구성: 564. Windows 10 - ClickOnce 배포처럼 사용할 수 있는 MSIX 설치 파일 [1]
... 46  47  48  49  50  51  52  [53]  54  55  56  57  58  59  60  ...