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

지난 글에 이어,

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

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

using FFmpeg.AutoGen;
using FFmpeg.AutoGen.Example;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace transcoding
{
    public unsafe struct FilteringContext
    {
        public AVFilterContext* buffersink_ctx;
        public AVFilterContext* buffersrc_ctx;
        public AVFilterGraph* filter_graph;

        public AVPacket* enc_pkt;
        public AVFrame* filtered_frame;
    }

    public unsafe struct StreamContext
    {
        public AVCodecContext* dec_ctx;
        public AVCodecContext* enc_ctx;

        public AVFrame* dec_frame;
    }

    internal unsafe class Program
    {
        static AVFormatContext* ifmt_ctx;
        static AVFormatContext* ofmt_ctx;

        static FilteringContext* filter_ctx;
        static StreamContext* stream_ctx;

        static int open_input_file(string filename)
        {
            int ret;
            uint i;

            ifmt_ctx = null;
            fixed (AVFormatContext** pfmt_ctx = &ifmt_ctx)
            {
                if ((ret = ffmpeg.avformat_open_input(pfmt_ctx, filename, null, null)) < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot open input file\n");
                    return ret;
                }

                if ((ret = ffmpeg.avformat_find_stream_info(ifmt_ctx, null)) < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot find stream information\n");
                    return ret;
                }

                ulong ctxSize = (ulong)sizeof(StreamContext);

                stream_ctx = (StreamContext*)ffmpeg.av_calloc(ifmt_ctx->nb_streams, ctxSize);

                if (stream_ctx == null)
                {
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }
            }

            for (i = 0; i < ifmt_ctx->nb_streams; i++)
            {
                AVStream* stream = ifmt_ctx->streams[i];
                AVCodec* dec = ffmpeg.avcodec_find_decoder(stream->codecpar->codec_id);
                AVCodecContext* codec_ctx;

                if (dec == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Failed to find decoder for stream #{i}\n");
                    return ffmpeg.AVERROR_DECODER_NOT_FOUND;
                }

                codec_ctx = ffmpeg.avcodec_alloc_context3(dec);
                if (codec_ctx == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Failed to allocate the decoder context for stream #{i}\n");
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }

                ret = ffmpeg.avcodec_parameters_to_context(codec_ctx, stream->codecpar);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Failed to copy decoder parameters to input decoder context for stream {i}\n");
                    return ret;
                }

                if (codec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                {
                    if (codec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                    {
                        codec_ctx->framerate = ffmpeg.av_guess_frame_rate(ifmt_ctx, stream, null);
                    }

                    ret = ffmpeg.avcodec_open2(codec_ctx, dec, null);
                    if (ret < 0)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Failed to open decoder for stream #{i}\n");
                        return ret;
                    }
                }

                stream_ctx[i].dec_ctx = codec_ctx;

                stream_ctx[i].dec_frame = ffmpeg.av_frame_alloc();
                if (stream_ctx[i].dec_frame == null)
                {
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }
            }

            ffmpeg.av_dump_format(ifmt_ctx, 0, filename, 0);
            return 0;
        }

        static unsafe int open_output_file(string filename)
        {
            AVStream* out_stream;
            AVStream* in_stream;
            AVCodecContext* dec_ctx, enc_ctx;
            AVCodec* encoder;
            int ret;
            uint i;

            ofmt_ctx = null;
            fixed (AVFormatContext** pfmt_ctx = &ofmt_ctx)
            {
                ffmpeg.avformat_alloc_output_context2(pfmt_ctx, null, null, filename);
                if (ofmt_ctx == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Could not create output context\n");
                    return ffmpeg.AVERROR_UNKNOWN;
                }
            }

            for (i = 0; i < ifmt_ctx->nb_streams; i++)
            {
                out_stream = ffmpeg.avformat_new_stream(ofmt_ctx, null);
                if (out_stream == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Failed allocating output stream\n");
                    return ffmpeg.AVERROR_UNKNOWN;
                }

                in_stream = ifmt_ctx->streams[i];
                dec_ctx = stream_ctx[i].dec_ctx;

                if (dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO || dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                {
                    encoder = ffmpeg.avcodec_find_encoder(dec_ctx->codec_id);
                    if (encoder == null)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_FATAL, "Necessary encoder not found\n");
                        return ffmpeg.AVERROR_INVALIDDATA;
                    }

                    enc_ctx = ffmpeg.avcodec_alloc_context3(encoder);
                    if (enc_ctx == null)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_FATAL, "Failed to allocate the encoder context\n");
                        return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    }

                    if (dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                    {
                        enc_ctx->height = dec_ctx->height;
                        enc_ctx->width = dec_ctx->width;
                        enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
                        if (encoder->pix_fmts != null)
                        {
                            enc_ctx->pix_fmt = encoder->pix_fmts[0];
                        }
                        else
                        {
                            enc_ctx->pix_fmt = dec_ctx->pix_fmt;
                        }

                        enc_ctx->time_base = FFmpegHelper.av_inv_q(dec_ctx->framerate);
                    }
                    else
                    {
                        // 2022-03-15 - cdba98bb80 - lavc 59.24.100 - avcodec.h codec_par.h
                        //   Update AVCodecContext for the new channel layout API: add ch_layout,
                        //   deprecate channels/channel_layout
                        // ret = FFmpegHelper.av_channel_layout_copy(&enc_ctx->channel_layout, &dec_ctx->channel_layout);

                        enc_ctx->sample_rate = dec_ctx->sample_rate;
                        enc_ctx->channel_layout = dec_ctx->channel_layout;
                        enc_ctx->channels = ffmpeg.av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
                        enc_ctx->sample_fmt = encoder->sample_fmts[0];
                        enc_ctx->time_base = new AVRational { num = 1, den = enc_ctx->sample_rate };
                    }

                    if ((ofmt_ctx->oformat->flags & ffmpeg.AVFMT_GLOBALHEADER) == ffmpeg.AVFMT_GLOBALHEADER)
                    {
                        enc_ctx->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER;
                    }

                    ret = ffmpeg.avcodec_open2(enc_ctx, encoder, null);
                    if (ret < 0)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Cannot open video encoder for stream {i}\n");
                        return ret;
                    }

                    ret = ffmpeg.avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
                    if (ret < 0)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Failed to copy encoder parameters to output stream #{i}\n");
                        return ret;
                    }

                    out_stream->time_base = enc_ctx->time_base;
                    stream_ctx[i].enc_ctx = enc_ctx;
                }
                else if (dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_UNKNOWN)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_FATAL, $"Elementary stream #{i} is of unknown type, cannot proceed\n");
                    return ffmpeg.AVERROR_INVALIDDATA;
                }
                else
                {
                    ret = ffmpeg.avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
                    if (ret < 0)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Copying parameters for stream #{i} failed\n");
                        return ret;
                    }

                    out_stream->time_base = in_stream->time_base;
                }
            }

            ffmpeg.av_dump_format(ofmt_ctx, 0, filename, 1);

            if ((ofmt_ctx->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0)
            {
                ret = ffmpeg.avio_open(&ofmt_ctx->pb, filename, ffmpeg.AVIO_FLAG_WRITE);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Could not open output file'{filename}'");
                    return ret;
                }
            }

            ret = ffmpeg.avformat_write_header(ofmt_ctx, null);
            if (ret < 0)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Error occurred when opening output file\n");
                return ret;
            }

            return 0;
        }

        static unsafe int init_filter(FilteringContext* fctx, AVCodecContext* dec_ctx, AVCodecContext* enc_ctx, string filter_spec)
        {
            string args;
            int ret = 0;
            AVFilter* buffersrc = null;
            AVFilter* buffersink = null;
            AVFilterContext* buffersrc_ctx = null;
            AVFilterContext* buffersink_ctx = null;
            AVFilterInOut* outputs = ffmpeg.avfilter_inout_alloc();
            AVFilterInOut* inputs = ffmpeg.avfilter_inout_alloc();
            AVFilterGraph* filter_graph = ffmpeg.avfilter_graph_alloc();

            if (outputs == null || inputs == null || filter_graph == null)
            {
                ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                goto end;
            }

            if (dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
            {
                buffersrc = ffmpeg.avfilter_get_by_name("buffer");
                buffersink = ffmpeg.avfilter_get_by_name("buffersink");
                if (buffersrc == null || buffersink == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "filtering source or sink element not found\n");
                    ret = ffmpeg.AVERROR_UNKNOWN;
                    goto end;
                }

                AVRational ts = dec_ctx->time_base;
                AVRational sar = dec_ctx->sample_aspect_ratio;
                args = $"video_size={dec_ctx->width}x{dec_ctx->height}:pix_fmt={(int)dec_ctx->pix_fmt}:time_base={ts.num}/{ts.den}:pixel_aspect={sar.num}/{sar.den}";
                ret = ffmpeg.avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, null, filter_graph);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot create buffer source\n");
                    goto end;
                }

                ret = ffmpeg.avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", null, null, filter_graph);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot create buffer sink\n");
                    goto end;
                }

                int pix_fmt_size = sizeof(AVPixelFormat); //  enc_ctx->pix_fmt;
                ret = ffmpeg.av_opt_set_bin(buffersink_ctx, "pix_fmts", (byte*)&enc_ctx->pix_fmt, pix_fmt_size, ffmpeg.AV_OPT_SEARCH_CHILDREN);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot set output pixel format\n");
                    goto end;
                }
            }
            else if (dec_ctx->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
            {
                buffersrc = ffmpeg.avfilter_get_by_name("abuffer");
                buffersink = ffmpeg.avfilter_get_by_name("abuffersink");
                if (buffersrc == null || buffersink == null)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "filtering source or sink element not found\n");
                    ret = ffmpeg.AVERROR_UNKNOWN;
                    goto end;
                }

                if (dec_ctx->channel_layout == 0)
                {
                    dec_ctx->channel_layout = (ulong)ffmpeg.av_get_default_channel_layout(dec_ctx->channels);
                }

                AVRational ts = dec_ctx->time_base;

                args = $"time_base={ts.num}/{ts.den}:sample_rate={dec_ctx->sample_rate}:sample_fmt={ffmpeg.av_get_sample_fmt_name(dec_ctx->sample_fmt)}:channel_layout=0x{dec_ctx->channel_layout.ToString("x")}";
                ret = ffmpeg.avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, null, filter_graph);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot create audio buffer source\n");
                    goto end;
                }

                ret = ffmpeg.avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", null, null, filter_graph);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot create audio buffer sink\n");
                    goto end;
                }

                int sample_fmt_size = sizeof(AVSampleFormat);
                ret = ffmpeg.av_opt_set_bin(buffersink_ctx, "sample_fmts", (byte*)&enc_ctx->sample_fmt, sample_fmt_size, ffmpeg.AV_OPT_SEARCH_CHILDREN);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot set output sample format\n");
                    goto end;
                }

                int channel_layout_size = sizeof(ulong);
                ret = ffmpeg.av_opt_set_bin(buffersink_ctx, "channel_layouts", (byte*)&enc_ctx->channel_layout, channel_layout_size, ffmpeg.AV_OPT_SEARCH_CHILDREN);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot set output channel layout\n");
                    goto end;
                }

                int sample_rate_size = sizeof(int);
                ret = ffmpeg.av_opt_set_bin(buffersink_ctx, "sample_rates", (byte*)&enc_ctx->sample_rate, sample_rate_size, ffmpeg.AV_OPT_SEARCH_CHILDREN);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Cannot set output sample rate\n");
                    goto end;
                }
            }
            else
            {
                ret = ffmpeg.AVERROR_UNKNOWN;
                goto end;
            }

            outputs->name = ffmpeg.av_strdup("in");
            outputs->filter_ctx = buffersrc_ctx;
            outputs->pad_idx = 0;
            outputs->next = null;

            inputs->name = ffmpeg.av_strdup("out");
            inputs->filter_ctx = buffersink_ctx;
            inputs->pad_idx = 0;
            inputs->next = null;

            if (outputs->name == null || inputs->name == null)
            {
                ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                goto end;
            }

            if ((ret = ffmpeg.avfilter_graph_parse_ptr(filter_graph, filter_spec, &inputs, &outputs, null)) < 0)
            {
                goto end;
            }

            if ((ret = ffmpeg.avfilter_graph_config(filter_graph, null)) < 0)
            {
                goto end;
            }

            fctx->buffersrc_ctx = buffersrc_ctx;
            fctx->buffersink_ctx = buffersink_ctx;
            fctx->filter_graph = filter_graph;

        end:
            ffmpeg.avfilter_inout_free(&inputs);
            ffmpeg.avfilter_inout_free(&outputs);

            return ret;
        }

        static unsafe int init_filters()
        {
            string filter_spec;
            uint i;
            int ret;

            ulong fcSize = (ulong)sizeof(FilteringContext);

            filter_ctx = (FilteringContext*)ffmpeg.av_malloc_array(ifmt_ctx->nb_streams, fcSize);
            if (filter_ctx == null)
            {
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            for (i = 0; i < ifmt_ctx->nb_streams; i++)
            {
                filter_ctx[i].buffersrc_ctx = null;
                filter_ctx[i].buffersink_ctx = null;
                filter_ctx[i].filter_graph = null;

                if (ifmt_ctx->streams[i]->codecpar->codec_type != AVMediaType.AVMEDIA_TYPE_AUDIO
                    && ifmt_ctx->streams[i]->codecpar->codec_type != AVMediaType.AVMEDIA_TYPE_VIDEO)
                {
                    continue;
                }

                if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                {
                    filter_spec = "null";
                }
                else
                {
                    filter_spec = "anull";
                }

                ret = init_filter(&filter_ctx[i], stream_ctx[i].dec_ctx, stream_ctx[i].enc_ctx, filter_spec);
                if (ret != 0)
                {
                    return ret;
                }

                filter_ctx[i].enc_pkt = ffmpeg.av_packet_alloc();
                if (filter_ctx[i].enc_pkt == null)
                {
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }

                filter_ctx[i].filtered_frame = ffmpeg.av_frame_alloc();
                if (filter_ctx[i].filtered_frame == null)
                {
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }
            }

            return 0;
        }

        static unsafe int encode_write_frame(uint stream_index, int flush)
        {
            StreamContext* stream = &stream_ctx[stream_index];
            FilteringContext* filter = &filter_ctx[stream_index];
            AVFrame* filt_frame = flush != 0 ? null : filter->filtered_frame;
            AVPacket* enc_pkt = filter->enc_pkt;
            int ret;

            ffmpeg.av_log(null, ffmpeg.AV_LOG_INFO, "Encoding frame\n");
            ffmpeg.av_packet_unref(enc_pkt);

            ret = ffmpeg.avcodec_send_frame(stream->enc_ctx, filt_frame);

            if (ret < 0)
            {
                return ret;
            }

            while (ret >= 0)
            {
                ret = ffmpeg.avcodec_receive_packet(stream->enc_ctx, enc_pkt);

                if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
                {
                    return 0;
                }

                enc_pkt->stream_index = (int)stream_index;
                ffmpeg.av_packet_rescale_ts(enc_pkt, stream->enc_ctx->time_base, ofmt_ctx->streams[stream_index]->time_base);

                ffmpeg.av_log(null, ffmpeg.AV_LOG_DEBUG, "Muxing frame\n");
                ret = ffmpeg.av_interleaved_write_frame(ofmt_ctx, enc_pkt);
            }

            return ret;
        }

        static unsafe int filter_encode_write_frame(AVFrame* frame, uint stream_index)
        {
            FilteringContext* filter = &filter_ctx[stream_index];
            int ret;

            ffmpeg.av_log(null, ffmpeg.AV_LOG_INFO, "Pushing decoded frame to filters\n");
            ret = ffmpeg.av_buffersrc_add_frame_flags(filter->buffersrc_ctx, frame, 0);
            if (ret < 0)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Error while feeding the filtergraph\n");
                return ret;
            }

            while (true)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_INFO, "Pulling filtered frame from filters\n");
                ret = ffmpeg.av_buffersink_get_frame(filter->buffersink_ctx, filter->filtered_frame);

                if (ret < 0)
                {
                    if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
                    {
                        ret = 0;
                    }

                    break;
                }

                filter->filtered_frame->pict_type = AVPictureType.AV_PICTURE_TYPE_NONE;
                ret = encode_write_frame(stream_index, 0);
                ffmpeg.av_frame_unref(filter->filtered_frame);

                if (ret < 0)
                {
                    break;
                }
            }

            return ret;
        }

        static unsafe int flush_encoder(uint stream_index)
        {
            if ((stream_ctx[stream_index].enc_ctx->codec->capabilities & ffmpeg.AV_CODEC_CAP_DELAY) == 0)
            {
                return 0;
            }

            ffmpeg.av_log(null, ffmpeg.AV_LOG_INFO, $"Flushing stream #{stream_index} encoder\n");
            return encode_write_frame(stream_index, 1);
        }

        static unsafe 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()}");
            Console.WriteLine();
