자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다. 
  • 제품설치/등록 오류 문의: 설치/등록 Q&A 이용 (제품 구매 고객 한정)

본 게시판은 개발자들이 자유롭게 질문과 답변을 공유하는 게시판입니다.
* 따라서 최대한 정중하게 질문을 올려 주세요.
* 질문을 상세히 작성해 주실 수록 좋은 답변이 올라 옵니다.
* 다른 분들도 참고할 수 있도록 결과 댓글 필수(또는 감사 댓글)
(결과 댓글을 달지 않는 경우 다음 질문에 대한 답변이 달리지 않는 불이익이 있을 수 있습니다.)
-----------------------------------------------------------------------------------------------
 

안녕하세요~~

날씨는 따듯해 지는데 코로나의 기세는 꺾일 줄 모르네요.

건강 조심하세요~~

 

질문 올립니다.

아래에서 위 두개의 procedure는 맨 아래 함수 decode_packet에서 참조하는 것입니다.

핵심은 decode_packet 함수입니다.

 

FFMpeg의 AVFrame을 TImage에 그리는 것이 목표입니다.

안되는건 아니구요.

비디오를 디코딩하여 마지막으로 프레임을 TImage로 그려주는데 CPU 점유율을 최대한 줄여보고자 합니다.

 

 

procedure cvBuffToFMXBitmap(const SrcData: pByte; const FMXBitmap: TBitmap; i_Width, i_Height : Integer); inline;  // 버퍼를 TImage에 그리는 프로시저

Var

  BitmapData: TBitmapData;

  i : Integer;

  DestData: pByte;

  pf: Integer;

begin

  Assert(Assigned(SrcData) and Assigned(FMXBitmap));

  try

    FMXBitmap.SetSize(i_Width, i_Height);

 

    if FMXBitmap.Map(TMapAccess.Write, BitmapData) then

      begin

        try

          FMXBitmap.Canvas.BeginScene;

          DestData := pByte(BitmapData.Data);

          pf := PixelFormatBytes[FMXBitmap.PixelFormat];

          for i := 0 to BitmapData.Width * BitmapData.Height - 1 do

            begin

              DestData[i * pf + 0] := SrcData[i * 3 + 0];

              DestData[i * pf + 1] := SrcData[i * 3 + 1];

              DestData[i * pf + 2] := SrcData[i * 3 + 2];

              DestData[i * pf + 3] := $FF;

            end;

        finally

          FMXBitmap.Unmap(BitmapData);

        FMXBitmap.Canvas.EndScene;

        end;

      end;

    finally

    end;

end;

 

procedure cvToFMXBitmap(const IpImage: pIplImage; const FMXBitmap: TBitmap); inline;  // iplframe을 TImage에 그리는 프로시저

Var

  BitmapData: TBitmapData;

  i, j : Integer;

  SrcData, DestData: pByte;

  nC: Integer;

  pf: Integer;

begin

  SrcData := nil;

  Assert(Assigned(IpImage) and Assigned(FMXBitmap));

  if (IpImage^.Width > 0) and (IpImage^.Height > 0) and Assigned(IpImage^.imageData) then

    try

      nC := IpImage^.nChannels;

      With IpImage^ do

      begin

        SrcData := AllocMem(Width * Height * nC);

        Move(imageData^, SrcData^, Width * Height * nC);

      end;

 

      FMXBitmap.SetSize(IpImage^.Width, IpImage^.Height);

 

      if FMXBitmap.Map(TMapAccess.Write, BitmapData) then

        try

        FMXBitmap.Canvas.BeginScene;

          DestData := pByte(BitmapData.Data);

          pf := PixelFormatBytes[FMXBitmap.PixelFormat];

          for i := 0 to BitmapData.Width * BitmapData.Height - 1 do

            begin

              DestData[i * pf + 0] := SrcData[i * nC + 0];

              DestData[i * pf + 1] := SrcData[i * nC + 1];

              DestData[i * pf + 2] := SrcData[i * nC + 2];

              DestData[i * pf + 3] := $FF;

            end;

        finally

          FMXBitmap.Unmap(BitmapData);

        FMXBitmap.Canvas.EndScene;

        end;

    finally

      if Assigned(SrcData) then

        FreeMem(SrcData);

    end;

