【问题标题】:Convert c-header function to Delphi function to pass YUV image将 c-header 函数转换为 Delphi 函数以传递 YUV 图像
【发布时间】:2014-05-26 15:33:22
【问题描述】:

我需要翻译包含此函数的图像处理 SDK 的 C 标头:

#define SDK_API __declspec(dllimport)
SDK_API   BOOL   WINAPI SetSourceYUVJ420(HANDLE Display, BYTE **YUV420P, int *LineSize, int srcWidth, int srcHeight);

该函数用于将 YUVJ420 帧传递给 SDK。

在我的代码中,帧存储在 PAVPicture 记录中(在 FFVCL 组件库中定义),其中:

data: array[0..7] of pbyte;
linesize: array[0..7] of integer;

解码视频帧后,FFVCL 会触发一个事件,在该事件中该帧可用作 PAVPicture 类型的 APicture。

我是这样翻译的:

function  SetSourceYUVJ420(Display: UIntPtr; YUV420P: Pointer; LineSize: Pointer; srcWidth, srcHeight: integer): boolean stdcall; external 'SDK.DLL' name '_SetSourceYUVJ420@20';

并像这样使用它:

SetSourceYUVJ420(Display, @APicture.data[0], @APicture.linesize[0], W, H);

.. 但是这样我会丢失一些指针/地址。

SDK 文档为中式英语且已过时。我确实有一个 C 示例,它使用 FFMpeg 进行解码,当一帧完成时,部分 pFrame(类型 AVFrame)被传递给 SDK:

unsigned WINAPI MYTest() {
   AVFormatContext *pFormatCtx;
   unsigned int    i, videoStream;
   AVCodecContext  *pCodecCtx;
   AVCodec         *pCodec;
   AVFrame         *pFrame = NULL;
   AVPacket        packet;
   int             frameFinished;

   av_register_all();

   pFormatCtx =  avformat_alloc_context();
   avformat_open_input(&pFormatCtx, "..\\..\\images\\Test.mp4",NULL,NULL);
   av_find_stream_info(pFormatCtx);

   videoStream=-1;
   for (i=0; i < pFormatCtx->nb_streams; i++) {
      if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
         videoStream = i;
         break;
      }
   }

   // Get a pointer to the codec context for the video stream
   pCodecCtx = pFormatCtx->streams[videoStream]->codec;
   pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
   // Open codec
   avcodec_open2(pCodecCtx, pCodec,0) < 0;

   // Allocate video frame
   pFrame = av_frame_alloc();

   // Read frames and save first five frames to disk
   i = 0;

   BOOL fSetBuffer = FALSE;
   BOOL f = FALSE;
   while (av_read_frame(pFormatCtx, &packet) >= 0 && !fTerminate) {
      // Is this a packet from the video stream?
      if(packet.stream_index==videoStream) {
         // Decode video frame
          avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

          if ( frameFinished ) {
            // This function told SDK, new video imcoming, with video format is YUV420
             SetSourceYUVJ420(gDisplay, pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height);
         }
    }

     // Free the packet that was allocated by av_read_frame
     av_free_packet(&packet);
  }
  // Free the YUV frame
  av_free(pFrame);
  // Close the codec
  avcodec_close(pCodecCtx);
  // Close the video file
  avformat_close_input(&pFormatCtx);

  return TRUE;
}

这是 AVFrame 的声明(取自 FFMPeg 的 frame.h):

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    /**
     * pointer to the picture/channel planes.
     * This might be different from the first allocated byte
     *
     * Some decoders access areas outside 0,0 - width,height, please
     * see avcodec_align_dimensions2(). Some filters and swscale can read
     * up to 16 bytes beyond the planes, if these filters are to be used,
     * then 16 extra bytes must be allocated.
     */
    uint8_t *data[AV_NUM_DATA_POINTERS];

    /**
     * For video, size in bytes of each picture line.
     * For audio, size in bytes of each plane.
     *
     * For audio, only linesize[0] may be set. For planar audio, each channel
     * plane must be the same size.
     *
     * For video the linesizes should be multiplies of the CPUs alignment
     * preference, this is 16 or 32 for modern desktop CPUs.
     * Some code requires such alignment other code can be slower without
     * correct alignment, for yet other it makes no difference.
     *
     * @note The linesize may be larger than the size of usable data -- there
     * may be extra padding present for performance reasons.
     */
    int linesize[AV_NUM_DATA_POINTERS];
<SNIP>
} AVFrame;

问题:

  • 如何正确翻译这个 C 函数定义
  • 如何使用提供的 FFVCL 类型 PAVPicture 调用此函数

【问题讨论】:

  • 这个问题无法回答。您还需要指定参数的语义。例如,int* 是什么?由 ref 或数组传递的单个 int。谁分配内存?你有 C++ 代码的文档吗?一些示例 C++ 调用代码会有所帮助。
  • 请注意,四个 32 位参数通常会产生 @_16 后缀,而不是 @_20(添加下划线以避免 SO 通知系统)
  • 这表明这可能是一个实例方法
  • 我编辑了问题以添加更多信息和一些 C 调用代码。

标签: delphi header-files delphi-xe4 yuv


【解决方案1】:

这里缺少很多细节。我会尽力引导您朝着正确的方向前进,但请不要认为这个答案是确定的。为了编写二进制互操作代码,您确实需要一个完整而清晰的二进制接口规范。你可能需要做更多的挖掘才能找到这个问题的根源。

首先我们来看HANDLE类型。这可能是 Win32 类型 HANDLE,在这种情况下,它将转换为 THandle。它更有可能是相关库定义的类型。也许最好翻译成PointerNativeInt。我会假设后者。

BYTE** 参数是BYTE* 的数组。这似乎是由调用者分配的。您可能会将其翻译成 Delphi 为PPByte。那是指向Byte的指针。

下一个参数是LineSize,类型为int*。这是int 的数组。所以字面意思还是PInteger,指向Integer的指针。

最后两个参数是简单的整数。

所以函数将被声明为:

function SetSourceYUVJ420(
  Display: NativeInt; 
  YUV420P: PPByte; 
  LineSize: PInteger; 
  srcWidth: Integer;
  srcHeight: integer
): LongBool; stdcall; external 'SDK.DLL' name '_SetSourceYUVJ420@20';

您还需要翻译结构。它是这样的:

type
  TAVFrame = record
    data: array [0..7] of PByte;
    linesize: array [0..7] of Integer;
  end;
  PAVFrame = ^TAVFrame;

很明显,您的代码需要获取显示句柄。我不知道你将如何去做。想必你已经知道如何做到这一点。同样,您需要创建一个调用av_frame_alloc 的框架。同样,我只能假设您已经知道如何做到这一点。

所以,假设您正确初始化了以下变量:

var
  Display: NativeInt;
  Frame: PAVFrame;

然后调用会是这样的:

if not SetSourceYUVJ420(Display, @Frame.data[0], @Frame.linesize[0], W, H) then
  .... handle error

从你展示的C代码来看,调用这个函数需要做很多事情。您将需要一切正确才能使该功能正常工作。也就是说,如果您的代码不起作用,那么问题可能完全出在我们看不到的代码中,即您的问题中未包含的代码。

【讨论】:

  • Data 和 linesize 确实是由调用者分配的,而 Display 在别处初始化。 PPByte 和 Pinteger 的建议正是我所需要的,而且非常有帮助。我将它集成到我的代码中,我将在早上测试它,硬件也到位了。
  • YUV 成功传递给 SDK!
猜你喜欢
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多