#endif

            int ret;
            AVPacket* packet = null;
            uint stream_index;
            uint i;

            string dirPath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? "";
            string input_filename = Path.Combine(dirPath, "..", "..", "..", "Samples", "sample-10s.mp4");

            string output_filename = Path.Combine(dirPath, "test.mp4");

            if ((ret = open_input_file(input_filename)) < 0)
            {
                goto end;
            }

            if ((ret = open_output_file(output_filename)) < 0)
            {
                goto end;
            }

            if ((ret = init_filters()) < 0)
            {
                goto end;
            }

            if ((packet = ffmpeg.av_packet_alloc()) == null)
            {
                goto end;
            }

            while (true)
            {
                if ((ret = ffmpeg.av_read_frame(ifmt_ctx, packet)) < 0)
                {
                    break;
                }

                stream_index = (uint)packet->stream_index;
                ffmpeg.av_log(null, ffmpeg.AV_LOG_DEBUG, $"Demuxer gave frame of stream_index {stream_index}\n");

                if (filter_ctx[stream_index].filter_graph != null)
                {
                    StreamContext* stream = &stream_ctx[stream_index];

                    ffmpeg.av_log(null, ffmpeg.AV_LOG_DEBUG, "Going to reencode & filter the frame\n");
                    ffmpeg.av_packet_rescale_ts(packet, ifmt_ctx->streams[stream_index]->time_base, stream->dec_ctx->time_base);
                    ret = ffmpeg.avcodec_send_packet(stream->dec_ctx, packet);
                    if (ret < 0)
                    {
                        ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Decoding failed\n");
                        break;
                    }

                    while (ret >= 0)
                    {
                        ret = ffmpeg.avcodec_receive_frame(stream->dec_ctx, stream->dec_frame);
                        if (ret == ffmpeg.AVERROR_EOF || ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
                        {
                            break;
                        }
                        else if (ret < 0)
                        {
                            goto end;
                        }

                        stream->dec_frame->pts = stream->dec_frame->best_effort_timestamp;
                        ret = filter_encode_write_frame(stream->dec_frame, stream_index);
                        if (ret < 0)
                        {
                            goto end;
                        }
                    }
                }
                else
                {
                    ffmpeg.av_packet_rescale_ts(packet, ifmt_ctx->streams[stream_index]->time_base, ofmt_ctx->streams[stream_index]->time_base);
                    ret = ffmpeg.av_interleaved_write_frame(ofmt_ctx, packet);
                    if (ret < 0)
                    {
                        goto end;
                    }
                }

                ffmpeg.av_packet_unref(packet);
            }

            for (i = 0; i < ifmt_ctx->nb_streams; i ++)
            {
                if (filter_ctx[i].filter_graph == null)
                {
                    continue;
                }

                ret = filter_encode_write_frame(null, i);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Flushing filter failed\n");
                    goto end;
                }

                ret = flush_encoder(i);
                if (ret < 0)
                {
                    ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, "Flushing encoder failed\n");
                    goto end;
                }
            }

            ffmpeg.av_write_trailer(ofmt_ctx);

        end:
            ffmpeg.av_packet_free(&packet);

            for (i = 0; i < ifmt_ctx->nb_streams; i ++)
            {
                ffmpeg.avcodec_free_context(&stream_ctx[i].dec_ctx);
                if (ofmt_ctx != null && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] != null && stream_ctx[i].enc_ctx != null)
                {
                    ffmpeg.avcodec_free_context(&stream_ctx[i].enc_ctx);
                }

                if (filter_ctx != null && filter_ctx[i].filter_graph != null)
                {
                    ffmpeg.avfilter_graph_free(&filter_ctx[i].filter_graph);
                    ffmpeg.av_packet_free(&filter_ctx[i].enc_pkt);
                    ffmpeg.av_frame_free(&filter_ctx[i].filtered_frame);
                }

                ffmpeg.av_frame_free(&stream_ctx[i].dec_frame);
            }

            ffmpeg.av_free(filter_ctx);
            ffmpeg.av_free(stream_ctx);

            fixed (AVFormatContext** pfmt_ctx = &ifmt_ctx)
            {
                ffmpeg.avformat_close_input(pfmt_ctx);
                if (ofmt_ctx != null && ((ofmt_ctx->oformat->flags & ffmpeg.AVFMT_NOFILE) == 0))
                {
                    ffmpeg.avio_closep(&ofmt_ctx->pb);
                }
            }

            ffmpeg.avformat_free_context(ofmt_ctx);

            if (ret < 0)
            {
                ffmpeg.av_log(null, ffmpeg.AV_LOG_ERROR, $"Error occurred: {FFmpegHelper.av_err2str(ret)}");
            }

            return ret != 0 ? 1 : 0;
        }
    }
}

