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

지난 글에 이어,

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

이번에는 ffmpeg 예제 중 마지막으로 "vaapi_encode.c", vaapi_transcode.c 파일을 FFmpeg.AutoGen으로 포팅하겠습니다.

우선 vaapi_encode.c를 포팅하면 이렇게 나옵니다.

// This example shows how to do VAAPI-accelerated encoding. now only support NV12 raw file, usage like: vaapi_encode 1920 1080 input.yuv output.h264

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

namespace vaapi_encode
{
    public unsafe class Program
    {
        static int width, height;

        static unsafe int set_hwframe_ctx(AVCodecContext* ctx, AVBufferRef* hw_device_ctx)
        {
            AVBufferRef* hw_frames_ref;
            AVHWFramesContext* frames_ctx = null;
            int err = 0;

            if ((hw_frames_ref = ffmpeg.av_hwframe_ctx_alloc(hw_device_ctx)) == null)
            {
                Console.WriteLine("Failed to create VAAPI frame context.");
                return -1;
            }

            frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data);
            frames_ctx->format = AVPixelFormat.AV_PIX_FMT_VAAPI;
            frames_ctx->sw_format = AVPixelFormat.AV_PIX_FMT_NV12;
            frames_ctx->width = width; 
            frames_ctx->height = height;
            frames_ctx->initial_pool_size = 20;
            if ((err = ffmpeg.av_hwframe_ctx_init(hw_frames_ref)) < 0)
            {
                Console.WriteLine($"Failed to initialize VAAPI frame context. Error code: {FFmpegHelper.av_err2str(err)}");
                ffmpeg.av_buffer_unref(&hw_frames_ref);
                return err;
            }

            ctx->hw_frames_ctx = ffmpeg.av_buffer_ref(hw_frames_ref);
            if (ctx->hw_frames_ctx == null)
            {
                err = ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            ffmpeg.av_buffer_unref(&hw_frames_ref);
            return err;
        }

        static unsafe int encode_write(AVCodecContext* avctx, AVFrame* frame, FileStream fs)
        {
            int ret = 0;
            AVPacket* enc_pkt;

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

            if ((ret = ffmpeg.avcodec_send_frame(avctx, frame)) < 0)
            {
                Console.WriteLine($"Error code: {FFmpegHelper.av_err2str(ret)}");
                goto end;
            }

            while (true)
            {
                ret = ffmpeg.avcodec_receive_packet(avctx, enc_pkt);
                if (ret != 0)
                {
                    break;
                }

                enc_pkt->stream_index = 0;
                ReadOnlySpan<byte> data = new ReadOnlySpan<byte>(enc_pkt->data, enc_pkt->size);
                fs.Write(data);
                ffmpeg.av_packet_unref(enc_pkt);
            }

        end:
            ffmpeg.av_packet_free(&enc_pkt);

            if (ret == ffmpeg.AVERROR_EOF)
            {
                return ret;
            }

            ret = (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN)) ? 0 : -1;
            return ret;
        }

        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 size, err;
            AVFrame* sw_frame = null, hw_frame = null;
            AVCodecContext* avctx = null;
            AVCodec* codec = null;
            string enc_name = "h264_vaapi";
            AVBufferRef* hw_device_ctx = null;

            width = 1920;
            height = 1080;
            size = width * height;

            string dirPath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? "";
            string input_filename = Path.Combine(dirPath, "..", "..", "..", "Samples", "sample_raw.h264");
            string output_filename = Path.Combine(dirPath, "test.h264");

            FileStream fin = File.OpenRead(input_filename);
            FileStream fout = File.OpenWrite(output_filename);

            err = ffmpeg.av_hwdevice_ctx_create(&hw_device_ctx, AVHWDeviceType.AV_HWDEVICE_TYPE_VAAPI, null, null, 0);

            if (err < 0)
            {
                Console.WriteLine($"Failed to create a VAAPI device. Error code: {FFmpegHelper.av_err2str(err)}");
                goto close;
            }

            if ((codec = ffmpeg.avcodec_find_encoder_by_name(enc_name)) == null)
            {
                Console.WriteLine("Could not find encoder.");
                err = -1;
                goto close;
            }

            if ((avctx = ffmpeg.avcodec_alloc_context3(codec)) == null)
            {
                err = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                goto close;
            }

            avctx->width = width;
            avctx->height = height;
            avctx->time_base = new AVRational() { num = 1, den = 25 };
            avctx->framerate = new AVRational() { num = 25, den = 1 };
            avctx->sample_aspect_ratio = new AVRational() { num = 1, den = 1 };

