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

비밀번호

댓글 작성자
 




... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11861정성태4/6/201919618디버깅 기술: 126. windbg - .NET x86 CLR2/CLR4 EXE의 EntryPoint
11860정성태4/5/201923470오류 유형: 527. Visual C++ 컴파일 오류 - error C2220: warning treated as error - no 'object' file generated
11859정성태4/4/201920699디버깅 기술: 125. WinDbg로 EXE의 EntryPoint에서 BP 거는 방법
11858정성태3/27/201921608VC++: 129. EXE를 LoadLibrary로 로딩해 PE 헤더에 있는 EntryPoint를 직접 호출하는 방법파일 다운로드1
11857정성태3/26/201919514VC++: 128. strncpy 사용 시 주의 사항(Linux / Windows)
11856정성태3/25/201919772VS.NET IDE: 134. 마이크로소프트의 CoreCLR 프로파일러 리눅스 예제를 Visual Studio F5 원격 디버깅하는 방법 [1]파일 다운로드1
11855정성태3/25/201921923개발 환경 구성: 436. 페이스북 HTTPS 인증을 localhost에서 테스트하는 방법
11854정성태3/25/201917576VS.NET IDE: 133. IIS Express로 호스팅하는 사이트를 https로 접근하는 방법
11853정성태3/24/201920385개발 환경 구성: 435. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면? - 두 번째 이야기 [1]
11852정성태3/20/201919605개발 환경 구성: 434. 존재하지 않는 IP 주소에 대한 Dns.GetHostByAddress/gethostbyaddr/GetNameInfoW 실행이 느리다면?파일 다운로드1
11851정성태3/19/201923359Linux: 8. C# - 리눅스 환경에서 DllImport 대신 라이브러리 동적 로드 처리 [2]
11850정성태3/18/201922387.NET Framework: 813. C# async 메서드에서 out/ref/in 유형의 인자를 사용하지 못하는 이유
11849정성태3/18/201921773.NET Framework: 812. pscp.exe 기능을 C#으로 제어하는 방법파일 다운로드1
11848정성태3/17/201918497스크립트: 14. 윈도우 CMD - 파일이 변경된 경우 파일명을 변경해 복사하고 싶다면?
11847정성태3/17/201922966Linux: 7. 리눅스 C/C++ - 공유 라이브러리 동적 로딩 후 export 함수 사용 방법파일 다운로드1
11846정성태3/15/201921611Linux: 6. getenv, setenv가 언어/운영체제마다 호환이 안 되는 문제
11845정성태3/15/201921763Linux: 5. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) [3]
11844정성태3/14/201923082개발 환경 구성: 434. Visual Studio 2019 - 리눅스 프로젝트를 이용한 공유/실행(so/out) 프로그램 개발 환경 설정 [1]파일 다운로드1
11843정성태3/14/201918036기타: 75. MSDN 웹 사이트를 기본으로 영문 페이지로 열고 싶다면?
11842정성태3/13/201916383개발 환경 구성: 433. 마이크로소프트의 CoreCLR 프로파일러 예제를 Visual Studio CMake로 빌드하는 방법 [1]파일 다운로드1
11841정성태3/13/201916695VS.NET IDE: 132. Visual Studio 2019 - CMake의 컴파일러를 기본 g++에서 clang++로 변경
11840정성태3/13/201918319오류 유형: 526. 윈도우 10 Ubuntu App 환경에서는 USB 외장 하드 접근 불가
11839정성태3/12/201922237디버깅 기술: 124. .NET Core 웹 앱을 호스팅하는 Azure App Services의 프로세스 메모리 덤프 및 windbg 분석 개요 [3]
11838정성태3/7/201925867.NET Framework: 811. (번역글) .NET Internals Cookbook Part 1 - Exceptions, filters and corrupted processes [1]파일 다운로드1
11837정성태3/6/201939807기타: 74. 도서: 시작하세요! C# 7.3 프로그래밍 [10]
11836정성태3/5/201923377오류 유형: 525. Visual Studio 2019 Preview 4/RC - C# 8.0 Missing compiler required member 'System.Range..ctor' [1]
... 76  77  78  79  80  81  82  [83]  84  85  86  87  88  89  90  ...