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)를 이용한 비디오 디코딩 예제(decode_video.c) - 두 번째 이야기

예전 글에서,

C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 디코딩 예제(decode_video.c)
; https://www.sysnet.pe.kr/2/0/12924

비디오 프레임을 디코딩 후 PGM 포맷으로 저장한 데이터가 정상적으로 보이지 않는다고 했는데요, 저도 이젠 나름 ^^ 지식이 쌓이다 보니 해당 문제를 다시 살펴볼 여유가 생겼습니다.

일단, 디코딩이 잘 되는지 "C# - ffmpeg(FFmpeg.AutoGen) - 비디오 프레임 디코딩" 글에서처럼 YUV 데이터를 RGB로 변환해 저장해 보았는데요,

while (ret >= 0)
{
    ret = ffmpeg.avcodec_receive_frame(pCodecContext, frame);
    if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN) || ret == ffmpeg.AVERROR_EOF)
    {
        return true;
    }
    else if (ret < 0)
    {
        Console.WriteLine("Error during decoding");
        return false;
    }

    AVPixelFormat format = (AVPixelFormat)frame->format;
    if (format == AVPixelFormat.AV_PIX_FMT_YUV420P)
    {
        byte* yData = frame->data[0];
        byte* uData = frame->data[1];
        byte* vData = frame->data[2];

        int yStride = frame->linesize[0];
        int uStride = frame->linesize[1];
        int vStride = frame->linesize[2];

        {
            byte Y, U, V;
            int r, g, b;
            Bitmap bitmap = new Bitmap(frame->width, frame->height, PixelFormat.Format24bppRgb);

            for (int y = 0; y < frame->height; y++)
            {
                for (int x = 0; x < frame->width; x++)
                {
                    Y = yData[yStride * y + x];
                    U = uData[uStride * (y / 2) + x / 2];
                    V = vData[vStride * (y / 2) + x / 2];

                    YUV2RGB_ByMS(Y, U, V, out r, out g, out b);
                    Color pixel = Color.FromArgb(0, r, g, b);
                    bitmap.SetPixel(x, y, pixel);
                }
            }

            bitmap.Save(Path.Combine(outfileDirPath, "yuv422p_" + pCodecContext->frame_number + ".bmp"));
        }
    }
}

위의 소스 코드를 실행해 보면, BMP 파일의 출력 결과가 깨져 나옵니다.

mepg1video_yuv_img_1.png

즉, 애당초 해당 코드는 정상적으로 video frame을 생성하지 못 하고 있었던 것입니다.




그래서 일단 decode_video.c가 av_parser_parse2를 이용해 디코딩하는 것을 "C# - ffmpeg(FFmpeg.AutoGen) - decoding 과정" 글에서처럼 av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로 바꿨습니다.

동일하게 mpeg1video 코덱이 선택되었고,

{
    videoStream = av_context->streams[videoStreamIndex];
    videoContext = ffmpeg.avcodec_alloc_context3(videoDecoder);
    ret = ffmpeg.avcodec_parameters_to_context(videoContext, videoStream->codecpar);
    ret = ffmpeg.avcodec_open2(videoContext, videoDecoder, null);
}

string codecName = Marshal.PtrToStringAnsi(new IntPtr(videoDecoder->name));
Console.WriteLine(codecName); // mpeg1video

YUV420P 프레임을 정상적으로 해석해 BMP 파일로도 저장이 되었습니다. 그렇기 때문에 Y(Luma channel) 값을 pgm 파일로 저장하는 코드 역시 정상적으로 gray 포맷으로 출력이 되었습니다.

PGM File Viewer (browser-based)
; https://smallpond.ca/jim/photomicrography/pgmViewer/index.html

(사진은 유튜브 영상 "디에이드"의 "안다은" 님이고 사용을 허락받고 올립니다.)
mepg1video_yuv_img_2.png

그러니까 애당초 decode_video.c가 정상적으로 동작하지 않았던 코드였습니다. ^^;




참고로, 프레임을 명시적으로 AV_PIX_FMT_GRAY8로 변환해도 됩니다. 이를 위해 YUV to GRAY 변경을 SwsContext를 이용해 다음과 같이 수행할 수 있습니다.

SwsContext* sws_ctx = ffmpeg.sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format,
    frame->width, frame->height, AVPixelFormat.AV_PIX_FMT_GRAY8, ffmpeg.SWS_BICUBIC, null, null, null);