            avctx->pix_fmt = AVPixelFormat.AV_PIX_FMT_VAAPI;

            if ((err = set_hwframe_ctx(avctx, hw_device_ctx)) < 0)
            {
                Console.WriteLine("Failed to set hwframe context.");
                goto close;
            }

            if ((err = ffmpeg.avcodec_open2(avctx, codec, null)) < 0)
            {
                Console.WriteLine($"Cannot open video encoder codec. Error code: {FFmpegHelper.av_err2str(err)}");
                goto close;
            }

            while (true)
            {
                if ((sw_frame = ffmpeg.av_frame_alloc()) == null)
                {
                    err = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto close;
                }

                sw_frame->width = width;
                sw_frame->height = height;
                sw_frame->format = (int)AVPixelFormat.AV_PIX_FMT_NV12;

                if ((err = ffmpeg.av_frame_get_buffer(sw_frame, 0)) < 0)
                {
                    goto close;
                }

                Span<byte> data = new Span<byte>(sw_frame->data[0], size);
                if ((err = fin.Read(data)) <= 0)
                {
                    break;
                }

                data = new Span<byte>(sw_frame->data[1], size / 2);
                if ((err = fin.Read(data)) <= 0)
                {
                    break;
                }

                if ((hw_frame = ffmpeg.av_frame_alloc()) == null)
                {
                    err = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto close;
                }

                if ((err = ffmpeg.av_hwframe_get_buffer(avctx->hw_frames_ctx, hw_frame, 0)) < 0)
                {
                    Console.WriteLine($"Error code: {FFmpegHelper.av_err2str(err)}");
                    goto close;
                }

                if (hw_frame->hw_frames_ctx == null)
                {
                    err = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto close;
                }

                if ((err = ffmpeg.av_hwframe_transfer_data(hw_frame, sw_frame, 0)) < 0)
                {
                    Console.WriteLine($"Error while transferring frame data to surface. Error code: {FFmpegHelper.av_err2str(err)}");
                    goto close;
                }

                if ((err = (encode_write(avctx, hw_frame, fout))) < 0)
                {
                    Console.WriteLine("Failed to encode");
                    goto close;
                }

                ffmpeg.av_frame_free(&hw_frame);
                ffmpeg.av_frame_free(&sw_frame);
            }

            err = encode_write(avctx, null, fout);
            if (err == ffmpeg.AVERROR_EOF)
            {
                Console.WriteLine($"ffplay -autoexit -f h264 -framerate 25 {output_filename}");
                err = 0;
            }

            close:
            if (fin != null)
            {
                fin.Close();
            }

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

            ffmpeg.av_frame_free(&sw_frame);
            ffmpeg.av_frame_free(&hw_frame);
            ffmpeg.avcodec_free_context(&avctx);
            ffmpeg.av_buffer_unref(&hw_device_ctx);

            return err;
        }
    }
}

아마도 위의 소스 코드를 실행하면 av_hwdevice_ctx_create 단계에서 -12를 반환하는 오류를 접하는 분들이 있을 것입니다.

Failed to create a VAAPI device. Error code: Cannot allocate memory

이때 자신의 실행환경이 윈도우 운영체제라면 그럴 수 있습니다. ^^ 왜냐하면, 다음의 문서를 보면,

Platform API Availability
; https://trac.ffmpeg.org/wiki/HWAccelIntro

VAAPI의 경우 Windows를 지원하지 않고 있습니다. Wikipedia 문서를 보면,

Video Acceleration API
; https://en.wikipedia.org/wiki/Video_Acceleration_API

The VA-API specification was originally designed by Intel for its GMA (Graphics Media Accelerator) series of GPU hardware with the specific purpose of eventually replacing the XvMC standard as the default Unix multi-platform equivalent of Microsoft Windows DirectX Video Acceleration (DxVA) API, but today the API is no longer limited to Intel-specific hardware or GPUs.[3] Other hardware and manufacturers can freely use this open standard API for hardware accelerated video processing with their own hardware without paying a royalty fee.


윈도우에서의 DirectX처럼, 유닉스 플랫폼에서의 하드웨어 가속을 위한 것을 목표로 나온 듯하기 때문에 윈도우에서의 dxva2(Direct-X Video Acceleration API)로 인해 굳이 지원이 필요 없었던 것 같습니다. 아쉽게도 제가 이걸 테스트할 만한 리눅스 물리 머신이 없군요. ^^;

