将c-header函数转换为Delphi函数以传递YUV图像



我需要翻译一个包含以下函数的图像处理SDK的C头文件:

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

该函数用于向SDK传递YUVJ420帧。

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

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

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

我是这样翻译的:

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类型调用此函数

这里缺少了很多细节。我会试着引导你走向正确的方向,但请不要把这个答案当成是确定的。为了编写二进制互操作代码,您确实需要一个完整而清晰的二进制接口规范。你可能需要再做些调查才能弄清这件事的真相。

首先,让我们看看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代码来看,为了调用这个函数,需要做很多事情。为了使函数工作,你需要所有的东西都是正确的。也就是说,如果你的代码不工作,问题可能完全在我们看不到的代码中,这些代码不包括在你的问题中。

最新更新