자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다. 
  • 제품설치/등록 오류 문의: 설치/등록 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 17224
공지 유용한 관련 사이트 관리자2 2014.03.20 56102
공지 본 게시판은 개발자 여러분들의 질문과 답변을 공유하는 공간입니다. 관리자 2012.01.10 99540
2799 안드로이드 태블릿PC를 인식하지 못합니다.. [3] heiman 2014.08.22 6766
2798 [XE6] Baas 컴포넌트로 인해 앱기본용량이 40메가가 되는 현상 [2] 가을이다 2014.08.23 1661
2797 [XE6] kinvey 에서 Active user란? [1] 가을이다 2014.08.27 2431
2796 fdQuery numeric 필드 displayformat [2] file sm2 2014.08.27 1699
2795 [XE6] Kinvey로 다중사용자에게 Push 보내기.. [3] 가을이다 2014.08.27 1625
2794 [XE6] kinvey Push 전송시, 특정 메세지 추가하기 [2] 가을이다 2014.08.28 2513
2793 iOS Simulator F9시 에러 [1] 강성범 2014.08.28 1826
2792 파이어몽키 모바일어플리케이션에서 가상키보드에 의한 화면가림현상 해결좀 [2] 농가사랑 2014.08.29 2976
2791 [XE6] VCL 리스트뷰 사용법 [1] 가을이다 2014.08.29 3572
2790 [이름 충돌로 인한 배포 문제 해결] Unable to create process: Unable to install [경로] .apk Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICAT... 관리자 2014.08.29 2398
2789 NFC 관련 앱을 XE 시리즈로 개발 가능한지요? [1] 유탐호호 2014.09.02 1325
2788 [문의]android target에 다른 pc emulator 추가 하는방법 [1] 강성범 2014.09.02 2079
2787 [xe7] baas관련 컴파일시 에러가납니다.. [10] 윤우민 2014.09.09 2478
2786 [XE7] 트라이얼 버전은 도움말이 원래 없나요? [4] file 소중한꿈 2014.09.12 1919
2785 [XE7] 트라이얼 버전에서 SVN 관련 기능의 제한이 있는지 문의드립니다. [2] 소중한꿈 2014.09.16 1945
2784 [세미나] 세미나를 다시 볼수는 없나요? [1] Humphery 2014.09.17 1302
2783 [세미나] XE7에서는 컴파일 크기가 개선되었나요? [1] Humphery 2014.09.17 1366
2782 [세미나] iOS앱을 개발하라면 맥북이 필요한가요? [1] Humphery 2014.09.17 1816
2781 [세미나] C++빌더 XE7에 자바 라이브러리 호출이 가능한가요? [1] Humphery 2014.09.17 2115
2780 [세미나] 평가판 사용자도 교육 참석 가능한가요? 평가판 사용자는 교육비용이 더 비싼가요? [2] Humphery 2014.09.17 1177