그런데, 아쉽게도 이번 예제는 최신 코드가 아닙니다. 왜냐하면, 현재 시점(2022-04-06)에 5.0.0 버전의 FFmpeg.AutoGen이 최근 3월의 변화를,

FFmpeg/doc/APIchanges
; https://github.com/FFmpeg/FFmpeg/blob/master/doc/APIchanges

반영하지 않았기 때문입니다. 나중에 나오면 수정해야겠군요. ^^ 일단, 현재까지의 소스 코드는 다음의 github에 올렸습니다.

ffmpeg_autogen_cs/transcoding/
; https://github.com/stjeong/ffmpeg_autogen_cs/tree/master/transcoding

예제 코드를 실행하면 다음의 mp4 입력 파일을,

...[생략]...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'E:\tempffmpeg_autogen_cs\transcoding\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(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 4207 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : [0][0][0][0]
  Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 121 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]

mov 파일로 트랜스코딩합니다.

...[생략]...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mov':
  Metadata:
    major_brand     : qt
    minor_version   : 512
    compatible_brands: qt
    encoder         : Lavf59.16.100
  Duration: 00:00:20.02, start: 0.000000, bitrate: 2780 kb/s
  Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2714 kb/s, 15.03 fps, 14.99 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : VideoHandler
      vendor_id       : FFMP
  Stream #0:1: Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 122 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
      vendor_id       : [0][0][0][0]




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







