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

비밀번호

댓글 작성자
 




... 151  152  [153]  154  155  156  157  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1227정성태2/3/201229270.NET Framework: 299. 해당 어셈블리가 Debug 빌드인지, Release 빌드인지 알아내는 방법파일 다운로드1
1226정성태1/28/201270207.NET Framework: 298. 홀 펀칭(Hole Punching)을 이용한 Private IP 간 통신 - C# [15]파일 다운로드3
1225정성태1/24/201225876.NET Framework: 297. 특정 EXE 파일의 실행을 Internet Explorer처럼 "Protected Mode"로 실행하는 방법 [1]파일 다운로드1
1224정성태1/21/201237365개발 환경 구성: 139. 아마존 EC2에 새로 추가된 "1년 무료 Windows 서버 인스턴스"가 있다는데, 직접 만들어 볼까요? ^^ [11]
1223정성태1/20/201227327.NET Framework: 296. 괜찮은 문자열 해시함수? - 두 번째 이야기 [1]파일 다운로드1
1222정성태1/18/201235044.NET Framework: 295. 괜찮은 문자열 해시 함수? [4]파일 다운로드1
1221정성태1/17/201224056오류 유형: 147. System.Runtime.InteropServices.COMException (0x80005000)
1220정성태1/15/201224244.NET Framework: 294. Master web.config 파일을 수정하려면?파일 다운로드1
1219정성태1/15/201226599.NET Framework: 293. Microsoft PowerPoint 슬라이드를 HTML 파일로 ".files" 폴더 없이 저장하는 방법 (C# 코드)파일 다운로드1
1218정성태1/15/201239152.NET Framework: 292. RSACryptoServiceProvider의 공개키와 개인키 구분 [1]파일 다운로드2
1217정성태1/14/201241258.NET Framework: 291. .NET에서 WAV, MP3 파일 재생하는 방법 [1]파일 다운로드1
1216정성태1/14/201229957오류 유형: 146. Microsoft Visual C++ 재배포 패키지 - 설치 로그 남기는 방법 [1]
1215정성태1/9/201227514제니퍼 .NET: 20. 제니퍼 닷넷 적용 사례 (3) - '닷넷'이 문제일까? '닷넷 개발자'가 문제일까? [6]
1214정성태1/3/201224342제니퍼 .NET: 19. 제니퍼 닷넷 설치/제거 방법 - IIS
1213정성태12/31/201124308.NET Framework: 290. WCF - 접속된 클라이언트의 IP 주소 알아내는 방법 - 두 번째 이야기
1212정성태12/31/201124387오류 유형: 145. The trust relationship between this workstation and the primary domain failed.
1211정성태12/31/201129168.NET Framework: 289. WindowsFormsHost를 사용하는 XBAP 응용 프로그램파일 다운로드1
1210정성태12/30/201148146.NET Framework: 288. FFmpeg.exe를 이용한 C# 동영상 인코더 예제 [9]파일 다운로드1
1209정성태12/29/201122794개발 환경 구성: 138. BizTalk 2006 설치 방법
1208정성태12/28/201145812.NET Framework: 287. Excel Sheet를 WinForm에서 사용하는 방법 [8]파일 다운로드2
1207정성태12/26/201125060.NET Framework: 286. x86/x64로 구분된 코드를 포함하는 경우, 다중으로 어셈블리를 만들어야 할까요?파일 다운로드1
1206정성태12/25/201126087.NET Framework: 285. Shader 강좌와 함께 배워보는 XNA Framework (3) - 텍스처 매핑 예제파일 다운로드1
1205정성태12/25/201131708.NET Framework: 284. Thread 개체의 Interrupt와 Abort의 차이점파일 다운로드1
1204정성태12/22/201125205.NET Framework: 283. MEF를 ASP.NET에 성능 손실 없이 적용하려면? [7]
1203정성태12/21/201125576제니퍼 .NET: 18. MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과! [6]
1202정성태12/21/201126027오류 유형: 144. The database '...' cannot be opened because it is version 661.
... 151  152  [153]  154  155  156  157  158  159  160  161  162  163  164  165  ...