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

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13404정성태8/29/20233502오류 유형: 876. Windows - 키보드의 등호(=, Equals sign) 키가 눌리지 않는 경우
13403정성태8/21/20233320오류 유형: 875. The following signatures couldn't be verified because the public key is not available: NO_PUBKEY EB3E94ADBE1229CF
13402정성태8/20/20233394닷넷: 2137. ILSpy의 nuget 라이브러리 버전 - ICSharpCode.Decompiler
13401정성태8/19/20233616닷넷: 2136. .NET 5+ 환경에서 P/Invoke의 성능을 높이기 위한 SuppressGCTransition 특성 [1]
13400정성태8/10/20233479오류 유형: 874. 파이썬 - pymssql을 윈도우 환경에서 설치 불가
13399정성태8/9/20233462닷넷: 2135. C# - 지역 변수로 이해하는 메서드 매개변수의 값/참조 전달
13398정성태8/3/20234271스크립트: 55. 파이썬 - pyodbc를 이용한 SQL Server 연결 사용법
13397정성태7/23/20233772닷넷: 2134. C# - 문자열 연결 시 string.Create를 이용한 GC 할당 최소화
13396정성태7/22/20233486스크립트: 54. 파이썬 pystack 소개 - 메모리 덤프로부터 콜 스택 열거
13395정성태7/20/20233405개발 환경 구성: 685. 로컬에서 개발 중인 ASP.NET Core/5+ 웹 사이트에 대해 localhost 이외의 호스트 이름으로 접근하는 방법
13394정성태7/16/20233362오류 유형: 873. Oracle.ManagedDataAccess.Client - 쿼리 수행 시 System.InvalidOperationException
13393정성태7/16/20233543닷넷: 2133. C# - Oracle 데이터베이스의 Sleep 쿼리 실행하는 방법
13392정성태7/16/20233433오류 유형: 872. Oracle - ORA-01031: insufficient privileges
13391정성태7/14/20233478닷넷: 2132. C# - sealed 클래스의 메서드를 callback 호출했을 때 인라인 처리가 될까요?
13390정성태7/12/20233437스크립트: 53. 파이썬 - localhost 호출 시의 hang 현상
13389정성태7/5/20233466개발 환경 구성: 684. IIS Express로 호스팅하는 웹을 WSL 환경에서 접근하는 방법
13388정성태7/3/20233580오류 유형: 871. 윈도우 탐색기에서 열리지 않는 zip 파일 - The Compressed (zipped) Folder '[...].zip' is invalid. [1]파일 다운로드1
13387정성태6/28/20233614오류 유형: 870. _mysql - Commands out of sync; you can't run this command now
13386정성태6/27/20233688Linux: 61. docker - 원격 제어를 위한 TCP 바인딩 추가
13385정성태6/27/20233901Linux: 60. Linux - 외부에서의 접속을 허용하기 위한 TCP 포트 여는 방법
13384정성태6/26/20233639.NET Framework: 2131. C# - Source Generator로 해결하는 enum 박싱 문제파일 다운로드1
13383정성태6/26/20233394개발 환경 구성: 683. GPU 런타임을 사용하는 Colab 노트북 설정
13382정성태6/25/20233454.NET Framework: 2130. C# - Win32 API를 이용한 윈도우 계정 정보 (예: 마지막 로그온 시간)파일 다운로드1
13381정성태6/25/20233849오류 유형: 869. Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
13380정성태6/24/20233288스크립트: 52. 파이썬 3.x에서의 동적 함수 추가
13379정성태6/23/20233301스크립트: 51. 파이썬 2.x에서의 동적 함수 추가
1  2  3  4  5  6  7  8  [9]  10  11  12  13  14  15  ...