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

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12671정성태6/15/202117793오류 유형: 724. Tomcat 실행 시 Failed to initialize connector [Connector[HTTP/1.1-8080]] 오류
12670정성태6/13/20219232.NET Framework: 1071. DLL Surrogate를 이용한 Out-of-process COM 개체에서의 CoInitializeSecurity 문제파일 다운로드1
12669정성태6/11/20219271.NET Framework: 1070. 사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.
12668정성태6/11/202110979.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작파일 다운로드2
12667정성태6/10/20219614.NET Framework: 1068. COM+ 서버 응용 프로그램을 이용해 CoInitializeSecurity 제약 해결파일 다운로드1
12666정성태6/10/20218167.NET Framework: 1067. 별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출파일 다운로드1
12665정성태6/9/20219495.NET Framework: 1066. Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약파일 다운로드1
12664정성태6/9/20217876오류 유형: 723. COM+ PIA 참조 시 "This operation failed because the QueryInterface call on the COM component" 오류
12663정성태6/9/20219444.NET Framework: 1065. Windows Forms - 속성 창의 디자인 설정 지원: 문자열 목록 내에서 항목을 선택하는 TypeConverter 제작파일 다운로드1
12662정성태6/8/20218475.NET Framework: 1064. C# COM 개체를 PIA(Primary Interop Assembly)로써 "Embed Interop Types" 참조하는 방법파일 다운로드1
12661정성태6/4/202119185.NET Framework: 1063. C# - MQTT를 이용한 클라이언트/서버(Broker) 통신 예제 [4]파일 다운로드1
12660정성태6/3/202110258.NET Framework: 1062. Windows Forms - 폼 내에서 발생하는 마우스 이벤트를 자식 컨트롤 영역에 상관없이 수신하는 방법 [1]파일 다운로드1
12659정성태6/2/202111466Linux: 40. 우분투 설치 후 MBR 디스크 드라이브 여유 공간이 인식되지 않은 경우 - Logical Volume Management
12658정성태6/2/20218901Windows: 194. Microsoft Store에 있는 구글의 공식 Youtube App
12657정성태6/2/202110216Windows: 193. 윈도우 패키지 관리자 - winget 설치
12656정성태6/1/20218465.NET Framework: 1061. 서버 유형의 COM+에 적용할 수 없는 Server GC
12655정성태6/1/20217907오류 유형: 722. windbg/sos - savemodule - Fail to read memory
12654정성태5/31/20217900오류 유형: 721. Hyper-V - Saved 상태의 VM을 시작 시 오류 발생
12653정성태5/31/202110591.NET Framework: 1060. 닷넷 GC에 새롭게 구현되는 DPAD(Dynamic Promotion And Demotion for GC)
12652정성태5/31/20218741VS.NET IDE: 164. Visual Studio - Web Deploy로 Publish 시 암호창이 매번 뜨는 문제
12651정성태5/31/20218962오류 유형: 720. PostgreSQL - ERROR: 22P02: malformed array literal: "..."
12650정성태5/17/20218269기타: 82. OpenTabletDriver의 버튼에 더블 클릭을 매핑 및 게임에서의 지원 방법
12649정성태5/16/20219613.NET Framework: 1059. 세대 별 GC(Garbage Collection) 방식에서 Card table의 사용 의미 [1]
12648정성태5/16/20218272사물인터넷: 66. PC -> FTDI -> NodeMCU v1 ESP8266 기기를 UART 핀을 연결해 직렬 통신하는 방법파일 다운로드1
12647정성태5/15/20219536.NET Framework: 1058. C# - C++과의 연동을 위한 구조체의 fixed 배열 필드 사용파일 다운로드1
12646정성태5/15/20218644사물인터넷: 65. C# - Arduino IDE의 Serial Monitor 기능 구현파일 다운로드1
... 31  32  33  34  35  36  37  38  [39]  40  41  42  43  44  45  ...