Working Directly with VA API for Linux*

Intel® Media Software Development Kit takes care of all memory and synchronization related operations in the VA API. The application may need to extend Intel® Media Software Development Kit functionality by working directly with the VA API for Linux*, for example to implement a customized external allocator. This section describes basic memory management and synchronization techniques.

To create the VA surface pool, the application should call the vaCreateSurfaces function:

 1const int num_surfaces = 5;
 2VASurfaceID surfaces[num_surfaces];
 3VASurfaceAttrib attrib;
 5attrib.type = VASurfaceAttribPixelFormat;
 6attrib.value.type = VAGenericValueTypeInteger;
 7attrib.value.value.i = VA_FOURCC_NV12;
10vaCreateSurfaces(va_display, VA_RT_FORMAT_YUV420, width, height,
11                 surfaces, num_surfaces, &attrib, 1);

To destroy the surface pool, the application should call the vaDestroySurfaces function:

1vaDestroySurfaces(va_display, surfaces, num_surfaces);

If the application works with hardware acceleration through Intel® Media Software Development Kit, then it can access surface data immediately after successful completion of the MFXVideoCORE_SyncOperation() call. If the application works with hardware acceleration directly, then it must check surface status before accessing data in video memory. This check can be done asynchronously by calling the vaQuerySurfaceStatus function or synchronously by calling the vaSyncSurface function.

After successful synchronization, the application can access surface data. Accessing surface data is performed in two steps:

  1. Create VAImage from surface.

  2. Map image buffer to system memory.

After mapping, the VAImage.offsets[3] array holds offsets to each color plain in a mapped buffer and the VAImage.pitches[3] array holds color plain pitches in bytes. For packed data formats, only first entries in these arrays are valid. The following example shows how to access data in a NV12 surface:

 1VAImage image;
 2unsigned char *Y, *U, *V;
 3void* buffer;
 5vaDeriveImage(va_display, surfaceToMap, &image);
 6vaMapBuffer(va_display, image.buf, &buffer);
 8/* NV12 */
 9Y = (unsigned char*)buffer + image.offsets[0];
10U = (unsigned char*)buffer + image.offsets[1];
11V = U + 1;

After processing data in a VA surface, the application should release resources allocated for the mapped buffer and VAImage object:

1vaUnmapBuffer(va_display, image.buf);
2vaDestroyImage(va_display, image.image_id);

In some cases, in order to retrieve encoded bitstream data from video memory, the application must use the VABuffer to store data. The following example shows how to create, use, and destroy the VABuffer:

 1VABufferID buf_id;
 2size_t size;
 3uint32_t offset;
 4void *buf;
 6/* create buffer */
 7vaCreateBuffer(va_display, va_context, VAEncCodedBufferType, buf_size, 1, NULL, & buf_id);
 9/* encode frame */
10// ...
12/* map buffer */
13VACodedBufferSegment *coded_buffer_segment;
15vaMapBuffer(va_display, buf_id, (void **)(&coded_buffer_segment));
17size   = coded_buffer_segment->size;
18offset = coded_buffer_segment->bit_offset;
19buf    = coded_buffer_segment->buf;
21/* retrieve encoded data*/
22// ...
24/* unmap and destroy buffer */
25vaUnmapBuffer(va_display, buf_id);
26vaDestroyBuffer(va_display, buf_id);

Note that the vaMapBuffer function returns pointers to different objects depending on the mapped buffer type. The VAImage is a plain data buffer and the encoded bitstream is a VACodedBufferSegment structure. The application cannot use VABuffer for synchronization. If encoding, it is recommended to synchronize using the VA surface as described above.