그래도 일단, 전체적인 소스 코드가 잘 동작하는지 확인을 하고 싶은데요, 이를 위해 다른 가속기로 대체할 수 있습니다. 그런데... 뭐가 좋을까요? dxva2의 경우 아쉽게도 디코더로만 제공되기 때문에 인코딩 예제로는 사용할 수 없습니다. 할 수 없군요, ^^ 지난번처럼 CUDA를 사용하도록 코드를 추가했습니다.

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

위의 소스 코드에는 "#define USE_CUDA" 전처리 상수를 포함시켰고, 기본적으로 CUDA를 사용하도록 설정했습니다. 아마도 리눅스에서 가속 기능을 사용할 수 있는 환경이라면 해당 상수를 주석 처리해 실행하면 정상적으로 동작할 것입니다.

참고로, 이전의 글에서도 설명했지만,

ffmpeg.exe - Intel Quick Sync Video(qsv)를 이용한 인코딩
; https://www.sysnet.pe.kr/2/0/12973

"ffmpeg -hwaccels" 명령어로, 현재 빌드된 ffmpeg의 vaaapi 지원 여부도 확인해야 합니다.

Brainiarc7/VAAPI-hwaccel-encode-Linux-Ffmpeg&Libav-setup.md
; https://gist.github.com/Brainiarc7/95c9338a737aa36d9bb2931bed379219

즉, "--enable-vaapi" 옵션을 포함한 상태로 빌드가 돼 있어야 합니다. (할 것입니다. ^^)




vaapi_encode 예제는 입력 파일도 중요한데요, 원본 c 파일의 주석에 보면,

// This example shows how to do VAAPI-accelerated encoding. now only support NV12 raw file, usage like: vaapi_encode 1920 1080 input.yuv output.h264

"NV12" 형식의 raw 파일을 입력으로 받는다고 합니다. 따라서, 일반적인 동영상 파일을 전달하면 안 되고, 이전에 설명했던,

C# - ffmpeg(FFmpeg.AutoGen)로 하드웨어 가속기를 이용한 비디오 디코딩 예제(hw_decode.c)
; https://www.sysnet.pe.kr/2/0/12932

위의 예제를 실행해 얻게 되는 NV12 raw 파일을 입력으로 전달해야 합니다. (물론, 여러분들이 가지고 있는 동일 포맷의 raw 파일을 사용해도 됩니다.)




자, 그다음 vaapi_transcode.c 파일의 포팅을 볼까요?

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

namespace vaapi_encode
{
    public unsafe class Program
    {
        static AVFormatContext* ifmt_ctx = null;
        static AVFormatContext* ofmt_ctx = null;
        static AVBufferRef* hw_device_ctx = null;
        static AVCodecContext* decoder_ctx = null;
        static int video_stream = -1;
        static AVStream* ost;
        static int initialized = 0;

        static unsafe AVPixelFormat get_vaapi_format(AVCodecContext* ctx, AVPixelFormat* pix_fmts)
        {
            AVPixelFormat* p;

            for (p = pix_fmts; *p != AVPixelFormat.AV_PIX_FMT_NONE; p++)
            {
                if (*p == AVPixelFormat.AV_PIX_FMT_VAAPI)
                {
                    return *p;
                }
            }

            Console.WriteLine("Unable to decode this file using VA-API.");
            return AVPixelFormat.AV_PIX_FMT_NONE;
        }

