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]

... 31  32  33  [34]  35  36  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12799정성태8/22/20218953.NET Framework: 1100. C# 10 - (5) 속성 패턴의 개선파일 다운로드1
12798정성태8/21/202110412개발 환경 구성: 598. PyCharm - 원격 프로세스를 디버그하는 방법
12797정성태8/21/20218029Windows: 197. TCP의 MSS(Maximum Segment Size) 크기는 고정된 것일까요?
12796정성태8/21/20218659.NET Framework: 1099. C# 10 - (4) 상수 문자열에 포맷 식 사용 가능파일 다운로드1
12795정성태8/20/20219330.NET Framework: 1098. .NET 6에 포함된 신규 BCL API - 스레드 관련
12794정성태8/20/20218773스크립트: 23. 파이썬 - WSGI를 만족하는 최소한의 구현 코드 및 PyCharm에서의 디버깅 방법 [1]
12793정성태8/20/20219471.NET Framework: 1097. C# 10 - (3) 개선된 변수 초기화 판정파일 다운로드1
12792정성태8/19/20219910.NET Framework: 1096. C# 10 - (2) 전역 네임스페이스 선언파일 다운로드1
12791정성태8/19/20218236.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제 [3]파일 다운로드1
12790정성태8/18/202110513.NET Framework: 1094. C# 10 - (1) 구조체를 생성하는 record struct파일 다운로드1
12789정성태8/18/20219631개발 환경 구성: 597. PyCharm - 윈도우 환경에서 WSL을 이용해 파이썬 앱 개발/디버깅하는 방법
12788정성태8/17/20218064.NET Framework: 1093. C# - 인터페이스의 메서드가 다형성을 제공할까요? (virtual일까요?)파일 다운로드1
12787정성태8/17/20218288.NET Framework: 1092. (책 내용 수정) "4.5.1.4 인터페이스"의 "인터페이스와 다형성"
12786정성태8/16/20219887.NET Framework: 1091. C# - Python range 함수 구현 (2) INumber<T>를 이용한 개선 [1]파일 다운로드1
12785정성태8/16/20218153.NET Framework: 1090. .NET 6 Preview 7에 추가된 숫자 형식에 대한 제네릭 연산 지원 [1]파일 다운로드1
12784정성태8/15/20217594오류 유형: 757. 구글 메일 - 아웃룩에서 메일 전송 시 Sending' reported error (0x800CCC0F, 0x800CCC92)
12783정성태8/15/20217067.NET Framework: 1089. C# - Indexer에 Range 및 람다 식을 이용한 필터 구현 [1]파일 다운로드1
12782정성태8/14/20216868오류 유형: 756. 파이썬 - 윈도우 환경에서 pytagcloud의 한글 출력 방법
12781정성태8/14/20219097오류 유형: 755. 파이썬 - konlpy 사용 시 JVM과 jpype1 관련 오류
12780정성태8/13/20217458.NET Framework: 1088. C# - 버스 노선 및 위치 정보 조회 API 사용을 위한 기초 라이브러리 [2]
12779정성태8/13/20219322개발 환경 구성: 596. 공공 데이터 포털에서 버스 노선 및 위치 정보 조회 API 사용법
12778정성태8/12/20216523오류 유형: 755. PyCharm - "Manage Repositories"의 목록이 나오지 않는 문제
12777정성태8/12/20218095오류 유형: 754. Visual Studio - Input or output cannot be redirected because the specified file is invalid.
12776정성태8/12/20217423오류 유형: 753. gunicorn과 uwsgi 함께 사용 시 ERR_CONNECTION_REFUSED
12775정성태8/12/202118790스크립트: 22. 파이썬 - 윈도우 환경에서 개발한 Django 앱을 WSL 환경의 gunicorn을 이용해 실행
12774정성태8/11/20219133.NET Framework: 1087. C# - Collection 개체의 다중 스레드 접근 시 "Operations that change non-concurrent collections must have exclusive access" 예외 발생
... 31  32  33  [34]  35  36  37  38  39  40  41  42  43  44  45  ...