자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다. 
  • 제품설치/등록 오류 문의: 설치/등록 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 17156
공지 유용한 관련 사이트 관리자2 2014.03.20 56023
공지 본 게시판은 개발자 여러분들의 질문과 답변을 공유하는 공간입니다. 관리자 2012.01.10 99456
359 도대체 아이폰에서 beacon.enable := true 시 에러나는 이유는 뭘까요? [1] 델파이_사랑 2020.03.19 897
358 앱개발이 처음이라 질문좀 받아주세요... [2] file dlleopvhei 2020.03.19 285
357 C++builder 10.1 Berlin DLL에 vcl 테마 적용 방법 질문 [5] file gildong 2020.03.19 794
356 델파이 (10.3.3)로 iOS앱 FireBase Push Notification 구현가능한가요? [3] samsmin 2020.03.20 342
355 catalina10.15.3 paserver20.0 실행에러 원인좀 알려주세요.. xe10.3 [3] 네오마스 2020.03.20 864
354 (공유) RAD 스튜디오 10.3.3에서 VCL에서 ImeMode를 imHanguel로 설정 시 자간(글자간격)이 늘어납니다. file 험프리 2020.03.24 1029
353 안드로이드에 사이드폼(작은폼)을 만들수 있나요? [1] 델마당쇠 2020.03.24 159
352 C++ Dll 구조체 데이터형식 관련 문의 [2] woofer 2020.03.25 332
351 fastreports 관련 문의 드립니다. [1] aaaaa 2020.03.26 4971
350 AWS 개발이 가능한지요? [1] DevCK 2020.03.31 2962
349 Datasanp FDQuery.Master/Details table Autoinc 필드사용에 대하여 file 넓다란세상 2020.03.31 145
348 갑자기 GetIt연결이 안 됩니다. [2] file 개성산전 2020.03.31 171
347 C++builder 10.1 Berlin DLL에 vcl 테마 적용 방법 정리 [1] file gildong 2020.04.03 215
346 MariaDB 10에 입력하는데 한글만 포함되면 임의의 값이 추가되어 입력됩니다 [1] 아폴로 2020.04.05 428
345 [마이그레이션] F2613 Unit 'DBTables' not found 오류가 발생합니다. file 험프리 2020.04.07 443
344 Disconnected session [2] file 제길슨 2020.04.07 379
343 BLE / IdHTTP 사용법 [1] 권동한 2020.04.07 198
342 아이폰/안드로이드 개발시 파일저장 [1] 권동한 2020.04.07 367
341 프로젝트 생성시 에러 관련 문의 [7] file 삼이 2020.04.07 409
340 http server Result 한글 값 [2] 델마당쇠 2020.04.07 153