        static unsafe int open_input_file(string filename)
        {
            int ret;
            AVCodec* decoder = null;
            AVStream* video = null;

            fixed (AVFormatContext** pfmt_ctx = &ifmt_ctx)
            {
                if ((ret = ffmpeg.avformat_open_input(pfmt_ctx, filename, null, null)) < 0)
                {
                    Console.WriteLine($"Cannot open input file '{filename}', Error code: {FFmpegHelper.av_err2str(ret)}");
                    return ret;
                }
            }

            if ((ret = ffmpeg.avformat_find_stream_info(ifmt_ctx, null)) < 0)
            {
                Console.WriteLine($"Cannot find input stream information. Error code: {FFmpegHelper.av_err2str(ret)}");
                return ret;
            }

            ret = ffmpeg.av_find_best_stream(ifmt_ctx, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
            if (ret < 0)
            {
                Console.WriteLine($"Cannot find a video stream in the input file. Error code: {FFmpegHelper.av_err2str(ret)}");
                return ret;
            }

            video_stream = ret;

            if ((decoder_ctx = ffmpeg.avcodec_alloc_context3(decoder)) == null)
            {
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            video = ifmt_ctx->streams[video_stream];
            if ((ret = ffmpeg.avcodec_parameters_to_context(decoder_ctx, video->codecpar)) < 0)
            {
                Console.WriteLine($"{nameof(ffmpeg.avcodec_parameters_to_context)} error. Error code: {FFmpegHelper.av_err2str(ret)} ");
                return ret;
            }

            decoder_ctx->hw_device_ctx = ffmpeg.av_buffer_ref(hw_device_ctx);
            if (decoder_ctx->hw_device_ctx == null)
            {
                Console.WriteLine("A hardware device reference create failed.");
                return ffmpeg.AVERROR(ffmpeg.ENOMEM);
            }

            decoder_ctx->get_format = (AVCodecContext_get_format_func)get_vaapi_format;
            if ((ret = ffmpeg.avcodec_open2(decoder_ctx, decoder, null)) < 0)
            {
                Console.WriteLine($"Failed to open codec for decoding. Error code: {FFmpegHelper.av_err2str(ret)}");
            }

            return ret;
        }

        static unsafe int encode_write(AVCodecContext* encoder_ctx, AVPacket* enc_pkt, AVFrame* frame)
        {
            int ret = 0;

            ffmpeg.av_packet_unref(enc_pkt);

            if ((ret = ffmpeg.avcodec_send_frame(encoder_ctx, frame)) < 0)
            {
                Console.WriteLine($"Error during encoding. Error code: {FFmpegHelper.av_err2str(ret)}");
                goto end;
            }

            while (true)
            {
                ret = ffmpeg.avcodec_receive_packet(encoder_ctx, enc_pkt);
                if (ret != 0)
                {
                    break;
                }

                enc_pkt->stream_index = 0;
                ffmpeg.av_packet_rescale_ts(enc_pkt, ifmt_ctx->streams[video_stream]->time_base, ofmt_ctx->streams[0]->time_base);

                ret = ffmpeg.av_interleaved_write_frame(ofmt_ctx, enc_pkt);
                if (ret < 0)
                {
                    Console.WriteLine($"Error during writing data to output file. Error code: {FFmpegHelper.av_err2str(ret)}");
                    return -1;
                }
            }

        end:
            if (ret == ffmpeg.AVERROR_EOF)
            {
                return 0;
            }

            ret = (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN)) ? 0 : -1;
            return ret;
        }

        static unsafe int dec_enc(AVCodecContext* encoder_ctx, AVPacket* pkt, AVCodec* enc_codec)
        {
            AVFrame* frame;
            int ret = ffmpeg.avcodec_send_packet(decoder_ctx, pkt);
            if (ret < 0)
            {
                Console.WriteLine($"Error during decoding. Error code: {FFmpegHelper.av_err2str(ret)}");
                return ret;
            }

            while (ret >= 0)
            {
                if ((frame = ffmpeg.av_frame_alloc()) == null)
                {
                    return ffmpeg.AVERROR(ffmpeg.ENOMEM);
                }

                ret = ffmpeg.avcodec_receive_frame(decoder_ctx, frame);
                if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
                {
                    ffmpeg.av_frame_free(&frame);
                    return 0;
                }
                else if (ret < 0)
                {
                    Console.WriteLine($"Error while decoding. Error code: {FFmpegHelper.av_err2str(ret)}");
                    goto fail;
                }

                if (initialized == 0)
                {
                    encoder_ctx->hw_frames_ctx = ffmpeg.av_buffer_ref(decoder_ctx->hw_frames_ctx);
                    if (encoder_ctx->hw_frames_ctx == null)
                    {
                        ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                        goto fail;
                    }

                    encoder_ctx->time_base = FFmpegHelper.av_inv_q(decoder_ctx->framerate);
                    encoder_ctx->pix_fmt = AVPixelFormat.AV_PIX_FMT_VAAPI;
                    encoder_ctx->width = decoder_ctx->width;
                    encoder_ctx->height = decoder_ctx->height;

                    if ((ret = ffmpeg.avcodec_open2(encoder_ctx, enc_codec, null)) < 0)
                    {
                        Console.WriteLine($"Failed to open encode codec. Error code: {FFmpegHelper.av_err2str(ret)}");
                        goto fail;
                    }

                    if ((ost = ffmpeg.avformat_new_stream(ofmt_ctx, enc_codec)) == null)
                    {
                        Console.WriteLine("Failed to allocate stream for output format.");
                        ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                        goto fail;
                    }

                    ost->time_base = encoder_ctx->time_base;
                    ret = ffmpeg.avcodec_parameters_from_context(ost->codecpar, encoder_ctx);
                    if (ret < 0)
                    {
                        Console.WriteLine($"Failed to copy the stream parameters. Error code: {FFmpegHelper.av_err2str(ret)}");
                        goto fail;
                    }

                    if ((ret = ffmpeg.avformat_write_header(ofmt_ctx, null)) < 0)
                    {
                        Console.WriteLine($"Error while writing stream header. Error code: {FFmpegHelper.av_err2str(ret)}");
                        goto fail;
                    }

                    initialized = 1;
                }

                if ((ret = encode_write(encoder_ctx, pkt, frame)) < 0)
                {
                    Console.WriteLine("Error during encoding and writing");
                }

            fail:
                ffmpeg.av_frame_free(&frame);
                if (ret < 0)
                {
                    return ret;
                }
            }

            return 0;
        }

        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

            AVCodec* enc_codec;
            int ret = 0;
            AVPacket* dec_pkt;
            AVCodecContext* encoder_ctx = null;

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

            string output_filename = Path.Combine(dirPath, "test.mp4");
            if (File.Exists(output_filename) == true)
            {
                File.Delete(output_filename);
            }

            fixed (AVBufferRef** phw_device_ctx = &hw_device_ctx)
            fixed (AVFormatContext** pofmt_ctx = &ofmt_ctx)
            fixed (AVCodecContext** pdecoder_ctx = &decoder_ctx)
            {
                ret = ffmpeg.av_hwdevice_ctx_create(phw_device_ctx, AVHWDeviceType.AV_HWDEVICE_TYPE_VAAPI, null, null, 0);
                if (ret < 0)
                {
                    Console.WriteLine($"Failed to create a VAAPI device. Error code: {FFmpegHelper.av_err2str(ret)}");
                    return -1;
                }

                dec_pkt = ffmpeg.av_packet_alloc();
                if (dec_pkt == null)
                {
                    Console.WriteLine("Failed to allocate decode packet");
                    goto end;
                }

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

                if ((enc_codec = ffmpeg.avcodec_find_encoder_by_name(encode_codec_name)) == null)
                {
                    Console.WriteLine($"Could not find encoder: '{encode_codec_name}'");
                    ret = -1;
                    goto end;
                }

                if ((ret = ffmpeg.avformat_alloc_output_context2(pofmt_ctx, null, null, output_filename)) < 0)
                {
                    Console.WriteLine($"Failed to deduce output format from file extension. Error code: {FFmpegHelper.av_err2str(ret)}");
                    goto end;
                }

                if ((encoder_ctx = ffmpeg.avcodec_alloc_context3(enc_codec)) == null)
                {
                    ret = ffmpeg.AVERROR(ffmpeg.ENOMEM);
                    goto end;
                }

                ret = ffmpeg.avio_open(&ofmt_ctx->pb, output_filename, ffmpeg.AVIO_FLAG_WRITE);
                if (ret < 0)
                {
                    Console.WriteLine($"Cannot open output file. Error code: {FFmpegHelper.av_err2str(ret)}");
                    goto end;
                }

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

                    if (video_stream == dec_pkt->stream_index)
                    {
                        ret = dec_enc(encoder_ctx, dec_pkt, enc_codec);
                    }

                    ffmpeg.av_packet_unref(dec_pkt);
                }

                ffmpeg.av_packet_unref(dec_pkt);
                ret = dec_enc(encoder_ctx, dec_pkt, null);

                ret = encode_write(encoder_ctx, dec_pkt, null);

                ffmpeg.av_write_trailer(ofmt_ctx);

            end:
                fixed (AVFormatContext** pifmt_ctx = &ifmt_ctx)
                {
                    ffmpeg.avformat_close_input(pifmt_ctx);
                }

                ffmpeg.avformat_close_input(pofmt_ctx);

                ffmpeg.avcodec_free_context(pdecoder_ctx);
                ffmpeg.avcodec_free_context(&encoder_ctx);

                ffmpeg.av_buffer_unref(phw_device_ctx);
                ffmpeg.av_packet_free(&dec_pkt);
            }