byte* pgray8 = (byte*)ffmpeg.av_calloc((ulong)(frame->width * frame->height), 1);
byte*[] ppgray8 = new byte*[1];
ppgray8[0] = pgray8;

int[] gray_stride = new int[1];
gray_stride[0] = frame->width;

int result = ffmpeg.sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, ppgray8, gray_stride);
// result == 1080 (1920 x 1080 인 경우)

Console.WriteLine($"saving frame {frame->coded_picture_number}");

string outputFile = Path.Combine(@"C:\temp\output", "noname_" + frame->coded_picture_number + ".pgm");
pgm_save(pgray8, frame->width, frame->width, frame->height, outputFile);

ffmpeg.av_free(pgray8);

ffmpeg.sws_freeContext(sws_ctx);

위에서는 pgm_save에 frame->data[0]을 전달하지 않고, sws_scale로 변환된 pgray8 포인터를 전달하고 있습니다. 당연히 저렇게 출력한 pgm 파일도 Y 채널만 출력한 pgm 파일과 동일한 흑백 영상이 나옵니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




av_read_frame/avcodec_send_packet/avcodec_receive_frame 방식으로는 비디오가 잘 해석이 되는데, av_parser_parse2를 이용한 버전은 왜 안 되는 것일까요? 음... 이 정도까지는 아직 제 수준에서 이해할 수가 없군요. ^^

decode_video.c 파일의 버그인지, 아니면 원래 저렇게는 안 되는 것인지 알 수가 없습니다.

av_parser_parse2가 잘 동작하는 코드가 있긴 했습니다. 바로 오디오 파일을 디코딩하는 경우입니다.

C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP2 오디오 파일 디코딩 예제(decode_audio.c)
; https://www.sysnet.pe.kr/2/0/12933

C# - ffmpeg(FFmpeg.AutoGen)를 이용해 MP3 오디오 파일 인코딩/디코딩하는 예제
; https://www.sysnet.pe.kr/2/0/12939

하지만 비디오 디코딩에도 사용하는 소스 코드가 있는 걸로 봐서는,

MediaPlayer/MediaPlayer/FFmpegDecoder.cpp
; https://github.com/xc724655471/MediaPlayer/blob/13f0f518fd4e9e0ecb763ca1b23bf12bbd1ab249/MediaPlayer/FFmpegDecoder.cpp

분명히 디코딩 과정은 잘 동작했을 것으로 추정은 됩니다.




혹시나 싶어서, gray 포맷의 (mpeg1video는 오직 yuv420p만 지원하므로) h264 동영상을 전달하면 잘 동작할까요?

D:\media_sample> ffmpeg -i mp4video_sample2.mp4 -pix_fmt gray mp4video_sample2_gray.mp4

gray mp4 파일을 decode_video.c의 입력으로 전달하고 codec id만 AVCodecID.AV_CODEC_ID_H264로 바꿔보았는데요, 그래도 여전히 정상적인 비디오 프레임이 나오지는 않았습니다. 어쩌면 저 작업이 굳이 필요하지도 않았는데요, 왜냐하면 pix_fmt을 gray로 지정하긴 했지만 ffprobe로 확인해 보면 mp4video_sample2_gray.mp4는 여전히 yuv420p로 나오기 때문입니다. 즉, 영상만 흑백이고 pix_fmt은 변함없이 YUV420P입니다.

비록 pgm 저장은 우회해서 성공했지만, decode_vide.c 소스 코드는 끝내 미스터리로 남는군요. ^^




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







[최초 등록일: ]
[최종 수정일: 2/8/2022]

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

비밀번호

댓글 작성자
 



2022-02-08 12시45분
[이승준] 저도 av_parser_parse2 이건 사용해 보지 않았어서 뭔가 뭔가 싶었는데요.
찾아보니 기본적으로 컨테이너를 지원하지 않는 api로 보입니다. 즉 비디오/오디오 압축데이타만 지원한다는거죠.
그래서 오디오에서는 정상 동작한것으로 보입니다. 오디오 압축 파일은 컨테이너가 없거든요.
https://titanwolf.org/Network/Articles/Article?AID=d650b07d-cc80-4c8b-be0c-cf04e0da6435
참고링크 입니다.
아마도 비디오만 추출해서 테스트 해 보시면 정상동작할거라고 보여집니다.
[guest]
2022-02-08 01시42분
퇴근 후에 한번 해보겠습니다. ^^ 이제서야 decode_video.c의 수수께끼가 풀리는군요. 게다가 av_parser_parse2와 av_read_frame의 차이점도 자연스럽게 이해되고. ^^
정성태