end;

 

// 비디오의 AVFrame을 디코딩하여 TImage에 그리는 함수

function decode_packet(avctx: PAVCodecContext; rpacket: PAVPacket): Integer;

var

  frame, sw_frame: PAVFrame;

  tmp_frame: PAVFrame;

  buffer: PByte;

  size: Integer;

  ret: Integer;

  swsCtx: PSwsContext;

label

  fail;

begin

  frame := nil;

  sw_frame := nil;

  buffer := nil;

 

  ret := avcodec_send_packet(avCtx, rpacket);  // avCtx는 AVCodecContext, rpacket는 프레임을 읽은 패킷정보

  if ret < 0 then

    begin

      Result := ret;

      Exit;

    end;

 

  while True do

    begin

      frame := av_frame_alloc();

      sw_frame := av_frame_alloc();

      ret := avcodec_receive_frame(avCtx, frame);

      if (ret = AVERROR_EAGAIN) or (ret = AVERROR_EOF) then

        begin

          av_frame_free(@frame);

          av_frame_free(@sw_frame);

          Result := 0;

          Exit;

        end

      else if ret < 0 then

        goto fail;

 

      if TAVPixelFormat(frame.format) = hw_pix_fmt then

        begin

          (* retrieve data from GPU to CPU *)

          ret := av_hwframe_transfer_data(sw_frame, frame, 0);

          if ret < 0 then

            begin

              goto fail;

            end;

          tmp_frame := sw_frame;

        end

      else

      tmp_frame := frame;

 

//1) --------------------------------------------------------------------------------------------------

      swsCtx := sws_getCachedContext(nil, tmp_frame^.Width, tmp_frame^.Height, integer(tmp_frame^.format), tmp_frame^.Width, tmp_frame^.Height, Integer(AV_PIX_FMT_BGR24), SWS_BILINEAR, nil, nil, nil);

      sws_scale(swsCtx, @tmp_frame^.data, @tmp_frame^.linesize, 0, tmp_frame^.Height, @iplframe^.imageData, @linesize);

      cvToFMXBitmap(iplframe, Image1.Bitmap);  // pIplImage 인 iplframe을 TImage에 그려주는 프로시저 CPU점유율 안습

// -----------------------------------------------------------------------------------------------------

 

// 2) -------------------------------------------------------------------------------------------------

      size := av_image_get_buffer_size(AV_PIX_FMT_BGR24, tmp_frame^.width, tmp_frame^.height, 1);

      buffer := av_malloc(size);

      av_image_copy_to_buffer(buffer, size, @tmp_frame.data[0], @tmp_frame.linesize[0], AV_PIX_FMT_BGR24, tmp_frame.width, tmp_frame.height, 1);

      av_image_fill_arrays(@iplframe^.imageData, @linesize, buffer, AV_PIX_FMT_BGR24, avCtx.width, avCtx.height, 1);  // buffer를 바로 TImage에 그려주면 안되는가? 굳이 iplframe에 넣고 다시 TImage에 그려야 하는가?

      cvToFMXBitmap(iplframe, Image1.Bitmap);

// -----------------------------------------------------------------------------------------------------

 

fail:

      av_frame_free(@frame);

      av_frame_free(@sw_frame);

      av_freep(@buffer);

      if ret < 0 then

        begin

          Result := ret;

          Exit;

        end;

    end;

    Result := 0;

end;

 

※ 핵심 목표는 AVFrame의 프레임을 TImage에 나타내고 싶습니다.

그래서 위의 1) 과 2) 의 두가지 방법이 있는데 

1)의 경우는 cvToFMXBitmap(iplframe, Image1.Bitmap); 하기전까지는 CPU점유율이 아주 낮습니다.

2)의 경우는 그냥 pbyte형인 buffer를 TImage에 바로 그려주면 더 좋겠는데 일단 iplframe(OpenCV의 pIplImage)에 밀어넣고 이것을 다시 cvToFMXBitmap(iplframe, Image1.Bitmap); 하여

  Timage에 그립니다.