            return 0;
        }
    }
}

아쉬운 점이 있다면, 일단 포팅은 했지만 정상적으로 동작하는 것을 확인할 수가 없습니다. ^^; VAAPI로는 테스트를 못 했고, CUDA로 가속을 바꿔 실행을 해봤지만 정상적으로 동영상 파일이 생성되지 않았습니다. 일단, 이 부분은 나중에 좀 더 ffmpeg에 익숙해지면 한 번 더 테스트를 해봐야겠습니다. (혹은, 위의 소스 코드를 리눅스에서 실행해 보신 분이 있다면 결과 부탁드립니다. ^^)

소스 코드는 다음의 github에 올려두었으니 참고하세요.

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




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







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

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

비밀번호

댓글 작성자
 




... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13191정성태12/11/20226327.NET Framework: 2077. C# - 직접 만들어 보는 SynchronizationContext파일 다운로드1
13190정성태12/9/20226866.NET Framework: 2076. C# - SynchronizationContext 기본 사용법파일 다운로드1
13189정성태12/9/20227674오류 유형: 831. Visual Studio - Windows Forms 디자이너의 도구 상자에 컨트롤이 보이지 않는 문제
13188정성태12/9/20226307.NET Framework: 2075. C# - 직접 만들어 보는 TaskScheduler 실습 (SingleThreadTaskScheduler)파일 다운로드1
13187정성태12/8/20226213개발 환경 구성: 654. openssl - CA로부터 인증받은 새로운 인증서를 생성하는 방법 (2)
13186정성태12/6/20224786오류 유형: 831. The framework 'Microsoft.AspNetCore.App', version '...' was not found.
13185정성태12/6/20225745개발 환경 구성: 653. Windows 환경에서의 Hello World x64 어셈블리 예제 (NASM 버전)
13184정성태12/5/20224976개발 환경 구성: 652. ml64.exe와 link.exe x64 실행 환경 구성
13183정성태12/4/20224927오류 유형: 830. MASM + CRT 함수를 사용하는 경우 발생하는 컴파일 오류 정리
13182정성태12/4/20225697Windows: 217. Windows 환경에서의 Hello World x64 어셈블리 예제 (MASM 버전)
13181정성태12/3/20225078Linux: 54. 리눅스/WSL - hello world 어셈블리 코드 x86/x64 (nasm)
13180정성태12/2/20225176.NET Framework: 2074. C# - 스택 메모리에 대한 여유 공간 확인하는 방법파일 다운로드1
13179정성태12/2/20224499Windows: 216. Windows 11 - 22H2 업데이트 이후 Terminal 대신 cmd 창이 뜨는 경우
13178정성태12/1/20225031Windows: 215. Win32 API 금지된 함수 - IsBadXxxPtr 유의 함수들이 안전하지 않은 이유파일 다운로드1
13177정성태11/30/20225759오류 유형: 829. uwsgi 설치 시 fatal error: Python.h: No such file or directory
13176정성태11/29/20224628오류 유형: 828. gunicorn - ModuleNotFoundError: No module named 'flask'
13175정성태11/29/20226387오류 유형: 827. Python - ImportError: cannot import name 'html5lib' from 'pip._vendor'
13174정성태11/28/20224854.NET Framework: 2073. C# - VMMap처럼 스택 메모리의 reserve/guard/commit 상태 출력파일 다운로드1
13173정성태11/27/20225618.NET Framework: 2072. 닷넷 응용 프로그램의 스레드 스택 크기 변경
13172정성태11/25/20225374.NET Framework: 2071. 닷넷에서 ESP/RSP 레지스터 값을 구하는 방법파일 다운로드1
13171정성태11/25/20225019Windows: 214. 윈도우 - 스레드 스택의 "red zone"
13170정성태11/24/20225271Windows: 213. 윈도우 - 싱글 스레드는 컨텍스트 스위칭이 없을까요?
13169정성태11/23/20225835Windows: 212. 윈도우의 Protected Process (Light) 보안 [1]파일 다운로드2
13168정성태11/22/20225161제니퍼 .NET: 31. 제니퍼 닷넷 적용 사례 (9) - DB 서비스에 부하가 걸렸다?!
13167정성태11/21/20225209.NET Framework: 2070. .NET 7 - Console.ReadKey와 리눅스의 터미널 타입
13166정성태11/20/20224965개발 환경 구성: 651. Windows 사용자 경험으로 WSL 환경에 dotnet 런타임/SDK 설치 방법
... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...