... 91  92  93  94  [95]  96  97  98  99  100  101  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11559정성태6/25/201828653개발 환경 구성: 384. 영문 설정의 Windows 10 명령행 창(cmd.exe)의 한글 지원 [6]
11558정성태6/24/201822297.NET Framework: 775. C# 7.3 - unmanaged(blittable) 제네릭 제약파일 다운로드1
11557정성태6/22/201821764.NET Framework: 774. C# - blittable 타입이란?파일 다운로드1
11556정성태6/19/201828632.NET Framework: 773. C# 7.3 - 구조체의 고정 크기를 갖는 fixed 배열 필드에 대한 직접 접근 가능 [1]파일 다운로드1
11555정성태6/18/201820309.NET Framework: 772. C# 7.3 - 사용자 정의 타입에 fixed 적용 가능(Custom fixed)파일 다운로드1
11554정성태6/17/201822343.NET Framework: 771. C# 7.3 - 자동 구현 속성에 특성 적용 가능(Attribute on backing field)
11553정성태6/15/201821904.NET Framework: 770. C# 7.3 - 개선된 메서드 선택 규칙 3가지(Improved overload candidates)파일 다운로드1
11552정성태6/15/201823905.NET Framework: 769. C# 7.3에서 개선된 문법 4개(Support == and != for tuples, Ref Reassignment, Constraints, Stackalloc initializers)파일 다운로드1
11551정성태6/14/201820504개발 환경 구성: 383. BenchmarkDotNet 사용 시 주의 사항
11550정성태6/13/201820404.NET Framework: 768. BenchmarkDotNet으로 Span<T> 성능 측정 [2]
11549정성태6/13/201822092개발 환경 구성: 382. BenchmarkDotNet에서 생성한 BuildPlots.R 파일을 실행하는 방법
11548정성태6/13/201819298오류 유형: 470. .NET Core + BenchmarkDotNet 실행 시 프레임워크를 찾지 못하는 문제
11547정성태6/13/201824413.NET Framework: 767. BenchmarkDotNet 라이브러리 소개파일 다운로드1
11546정성태6/12/201824555.NET Framework: 766. C# 7.2의 특징 - GC 및 메모리 복사 방지를 위한 struct 타입 개선 [9]파일 다운로드1
11545정성태6/11/201822808오류 유형: 469. .NET Core 프로젝트를 Visual Studio에서 실행 시 System.BadImageFormatException 발생하는 경우 [1]
11544정성태6/10/201822390.NET Framework: 765. C# 7.2 - 숫자 리터럴의 선행 밑줄과 뒤에 오지 않는 명명된 인수
11543정성태6/9/201821966.NET Framework: 764. C# 7.2 - private protected 접근자 추가파일 다운로드1
11542정성태6/9/201860382개발 환경 구성: 381. Azure Web App 확장 예제 - Remove Custom Headers
11541정성태6/9/201819527개발 환경 구성: 380. Azure Web App 확장 배포 방법 [1]
11540정성태6/9/201820296개발 환경 구성: 379. Azure Web App 확장 예제 제작 [2]
11539정성태6/8/201820127.NET Framework: 763. .NET Core 2.1 - Tiered Compilation 도입파일 다운로드1
11538정성태6/8/201819413.NET Framework: 762. .NET Core 2.1 - 확장 도구(Tools) 관리 [1]
11537정성태6/8/201823916.NET Framework: 761. C# - SmtpClient로 SMTP + SSL/TLS 서버를 이용하는 방법 [5]
11536정성태6/7/201821572.NET Framework: 760. Microsoft Build 2018 - The future of C# 동영상 내용 정리 [1]파일 다운로드1
11535정성태6/7/201823385.NET Framework: 759. C# - System.Span<T> 성능 [1]
11534정성태6/6/201829120.NET Framework: 758. C# 7.2 - Span<T> [6]
... 91  92  93  94  [95]  96  97  98  99  100  101  102  103  104  105  ...