Memory Allocation and External Allocators

There are two models of memory management in oneVPL: internal and external.

External Memory Management

In the external memory model, the application must allocate sufficient memory for input and output parameters and buffers and deallocate it when oneVPL functions complete their operations. During execution, the oneVPL functions use callback functions to the application to manage memory for video frames through the external allocator interface mfxFrameAllocator.

If an application needs to control the allocation of video frames, it can use callback functions through the mfxFrameAllocator interface. If an application does not specify an allocator, an internal allocator is used. However, if an application uses video memory surfaces for input and output, it must specify the hardware acceleration device and an external frame allocator using mfxFrameAllocator.

The external frame allocator can allocate different frame types:

The external frame allocator responds only to frame allocation requests for the requested memory type and returns mfxStatus::MFX_ERR_UNSUPPORTED for all other types. The allocation request uses flags (part of the memory type field) to indicate which oneVPL class initiated the request so that the external frame allocator can respond accordingly.

The following example shows a simple external frame allocator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#define ALIGN32(X) (((mfxU32)((X)+31)) & (~ (mfxU32)31))

typedef struct {
   mfxU16 width, height;
   mfxU8 *base;
} mid_struct;

mfxStatus fa_alloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) {
   if (!(request->Type&MFX_MEMTYPE_SYSTEM_MEMORY))
      return MFX_ERR_UNSUPPORTED;
   if (request->Info.FourCC!=MFX_FOURCC_NV12)
      return MFX_ERR_UNSUPPORTED;
   response->NumFrameActual=request->NumFrameMin;
   for (int i=0;i<request->NumFrameMin;i++) {
      mid_struct *mmid=(mid_struct *)malloc(sizeof(mid_struct));
      mmid->width=ALIGN32(request->Info.Width);
      mmid->height=ALIGN32(request->Info.Height);
      mmid->base=(mfxU8*)malloc(mmid->width*mmid->height*3/2);
      response->mids[i]=mmid;
   }
   return MFX_ERR_NONE;
}

mfxStatus fa_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) {
   mid_struct *mmid=(mid_struct *)mid;
   ptr->Pitch=mmid->width;
   ptr->Y=mmid->base;
   ptr->U=ptr->Y+mmid->width*mmid->height;
   ptr->V=ptr->U+1;
   return MFX_ERR_NONE;
}

mfxStatus fa_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) {
   if (ptr) ptr->Y=ptr->U=ptr->V=ptr->A=0;
   return MFX_ERR_NONE;
}

mfxStatus fa_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) {
   return MFX_ERR_UNSUPPORTED;
}

mfxStatus fa_free(mfxHDL pthis, mfxFrameAllocResponse *response) {
   for (int i=0;i<response->NumFrameActual;i++) {
      mid_struct *mmid=(mid_struct *)response->mids[i];
      free(mmid->base); free(mmid);
   }
   return MFX_ERR_NONE;
}

For system memory, it is highly recommended to allocate memory for all planes of the same frame as a single buffer (using one single malloc call).

Internal Memory Management

In the internal memory management model, oneVPL provides interface functions for frames allocation:

These functions are used together with mfxFrameSurfaceInterface for surface management. The surface returned by these functions is a reference counted object and the application must call mfxFrameSurfaceInterface::Release after finishing all operations with the surface. In this model the application does not need to create and set the external allocator to oneVPL.

Another method to obtain an internally allocated surface is to call MFXVideoDECODE_DecodeFrameAsync() with a working surface equal to NULL (see Simplified decoding procedure). In this scenario, the decoder will allocate a new refcountable mfxFrameSurface1 and return it to the user. All assumed contracts with the user are similar to the MFXMemory_GetSurfaceForXXX functions.

mfxFrameSurfaceInterface

oneVPL API version 2.0 introduces mfxFrameSurfaceInterface. This interface is a set of callback functions to manage the lifetime of allocated surfaces, get access to pixel data, and obtain native handles and device abstractions (if suitable). Instead of directly accessing mfxFrameSurface1 structure members, it’s recommended to use the mfxFrameSurfaceInterface if present or call external allocator callback functions if set.

The following pseudo code shows the usage of mfxFrameSurfaceInterface for memory sharing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// lets decode frame and try to access output in a an optimal way.
sts = MFXVideoDECODE_DecodeFrameAsync(session, NULL, NULL, &outsurface, &syncp);
if (MFX_ERR_NONE == sts)
{
    mfxStatus s = outsurface->FrameInterface->GetDeviceHandle(outsurface,
                                                  &device_handle, &device_type);
    // if application or component is familar with mfxHandleType and it's
    // possible to share memory created by device_handle.
    if (MFX_ERR_NONE == s && isDeviceTypeCompatible(device_type)
                          && isPossibleForMemorySharing(device_handle)) {
        // get native handle and type
        outsurface->FrameInterface->GetNativeHandle(outsurface,
                                                      &resource, &resource_type);
        if (isResourceTypeCompatible(resource_type)) {
            //use memory directly
            ProcessNativeMemory(resource);
            outsurface->FrameInterface->Release(outsurface);
        }
    } else {
      // Application or component is not aware about such DeviceHandle or
      // Resource type need to map to system memory.
      outsurface->FrameInterface->Map(outsurface, MFX_MAP_READ);
      ProcessSystemMemory(outsurface);
      outsurface->FrameInterface->Unmap(outsurface);
      outsurface->FrameInterface->Release(outsurface);
    }
}