두가지의 경우 모두 cvToFMXBitmap(iplframe, Image1.Bitmap); 에서 CPU점유율이 높다는 점입니다.

당연할 수도 있습니다. 동영상의 사이즈가 1280정도로 크기 때문이라 볼 수 있는데 저는 AVFrame -> TImage에 가장 빠르고 CPU에 부담을 주지 않는 방법을 고민중입니다.

 

2)와 같이 av_image_copy_to_buffer까지만 한 상태에서 buffer를 TImage에 그려보았는데 아래와 같이 영상이 여러개가 나오더군요.

맨위의 cvBuffToFMXBitmap 프로시저를 사용해 cvBuffToFMXBitmap(buffer, Image1.Bitmap, sw_frame.width, sw_frame.height); 이렇게 해봤는데 영상이 하나의 프레임이 아닌 여러개가 나오더라구요.

버퍼를 TImage에 그리면.png

 

 부분만 해결이 되어도 CPU 부담은 많이 적어질 거 같은데 도통 모르겠습니다 ㅠ.ㅠ

 

AVFrame 또는 pIplImage 또는 buffer를 TImage에 그리는 가장 빠른 방법이 어떤게 있을까요?

감사합니다.

번호 제목 글쓴이 날짜 조회 수
공지 [프로그래밍 강의] 2021.6~2021.12 관리자 2015.01.22 20049
공지 유용한 관련 사이트 관리자2 2014.03.20 58957
공지 본 게시판은 개발자 여러분들의 질문과 답변을 공유하는 공간입니다. 관리자 2012.01.10 102574
2799 폼에 콤보 박스 200개(item 60개) 실행시 시간 오래 걸림 현상 [2] file 포세가이 2020.03.05 643
2798 libsodium 모바일앱에서 사용하기 [2] file 여유와행복 2020.02.27 355
2797 안드로이드 9.0 버젼일때 앱 튕기는 현상 [2] 희망나라 2020.03.06 291
2796 리눅스서버에 미들웨어 서버 실행과 관련하여 궁금한 사항이 있습니다. 루키 2020.03.06 138
2795 fast report가 안보입니다. [5] file icj 2020.03.03 199
2794 QA Audit, QA Metric 분석 시 오류 발생 [1] ain.yoo 2020.03.05 152
2793 MacOS, iOS시뮬레이터로 어플 테스트중인데, dlopen(libmidas.dylib, 1): image not found .... [2] samsmin 2020.03.04 192
2792 빨간 주석 줄이 위에 나타납니다... file 거북선생 2020.03.04 142
2791 캔들차트 [1] pky4437 2020.03.02 1294
2790 안드로이드에서 음성인식 및 TTS(Text To Speach) 델마당쇠 2020.03.03 832
2789 [공유] 델파이 소스코드로 델파이 버전을 알수 있나요? 험프리 2020.03.03 536
2788 로딩화면 관련해서 질문드립니다. [1] 델마당쇠 2020.03.02 335
2787 fast report 재설치 문의 [3] sub3 2020.02.28 248
2786 파이어몽키에서 TPanel에 Form을 넣을 수는 없는 걸까요? [2] 아크나톤 2020.02.28 172
2785 Delphi 10.3 에서 iOS 시뮬레이터 사용 가능한 방법이 없나요? [2] samsmin 2020.02.25 348
2784 디버깅 시 tList<t>.List w.a.t.c.h. 이상 작동 [9] 신재국 2020.02.25 403
» FFMpeg 디코딩된 AVFrame을 최대한 빨리 TImage에 그리는 방법 질문. [4] file 아크나톤 2020.02.22 3332
2782 작업 시간 초과 SetTimeouts 질문 드립니다. [1] HOTDOG 2020.02.24 329
2781 실행이 너~무 느립니다. [1] 포세가이 2020.02.27 298
2780 안녕하세요 다국어 지원 질문드립니다. [2] 김씨 2020.02.24 262