자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다. 
  • 제품설치/등록 오류 문의: 설치/등록 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 17204
공지 유용한 관련 사이트 관리자2 2014.03.20 56080
공지 본 게시판은 개발자 여러분들의 질문과 답변을 공유하는 공간입니다. 관리자 2012.01.10 99517
2799 unable to create process adb:error failed to copy 송세건 2016.07.06 153
2798 [마이그레이션][팁] 함수(또는 변수)에 빨간밑줄이 가고 컴파일 시 Undeclared Identifier 오류 시 조치방법 험프리 2015.10.12 153
2797 BindingsList 에 LinkList 를 추가하는 방법을 알고 싶습니다. [2] file 거북이 2015.12.05 153
2796 [마이그레이션] 마이그레이션시 배경등 색상이 기본 흰색으로 변경되는 현상 [1] 개미 2020.11.24 153
2795 [시작하는 사람들을 위한 델파이 프로그래밍] 모듈 25 - Exercise 3 [2] 관리자 2016.05.27 154
2794 베를린 설치 후 시애틀에서 개발하던 소스 읽어들인후,, 코드 도움말 팝업 안됨.. 별땅땅쫑 2016.06.07 154
2793 파이어몽키에서 배포용 라이브러리 생성 [1] 블루히아신스 2016.10.28 154
2792 델파이 도쿄10.2.1 에서 개발한 앱을 ios Enterprise program형태로 배포 [1] 밤바야 2017.12.11 154
2791 [10.3.3 브리핑] 파이어몽키 성능향상으로 어떤 것이 있었나요? 험프리 2019.12.05 154
2790 cannot compile this 'this' captured by SEH yet 오류 현상 [1] sihwan 2020.05.28 154
2789 다시 글올립니다. dll 불러온 화면에서 탭키, 방향키가 작동이 안됩니다. 쭈니쿤 2016.04.29 154
2788 delphi dx 및 er stduio 설치후 기존 버전 문제 [1] voyager 2015.10.12 154
2787 로그출력 기능 문의 [2] 공수래 2021.03.17 154
2786 Timage 에 특정 이미지 사용시 앱이 꺼짐니다. file 천지조율 2018.09.12 155
2785 [시작하는 사람들을 위한 델파이 프로그래밍] 모듈 9 - Exercise 3 [1] 관리자 2016.05.02 156
2784 모바일 앱개발 기술지원요청 [2] 파란김치 2018.07.25 156
2783 TLocationSensor 의 중지가 안됩니다. silkroad99 2019.01.11 156
2782 프로그램 하나에 메인폼에 서브폼이 여러개잇을때...? 헨씀히포 2019.08.07 156
2781 RemObject 사용시 파일 업로드 삭제 방법? [1] 가나다 2020.02.06 156
2780 FMX 안드로이드 개발시 폰트리스트 가져오는 방법? Firemonkey 2020.10.13 156