[최초 등록일: ]
[최종 수정일: 4/6/2022]

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

비밀번호

댓글 작성자
 



2022-04-20 11시39분
[후드] 드뎌 반가운 소식이군요.!! 다시 ffmpeg 포스팅 올라오기만을 기다렷는데..
감사합니다!!
[guest]
2022-04-21 09시43분
아니, 그런 건 아니고... ^^; 어째 요즘에 개인적으로 ffmpeg에 대해 좀 관심이 식어가는군요. 천천히 접근해야겠습니다. ^^
정성태
2022-07-07 03시05분
[Bernie] Thank you for this port. Love it.
[guest]

1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...
NoWriterDateCnt.TitleFile(s)
13247정성태2/7/20234962VS.NET IDE: 180. Visual Studio - 닷넷 소스 코드 디버깅 중 "Decompile source code"가 동작하는 않는 문제
13246정성태2/6/20234085개발 환경 구성: 664. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 - 두 번째 이야기
13245정성태2/6/20234627.NET Framework: 2093. C# - PEM 파일을 이용한 RSA 개인키/공개키 설정 방법파일 다운로드1
13244정성태2/5/20233983VS.NET IDE: 179. Visual Studio - External Tools에 Shell 내장 명령어 등록
13243정성태2/5/20234855디버깅 기술: 190. windbg - Win32 API 호출 시점에 BP 거는 방법 [1]
13242정성태2/4/20234292디버깅 기술: 189. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.UnauthorizedAccessException
13241정성태2/3/20233824디버깅 기술: 188. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.IO.FileNotFoundException
13240정성태2/1/20233982디버깅 기술: 187. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.Web.HttpException
13239정성태2/1/20233625디버깅 기술: 186. C# - CacheDependency의 숨겨진 예외 - System.Web.HttpException
13238정성태1/31/20235631.NET Framework: 2092. IIS 웹 사이트를 TLS 1.2 또는 TLS 1.3 프로토콜로만 운영하는 방법
13237정성태1/30/20235320.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/20234964개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/20234508개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/20235547개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/20236897오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234698스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20233614오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234030개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20234966.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235112.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20234820개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234492.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20233746개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234090Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234281오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20233932개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...