blob: 025ce00c1f8417dcf5c6e508d8f3106ce2b0ceb9 [file] [log] [blame] [edit]
/**************************************************************************
*
* Copyright (C) 2022 Kylin Software Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
/**
* @file
* Implementation of general video codec interface.
*
* This implementation is currently based on VA-API, and other interfaces,
* such as VDPAU and proprietary interfaces, can also be considered in the
* future.
*
* Two objects are implemented here:
* virgl_video_buffer:
* Buffer for storing raw YUV formatted data. Currently, it is a wrapper
* for VASurface.
* virgl_video_codec:
* Represents a video encoder or decoder. It's a wrapper of VAContext and
* mainly provides the following methods:
* - virgl_video_begin_frame()
* It calls vaBeginPicture() to prepare for encoding and decoding. For
* encoding, it also needs to upload the raw picture data from the guest
* side into the local VASurface.
* - virgl_video_decode_bitstream()
* It constructs the decoding-related VABuffers according to the picture
* description information, and then calls vaRenderPicture() for decoding.
* - virgl_video_encode_bitstream()
* It constructs the encoding-related VABuffers according to the picture
* description information, and then calls vaRenderPicture() for encoding.
* - virgl_video_end_frame()
* It calls vaEndPicture() to end encoding and decoding. After decoding,
* it transmits the raw picture data from VASurface to the guest side,
* and after encoding, it transmits the result and the coded data in
* VACodedBuffer to the guest side.
*
* @author Feng Jiang <jiangfeng@kylinos.cn>
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <epoxy/gl.h>
#include <epoxy/egl.h>
#include <va/va.h>
#include <va/va_drm.h>
#include <va/va_drmcommon.h>
#include <drm_fourcc.h>
#include "pipe/p_video_state.h"
#include "util/u_memory.h"
#include "virgl_hw.h"
#include "virgl_video_hw.h"
#include "virgl_util.h"
#include "virgl_video.h"
/*
* The max size of codec buffer is approximately:
* num_of_macroblocks * max_size_of_per_macroblock + size_of_some_headers
* Now, we only support YUV420 formats, this means that we have a limit of
* 3200 bits(400 Bytes) per macroblock. To simplify the calculation, we
* directly use 512 instead of 400.
*/
#define CODED_BUF_DEFAULT_SIZE(width, height) \
((width) * (height) / (16 * 16) * 512)
struct virgl_video_buffer {
enum pipe_format format;
uint32_t width;
uint32_t height;
bool interlanced;
VASurfaceID va_sfc;
struct virgl_video_dma_buf *dmabuf;
void *opaque; /* User opaque data */
};
struct virgl_video_codec {
enum pipe_video_profile profile;
uint32_t level;
enum pipe_video_entrypoint entrypoint;
enum pipe_video_chroma_format chroma_format;
uint32_t width;
uint32_t height;
uint32_t max_references;
VAContextID va_ctx;
VAConfigID va_cfg;
struct virgl_video_buffer *buffer;
struct virgl_video_buffer *ref_pic_list[32]; /* Enc: reference pictures */
VABufferID va_coded_buf; /* Enc: VACodedBuffer */
void *opaque; /* User opaque data */
};
static VADisplay va_dpy;
static struct virgl_video_callbacks *callbacks = NULL;
static enum pipe_video_profile pipe_profile_from_va(VAProfile profile)
{
switch (profile) {
case VAProfileMPEG2Simple:
return PIPE_VIDEO_PROFILE_MPEG2_SIMPLE;
case VAProfileMPEG2Main:
return PIPE_VIDEO_PROFILE_MPEG2_MAIN;
case VAProfileMPEG4Simple:
return PIPE_VIDEO_PROFILE_MPEG4_SIMPLE;
case VAProfileMPEG4AdvancedSimple:
return PIPE_VIDEO_PROFILE_MPEG4_ADVANCED_SIMPLE;
case VAProfileVC1Simple:
return PIPE_VIDEO_PROFILE_VC1_SIMPLE;
case VAProfileVC1Main:
return PIPE_VIDEO_PROFILE_VC1_MAIN;
case VAProfileVC1Advanced:
return PIPE_VIDEO_PROFILE_VC1_ADVANCED;
case VAProfileH264ConstrainedBaseline:
return PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE;
case VAProfileH264Main:
return PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN;
case VAProfileH264High:
return PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH;
case VAProfileHEVCMain:
return PIPE_VIDEO_PROFILE_HEVC_MAIN;
case VAProfileHEVCMain10:
return PIPE_VIDEO_PROFILE_HEVC_MAIN_10;
case VAProfileJPEGBaseline:
return PIPE_VIDEO_PROFILE_JPEG_BASELINE;
case VAProfileVP9Profile0:
return PIPE_VIDEO_PROFILE_VP9_PROFILE0;
case VAProfileVP9Profile2:
return PIPE_VIDEO_PROFILE_VP9_PROFILE2;
case VAProfileAV1Profile0:
return PIPE_VIDEO_PROFILE_AV1_MAIN;
case VAProfileNone:
return PIPE_VIDEO_PROFILE_UNKNOWN;
default:
return PIPE_VIDEO_PROFILE_UNKNOWN;
}
}
/* NOTE: mesa va frontend only supports VLD and EncSlice */
static enum pipe_video_entrypoint pipe_entrypoint_from_va(
VAEntrypoint entrypoint)
{
switch (entrypoint) {
case VAEntrypointVLD:
return PIPE_VIDEO_ENTRYPOINT_BITSTREAM;
case VAEntrypointIDCT:
return PIPE_VIDEO_ENTRYPOINT_IDCT;
case VAEntrypointMoComp:
return PIPE_VIDEO_ENTRYPOINT_MC;
case VAEntrypointEncSlice: /* fall through */
case VAEntrypointEncSliceLP:
return PIPE_VIDEO_ENTRYPOINT_ENCODE;
default:
return PIPE_VIDEO_ENTRYPOINT_UNKNOWN;
}
}
static enum pipe_format pipe_format_from_va_fourcc(unsigned format)
{
switch(format) {
case VA_FOURCC('N','V','1','2'):
return PIPE_FORMAT_NV12;
/* TODO: These are already defined in mesa, but not yet in virglrenderer
case VA_FOURCC('P','0','1','0'):
return PIPE_FORMAT_P010;
case VA_FOURCC('P','0','1','6'):
return PIPE_FORMAT_P016;
*/
case VA_FOURCC('I','4','2','0'):
return PIPE_FORMAT_IYUV;
case VA_FOURCC('Y','V','1','2'):
return PIPE_FORMAT_YV12;
case VA_FOURCC('Y','U','Y','V'):
case VA_FOURCC('Y','U','Y','2'):
return PIPE_FORMAT_YUYV;
case VA_FOURCC('U','Y','V','Y'):
return PIPE_FORMAT_UYVY;
case VA_FOURCC('B','G','R','A'):
return PIPE_FORMAT_B8G8R8A8_UNORM;
case VA_FOURCC('R','G','B','A'):
return PIPE_FORMAT_R8G8B8A8_UNORM;
case VA_FOURCC('B','G','R','X'):
return PIPE_FORMAT_B8G8R8X8_UNORM;
case VA_FOURCC('R','G','B','X'):
return PIPE_FORMAT_R8G8B8X8_UNORM;
default:
return PIPE_FORMAT_NONE;
}
}
static VAProfile va_profile_from_pipe(enum pipe_video_profile profile)
{
switch (profile) {
case PIPE_VIDEO_PROFILE_MPEG2_SIMPLE:
return VAProfileMPEG2Simple;
case PIPE_VIDEO_PROFILE_MPEG2_MAIN:
return VAProfileMPEG2Main;
case PIPE_VIDEO_PROFILE_MPEG4_SIMPLE:
return VAProfileMPEG4Simple;
case PIPE_VIDEO_PROFILE_MPEG4_ADVANCED_SIMPLE:
return VAProfileMPEG4AdvancedSimple;
case PIPE_VIDEO_PROFILE_VC1_SIMPLE:
return VAProfileVC1Simple;
case PIPE_VIDEO_PROFILE_VC1_MAIN:
return VAProfileVC1Main;
case PIPE_VIDEO_PROFILE_VC1_ADVANCED:
return VAProfileVC1Advanced;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
return VAProfileH264ConstrainedBaseline;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
return VAProfileH264Main;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
return VAProfileH264High;
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
return VAProfileHEVCMain;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
return VAProfileHEVCMain10;
case PIPE_VIDEO_PROFILE_JPEG_BASELINE:
return VAProfileJPEGBaseline;
case PIPE_VIDEO_PROFILE_VP9_PROFILE0:
return VAProfileVP9Profile0;
case PIPE_VIDEO_PROFILE_VP9_PROFILE2:
return VAProfileVP9Profile2;
case PIPE_VIDEO_PROFILE_AV1_MAIN:
return VAProfileAV1Profile0;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH422:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH444:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_12:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_STILL:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
case PIPE_VIDEO_PROFILE_UNKNOWN:
return VAProfileNone;
default:
return -1;
}
}
/*
* There is no invalid entrypoint defined in libva,
* so add this definition to make the code clear
*/
#define VAEntrypointNone 0
static int va_entrypoint_from_pipe(enum pipe_video_entrypoint entrypoint)
{
switch (entrypoint) {
case PIPE_VIDEO_ENTRYPOINT_BITSTREAM:
return VAEntrypointVLD;
case PIPE_VIDEO_ENTRYPOINT_IDCT:
return VAEntrypointIDCT;
case PIPE_VIDEO_ENTRYPOINT_MC:
return VAEntrypointMoComp;
case PIPE_VIDEO_ENTRYPOINT_ENCODE:
return VAEntrypointEncSlice;
default:
return VAEntrypointNone;
}
}
static uint32_t va_format_from_pipe_chroma(
enum pipe_video_chroma_format chroma_format)
{
switch (chroma_format) {
case PIPE_VIDEO_CHROMA_FORMAT_400:
return VA_RT_FORMAT_YUV400;
case PIPE_VIDEO_CHROMA_FORMAT_420:
return VA_RT_FORMAT_YUV420;
case PIPE_VIDEO_CHROMA_FORMAT_422:
return VA_RT_FORMAT_YUV422;
case PIPE_VIDEO_CHROMA_FORMAT_444:
return VA_RT_FORMAT_YUV444;
case PIPE_VIDEO_CHROMA_FORMAT_NONE:
default:
return 0;
}
}
static uint32_t drm_format_from_va_fourcc(uint32_t va_fourcc)
{
switch (va_fourcc) {
case VA_FOURCC_NV12:
return DRM_FORMAT_NV12;
case VA_FOURCC_NV21:
return DRM_FORMAT_NV21;
default:
return DRM_FORMAT_INVALID;
}
}
static void fill_video_dma_buf(struct virgl_video_dma_buf *dmabuf,
const VADRMPRIMESurfaceDescriptor *desc)
{
unsigned i, j, obj_idx;
struct virgl_video_dma_buf_plane *plane;
/*
virgl_log("surface: fourcc=0x%08x, size=%ux%u, num_objects=%u,
num_layers=%u\n", desc->fourcc, desc->width, desc->height,
desc->num_objects, desc->num_layers);
for (i = 0; i < desc->num_objects; i++)
virgl_log(" objects[%u]: fd=%d, size=%u, modifier=0x%lx\n",
i, desc->objects[i].fd, desc->objects[i].size,
desc->objects[i].drm_format_modifier);
for (i = 0; i < desc->num_layers; i++)
virgl_log(" layers[%u] : format=0x%08x, num_planes=%u, "
"obj=%u,%u,%u,%u, offset=%u,%u,%u,%u, pitch=%u,%u,%u,%u\n",
i, desc->layers[i].drm_format, desc->layers[i].num_planes,
desc->layers[i].object_index[0],
desc->layers[i].object_index[1],
desc->layers[i].object_index[2],
desc->layers[i].object_index[3],
desc->layers[i].offset[0],
desc->layers[i].offset[1],
desc->layers[i].offset[2],
desc->layers[i].offset[3],
desc->layers[i].pitch[0],
desc->layers[i].pitch[1],
desc->layers[i].pitch[2],
desc->layers[i].pitch[3]);
*/
dmabuf->drm_format = drm_format_from_va_fourcc(desc->fourcc);
dmabuf->width = desc->width;
dmabuf->height = desc->height;
for (i = 0, dmabuf->num_planes = 0; i < desc->num_layers; i++) {
for (j = 0; j < desc->layers[i].num_planes &&
dmabuf->num_planes < ARRAY_SIZE(dmabuf->planes); j++) {
obj_idx = desc->layers[i].object_index[j];
plane = &dmabuf->planes[dmabuf->num_planes++];
plane->drm_format = desc->layers[i].drm_format;
plane->offset = desc->layers[i].offset[j];
plane->pitch = desc->layers[i].pitch[j];
plane->fd = desc->objects[obj_idx].fd;
plane->size = desc->objects[obj_idx].size;
plane->modifier = desc->objects[obj_idx].drm_format_modifier;
}
}
}
static struct virgl_video_dma_buf *export_video_dma_buf(
struct virgl_video_buffer *buffer,
unsigned flags)
{
struct virgl_video_dma_buf *dmabuf;
uint32_t exp_flags;
VAStatus va_stat;
VADRMPRIMESurfaceDescriptor desc;
exp_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
if (flags & VIRGL_VIDEO_DMABUF_READ_ONLY)
exp_flags |= VA_EXPORT_SURFACE_READ_ONLY;
if (flags & VIRGL_VIDEO_DMABUF_WRITE_ONLY)
exp_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
dmabuf = calloc(1, sizeof(*dmabuf));
if (!dmabuf)
return NULL;
va_stat = vaExportSurfaceHandle(va_dpy, buffer->va_sfc,
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, exp_flags, &desc);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("export surface failed, err = 0x%X\n", va_stat);
goto free_dmabuf;
}
fill_video_dma_buf(dmabuf, &desc);
dmabuf->flags = flags;
dmabuf->buf = buffer;
return dmabuf;
free_dmabuf:
free(dmabuf);
return NULL;
}
static void destroy_video_dma_buf(struct virgl_video_dma_buf *dmabuf)
{
unsigned i;
if (dmabuf) {
for (i = 0; i < dmabuf->num_planes; i++)
close(dmabuf->planes[i].fd);
free(dmabuf);
}
}
static void encode_upload_picture(struct virgl_video_codec *codec,
struct virgl_video_buffer *buffer)
{
VAStatus va_stat;
if (!callbacks || !callbacks->encode_upload_picture)
return;
va_stat = vaSyncSurface(va_dpy, buffer->va_sfc);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("sync surface failed, err = 0x%x\n", va_stat);
return;
}
if (!buffer->dmabuf)
buffer->dmabuf = export_video_dma_buf(buffer, VIRGL_VIDEO_DMABUF_WRITE_ONLY);
if (buffer->dmabuf)
callbacks->encode_upload_picture(codec, buffer->dmabuf);
}
static void encode_completed(struct virgl_video_codec *codec,
struct virgl_video_buffer *buffer)
{
VAStatus va_stat;
VACodedBufferSegment *buf, *buf_list;
void **coded_bufs = NULL;
unsigned *coded_sizes = NULL;
unsigned i, num_coded_bufs = 0;
if (!callbacks || !callbacks->encode_completed)
return;
va_stat = vaMapBuffer(va_dpy, codec->va_coded_buf, (void **)(&buf_list));
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("map coded buffer failed, err = 0x%x\n", va_stat);
return;
}
for (buf = buf_list; buf; buf = (VACodedBufferSegment *)buf->next)
num_coded_bufs++;
coded_bufs = calloc(num_coded_bufs, sizeof(void *));
coded_sizes = calloc(num_coded_bufs, sizeof(unsigned));
if (!coded_bufs || !coded_sizes) {
virgl_log("alloc memory failed, num_coded_bufs %u\n", num_coded_bufs);
goto fail_unmap_buffer;
}
for (buf = buf_list, i = 0; buf; buf = (VACodedBufferSegment *)buf->next) {
coded_bufs[i] = buf->buf;
coded_sizes[i++] = buf->size;
}
callbacks->encode_completed(codec, buffer->dmabuf, NULL, num_coded_bufs,
(const void * const*)coded_bufs, coded_sizes);
fail_unmap_buffer:
vaUnmapBuffer(va_dpy, codec->va_coded_buf);
free(coded_bufs);
free(coded_sizes);
}
static void decode_completed(struct virgl_video_codec *codec,
struct virgl_video_buffer *buffer)
{
if (!callbacks || !callbacks->decode_completed)
return;
if (!buffer->dmabuf)
buffer->dmabuf = export_video_dma_buf(buffer, VIRGL_VIDEO_DMABUF_READ_ONLY);
if (buffer->dmabuf)
callbacks->decode_completed(codec, buffer->dmabuf);
}
static VASurfaceID get_enc_ref_pic(struct virgl_video_codec *codec,
uint32_t frame_num)
{
uint32_t idx;
struct virgl_video_create_buffer_args args;
if (frame_num == VA_INVALID_ID)
return VA_INVALID_ID;
idx = frame_num % ARRAY_SIZE(codec->ref_pic_list);
if (!codec->ref_pic_list[idx]) {
args.format = PIPE_FORMAT_NV21;
args.width = codec->width;
args.height = codec->height;
args.interlaced = 0;
args.opaque = NULL;
codec->ref_pic_list[idx] = virgl_video_create_buffer(&args);
if (!codec->ref_pic_list[idx]) {
virgl_log("create ref pic for frame_num %u failed\n", frame_num);
return VA_INVALID_ID;
}
}
return codec->ref_pic_list[idx]->va_sfc;
}
int virgl_video_init(int drm_fd,
struct virgl_video_callbacks *cbs, unsigned int flags)
{
VAStatus va_stat;
int major_ver, minor_ver;
const char *driver;
(void)flags;
if (drm_fd < 0) {
virgl_log("invalid drm fd: %d\n", drm_fd);
return -1;
}
va_dpy = vaGetDisplayDRM(drm_fd);
if (!va_dpy) {
virgl_log("get va display failed\n");
return -1;
}
va_stat = vaInitialize(va_dpy, &major_ver, &minor_ver);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("init va library failed\n");
virgl_video_destroy();
return -1;
}
virgl_log("VA-API version: %d.%d\n", major_ver, minor_ver);
driver = vaQueryVendorString(va_dpy);
virgl_log("Driver version: %s\n", driver ? driver : "<unknown>");
if (!driver || !strstr(driver, "Mesa Gallium")) {
virgl_log("only supports mesa va drivers now\n");
virgl_video_destroy();
return -1;
}
callbacks = cbs;
return 0;
}
void virgl_video_destroy(void)
{
if (va_dpy) {
vaTerminate(va_dpy);
va_dpy = NULL;
}
callbacks = NULL;
}
static int fill_vcaps_entry(VAProfile profile, VAEntrypoint entrypoint,
struct virgl_video_caps *vcaps)
{
VAConfigID cfg;
VASurfaceAttrib *attrs;
unsigned i, num_attrs;
/* FIXME: default values */
vcaps->profile = pipe_profile_from_va(profile);
vcaps->entrypoint = pipe_entrypoint_from_va(entrypoint);
vcaps->max_level = 0;
vcaps->stacked_frames = 0;
vcaps->max_width = 0;
vcaps->max_height = 0;
vcaps->prefered_format = PIPE_FORMAT_NONE;
vcaps->max_macroblocks = 1;
vcaps->npot_texture = 1;
vcaps->supports_progressive = 1;
vcaps->supports_interlaced = 0;
vcaps->prefers_interlaced = 0;
vcaps->max_temporal_layers = 0;
vaCreateConfig(va_dpy, profile, entrypoint, NULL, 0, &cfg);
vaQuerySurfaceAttributes(va_dpy, cfg, NULL, &num_attrs);
attrs = calloc(num_attrs, sizeof(VASurfaceAttrib));
if (!attrs)
return -1;
vaQuerySurfaceAttributes(va_dpy, cfg, attrs, &num_attrs);
for (i = 0; i < num_attrs; i++) {
switch (attrs[i].type) {
case VASurfaceAttribMaxHeight:
vcaps->max_height = attrs[i].value.value.i;
break;
case VASurfaceAttribMaxWidth:
vcaps->max_width = attrs[i].value.value.i;
break;
case VASurfaceAttribPixelFormat:
if (PIPE_FORMAT_NONE == vcaps->prefered_format)
vcaps->prefered_format = \
pipe_format_from_va_fourcc(attrs[i].value.value.i);
break;
default:
break;
}
}
free(attrs);
vaDestroyConfig(va_dpy, cfg);
return 0;
}
int virgl_video_fill_caps(union virgl_caps *caps)
{
int i, j;
int num_profiles, num_entrypoints;
VAProfile *profiles = NULL;
VAEntrypoint *entrypoints = NULL;
if (!va_dpy || !caps)
return -1;
num_entrypoints = vaMaxNumEntrypoints(va_dpy);
entrypoints = calloc(num_entrypoints, sizeof(VAEntrypoint));
if (!entrypoints)
return -1;
num_profiles = vaMaxNumProfiles(va_dpy);
profiles = calloc(num_profiles, sizeof(VAProfile));
if (!profiles) {
free(entrypoints);
return -1;
}
vaQueryConfigProfiles(va_dpy, profiles, &num_profiles);
for (i = 0, caps->v2.num_video_caps = 0; i < num_profiles; i++) {
/* only support H.264 and H.265 now */
if (profiles[i] != VAProfileH264Main &&
profiles[i] != VAProfileH264High &&
profiles[i] != VAProfileH264ConstrainedBaseline &&
profiles[i] != VAProfileHEVCMain)
continue;
vaQueryConfigEntrypoints(va_dpy, profiles[i],
entrypoints, &num_entrypoints);
for (j = 0; j < num_entrypoints &&
caps->v2.num_video_caps < ARRAY_SIZE(caps->v2.video_caps); j++) {
/* support encoding and decoding */
if (VAEntrypointVLD != entrypoints[j] &&
VAEntrypointEncSlice != entrypoints[j])
continue;
fill_vcaps_entry(profiles[i], entrypoints[j],
&caps->v2.video_caps[caps->v2.num_video_caps++]);
}
}
free(profiles);
free(entrypoints);
return 0;
}
struct virgl_video_codec *virgl_video_create_codec(
const struct virgl_video_create_codec_args *args)
{
VAStatus va_stat;
VAConfigID cfg;
VAContextID ctx;
VAConfigAttrib attr;
VAProfile profile;
VAEntrypoint entrypoint;
uint32_t format;
struct virgl_video_codec *codec;
if (!va_dpy || !args)
return NULL;
profile = va_profile_from_pipe(args->profile);
entrypoint = va_entrypoint_from_pipe(args->entrypoint);
format = va_format_from_pipe_chroma(args->chroma_format);
if (VAProfileNone == profile || VAEntrypointNone == entrypoint)
return NULL;
codec = (struct virgl_video_codec *)calloc(1, sizeof(*codec));
if (!codec)
return NULL;
attr.type = VAConfigAttribRTFormat;
vaGetConfigAttributes(va_dpy, profile, entrypoint, &attr, 1);
if (!(attr.value & format)) {
virgl_log("format 0x%x not supported, supported formats: 0x%x\n",
format, attr.value);
goto err;
}
va_stat = vaCreateConfig(va_dpy, profile, entrypoint, &attr, 1, &cfg);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("create config failed, err = 0x%x\n", va_stat);
goto err;
}
codec->va_cfg = cfg;
va_stat = vaCreateContext(va_dpy, cfg, args->width, args->height,
VA_PROGRESSIVE, NULL, 0, &ctx);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("create context failed, err = 0x%x\n", va_stat);
goto err;
}
codec->va_ctx = ctx;
codec->profile = args->profile;
codec->level = args->level;
codec->entrypoint = args->entrypoint;
codec->chroma_format = args->chroma_format;
codec->width = args->width;
codec->height = args->height;
codec->max_references = args->max_references;
codec->opaque = args->opaque;
if (entrypoint == VAEntrypointEncSlice) {
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncCodedBufferType,
CODED_BUF_DEFAULT_SIZE(codec->width, codec->height),
1, NULL, &codec->va_coded_buf);
}
return codec;
err:
virgl_video_destroy_codec(codec);
return NULL;
}
void virgl_video_destroy_codec(struct virgl_video_codec *codec)
{
unsigned i;
if (!va_dpy || !codec)
return;
if (codec->va_ctx)
vaDestroyContext(va_dpy, codec->va_ctx);
if (codec->va_cfg)
vaDestroyConfig(va_dpy, codec->va_cfg);
if (codec->va_coded_buf)
vaDestroyBuffer(va_dpy, codec->va_coded_buf);
for (i = 0; i < ARRAY_SIZE(codec->ref_pic_list); i++) {
if (codec->ref_pic_list[i])
free(codec->ref_pic_list[i]);
}
free(codec);
}
struct virgl_video_buffer *virgl_video_create_buffer(
const struct virgl_video_create_buffer_args *args)
{
VAStatus va_stat;
VASurfaceID sfc;
uint32_t format;
struct virgl_video_buffer *buffer;
if (!va_dpy || !args)
return NULL;
/*
* FIXME: always use YUV420 now,
* may be use va_format_from_pipe(args->format)
*/
format = VA_RT_FORMAT_YUV420;
if (!format) {
virgl_log("pipe format %d not supported\n", args->format);
return NULL;
}
buffer = (struct virgl_video_buffer *)calloc(1, sizeof(*buffer));
if (!buffer)
return NULL;
va_stat = vaCreateSurfaces(va_dpy, format,
args->width, args->height, &sfc, 1, NULL, 0);
if (VA_STATUS_SUCCESS != va_stat) {
free(buffer);
return NULL;
}
buffer->va_sfc = sfc;
buffer->format = args->format;
buffer->width = args->width;
buffer->height = args->height;
buffer->opaque = args->opaque;
return buffer;
}
void virgl_video_destroy_buffer(struct virgl_video_buffer *buffer)
{
if (!va_dpy || !buffer)
return;
if (buffer->dmabuf)
destroy_video_dma_buf(buffer->dmabuf);
if (buffer->va_sfc)
vaDestroySurfaces(va_dpy, &buffer->va_sfc, 1);
free(buffer);
}
void *virgl_video_codec_opaque_data(struct virgl_video_codec *codec)
{
return codec ? codec->opaque : NULL;
}
enum pipe_video_profile virgl_video_codec_profile(
const struct virgl_video_codec *codec)
{
return codec ? codec->profile : PIPE_VIDEO_PROFILE_UNKNOWN;
}
uint32_t virgl_video_buffer_id(const struct virgl_video_buffer *buffer)
{
return (uint32_t)(buffer ? buffer->va_sfc : VA_INVALID_SURFACE);
}
void *virgl_video_buffer_opaque_data(struct virgl_video_buffer *buffer)
{
return buffer ? buffer->opaque : NULL;
}
int virgl_video_begin_frame(struct virgl_video_codec *codec,
struct virgl_video_buffer *target)
{
VAStatus va_stat;
if (!va_dpy || !codec || !target)
return -1;
if (codec->entrypoint == PIPE_VIDEO_ENTRYPOINT_ENCODE)
encode_upload_picture(codec, target);
codec->buffer = target;
va_stat = vaBeginPicture(va_dpy, codec->va_ctx, target->va_sfc);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("begin picture failed, err = 0x%x\n", va_stat);
return -1;
}
return 0;
}
#define ITEM_SET(dest, src, member) \
(dest)->member = (src)->member
#define ITEM_CPY(dest, src, member) \
memcpy(&(dest)->member, &(src)->member, sizeof((dest)->member))
static void h264_init_picture(VAPictureH264 *pic)
{
pic->picture_id = VA_INVALID_SURFACE;
pic->frame_idx = 0;
pic->flags = VA_PICTURE_H264_INVALID;
pic->TopFieldOrderCnt = 0;
pic->BottomFieldOrderCnt = 0;
}
/*
* Refer to vlVaHandlePictureParameterBufferH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_picture_param(struct virgl_video_codec *codec,
struct virgl_video_buffer *target,
const struct virgl_h264_picture_desc *desc,
VAPictureParameterBufferH264 *vapp)
{
unsigned i;
VAPictureH264 *pic;
(void)codec;
/* CurrPic */
pic = &vapp->CurrPic;
pic->picture_id = target->va_sfc;
pic->frame_idx = desc->frame_num;
pic->flags = desc->is_reference ? VA_PICTURE_H264_SHORT_TERM_REFERENCE : 0;
if (desc->field_pic_flag)
pic->flags |= (desc->bottom_field_flag ? VA_PICTURE_H264_BOTTOM_FIELD
: VA_PICTURE_H264_TOP_FIELD);
pic->TopFieldOrderCnt = desc->field_order_cnt[0];
pic->BottomFieldOrderCnt = desc->field_order_cnt[1];
/* ReferenceFrames */
for (i = 0; i < ARRAY_SIZE(vapp->ReferenceFrames); i++)
h264_init_picture(&vapp->ReferenceFrames[i]);
for (i = 0; i < desc->num_ref_frames; i++) {
pic = &vapp->ReferenceFrames[i];
pic->picture_id = desc->buffer_id[i];
pic->frame_idx = desc->frame_num_list[i];
pic->flags = (desc->is_long_term[i]
? VA_PICTURE_H264_LONG_TERM_REFERENCE
: VA_PICTURE_H264_SHORT_TERM_REFERENCE);
if (desc->top_is_reference[i] && desc->bottom_is_reference[i]) {
// Full frame. This block intentionally left blank. No flags set.
} else {
if (desc->top_is_reference[i])
pic->flags |= VA_PICTURE_H264_TOP_FIELD;
else
pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD;
}
pic->TopFieldOrderCnt = desc->field_order_cnt_list[i][0];
pic->BottomFieldOrderCnt = desc->field_order_cnt_list[i][1];
}
//vapp->picture_width_in_mbs_minus1 = (codec->width - 1) / 16;
//vapp->picture_height_in_mbs_minus1 = (codec->height - 1) / 16;
ITEM_SET(vapp, &desc->pps.sps, bit_depth_luma_minus8);
ITEM_SET(vapp, &desc->pps.sps, bit_depth_chroma_minus8);
ITEM_SET(vapp, desc, num_ref_frames);
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, chroma_format_idc);
//vapp->seq_fields.bits.residual_colour_transform_flag = 0;
//vapp->seq_fields.bits.gaps_in_frame_num_value_allowed_flag = 0;
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, frame_mbs_only_flag);
ITEM_SET(&vapp->seq_fields.bits,
&desc->pps.sps, mb_adaptive_frame_field_flag);
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, direct_8x8_inference_flag);
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, MinLumaBiPredSize8x8);
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, log2_max_frame_num_minus4);
ITEM_SET(&vapp->seq_fields.bits, &desc->pps.sps, pic_order_cnt_type);
ITEM_SET(&vapp->seq_fields.bits,
&desc->pps.sps, log2_max_pic_order_cnt_lsb_minus4);
ITEM_SET(&vapp->seq_fields.bits,
&desc->pps.sps, delta_pic_order_always_zero_flag);
//ITEM_SET(vapp, &desc->pps, num_slice_groups_minus1);
//ITEM_SET(vapp, &desc->pps, slice_group_map_type);
//ITEM_SET(vapp, &desc->pps, slice_group_change_rate_minus1);
ITEM_SET(vapp, &desc->pps, pic_init_qp_minus26);
ITEM_SET(vapp, &desc->pps, pic_init_qs_minus26);
ITEM_SET(vapp, &desc->pps, chroma_qp_index_offset);
ITEM_SET(vapp, &desc->pps, second_chroma_qp_index_offset);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, entropy_coding_mode_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, weighted_pred_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, weighted_bipred_idc);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, transform_8x8_mode_flag);
ITEM_SET(&vapp->pic_fields.bits, desc, field_pic_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, constrained_intra_pred_flag);
vapp->pic_fields.bits.pic_order_present_flag =
desc->pps.bottom_field_pic_order_in_frame_present_flag;
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, deblocking_filter_control_present_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, redundant_pic_cnt_present_flag);
vapp->pic_fields.bits.reference_pic_flag = desc->is_reference;
ITEM_SET(vapp, desc, frame_num);
}
/* Refer to vlVaHandleIQMatrixBufferH264() in mesa */
static void h264_fill_iq_matrix(const struct virgl_h264_picture_desc *desc,
VAIQMatrixBufferH264 *vaiqm)
{
ITEM_CPY(vaiqm, &desc->pps, ScalingList4x4);
ITEM_CPY(vaiqm, &desc->pps, ScalingList8x8);
}
/*
* Refer to vlVaHandleSliceParameterBufferH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_slice_param(const struct virgl_h264_picture_desc *desc,
VASliceParameterBufferH264 *vasp)
{
//vasp->slice_data_size;
//vasp->slice_data_offset;
//vasp->slice_data_flag;
//vasp->slice_data_bit_offset;
//vasp->first_mb_in_slice;
//vasp->slice_type;
//vasp->direct_spatial_mv_pred_flag;
ITEM_SET(vasp, desc, num_ref_idx_l0_active_minus1);
ITEM_SET(vasp, desc, num_ref_idx_l1_active_minus1);
//vasp->cabac_init_idc;
//vasp->slice_qp_delta;
//vasp->disable_deblocking_filter_idc;
//vasp->slice_alpha_c0_offset_div2;
//vasp->slice_beta_offset_div2;
//vasp->RefPicList0[32];
//vasp->RefPicList1[32];
/* see pred_weight_table */
//vasp->luma_log2_weight_denom;
//vasp->chroma_log2_weight_denom;
//vasp->luma_weight_l0_flag;
//vasp->luma_weight_l0[32];
//vasp->luma_offset_l0[32];
//vasp->chroma_weight_l0_flag;
//vasp->chroma_weight_l0[32][2];
//vasp->chroma_offset_l0[32][2];
//vasp->luma_weight_l1_flag;
//vasp->luma_weight_l1[32];
//vasp->luma_offset_l1[32];
//vasp->chroma_weight_l1_flag;
//vasp->chroma_weight_l1[32][2];
//vasp->chroma_offset_l1[32][2];
}
/*
* Refer to vlVaHandleVAEncPictureParameterBufferTypeH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_enc_picture_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc,
VAEncPictureParameterBufferH264 *param)
{
unsigned i;
(void)codec;
(void)source;
/* CurrPic */
param->CurrPic.picture_id = get_enc_ref_pic(codec, desc->frame_num);
//CurrPic.frame_idx;
//CurrPic.flags;
param->CurrPic.TopFieldOrderCnt = desc->pic_order_cnt;
//CurrPic.BottomFieldOrderCnt;
/* ReferenceFrames */
for (i = 0; i < ARRAY_SIZE(param->ReferenceFrames); i++)
h264_init_picture(&param->ReferenceFrames[i]);
/* coded_buf */
param->coded_buf = codec->va_coded_buf;
//pic_parameter_set_id;
//seq_parameter_set_id;
//last_picture;
//frame_num
param->pic_init_qp = desc->quant_i_frames;
param->num_ref_idx_l0_active_minus1 = desc->num_ref_idx_l0_active_minus1;
param->num_ref_idx_l1_active_minus1 = desc->num_ref_idx_l1_active_minus1;
//chroma_qp_index_offset;
//second_chroma_qp_index_offset;
/* pic_fields */
param->pic_fields.bits.idr_pic_flag =
(desc->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR);
param->pic_fields.bits.reference_pic_flag = !desc->not_referenced;
param->pic_fields.bits.entropy_coding_mode_flag = desc->pic_ctrl.enc_cabac_enable;
//pic_fields.bits.weighted_pred_flag
//pic_fields.bits.weighted_bipred_idc
//pic_fields.bits.constrained_intra_pred_flag
//pic_fields.bits.transform_8x8_mode_flag
//pic_fields.bits.deblocking_filter_control_present_flag
//pic_fields.bits.redundant_pic_cnt_present_flag
//pic_fields.bits.pic_order_present_flag
//pic_fields.bits.pic_scaling_matrix_present_flag
}
/*
* Refer to vlVaHandleVAEncSliceParameterBufferTypeH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_enc_slice_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc,
VAEncSliceParameterBufferH264 *param)
{
unsigned i;
const struct virgl_h264_slice_descriptor *sd;
(void)codec;
(void)source;
/* Get the lastest slice descriptor */
if (desc->num_slice_descriptors &&
desc->num_slice_descriptors <= ARRAY_SIZE(desc->slices_descriptors)) {
sd = &desc->slices_descriptors[desc->num_slice_descriptors - 1];
param->macroblock_address = sd->macroblock_address;
param->num_macroblocks = sd->num_macroblocks;
//macroblock_info;
}
switch (desc->picture_type) {
case PIPE_H2645_ENC_PICTURE_TYPE_P:
param->slice_type = 0;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
param->slice_type = 1;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_I:
case PIPE_H2645_ENC_PICTURE_TYPE_IDR: /* fall through */
param->slice_type = 2;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_SKIP:
default:
break;
}
//pic_parameter_set_id;
//idr_pic_id;
//pic_order_cnt_lsb;
//delta_pic_order_cnt_bottom;
//delta_pic_order_cnt[2];
//direct_spatial_mv_pred_flag;
/*
* Sine num_ref_idx_l0_active_minus1 and num_ref_idx_l1_active_minus1
* have been passed by VAEncPictureParameterBufferH264,
* num_ref_idx_active_override_flag is always set to 0.
*/
param->num_ref_idx_active_override_flag = 0;
//num_ref_idx_l0_active_minus1
//num_ref_idx_l1_active_minus1
/* Reference List */
for (i = 0; i < 32; i++) {
h264_init_picture(&param->RefPicList0[i]);
h264_init_picture(&param->RefPicList1[i]);
param->RefPicList0[i].picture_id =
get_enc_ref_pic(codec, desc->ref_idx_l0_list[i]);
param->RefPicList1[i].picture_id =
get_enc_ref_pic(codec, desc->ref_idx_l1_list[i]);
if (param->RefPicList0[i].picture_id != VA_INVALID_ID)
param->RefPicList0[i].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
if (param->RefPicList1[i].picture_id != VA_INVALID_ID)
param->RefPicList1[i].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
}
//luma_log2_weight_denom;
//chroma_log2_weight_denom;
//luma_weight_l0_flag;
//luma_weight_l0[32];
//luma_offset_l0[32];
//chroma_weight_l0_flag;
//chroma_weight_l0[32][2];
//chroma_offset_l0[32][2];
//luma_weight_l1_flag;
//luma_weight_l1[32];
//luma_offset_l1[32];
//chroma_weight_l1_flag;
//chroma_weight_l1[32][2];
//chroma_offset_l1[32][2];
param->cabac_init_idc = desc->pic_ctrl.enc_cabac_init_idc;
//slice_qp_delta;
//disable_deblocking_filter_idc;
//slice_alpha_c0_offset_div2;
//slice_beta_offset_div2;
}
/*
* Refer to vlVaHandleVAEncSequenceParameterBufferTypeH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_enc_seq_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc,
VAEncSequenceParameterBufferH264 *param)
{
(void)codec;
(void)source;
//seq_parameter_set_id;
param->level_idc = codec->level;
//intra_period;
param->intra_idr_period = desc->intra_idr_period;
//ip_period;
//bits_per_second;
param->max_num_ref_frames = codec->max_references;
//picture_width_in_mbs;
//picture_height_in_mbs;
/* seq_fields.bits */
//seq_fields.bits.chroma_format_idc
//seq_fields.bits.frame_mbs_only_flag
//seq_fields.bits.mb_adaptive_frame_field_flag
//seq_fields.bits.seq_scaling_matrix_present_flag
//seq_fields.bits.direct_8x8_inference_flag
//seq_fields.bits.log2_max_frame_num_minus4
ITEM_SET(&param->seq_fields.bits, &desc->seq, pic_order_cnt_type);
//seq_fields.bit.log2_max_pic_order_cnt_lsb_minus4
//seq_fields.bit.delta_pic_order_always_zero_flag
//bit_depth_luma_minus8;
//bit_depth_chroma_minus8;
//num_ref_frames_in_pic_order_cnt_cycle;
//offset_for_non_ref_pic;
//offset_for_top_to_bottom_field;
//offset_for_ref_frame[256];
if (desc->seq.enc_frame_cropping_flag) {
param->frame_cropping_flag = desc->seq.enc_frame_cropping_flag;
param->frame_crop_left_offset = desc->seq.enc_frame_crop_left_offset;
param->frame_crop_right_offset = desc->seq.enc_frame_crop_right_offset;
param->frame_crop_top_offset = desc->seq.enc_frame_crop_top_offset;
param->frame_crop_bottom_offset = desc->seq.enc_frame_crop_bottom_offset;
}
ITEM_SET(param, &desc->seq, vui_parameters_present_flag);
// vui_fields.bits
if (desc->seq.vui_parameters_present_flag) {
ITEM_SET(&param->vui_fields.bits, &desc->seq.vui_flags,
aspect_ratio_info_present_flag);
ITEM_SET(&param->vui_fields.bits, &desc->seq.vui_flags,
timing_info_present_flag);
}
//vui_fields.bits.bitstream_restriction_flag
//vui_fields.bits.log2_max_mv_length_horizontal
//vui_fields.bits.log2_max_mv_length_vertical
//vui_fields.bits.fixed_frame_rate_flag
//vui_fields.bits.low_delay_hrd_flag
//vui_fields.bits.motion_vectors_over_pic_boundaries_flag
if (desc->seq.vui_parameters_present_flag) {
ITEM_SET(param, &desc->seq, aspect_ratio_idc);
ITEM_SET(param, &desc->seq, sar_width);
ITEM_SET(param, &desc->seq, sar_height);
}
ITEM_SET(param, &desc->seq, num_units_in_tick);
ITEM_SET(param, &desc->seq, time_scale);
}
/*
* Refer to vlVaHandleVAEncMiscParameterTypeRateControlH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_enc_misc_param_rate_ctrl(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc,
VAEncMiscParameterRateControl *param)
{
unsigned temporal_id = 0; /* always 0 now */
const struct virgl_h264_enc_rate_control *rc = &desc->rate_ctrl[temporal_id];
(void)codec;
(void)source;
param->bits_per_second = rc->peak_bitrate;
if (desc->rate_ctrl[0].rate_ctrl_method !=
PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT) {
param->target_percentage = rc->target_bitrate *
param->bits_per_second / 100.0;
}
//window_size;
//initial_qp;
param->min_qp = rc->min_qp;
//basic_unit_size;
/* rc_flags */
//rc_flags.bits.reset
param->rc_flags.bits.disable_frame_skip = !rc->skip_frame_enable;
param->rc_flags.bits.disable_bit_stuffing = !rc->fill_data_enable;
//rc_flags.bits.mb_rate_control
param->rc_flags.bits.temporal_id = temporal_id;
//rc_flags.bits.cfs_I_frames
//rc_flags.bits.enable_parallel_brc
//rc_flags.bits.enable_dynamic_scaling
//rc_flags.bits.frame_tolerance_mode
//ICQ_quality_factor;
param->max_qp = rc->max_qp;
//quality_factor;
//target_frame_size;
}
/*
* Refer to vlVaHandleVAEncMiscParameterTypeFrameRateH264() in mesa,
* and comment out some unused parameters.
*/
static void h264_fill_enc_misc_param_frame_rate(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc,
VAEncMiscParameterFrameRate *param)
{
unsigned temporal_id = 0; /* always 0 now */
const struct virgl_h264_enc_rate_control *rc = &desc->rate_ctrl[temporal_id];
(void)codec;
(void)source;
param->framerate = rc->frame_rate_num | (rc->frame_rate_den << 16);
param->framerate_flags.bits.temporal_id = temporal_id;
}
static int h264_decode_bitstream(struct virgl_video_codec *codec,
struct virgl_video_buffer *target,
const struct virgl_h264_picture_desc *desc,
unsigned num_buffers,
const void * const *buffers,
const unsigned *sizes)
{
unsigned i;
int err = 0;
VAStatus va_stat;
VABufferID *slice_data_buf, pic_param_buf, iq_matrix_buf, slice_param_buf;
VAPictureParameterBufferH264 pic_param;
VAIQMatrixBufferH264 iq_matrix;
VASliceParameterBufferH264 slice_param;
slice_data_buf = calloc(num_buffers, sizeof(VABufferID));
if (!slice_data_buf) {
virgl_log("alloc slice data buffer id failed\n");
return -1;
}
h264_fill_picture_param(codec, target, desc, &pic_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAPictureParameterBufferType,
sizeof(pic_param), 1, &pic_param, &pic_param_buf);
h264_fill_iq_matrix(desc, &iq_matrix);
vaCreateBuffer(va_dpy, codec->va_ctx, VAIQMatrixBufferType,
sizeof(iq_matrix), 1, &iq_matrix, &iq_matrix_buf);
h264_fill_slice_param(desc, &slice_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VASliceParameterBufferType,
sizeof(slice_param), 1, &slice_param, &slice_param_buf);
for (i = 0; i < num_buffers; i++) {
vaCreateBuffer(va_dpy, codec->va_ctx, VASliceDataBufferType,
sizes[i], 1, (void *)(buffers[i]), &slice_data_buf[i]);
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &pic_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render picture param failed, err = 0x%x\n", va_stat);
err = -1;
goto err;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &iq_matrix_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render iq matrix failed, err = 0x%x\n", va_stat);
err = -1;
goto err;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render slice param failed, err = 0x%x\n", va_stat);
err = -1;
goto err;
}
for (i = 0; i < num_buffers; i++) {
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_data_buf[i], 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render slice data failed, err = 0x%x\n", va_stat);
err = -1;
}
}
err:
vaDestroyBuffer(va_dpy, pic_param_buf);
vaDestroyBuffer(va_dpy, iq_matrix_buf);
vaDestroyBuffer(va_dpy, slice_param_buf);
for (i = 0; i < num_buffers; i++)
vaDestroyBuffer(va_dpy, slice_data_buf[i]);
free(slice_data_buf);
return err;
}
static int h264_encode_render_sequence(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc)
{
int err = 0;
VAStatus va_stat;
VAEncSequenceParameterBufferH264 seq_param;
VAEncMiscParameterBuffer *misc_param;
VABufferID seq_param_buf, rc_param_buf, fr_param_buf;
memset(&seq_param, 0, sizeof(seq_param));
h264_fill_enc_seq_param(codec, source, desc, &seq_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncSequenceParameterBufferType,
sizeof(seq_param), 1, &seq_param, &seq_param_buf);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) +
sizeof(VAEncMiscParameterRateControl), 1, NULL, &rc_param_buf);
vaMapBuffer(va_dpy, rc_param_buf, (void **)&misc_param);
misc_param->type = VAEncMiscParameterTypeRateControl;
h264_fill_enc_misc_param_rate_ctrl(codec, source, desc,
(VAEncMiscParameterRateControl *)misc_param->data);
vaUnmapBuffer(va_dpy, rc_param_buf);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) +
sizeof(VAEncMiscParameterFrameRate), 1, NULL, &fr_param_buf);
vaMapBuffer(va_dpy, fr_param_buf, (void **)&misc_param);
misc_param->type = VAEncMiscParameterTypeFrameRate;
h264_fill_enc_misc_param_frame_rate(codec, source, desc,
(VAEncMiscParameterFrameRate *)misc_param->data);
vaUnmapBuffer(va_dpy, fr_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &seq_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h264 sequence param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &rc_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h264 rate control param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &fr_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h264 frame rate param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
error:
vaDestroyBuffer(va_dpy, seq_param_buf);
vaDestroyBuffer(va_dpy, rc_param_buf);
vaDestroyBuffer(va_dpy, fr_param_buf);
return err;
}
static int h264_encode_render_picture(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc)
{
VAStatus va_stat;
VABufferID pic_param_buf;
VAEncPictureParameterBufferH264 pic_param;
memset(&pic_param, 0, sizeof(pic_param));
h264_fill_enc_picture_param(codec, source, desc, &pic_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncPictureParameterBufferType,
sizeof(pic_param), 1, &pic_param, &pic_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &pic_param_buf, 1);
vaDestroyBuffer(va_dpy, pic_param_buf);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h264 picture param failed, err = 0x%x\n", va_stat);
return -1;
}
return 0;
}
static int h264_encode_render_slice(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc)
{
VAStatus va_stat;
VABufferID slice_param_buf;
VAEncSliceParameterBufferH264 slice_param;
memset(&slice_param, 0, sizeof(slice_param));
h264_fill_enc_slice_param(codec, source, desc, &slice_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncSliceParameterBufferType,
sizeof(slice_param), 1, &slice_param, &slice_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_param_buf, 1);
vaDestroyBuffer(va_dpy, slice_param_buf);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h264 slice param failed, err = 0x%x\n", va_stat);
return -1;
}
return 0;
}
static int h264_encode_bitstream(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h264_enc_picture_desc *desc)
{
if (desc->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR) {
h264_encode_render_sequence(codec, source, desc);
}
h264_encode_render_picture(codec, source, desc);
h264_encode_render_slice(codec, source, desc);
return 0;
}
static void h265_init_picture(VAPictureHEVC *pic)
{
pic->picture_id = VA_INVALID_SURFACE;
pic->pic_order_cnt = 0;
pic->flags = VA_PICTURE_HEVC_INVALID;
}
/*
* Refer to vlVaHandlePictureParameterBufferHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_picture_param(struct virgl_video_codec *codec,
struct virgl_video_buffer *target,
const struct virgl_h265_picture_desc *desc,
VAPictureParameterBufferHEVC *vapp)
{
unsigned i;
(void)codec;
(void)target;
//vapp->CurrPic.picture_id
vapp->CurrPic.pic_order_cnt = desc->CurrPicOrderCntVal;
//vapp->CurrPic.flags
for (i = 0; i < 15; i++) {
vapp->ReferenceFrames[i].pic_order_cnt = desc->PicOrderCntVal[i];
vapp->ReferenceFrames[i].picture_id = desc->ref[i];
vapp->ReferenceFrames[i].flags = VA_INVALID_SURFACE == desc->ref[i]
? VA_PICTURE_HEVC_INVALID : 0;
}
for (i = 0; i < desc->NumPocStCurrBefore; i++)
vapp->ReferenceFrames[desc->RefPicSetStCurrBefore[i]].flags |= \
VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE;
for (i = 0; i < desc->NumPocStCurrAfter; i++)
vapp->ReferenceFrames[desc->RefPicSetStCurrAfter[i]].flags |= \
VA_PICTURE_HEVC_RPS_ST_CURR_AFTER;
for (i = 0; i < desc->NumPocLtCurr; i++)
vapp->ReferenceFrames[desc->RefPicSetLtCurr[i]].flags |= \
VA_PICTURE_HEVC_RPS_LT_CURR;
ITEM_SET(vapp, &desc->pps.sps, pic_width_in_luma_samples);
ITEM_SET(vapp, &desc->pps.sps, pic_height_in_luma_samples);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps.sps, chroma_format_idc);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps.sps, separate_colour_plane_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps.sps, pcm_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps.sps, scaling_list_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, transform_skip_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps.sps, amp_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps.sps, strong_intra_smoothing_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, sign_data_hiding_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, constrained_intra_pred_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, cu_qp_delta_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, weighted_pred_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, weighted_bipred_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, transquant_bypass_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps, tiles_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, entropy_coding_sync_enabled_flag);
ITEM_SET(&vapp->pic_fields.bits, &desc->pps,
pps_loop_filter_across_slices_enabled_flag);
if (desc->pps.tiles_enabled_flag)
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps, loop_filter_across_tiles_enabled_flag);
if (desc->pps.sps.pcm_enabled_flag)
ITEM_SET(&vapp->pic_fields.bits,
&desc->pps.sps, pcm_loop_filter_disabled_flag);
//ITEM_SET(vapp->pic_fields.bits, desc->pps.sps, NoPicReorderingFlag);
//ITEM_SET(vapp->pic_fields.bits, desc->pps.sps, NoBiPredFlag);
ITEM_SET(vapp, &desc->pps.sps, sps_max_dec_pic_buffering_minus1);
ITEM_SET(vapp, &desc->pps.sps, bit_depth_luma_minus8);
ITEM_SET(vapp, &desc->pps.sps, bit_depth_chroma_minus8);
if (desc->pps.sps.pcm_enabled_flag) {
ITEM_SET(vapp, &desc->pps.sps, pcm_sample_bit_depth_luma_minus1);
ITEM_SET(vapp, &desc->pps.sps, pcm_sample_bit_depth_chroma_minus1);
}
ITEM_SET(vapp, &desc->pps.sps, log2_min_luma_coding_block_size_minus3);
ITEM_SET(vapp, &desc->pps.sps, log2_diff_max_min_luma_coding_block_size);
ITEM_SET(vapp, &desc->pps.sps, log2_min_transform_block_size_minus2);
ITEM_SET(vapp, &desc->pps.sps, log2_diff_max_min_transform_block_size);
if (desc->pps.sps.pcm_enabled_flag) {
ITEM_SET(vapp, &desc->pps.sps,
log2_min_pcm_luma_coding_block_size_minus3);
ITEM_SET(vapp, &desc->pps.sps,
log2_diff_max_min_pcm_luma_coding_block_size);
}
ITEM_SET(vapp, &desc->pps.sps, max_transform_hierarchy_depth_intra);
ITEM_SET(vapp, &desc->pps.sps, max_transform_hierarchy_depth_inter);
ITEM_SET(vapp, &desc->pps, init_qp_minus26);
ITEM_SET(vapp, &desc->pps, diff_cu_qp_delta_depth);
ITEM_SET(vapp, &desc->pps, pps_cb_qp_offset);
ITEM_SET(vapp, &desc->pps, pps_cr_qp_offset);
ITEM_SET(vapp, &desc->pps, log2_parallel_merge_level_minus2);
if (desc->pps.tiles_enabled_flag) {
ITEM_SET(vapp, &desc->pps, num_tile_columns_minus1);
ITEM_SET(vapp, &desc->pps, num_tile_rows_minus1);
ITEM_CPY(vapp, &desc->pps, column_width_minus1);
ITEM_CPY(vapp, &desc->pps, row_height_minus1);
}
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, lists_modification_present_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps.sps, long_term_ref_pics_present_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps.sps, sps_temporal_mvp_enabled_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, cabac_init_present_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, output_flag_present_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, dependent_slice_segments_enabled_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, pps_slice_chroma_qp_offsets_present_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps.sps, sample_adaptive_offset_enabled_flag);
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, deblocking_filter_override_enabled_flag);
vapp->slice_parsing_fields.bits.pps_disable_deblocking_filter_flag = \
desc->pps.pps_deblocking_filter_disabled_flag;
ITEM_SET(&vapp->slice_parsing_fields.bits,
&desc->pps, slice_segment_header_extension_present_flag);
vapp->slice_parsing_fields.bits.RapPicFlag = desc->RAPPicFlag;
vapp->slice_parsing_fields.bits.IdrPicFlag = desc->IDRPicFlag;
//vapp->slice_parsing_fields.bits.IntraPicFlag
ITEM_SET(vapp, &desc->pps.sps, log2_max_pic_order_cnt_lsb_minus4);
ITEM_SET(vapp, &desc->pps.sps, num_short_term_ref_pic_sets);
vapp->num_long_term_ref_pic_sps = desc->pps.sps.num_long_term_ref_pics_sps;
ITEM_SET(vapp, &desc->pps, num_ref_idx_l0_default_active_minus1);
ITEM_SET(vapp, &desc->pps, num_ref_idx_l1_default_active_minus1);
ITEM_SET(vapp, &desc->pps, pps_beta_offset_div2);
ITEM_SET(vapp, &desc->pps, pps_tc_offset_div2);
ITEM_SET(vapp, &desc->pps, num_extra_slice_header_bits);
ITEM_SET(vapp, &desc->pps, st_rps_bits);
}
/*
* Refer to vlVaHandleSliceParameterBufferHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_slice_param(const struct virgl_h265_picture_desc *desc,
VASliceParameterBufferHEVC *vapp)
{
unsigned i, j;
//slice_data_size;
//slice_data_offset;
//slice_data_flag;
//slice_data_byte_offset;
//slice_segment_address;
for (i = 0; i < 2; i++) {
for (j = 0; j < 15; j++)
vapp->RefPicList[i][j] = desc->RefPicList[i][j];
}
//LongSliceFlags;
//collocated_ref_idx;
//num_ref_idx_l0_active_minus1;
//num_ref_idx_l1_active_minus1;
//slice_qp_delta;
//slice_cb_qp_offset;
//slice_cr_qp_offset;
//slice_beta_offset_div2;
//slice_tc_offset_div2;
//luma_log2_weight_denom;
//delta_chroma_log2_weight_denom;
//delta_luma_weight_l0[15];
//luma_offset_l0[15];
//delta_chroma_weight_l0[15][2];
//ChromaOffsetL0[15][2];
//delta_luma_weight_l1[15];
//luma_offset_l1[15];
//delta_chroma_weight_l1[15][2];
//ChromaOffsetL1[15][2];
//five_minus_max_num_merge_cand;
//num_entry_point_offsets;
//entry_offset_to_subset_array;
//slice_data_num_emu_prevn_bytes;
//va_reserved[VA_PADDING_LOW - 2];
}
/*
* Refer to vlVaHandleVAEncSequenceParameterBufferTypeHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_enc_seq_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc,
VAEncSequenceParameterBufferHEVC *param)
{
(void)codec;
(void)source;
ITEM_SET(param, &desc->seq, general_profile_idc);
ITEM_SET(param, &desc->seq, general_level_idc);
ITEM_SET(param, &desc->seq, general_tier_flag);
ITEM_SET(param, &desc->seq, intra_period);
//intra_idr_period
ITEM_SET(param, &desc->seq, ip_period);
//bits_per_second
ITEM_SET(param, &desc->seq, pic_width_in_luma_samples);
ITEM_SET(param, &desc->seq, pic_height_in_luma_samples);
/* seq_fields.bits */
ITEM_SET(&param->seq_fields.bits, &desc->seq, chroma_format_idc);
//seq_fields.bits.separate_colour_plane_flag
ITEM_SET(&param->seq_fields.bits, &desc->seq, bit_depth_luma_minus8);
ITEM_SET(&param->seq_fields.bits, &desc->seq, bit_depth_chroma_minus8);
//seq_fields.bits.scaling_list_enabled_flag
ITEM_SET(&param->seq_fields.bits, &desc->seq, strong_intra_smoothing_enabled_flag);
ITEM_SET(&param->seq_fields.bits, &desc->seq, amp_enabled_flag);
ITEM_SET(&param->seq_fields.bits, &desc->seq, sample_adaptive_offset_enabled_flag);
ITEM_SET(&param->seq_fields.bits, &desc->seq, pcm_enabled_flag);
//seq_fields.bits.pcm_loop_filter_disabled_flag
ITEM_SET(&param->seq_fields.bits, &desc->seq, sps_temporal_mvp_enabled_flag);
//seq_fields.bits.low_delay_seq
//seq_fields.bits.hierachical_flag
//seq_fields.bits.reserved_bits
ITEM_SET(param, &desc->seq, log2_min_luma_coding_block_size_minus3);
ITEM_SET(param, &desc->seq, log2_diff_max_min_luma_coding_block_size);
ITEM_SET(param, &desc->seq, log2_min_transform_block_size_minus2);
ITEM_SET(param, &desc->seq, log2_diff_max_min_transform_block_size);
ITEM_SET(param, &desc->seq, max_transform_hierarchy_depth_inter);
ITEM_SET(param, &desc->seq, max_transform_hierarchy_depth_intra);
//pcm_sample_bit_depth_luma_minus1
//pcm_sample_bit_depth_chroma_minus1
//log2_min_pcm_luma_coding_block_size_minus3
//log2_max_pcm_luma_coding_block_size_minus3
ITEM_SET(param, &desc->seq, vui_parameters_present_flag);
/* vui_fields.bits */
if (desc->seq.vui_parameters_present_flag) {
ITEM_SET(&param->vui_fields.bits, &desc->seq.vui_flags,
aspect_ratio_info_present_flag);
}
//vui_fields.bits.neutral_chroma_indication_flag
//vui_fields.bits.field_seq_flag
if (desc->seq.vui_parameters_present_flag) {
param->vui_fields.bits.vui_timing_info_present_flag =
desc->seq.vui_flags.timing_info_present_flag;
}
//vui_fields.bits.bitstream_restriction_flag
//vui_fields.bits.tiles_fixed_structure_flag
//vui_fields.bits.motion_vectors_over_pic_boundaries_flag
//vui_fields.bits.restricted_ref_pic_lists_flag
//vui_fields.bits.log2_max_mv_length_horizontal
//vui_fields.bits.log2_max_mv_length_vertical
if (desc->seq.vui_parameters_present_flag) {
ITEM_SET(param, &desc->seq, aspect_ratio_idc);
ITEM_SET(param, &desc->seq, sar_width);
ITEM_SET(param, &desc->seq, sar_height);
}
param->vui_num_units_in_tick = desc->seq.num_units_in_tick;
param->vui_time_scale = desc->seq.time_scale;
//min_spatial_segmentation_idc
//max_bytes_per_pic_denom
//max_bits_per_min_cu_denom
//scc_fields.bits.palette_mode_enabled_flag
}
/*
* Refer to vlVaHandleVAEncPictureParameterBufferTypeHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_enc_picture_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc,
VAEncPictureParameterBufferHEVC *param)
{
unsigned i;
(void)source;
param->decoded_curr_pic.picture_id = get_enc_ref_pic(codec, desc->frame_num);
param->decoded_curr_pic.pic_order_cnt = desc->pic_order_cnt;
for (i = 0; i < 15; i++) {
h265_init_picture(&param->reference_frames[i]);
}
param->coded_buf = codec->va_coded_buf;
//collocated_ref_pic_index
//last_picture
param->pic_init_qp = desc->rc.quant_i_frames;
//diff_cu_qp_delta_depth
//pps_cb_qp_offset
//pps_cr_qp_offset
//num_tile_columns_minus1
//num_tile_rows_minus1
//column_width_minus1[19]
//row_height_minus1[21]
ITEM_SET(param, &desc->pic, log2_parallel_merge_level_minus2);
//ctu_max_bitsize_allowed
param->num_ref_idx_l0_default_active_minus1 = desc->num_ref_idx_l0_active_minus1;
param->num_ref_idx_l1_default_active_minus1 = desc->num_ref_idx_l1_active_minus1;
//slice_pic_parameter_set_id
ITEM_SET(param, &desc->pic, nal_unit_type);
param->pic_fields.bits.idr_pic_flag =
(desc->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR);
switch (desc->picture_type) {
case PIPE_H2645_ENC_PICTURE_TYPE_IDR: /* fallthrough */
case PIPE_H2645_ENC_PICTURE_TYPE_I:
param->pic_fields.bits.coding_type = 1;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_P:
param->pic_fields.bits.coding_type = 2;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
param->pic_fields.bits.coding_type = 3;
break;
default:
break;
}
param->pic_fields.bits.reference_pic_flag = !desc->not_referenced;
//pic_fields.bits.dependent_slice_segments_enabled_flag
//pic_fields.bits.sign_data_hiding_enabled_flag
ITEM_SET(&param->pic_fields.bits, &desc->pic, constrained_intra_pred_flag);
ITEM_SET(&param->pic_fields.bits, &desc->pic, transform_skip_enabled_flag);
//pic_fields.bits.cu_qp_delta_enabled_flag
//pic_fields.bits.weighted_pred_flag
//pic_fields.bits.weighted_bipred_flag
//pic_fields.bits.transquant_bypass_enabled_flag
//pic_fields.bits.tiles_enabled_flag
//pic_fields.bits.entropy_coding_sync_enabled_flag
//pic_fields.bits.loop_filter_across_tiles_enabled_flag
ITEM_SET(&param->pic_fields.bits, &desc->pic,
pps_loop_filter_across_slices_enabled_flag);
//pic_fields.bits.scaling_list_data_present_flag
//pic_fields.bits.screen_content_flag
//pic_fields.bits.enable_gpu_weighted_prediction
//pic_fields.bits.no_output_of_prior_pics_flag
//hierarchical_level_plus1
//scc_fields.bits.pps_curr_pic_ref_enabled_flag
}
/*
* Refer to vlVaHandleVAEncSliceParameterBufferTypeHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_enc_slice_param(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc,
VAEncSliceParameterBufferHEVC *param)
{
unsigned i;
const struct virgl_h265_slice_descriptor *sd;
(void)source;
/* Get the lastest slice descriptor */
if (desc->num_slice_descriptors &&
desc->num_slice_descriptors <= ARRAY_SIZE(desc->slices_descriptors)) {
sd = &desc->slices_descriptors[desc->num_slice_descriptors - 1];
ITEM_SET(param, sd, slice_segment_address);
ITEM_SET(param, sd, num_ctu_in_slice);
}
switch (desc->picture_type) {
case PIPE_H2645_ENC_PICTURE_TYPE_P:
param->slice_type = 0;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
param->slice_type = 1;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_I:
case PIPE_H2645_ENC_PICTURE_TYPE_IDR: /* fall through */
param->slice_type = 2;
break;
case PIPE_H2645_ENC_PICTURE_TYPE_SKIP:
default:
break;
}
//slice_pic_parameter_set_id
//num_ref_idx_l0_active_minus1
//num_ref_idx_l1_active_minus1
for (i = 0; i < 15; i++) {
h265_init_picture(&param->ref_pic_list0[i]);
h265_init_picture(&param->ref_pic_list1[i]);
param->ref_pic_list0[i].picture_id =
get_enc_ref_pic(codec, desc->ref_idx_l0_list[i]);
param->ref_pic_list1[i].picture_id =
get_enc_ref_pic(codec, desc->ref_idx_l1_list[i]);
if (param->ref_pic_list0[i].picture_id != VA_INVALID_ID)
param->ref_pic_list0[i].flags = VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE;
if (param->ref_pic_list1[i].picture_id != VA_INVALID_ID)
param->ref_pic_list1[i].flags = VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE;
}
//luma_log2_weight_denom
//delta_chroma_log2_weight_denom
//delta_luma_weight_l0[15]
//luma_offset_l0[15]
//delta_chroma_weight_l0[15][2]
//chroma_offset_l0[15][2]
//delta_luma_weight_l1[15]
//luma_offset_l1[15]
//delta_chroma_weight_l1[15][2]
//chroma_offset_l1[15][2]
ITEM_SET(param, &desc->slice, max_num_merge_cand);
//slice_qp_delta
ITEM_SET(param, &desc->slice, slice_cb_qp_offset);
ITEM_SET(param, &desc->slice, slice_cr_qp_offset);
ITEM_SET(param, &desc->slice, slice_beta_offset_div2);
ITEM_SET(param, &desc->slice, slice_tc_offset_div2);
//slice_fields.bits.last_slice_of_pic_flag
//slice_fields.bits.dependent_slice_segment_flag
//slice_fields.bits.colour_plane_id
//slice_fields.bits.slice_temporal_mvp_enabled_flag
//slice_fields.bits.slice_sao_luma_flag
//slice_fields.bits.slice_sao_chroma_flag
/*
* Sine num_ref_idx_l0_active_minus1 and num_ref_idx_l1_active_minus1
* have been passed by VAEncPictureParameterBufferHEVC,
* num_ref_idx_active_override_flag is always set to 0.
*/
param->slice_fields.bits.num_ref_idx_active_override_flag = 0;
//slice_fields.bits.mvd_l1_zero_flag
ITEM_SET(&param->slice_fields.bits, &desc->slice, cabac_init_flag);
ITEM_SET(&param->slice_fields.bits, &desc->slice,
slice_deblocking_filter_disabled_flag);
ITEM_SET(&param->slice_fields.bits,
&desc->slice, slice_loop_filter_across_slices_enabled_flag);
//slice_fields.bits.collocated_from_l0_flag
//pred_weight_table_bit_offset
//pred_weight_table_bit_length;
}
/*
* Refer to vlVaHandleVAEncMiscParameterTypeRateControlHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_enc_misc_param_rate_ctrl(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc,
VAEncMiscParameterRateControl *param)
{
(void)codec;
(void)source;
param->bits_per_second = desc->rc.peak_bitrate;
if (desc->rc.rate_ctrl_method !=
PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT) {
param->target_percentage = desc->rc.target_bitrate *
param->bits_per_second / 100.0;
}
//window_size;
//initial_qp;
param->min_qp = desc->rc.min_qp;
//basic_unit_size;
/* rc_flags */
//rc_flags.bits.reset
param->rc_flags.bits.disable_frame_skip = !desc->rc.skip_frame_enable;
param->rc_flags.bits.disable_bit_stuffing = !desc->rc.fill_data_enable;
//rc_flags.bits.mb_rate_control
//rc_flags.bits.temporal_id
//rc_flags.bits.cfs_I_frames
//rc_flags.bits.enable_parallel_brc
//rc_flags.bits.enable_dynamic_scaling
//rc_flags.bits.frame_tolerance_mode
//ICQ_quality_factor;
param->max_qp = desc->rc.max_qp;
//quality_factor;
//target_frame_size;
}
/*
* Refer to vlVaHandleVAEncMiscParameterTypeFrameRateHEVC() in mesa,
* and comment out some unused parameters.
*/
static void h265_fill_enc_misc_param_frame_rate(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc,
VAEncMiscParameterFrameRate *param)
{
(void)codec;
(void)source;
param->framerate = desc->rc.frame_rate_num | (desc->rc.frame_rate_den << 16);
//framerate_flags
}
static int h265_decode_bitstream(struct virgl_video_codec *codec,
struct virgl_video_buffer *target,
const struct virgl_h265_picture_desc *desc,
unsigned num_buffers,
const void * const *buffers,
const unsigned *sizes)
{
unsigned i;
int err = 0;
VAStatus va_stat;
VABufferID *slice_data_buf, pic_param_buf, slice_param_buf;
VAPictureParameterBufferHEVC pic_param = {0};
VASliceParameterBufferHEVC slice_param = {0};
slice_data_buf = calloc(num_buffers, sizeof(VABufferID));
if (!slice_data_buf) {
virgl_log("alloc slice data buffer id failed\n");
return -1;
}
h265_fill_picture_param(codec, target, desc, &pic_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAPictureParameterBufferType,
sizeof(pic_param), 1, &pic_param, &pic_param_buf);
h265_fill_slice_param(desc, &slice_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VASliceParameterBufferType,
sizeof(slice_param), 1, &slice_param, &slice_param_buf);
for (i = 0; i < num_buffers; i++) {
vaCreateBuffer(va_dpy, codec->va_ctx, VASliceDataBufferType,
sizes[i], 1, (void *)(buffers[i]), &slice_data_buf[i]);
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &pic_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render picture param failed, err = 0x%x\n", va_stat);
err = -1;
goto err;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render slice param failed, err = 0x%x\n", va_stat);
err = -1;
goto err;
}
for (i = 0; i < num_buffers; i++) {
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_data_buf[i], 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render slice data failed, err = 0x%x\n", va_stat);
err = -1;
}
}
err:
vaDestroyBuffer(va_dpy, pic_param_buf);
vaDestroyBuffer(va_dpy, slice_param_buf);
for (i = 0; i < num_buffers; i++)
vaDestroyBuffer(va_dpy, slice_data_buf[i]);
free(slice_data_buf);
return err;
}
static int h265_encode_render_sequence(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc)
{
int err = 0;
VAStatus va_stat;
VAEncSequenceParameterBufferHEVC seq_param;
VAEncMiscParameterBuffer *misc_param;
VABufferID seq_param_buf, rc_param_buf, fr_param_buf;
memset(&seq_param, 0, sizeof(seq_param));
h265_fill_enc_seq_param(codec, source, desc, &seq_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncSequenceParameterBufferType,
sizeof(seq_param), 1, &seq_param, &seq_param_buf);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) +
sizeof(VAEncMiscParameterRateControl), 1, NULL, &rc_param_buf);
vaMapBuffer(va_dpy, rc_param_buf, (void **)&misc_param);
misc_param->type = VAEncMiscParameterTypeRateControl;
h265_fill_enc_misc_param_rate_ctrl(codec, source, desc,
(VAEncMiscParameterRateControl *)misc_param->data);
vaUnmapBuffer(va_dpy, rc_param_buf);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncMiscParameterBufferType,
sizeof(VAEncMiscParameterBuffer) +
sizeof(VAEncMiscParameterFrameRate), 1, NULL, &fr_param_buf);
vaMapBuffer(va_dpy, fr_param_buf, (void **)&misc_param);
misc_param->type = VAEncMiscParameterTypeFrameRate;
h265_fill_enc_misc_param_frame_rate(codec, source, desc,
(VAEncMiscParameterFrameRate *)misc_param->data);
vaUnmapBuffer(va_dpy, fr_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &seq_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h265 sequence param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &rc_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h265 rate control param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &fr_param_buf, 1);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h265 frame rate param failed, err = 0x%x\n", va_stat);
err = -1;
goto error;
}
error:
vaDestroyBuffer(va_dpy, seq_param_buf);
vaDestroyBuffer(va_dpy, rc_param_buf);
vaDestroyBuffer(va_dpy, fr_param_buf);
return err;
}
static int h265_encode_render_picture(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc)
{
VAStatus va_stat;
VABufferID pic_param_buf;
VAEncPictureParameterBufferHEVC pic_param;
memset(&pic_param, 0, sizeof(pic_param));
h265_fill_enc_picture_param(codec, source, desc, &pic_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncPictureParameterBufferType,
sizeof(pic_param), 1, &pic_param, &pic_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &pic_param_buf, 1);
vaDestroyBuffer(va_dpy, pic_param_buf);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h265 picture param failed, err = 0x%x\n", va_stat);
return -1;
}
return 0;
}
static int h265_encode_render_slice(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc)
{
VAStatus va_stat;
VABufferID slice_param_buf;
VAEncSliceParameterBufferHEVC slice_param;
memset(&slice_param, 0, sizeof(slice_param));
h265_fill_enc_slice_param(codec, source, desc, &slice_param);
vaCreateBuffer(va_dpy, codec->va_ctx, VAEncSliceParameterBufferType,
sizeof(slice_param), 1, &slice_param, &slice_param_buf);
va_stat = vaRenderPicture(va_dpy, codec->va_ctx, &slice_param_buf, 1);
vaDestroyBuffer(va_dpy, slice_param_buf);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("render h265 slice param failed, err = 0x%x\n", va_stat);
return -1;
}
return 0;
}
static int h265_encode_bitstream(
struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const struct virgl_h265_enc_picture_desc *desc)
{
if (desc->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR) {
h265_encode_render_sequence(codec, source, desc);
}
h265_encode_render_picture(codec, source, desc);
h265_encode_render_slice(codec, source, desc);
return 0;
}
int virgl_video_decode_bitstream(struct virgl_video_codec *codec,
struct virgl_video_buffer *target,
const union virgl_picture_desc *desc,
unsigned num_buffers,
const void * const *buffers,
const unsigned *sizes)
{
if (!va_dpy || !codec || !target || !desc
|| !num_buffers || !buffers || !sizes)
return -1;
if (desc->base.profile != codec->profile) {
virgl_log("profiles not matched, picture: %d, codec: %d\n",
desc->base.profile, codec->profile);
return -1;
}
switch (codec->profile) {
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH422:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH444:
return h264_decode_bitstream(codec, target, &desc->h264,
num_buffers, buffers, sizes);
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_STILL:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_12:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
return h265_decode_bitstream(codec, target, &desc->h265,
num_buffers, buffers, sizes);
default:
break;
}
return -1;
}
int virgl_video_encode_bitstream(struct virgl_video_codec *codec,
struct virgl_video_buffer *source,
const union virgl_picture_desc *desc)
{
if (!va_dpy || !codec || !source || !desc)
return -1;
if (desc->base.profile != codec->profile) {
virgl_log("profiles not matched, picture: %d, codec: %d\n",
desc->base.profile, codec->profile);
return -1;
}
switch (codec->profile) {
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH422:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH444:
return h264_encode_bitstream(codec, source, &desc->h264_enc);
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_STILL:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_12:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
return h265_encode_bitstream(codec, source, &desc->h265_enc);
default:
break;
}
return -1;
}
int virgl_video_end_frame(struct virgl_video_codec *codec,
struct virgl_video_buffer *target)
{
VAStatus va_stat;
if (!va_dpy || !codec || !target)
return -1;
va_stat = vaEndPicture(va_dpy, codec->va_ctx);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("end picture failed, err = 0x%x\n", va_stat);
return -1;
}
va_stat = vaSyncSurface(va_dpy, target->va_sfc);
if (VA_STATUS_SUCCESS != va_stat) {
virgl_log("sync surface failed, err = 0x%x\n", va_stat);
return -1;
}
if (codec->entrypoint != PIPE_VIDEO_ENTRYPOINT_ENCODE) {
decode_completed(codec, target);
} else {
encode_completed(codec, target);
}
return 0;
}