| /************************************************************************** |
| * |
| * 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 |
| * The video implementation of the vrend renderer. |
| * |
| * It is based on the general virgl video submodule and handles data transfer |
| * and synchronization between host and guest. |
| * |
| * The relationship between vaSurface and video buffer objects: |
| * |
| * GUEST (Mesa) | HOST (Virglrenderer) |
| * | |
| * +------------+ | +------------+ |
| * | vaSurface | | | vaSurface | <------+ |
| * +------------+ | +------------+ | |
| * | | | |
| * +---------------------------+ | +-------------------------+ | |
| * | virgl_video_buffer | | | vrend_video_buffer | | |
| * | +-----------------------+ | | | +-------------------+ | | |
| * | | vl_video_buffer | | | | | vrend_resource(s) | | | |
| * | | +-------------------+ | |<--+-->| +-------------------+ | | |
| * | | | virgl_resource(s) | | | | | +--------------------+ | | |
| * | | +-------------------+ | | | | | virgl_video_buffer |-+--+ |
| * | +-----------------------+ | | | +--------------------+ | |
| * +---------------------------+ | +-------------------------+ |
| * |
| * The relationship between vaContext and video codec objects: |
| * |
| * GUEST (Mesa) | HOST (Virglrenderer) |
| * | |
| * +------------+ | +------------+ |
| * | vaContext | | | vaContext | <-------+ |
| * +------------+ | +------------+ | |
| * | | | |
| * +------------------------+ | +--------------------------+ | |
| * | virgl_video_codec | <--+--> | vrend_video_codec | | |
| * +------------------------+ | | +--------------------+ | | |
| * | | | virgl_video_codec | -+--+ |
| * | | +--------------------+ | |
| * | +--------------------------+ |
| * |
| * @author Feng Jiang <jiangfeng@kylinos.cn> |
| */ |
| |
| |
| #include <sys/param.h> |
| |
| #include "virgl_video.h" |
| #include "virgl_video_hw.h" |
| |
| #include "vrend_debug.h" |
| #include "vrend_winsys.h" |
| #include "vrend_renderer.h" |
| #include "vrend_video.h" |
| |
| struct vrend_context; |
| |
| struct vrend_video_context { |
| struct vrend_context *ctx; |
| struct list_head codecs; |
| struct list_head buffers; |
| }; |
| |
| struct vrend_video_codec { |
| struct virgl_video_codec *codec; |
| uint32_t handle; |
| struct vrend_resource *feed_res; /* encoding feedback */ |
| struct vrend_resource *dest_res; /* encoding coded buffer */ |
| struct vrend_video_context *ctx; |
| struct list_head head; |
| }; |
| |
| struct vrend_video_plane { |
| uint32_t res_handle; |
| GLuint texture; /* texture for temporary use */ |
| GLuint framebuffer; /* framebuffer for temporary use */ |
| EGLImageKHR egl_image; /* egl image for temporary use */ |
| }; |
| |
| struct vrend_video_buffer { |
| struct virgl_video_buffer *buffer; |
| |
| uint32_t handle; |
| struct vrend_video_context *ctx; |
| struct list_head head; |
| |
| uint32_t num_planes; |
| struct vrend_video_plane planes[3]; |
| }; |
| |
| static struct vrend_video_codec *vrend_video_codec( |
| struct virgl_video_codec *codec) |
| { |
| return virgl_video_codec_opaque_data(codec); |
| } |
| |
| static struct vrend_video_buffer *vrend_video_buffer( |
| struct virgl_video_buffer *buffer) |
| { |
| return virgl_video_buffer_opaque_data(buffer); |
| } |
| |
| static struct vrend_video_codec *get_video_codec( |
| struct vrend_video_context *ctx, |
| uint32_t cdc_handle) |
| { |
| struct vrend_video_codec *cdc; |
| |
| LIST_FOR_EACH_ENTRY(cdc, &ctx->codecs, head) { |
| if (cdc->handle == cdc_handle) |
| return cdc; |
| } |
| |
| return NULL; |
| } |
| |
| static struct vrend_video_buffer *get_video_buffer( |
| struct vrend_video_context *ctx, |
| uint32_t buf_handle) |
| { |
| struct vrend_video_buffer *buf; |
| |
| LIST_FOR_EACH_ENTRY(buf, &ctx->buffers, head) { |
| if (buf->handle == buf_handle) |
| return buf; |
| } |
| |
| return NULL; |
| } |
| |
| |
| static int sync_dmabuf_to_video_buffer(struct vrend_video_buffer *buf, |
| const struct virgl_video_dma_buf *dmabuf) |
| { |
| if (!(dmabuf->flags & VIRGL_VIDEO_DMABUF_READ_ONLY)) { |
| vrend_printf("%s: dmabuf is not readable\n", __func__); |
| return -1; |
| } |
| |
| for (unsigned i = 0; i < dmabuf->num_planes && i < buf->num_planes; i++) { |
| struct vrend_video_plane *plane = &buf->planes[i]; |
| struct vrend_resource *res; |
| |
| res = vrend_renderer_ctx_res_lookup(buf->ctx->ctx, plane->res_handle); |
| if (!res) { |
| vrend_printf("%s: res %d not found\n", __func__, plane->res_handle); |
| continue; |
| } |
| |
| /* dmabuf -> eglimage */ |
| if (EGL_NO_IMAGE_KHR == plane->egl_image) { |
| EGLint img_attrs[16] = { |
| EGL_LINUX_DRM_FOURCC_EXT, dmabuf->planes[i].drm_format, |
| EGL_WIDTH, dmabuf->width / (i + 1), |
| EGL_HEIGHT, dmabuf->height / (i + 1), |
| EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->planes[i].fd, |
| EGL_DMA_BUF_PLANE0_OFFSET_EXT, dmabuf->planes[i].offset, |
| EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->planes[i].pitch, |
| EGL_NONE |
| }; |
| |
| plane->egl_image = eglCreateImageKHR(eglGetCurrentDisplay(), |
| EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, img_attrs); |
| } |
| |
| if (EGL_NO_IMAGE_KHR == plane->egl_image) { |
| vrend_printf("%s: create egl image failed\n", __func__); |
| continue; |
| } |
| |
| /* eglimage -> texture */ |
| glBindTexture(GL_TEXTURE_2D, plane->texture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, |
| (GLeglImageOES)(plane->egl_image)); |
| |
| /* texture -> framebuffer */ |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, plane->framebuffer); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, plane->texture, 0); |
| |
| /* framebuffer -> vrend_video_buffer.planes[i] */ |
| glBindTexture(GL_TEXTURE_2D, res->id); |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, |
| res->base.width0, res->base.height0); |
| } |
| |
| glBindTexture(GL_TEXTURE_2D, 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| return 0; |
| } |
| |
| static int sync_video_buffer_to_dmabuf(struct vrend_video_buffer *buf, |
| const struct virgl_video_dma_buf *dmabuf) |
| { |
| if (!(dmabuf->flags & VIRGL_VIDEO_DMABUF_WRITE_ONLY)) { |
| vrend_printf("%s: dmabuf is not writable\n", __func__); |
| return -1; |
| } |
| |
| for (unsigned i = 0; i < dmabuf->num_planes && i < buf->num_planes; i++) { |
| struct vrend_video_plane *plane = &buf->planes[i]; |
| struct vrend_resource *res; |
| |
| res = vrend_renderer_ctx_res_lookup(buf->ctx->ctx, plane->res_handle); |
| if (!res) { |
| vrend_printf("%s: res %d not found\n", __func__, plane->res_handle); |
| continue; |
| } |
| |
| /* dmabuf -> eglimage */ |
| if (EGL_NO_IMAGE_KHR == plane->egl_image) { |
| EGLint img_attrs[16] = { |
| EGL_LINUX_DRM_FOURCC_EXT, dmabuf->planes[i].drm_format, |
| EGL_WIDTH, dmabuf->width / (i + 1), |
| EGL_HEIGHT, dmabuf->height / (i + 1), |
| EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf->planes[i].fd, |
| EGL_DMA_BUF_PLANE0_OFFSET_EXT, dmabuf->planes[i].offset, |
| EGL_DMA_BUF_PLANE0_PITCH_EXT, dmabuf->planes[i].pitch, |
| EGL_NONE |
| }; |
| |
| plane->egl_image = eglCreateImageKHR(eglGetCurrentDisplay(), |
| EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, img_attrs); |
| } |
| |
| if (EGL_NO_IMAGE_KHR == plane->egl_image) { |
| vrend_printf("%s: create egl image failed\n", __func__); |
| continue; |
| } |
| |
| /* eglimage -> texture */ |
| glBindTexture(GL_TEXTURE_2D, plane->texture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, |
| (GLeglImageOES)(plane->egl_image)); |
| |
| /* vrend_video_buffer.planes[i] -> framebuffer */ |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, plane->framebuffer); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, res->id, 0); |
| |
| /* framebuffer -> texture */ |
| glBindTexture(GL_TEXTURE_2D, plane->texture); |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, |
| res->base.width0, res->base.height0); |
| |
| } |
| |
| glBindTexture(GL_TEXTURE_2D, 0); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| return 0; |
| } |
| |
| |
| static void vrend_video_decode_completed( |
| struct virgl_video_codec *codec, |
| const struct virgl_video_dma_buf *dmabuf) |
| { |
| struct vrend_video_buffer *buf = vrend_video_buffer(dmabuf->buf); |
| |
| (void)codec; |
| |
| sync_dmabuf_to_video_buffer(buf, dmabuf); |
| } |
| |
| |
| static void vrend_video_enocde_upload_picture( |
| struct virgl_video_codec *codec, |
| const struct virgl_video_dma_buf *dmabuf) |
| { |
| struct vrend_video_buffer *buf = vrend_video_buffer(dmabuf->buf); |
| |
| (void)codec; |
| |
| sync_video_buffer_to_dmabuf(buf, dmabuf); |
| } |
| |
| static void vrend_video_encode_completed( |
| struct virgl_video_codec *codec, |
| const struct virgl_video_dma_buf *src_buf, |
| const struct virgl_video_dma_buf *ref_buf, |
| unsigned num_coded_bufs, |
| const void * const *coded_bufs, |
| const unsigned *coded_sizes) |
| { |
| void *buf; |
| unsigned i, size, data_size; |
| struct virgl_video_encode_feedback feedback; |
| struct vrend_video_codec *cdc = vrend_video_codec(codec); |
| |
| (void)src_buf; |
| (void)ref_buf; |
| |
| if (!cdc->dest_res || !cdc->feed_res) |
| return; |
| |
| memset(&feedback, 0, sizeof(feedback)); |
| |
| /* sync coded data to guest */ |
| if (has_bit(cdc->dest_res->storage_bits, VREND_STORAGE_GL_BUFFER)) { |
| glBindBufferARB(cdc->dest_res->target, cdc->dest_res->id); |
| buf = glMapBufferRange(cdc->dest_res->target, 0, |
| cdc->dest_res->base.width0, GL_MAP_WRITE_BIT); |
| for (i = 0, data_size = 0; i < num_coded_bufs && |
| data_size < cdc->dest_res->base.width0; i++) { |
| size = MIN(cdc->dest_res->base.width0 - data_size, coded_sizes[i]); |
| memcpy((uint8_t *)buf + data_size, coded_bufs[i], size); |
| vrend_write_to_iovec(cdc->dest_res->iov, cdc->dest_res->num_iovs, |
| data_size, coded_bufs[i], size); |
| data_size += size; |
| } |
| glUnmapBuffer(cdc->dest_res->target); |
| glBindBufferARB(cdc->dest_res->target, 0); |
| feedback.stat = VIRGL_VIDEO_ENCODE_STAT_SUCCESS; |
| feedback.coded_size = data_size; |
| } else { |
| vrend_printf("unexcepted coded res type\n"); |
| feedback.stat = VIRGL_VIDEO_ENCODE_STAT_FAILURE; |
| feedback.coded_size = 0; |
| } |
| |
| /* send feedback */ |
| vrend_write_to_iovec(cdc->feed_res->iov, cdc->feed_res->num_iovs, |
| 0, (char *)(&feedback), |
| MIN(cdc->feed_res->base.width0, sizeof(feedback))); |
| |
| cdc->dest_res = NULL; |
| cdc->feed_res = NULL; |
| } |
| |
| static struct virgl_video_callbacks video_callbacks = { |
| .decode_completed = vrend_video_decode_completed, |
| .encode_upload_picture = vrend_video_enocde_upload_picture, |
| .encode_completed = vrend_video_encode_completed, |
| }; |
| |
| int vrend_video_init(int drm_fd) |
| { |
| if (drm_fd < 0) |
| return -1; |
| |
| return virgl_video_init(drm_fd, &video_callbacks, 0); |
| } |
| |
| void vrend_video_fini(void) |
| { |
| virgl_video_destroy(); |
| } |
| |
| int vrend_video_fill_caps(union virgl_caps *caps) |
| { |
| return virgl_video_fill_caps(caps); |
| } |
| |
| int vrend_video_create_codec(struct vrend_video_context *ctx, |
| uint32_t handle, |
| uint32_t profile, |
| uint32_t entrypoint, |
| uint32_t chroma_format, |
| uint32_t level, |
| uint32_t width, |
| uint32_t height, |
| uint32_t max_ref, |
| uint32_t flags) |
| { |
| struct vrend_video_codec *cdc = get_video_codec(ctx, handle); |
| struct virgl_video_create_codec_args args; |
| |
| if (cdc) |
| return 0; |
| |
| if (profile <= PIPE_VIDEO_PROFILE_UNKNOWN || |
| profile >= PIPE_VIDEO_PROFILE_MAX) |
| return -1; |
| |
| if (entrypoint <= PIPE_VIDEO_ENTRYPOINT_UNKNOWN || |
| entrypoint > PIPE_VIDEO_ENTRYPOINT_ENCODE) |
| return -1; |
| |
| if (chroma_format >= PIPE_VIDEO_CHROMA_FORMAT_NONE) |
| return -1; |
| |
| if (!width || !height) |
| return -1; |
| |
| cdc = (struct vrend_video_codec *)calloc(1, sizeof(*cdc)); |
| if (!cdc) |
| return -1; |
| |
| args.profile = profile; |
| args.entrypoint = entrypoint; |
| args.chroma_format = chroma_format; |
| args.level = level; |
| args.width = width; |
| args.height = height; |
| args.max_references = max_ref; |
| args.flags = flags; |
| args.opaque = cdc; |
| cdc->codec = virgl_video_create_codec(&args); |
| if (!cdc->codec) { |
| free(cdc); |
| return -1; |
| } |
| |
| cdc->handle = handle; |
| cdc->ctx = ctx; |
| list_add(&cdc->head, &ctx->codecs); |
| |
| return 0; |
| } |
| |
| static void destroy_video_codec(struct vrend_video_codec *cdc) |
| { |
| if (cdc) { |
| list_del(&cdc->head); |
| virgl_video_destroy_codec(cdc->codec); |
| free(cdc); |
| } |
| } |
| |
| void vrend_video_destroy_codec(struct vrend_video_context *ctx, |
| uint32_t handle) |
| { |
| struct vrend_video_codec *cdc = get_video_codec(ctx, handle); |
| |
| destroy_video_codec(cdc); |
| } |
| |
| int vrend_video_create_buffer(struct vrend_video_context *ctx, |
| uint32_t handle, |
| uint32_t format, |
| uint32_t width, |
| uint32_t height, |
| uint32_t *res_handles, |
| unsigned int num_res) |
| { |
| unsigned i; |
| struct vrend_video_plane *plane; |
| struct vrend_video_buffer *buf = get_video_buffer(ctx, handle); |
| struct virgl_video_create_buffer_args args; |
| |
| if (buf) |
| return 0; |
| |
| if (format <= PIPE_FORMAT_NONE || format >= PIPE_FORMAT_COUNT) |
| return -1; |
| |
| if (!width || !height || !res_handles || !num_res) |
| return -1; |
| |
| buf = (struct vrend_video_buffer *)calloc(1, sizeof(*buf)); |
| if (!buf) |
| return -1; |
| |
| args.format = format; |
| args.width = width; |
| args.height = height; |
| args.interlaced = 0; |
| args.opaque = buf; |
| buf->buffer = virgl_video_create_buffer(&args); |
| if (!buf->buffer) { |
| free(buf); |
| return -1; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(buf->planes); i++) |
| buf->planes[i].egl_image = EGL_NO_IMAGE_KHR; |
| |
| for (i = 0, buf->num_planes = 0; |
| i < num_res && buf->num_planes < ARRAY_SIZE(buf->planes); i++) { |
| |
| if (!res_handles[i]) |
| continue; |
| |
| plane = &buf->planes[buf->num_planes++]; |
| plane->res_handle = res_handles[i]; |
| glGenFramebuffers(1, &plane->framebuffer); |
| glGenTextures(1, &plane->texture); |
| glBindTexture(GL_TEXTURE_2D, plane->texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| } |
| |
| buf->handle = handle; |
| buf->ctx = ctx; |
| list_add(&buf->head, &ctx->buffers); |
| |
| return 0; |
| } |
| |
| static void destroy_video_buffer(struct vrend_video_buffer *buf) |
| { |
| unsigned i; |
| struct vrend_video_plane *plane; |
| |
| if (!buf) |
| return; |
| |
| list_del(&buf->head); |
| |
| for (i = 0; i < buf->num_planes; i++) { |
| plane = &buf->planes[i]; |
| |
| glDeleteTextures(1, &plane->texture); |
| glDeleteFramebuffers(1, &plane->framebuffer); |
| if (plane->egl_image == EGL_NO_IMAGE_KHR) |
| eglDestroyImageKHR(eglGetCurrentDisplay(), plane->egl_image); |
| } |
| |
| virgl_video_destroy_buffer(buf->buffer); |
| |
| free(buf); |
| } |
| |
| void vrend_video_destroy_buffer(struct vrend_video_context *ctx, |
| uint32_t handle) |
| { |
| struct vrend_video_buffer *buf = get_video_buffer(ctx, handle); |
| |
| destroy_video_buffer(buf); |
| } |
| |
| struct vrend_video_context *vrend_video_create_context(struct vrend_context *ctx) |
| { |
| struct vrend_video_context *vctx; |
| |
| vctx = (struct vrend_video_context *)calloc(1, sizeof(*vctx)); |
| if (vctx) { |
| vctx->ctx = ctx; |
| list_inithead(&vctx->codecs); |
| list_inithead(&vctx->buffers); |
| } |
| |
| return vctx; |
| } |
| |
| void vrend_video_destroy_context(struct vrend_video_context *ctx) |
| { |
| struct vrend_video_codec *vcdc, *vcdc_tmp; |
| struct vrend_video_buffer *vbuf, *vbuf_tmp; |
| |
| LIST_FOR_EACH_ENTRY_SAFE(vcdc, vcdc_tmp, &ctx->codecs, head) |
| destroy_video_codec(vcdc); |
| |
| LIST_FOR_EACH_ENTRY_SAFE(vbuf, vbuf_tmp, &ctx->buffers, head) |
| destroy_video_buffer(vbuf); |
| |
| free(ctx); |
| } |
| |
| int vrend_video_begin_frame(struct vrend_video_context *ctx, |
| uint32_t cdc_handle, |
| uint32_t tgt_handle) |
| { |
| struct vrend_video_codec *cdc = get_video_codec(ctx, cdc_handle); |
| struct vrend_video_buffer *tgt = get_video_buffer(ctx, tgt_handle); |
| |
| if (!cdc || !tgt) |
| return -1; |
| |
| return virgl_video_begin_frame(cdc->codec, tgt->buffer); |
| } |
| |
| static void modify_h264_picture_desc(struct vrend_video_codec *cdc, |
| struct vrend_video_buffer *tgt, |
| struct virgl_h264_picture_desc *desc) |
| { |
| unsigned i; |
| struct vrend_video_buffer *vbuf; |
| |
| (void)tgt; |
| |
| for (i = 0; i < ARRAY_SIZE(desc->buffer_id); i++) { |
| vbuf = get_video_buffer(cdc->ctx, desc->buffer_id[i]); |
| desc->buffer_id[i] = virgl_video_buffer_id(vbuf ? vbuf->buffer : NULL); |
| } |
| } |
| |
| static void modify_h265_picture_desc(struct vrend_video_codec *cdc, |
| struct vrend_video_buffer *tgt, |
| struct virgl_h265_picture_desc *desc) |
| { |
| unsigned i; |
| struct vrend_video_buffer *vbuf; |
| |
| (void)tgt; |
| |
| for (i = 0; i < ARRAY_SIZE(desc->ref); i++) { |
| vbuf = get_video_buffer(cdc->ctx, desc->ref[i]); |
| desc->ref[i] = virgl_video_buffer_id(vbuf ? vbuf->buffer : NULL); |
| } |
| } |
| |
| static void modify_picture_desc(struct vrend_video_codec *cdc, |
| struct vrend_video_buffer *tgt, |
| union virgl_picture_desc *desc) |
| { |
| switch(virgl_video_codec_profile(cdc->codec)) { |
| 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: |
| modify_h264_picture_desc(cdc, tgt, &desc->h264); |
| break; |
| 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: |
| modify_h265_picture_desc(cdc, tgt, &desc->h265); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| int vrend_video_decode_bitstream(struct vrend_video_context *ctx, |
| uint32_t cdc_handle, |
| uint32_t tgt_handle, |
| uint32_t desc_handle, |
| unsigned num_buffers, |
| const uint32_t *buffer_handles, |
| const uint32_t *buffer_sizes) |
| { |
| int err = -1; |
| unsigned i, num_bs, *bs_sizes = NULL; |
| void **bs_buffers = NULL; |
| struct vrend_resource *res; |
| struct vrend_video_codec *cdc = get_video_codec(ctx, cdc_handle); |
| struct vrend_video_buffer *tgt = get_video_buffer(ctx, tgt_handle); |
| union virgl_picture_desc desc; |
| |
| if (!cdc || !tgt) |
| return -1; |
| |
| bs_buffers = calloc(num_buffers, sizeof(void *)); |
| if (!bs_buffers) { |
| vrend_printf("%s: alloc bs_buffers failed\n", __func__); |
| return -1; |
| } |
| |
| bs_sizes = calloc(num_buffers, sizeof(unsigned)); |
| if (!bs_sizes) { |
| vrend_printf("%s: alloc bs_sizes failed\n", __func__); |
| goto err; |
| } |
| |
| for (i = 0, num_bs = 0; i < num_buffers; i++) { |
| res = vrend_renderer_ctx_res_lookup(ctx->ctx, buffer_handles[i]); |
| if (!res || !res->ptr) { |
| vrend_printf("%s: bs res %d invalid or not found", |
| __func__, buffer_handles[i]); |
| continue; |
| } |
| |
| vrend_read_from_iovec(res->iov, res->num_iovs, 0, |
| res->ptr, buffer_sizes[i]); |
| bs_buffers[num_bs] = res->ptr; |
| bs_sizes[num_bs] = buffer_sizes[i]; |
| num_bs++; |
| } |
| |
| res = vrend_renderer_ctx_res_lookup(ctx->ctx, desc_handle); |
| if (!res) { |
| vrend_printf("%s: desc res %d not found\n", __func__, desc_handle); |
| goto err; |
| } |
| memset(&desc, 0, sizeof(desc)); |
| vrend_read_from_iovec(res->iov, res->num_iovs, 0, (char *)(&desc), |
| MIN(res->base.width0, sizeof(desc))); |
| modify_picture_desc(cdc, tgt, &desc); |
| |
| err = virgl_video_decode_bitstream(cdc->codec, tgt->buffer, &desc, |
| num_bs, (const void * const *)bs_buffers, bs_sizes); |
| |
| err: |
| free(bs_buffers); |
| free(bs_sizes); |
| |
| return err; |
| } |
| |
| int vrend_video_encode_bitstream(struct vrend_video_context *ctx, |
| uint32_t cdc_handle, |
| uint32_t src_handle, |
| uint32_t dest_handle, |
| uint32_t desc_handle, |
| uint32_t feed_handle) |
| { |
| union virgl_picture_desc desc; |
| struct vrend_resource *dest_res, *desc_res, *feed_res; |
| struct vrend_video_codec *cdc = get_video_codec(ctx, cdc_handle); |
| struct vrend_video_buffer *src = get_video_buffer(ctx, src_handle); |
| |
| if (!cdc || !src) |
| return -1; |
| |
| /* Feedback resource */ |
| feed_res = vrend_renderer_ctx_res_lookup(ctx->ctx, feed_handle); |
| if (!feed_res) { |
| vrend_printf("%s: feedback res %d not found\n", __func__, feed_handle); |
| return -1; |
| } |
| |
| /* Picture descriptor resource */ |
| desc_res = vrend_renderer_ctx_res_lookup(ctx->ctx, desc_handle); |
| if (!desc_res) { |
| vrend_printf("%s: desc res %d not found\n", __func__, desc_handle); |
| return -1; |
| } |
| memset(&desc, 0, sizeof(desc)); |
| vrend_read_from_iovec(desc_res->iov, desc_res->num_iovs, 0, (char *)(&desc), |
| MIN(desc_res->base.width0, sizeof(desc))); |
| |
| /* Destination buffer resource. */ |
| dest_res = vrend_renderer_ctx_res_lookup(ctx->ctx, dest_handle); |
| if (!dest_res) { |
| vrend_printf("%s: dest res %d not found\n", __func__, dest_handle); |
| return -1; |
| } |
| |
| cdc->feed_res = feed_res; |
| cdc->dest_res = dest_res; |
| |
| return virgl_video_encode_bitstream(cdc->codec, src->buffer, &desc); |
| } |
| |
| int vrend_video_end_frame(struct vrend_video_context *ctx, |
| uint32_t cdc_handle, |
| uint32_t tgt_handle) |
| { |
| struct vrend_video_codec *cdc = get_video_codec(ctx, cdc_handle); |
| struct vrend_video_buffer *tgt = get_video_buffer(ctx, tgt_handle); |
| |
| if (!cdc || !tgt) |
| return -1; |
| |
| return virgl_video_end_frame(cdc->codec, tgt->buffer); |
| } |
| |