From 456c8b913b724c0926862bdfbfbe2f1d93371afc Mon Sep 17 00:00:00 2001 From: "Du, Kelly" Date: Wed, 7 Aug 2019 14:42:37 +0800 Subject: [PATCH] add VP sample for usrptr and 1:N output Signed-off-by: Du, Kelly --- videoprocess/Makefile.am | 4 +- .../process_scaling_n_out_usrptr.cfg.template | 90 ++ videoprocess/vppscaling_n_out_usrptr.cpp | 1066 +++++++++++++++++ 3 files changed, 1159 insertions(+), 1 deletion(-) create mode 100644 videoprocess/process_scaling_n_out_usrptr.cfg.template create mode 100644 videoprocess/vppscaling_n_out_usrptr.cpp diff --git a/videoprocess/Makefile.am b/videoprocess/Makefile.am index 0c527634..1a372696 100644 --- a/videoprocess/Makefile.am +++ b/videoprocess/Makefile.am @@ -20,7 +20,7 @@ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -bin_PROGRAMS = vavpp vppscaling_csc vppdenoise vppsharpness vppchromasitting vppblending +bin_PROGRAMS = vavpp vppscaling_csc vppdenoise vppsharpness vppchromasitting vppblending vppscaling_n_out_usrptr AM_CPPFLAGS = \ -Wall \ @@ -55,6 +55,8 @@ vppchromasitting_LDADD = $(TEST_LIBS) vppblending_SOURCES = vppblending.cpp vppblending_LDADD = $(TEST_LIBS) +vppscaling_n_out_usrptr_SOURCES = vppscaling_n_out_usrptr.cpp +vppscaling_n_out_usrptr_LDADD = $(TEST_LIBS) valgrind:(bin_PROGRAMS) for a in(bin_PROGRAMS); do \ valgrind --leak-check=full --show-reachable=yes .libs/$$a; \ diff --git a/videoprocess/process_scaling_n_out_usrptr.cfg.template b/videoprocess/process_scaling_n_out_usrptr.cfg.template new file mode 100644 index 00000000..9e79d435 --- /dev/null +++ b/videoprocess/process_scaling_n_out_usrptr.cfg.template @@ -0,0 +1,90 @@ +# Configuration information for video process test case. +# This application will firstly load yuv frames to one type of surface(NV12/YV12/I420) +# you require. After video processing, the processed content (NV12/YV12/I420 surface) +# will be stored to frames(yv12 format in file). +# Supported features include scaling and implicit format conversion(NV12<->YV12<->I420). +# input and output crop and usrptr. +# you can modify this configuration file to set the corresponding parameters. + +#1.Source YUV(RGB) file information +SRC_FILE_NAME: ./flowersky_352x288_writer352x288.yv12 +SRC_FRAME_WIDTH: 352 +SRC_FRAME_HEIGHT: 288 +SRC_FRAME_FORMAT: YV12 + +# supported type: (CPU, VA), default: VA +SRC_SURFACE_MEMORY_TYPE: VA + +#if use usrptr CPU memory type, can support 16/128 align mode +SRC_SURFACE_CPU_ALIGN_MODE: 128 + +#if you want to do source crop, you need define the area below to crop +#do the crop: 1; not do the crop: 0(default) +SRC_SURFACE_CROP: 1 + +#Cropping info +SRC_CROP_LEFT_X: 10 +SRC_CROP_TOP_Y: 20 +SRC_CROP_WIDTH:200 +SRC_CROP_HEIGHT:200 + +#2.Destination YUV(RGB) file information +#dest file number: +DST_NUMBER: 1 + +#we can support N>=2 outputs, you just need add the para to xx_N format +DST_FILE_NAME_1: ./scaling_out_1400x1200.nv12 +DST_FRAME_WIDTH_1: 1400 +DST_FRAME_HEIGHT_1: 1200 +DST_FRAME_FORMAT_1: NV12 + +#dest surface memory type +# supported type: (CPU, VA), default: VA +DST_SURFACE_MEMORY_TYPE_1: VA + +#if use usrptr CPU memory type, can support 16/128 align mode +DST_SURFACE_CPU_ALIGN_MODE_1: 128 + +#we can support the output crop with none (0, 0) top/left in render target +#if the value set to 1, the output can be scale to the positon below defined +#if the value set to 0, ignore and do not crop output +#if the value set to 1, please ensure the DST_NUMBER to 1 +DST_SURFACE_CROP: 1 +#destination area (used in scaling) +DST_CROP_LEFT_X: 100 +DST_CROP_TOP_Y: 100 +DST_CROP_WIDTH: 1000 +DST_CROP_HEIGHT: 800 + +DST_FILE_NAME_2: ./scaling_out_1000x800_2.yuy2 +DST_FRAME_WIDTH_2: 1000 +DST_FRAME_HEIGHT_2: 800 +DST_FRAME_FORMAT_2: YUY2 + +DST_FILE_FORMAT_2: YUY2 +#dest surface memory type +# supported type: (CPU, VA), default: VA +DST_SURFACE_MEMORY_TYPE_2: CPU + +#if use usrptr CPU memory type, can support 16/128 align mode +DST_SURFACE_CPU_ALIGN_MODE_2: 128 + +DST_FILE_NAME_3: ./scaling_out_1200x1000_3.nv12 +DST_FRAME_WIDTH_3: 1200 +DST_FRAME_HEIGHT_3: 1000 +DST_FRAME_FORMAT_3: NV12 + +#dest surface memory type +# supported type: (CPU, VA), default: VA +DST_SURFACE_MEMORY_TYPE_3: CPU + +#if use usrptr CPU memory type, can support 16/128 align mode +DST_SURFACE_CPU_ALIGN_MODE_3: 128 + +#if you need to scale the 16align output as input, you can add the para 2ND_SCALE, +#xx_02 will be the 2nd scale output, regarding the 1st scale output as input +# 1: enable the 2ndscale, default:0 +2ND_SCALE: 0 +#3.How many frames to be processed +FRAME_SUM: 1 + diff --git a/videoprocess/vppscaling_n_out_usrptr.cpp b/videoprocess/vppscaling_n_out_usrptr.cpp new file mode 100644 index 00000000..4a3a222c --- /dev/null +++ b/videoprocess/vppscaling_n_out_usrptr.cpp @@ -0,0 +1,1066 @@ +/* +* Copyright (c) 2009-2018, Intel Corporation +* +* 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. +*/ +/* + * Video process test case based on LibVA. + * This test covers 1:N(N>=2) output for scaling and several surface format conversion. + * also support 2nd scale, regarding the 1st scale output as input for 2nd scale + * also support UserPtr 16 alignment as NV12/YV12/YUY2 input + * support none (0, 0) top/left in render target as RGB/YV12 output + * support none (0, 0) top/left input crop + * Usage: ./vppscaling_n_out_usrptr process_scaling_n_out_usrptr.cfg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "va_display.h" +#include + +#define MAX_LEN 1024 + +#define CHECK_VASTATUS(va_status,func) \ + if (va_status != VA_STATUS_SUCCESS) { \ + fprintf(stderr,"%s:%s (%d) failed,exit\n", __func__, func, __LINE__); \ + exit(1); \ + } + +#define VPP_FREE(p) \ + if(p != NULL){ \ + free(p); p = NULL; \ + } + +typedef struct { + char file_name[100]; + FILE* file_fd; + uint32_t pic_width; + uint32_t pic_height; + uint32_t fourcc; + uint32_t rtformat; + uint32_t memtype; + uint32_t align_mode; + void *pBuf; + uint8_t *pUserBase; + uintptr_t ptrb; +} VPP_ImageInfo; + + +#if 0 +static Display *x11_display = NULL; +#endif +static VADisplay va_dpy = NULL; +static VAContextID context_id = 0; +static VAConfigID config_id = 0; +static VASurfaceID g_in_surface_id = VA_INVALID_ID; +static VASurfaceID *g_out_surface_ids = NULL; + +static FILE* g_config_file_fd = NULL; +static char g_config_file_name[MAX_LEN]; +static VPP_ImageInfo g_src_info; +static VPP_ImageInfo *g_dst_info = NULL; + +static uint32_t g_frame_count = 0; +static uint32_t g_dst_count = 1; +static uint32_t g_scale_again = 0; + + +static int8_t +read_value_string(FILE *fp, const char* field_name, char* value) +{ + char strLine[MAX_LEN]; + char* field = NULL; + char* str = NULL; + uint16_t i; + + if (!fp || !field_name || !value) { + printf("Invalid fuction parameters\n"); + return -1; + } + + rewind(fp); + + while (!feof(fp)) { + if (!fgets(strLine, MAX_LEN, fp)) + continue; + + for (i = 0; i < MAX_LEN && strLine[i]; i++) + if (strLine[i] != ' ') break; + + if (i == MAX_LEN || strLine[i] == '#' || strLine[i] == '\n') + continue; + + field = strtok(&strLine[i], ":"); + if (strncmp(field, field_name, strlen(field_name))) + continue; + + if (!(str = strtok(NULL, ":"))) + continue; + + /* skip blank space in string */ + while (*str == ' ') + str++; + + *(str + strlen(str)-1) = '\0'; + strcpy(value, str); + + return 0; + } + + return -1; +} + +static int8_t +read_value_uint32(FILE* fp, const char* field_name, uint32_t* value) +{ + char str[MAX_LEN]; + + if (read_value_string(fp, field_name, str)) { + printf("Failed to find integer field: %s", field_name); + return -1; + } + + *value = (uint32_t)atoi(str); + return 0; +} + +static int8_t +read_value_int16(FILE* fp, const char* field_name, int16_t* value) +{ + char str[MAX_LEN]; + + if (read_value_string(fp, field_name, str)) { + printf("Failed to find integer field: %s", field_name); + return -1; + } + + *value = (int16_t)atoi(str); + return 0; +} + +static int8_t +read_value_uint16(FILE* fp, const char* field_name, uint16_t* value) +{ + char str[MAX_LEN]; + + if (read_value_string(fp, field_name, str)) { + printf("Failed to find integer field: %s", field_name); + return -1; + } + + *value = (uint16_t)atoi(str); + return 0; +} + + +static VAStatus +create_surface(VPP_ImageInfo &img_info,VASurfaceID * p_surface_id) +{ + VAStatus va_status; + if(img_info.memtype == VA_SURFACE_ATTRIB_MEM_TYPE_VA){ + VASurfaceAttrib surface_attrib; + surface_attrib.type = VASurfaceAttribPixelFormat; + surface_attrib.flags = VA_SURFACE_ATTRIB_SETTABLE; + surface_attrib.value.type = VAGenericValueTypeInteger; + surface_attrib.value.value.i = img_info.fourcc; + + va_status = vaCreateSurfaces(va_dpy, + img_info.rtformat, + img_info.pic_width , + img_info.pic_height, + p_surface_id, + 1, + &surface_attrib, + 1); + CHECK_VASTATUS(va_status, "vaCreateSurfaces"); + + }else if(img_info.memtype == VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR){ + VASurfaceAttrib surfaceAttrib[3]; + VASurfaceAttribExternalBuffers extBuffer; + uint32_t base_addr_align = 0x1000; + //VAAppSurfaceResInfo surfaceinfo; + //va_status = vaQuerySurfaceAllocation(va_dpy, format, fourCC,width, height, VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR, &surfaceinfo); + uint32_t size = 0; + surfaceAttrib[0].flags = VA_SURFACE_ATTRIB_SETTABLE; + surfaceAttrib[0].type = VASurfaceAttribPixelFormat; + surfaceAttrib[0].value.type = VAGenericValueTypeInteger; + surfaceAttrib[0].value.value.i = img_info.fourcc; + + surfaceAttrib[1].flags = VA_SURFACE_ATTRIB_SETTABLE; + surfaceAttrib[1].type = VASurfaceAttribMemoryType; + surfaceAttrib[1].value.type = VAGenericValueTypeInteger; + surfaceAttrib[1].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR; + + surfaceAttrib[2].flags = VA_SURFACE_ATTRIB_SETTABLE; + surfaceAttrib[2].type = VASurfaceAttribExternalBufferDescriptor; + surfaceAttrib[2].value.type = VAGenericValueTypePointer; + surfaceAttrib[2].value.value.p = (void *)&extBuffer; + memset(&extBuffer, 0, sizeof(extBuffer)); + + uint32_t pitch_align = img_info.align_mode; + switch(img_info.fourcc) + { + case VA_FOURCC_NV12: + extBuffer.pitches[0] = ((img_info.pic_width + pitch_align - 1)/pitch_align) * pitch_align; + size = (extBuffer.pitches[0] *img_info.pic_height)*3/2; + size = (size+base_addr_align-1)/base_addr_align*base_addr_align; + extBuffer.offsets[0] = 0; + extBuffer.offsets[1] = extBuffer.pitches[0] * img_info.pic_height; + extBuffer.pitches[1] = extBuffer.pitches[0]; + extBuffer.num_planes =2; + break; + case VA_FOURCC_YUY2: + extBuffer.pitches[0] = ((img_info.pic_width + pitch_align - 1)/pitch_align) * pitch_align; + size = (extBuffer.pitches[0] *(img_info.pic_height + 2))*2; + size = (size+base_addr_align-1)/base_addr_align*base_addr_align; + extBuffer.offsets[0] = 0; + extBuffer.pitches[0] = extBuffer.pitches[0] * 2; + extBuffer.num_planes = 1; + break; + case VA_FOURCC_YV12: + { + int y_align = 32; + int uv_align = 16; + extBuffer.pitches[0] = ((img_info.pic_width + y_align - 1)/y_align) * y_align; + extBuffer.pitches[1] = ((img_info.pic_width/2 + uv_align - 1)/uv_align) * uv_align; + extBuffer.pitches[2] = extBuffer.pitches[1]; + size = extBuffer.pitches[0] *img_info.pic_height + extBuffer.pitches[1] *img_info.pic_height; + size = (size+base_addr_align-1)/base_addr_align*base_addr_align; + extBuffer.offsets[0] = 0; + extBuffer.offsets[1] = extBuffer.pitches[0] * img_info.pic_height; + extBuffer.offsets[2] = extBuffer.offsets[1] + extBuffer.pitches[1] * img_info.pic_height/2; + extBuffer.num_planes =3; + break; + } + case VA_FOURCC_ARGB: + extBuffer.pitches[0] = ((img_info.pic_width + pitch_align - 1)/pitch_align) * pitch_align; + size = (extBuffer.pitches[0] *(img_info.pic_height + 2))*4; + size = (size+base_addr_align-1)/base_addr_align*base_addr_align; + extBuffer.offsets[0] = 0; + extBuffer.pitches[0] = extBuffer.pitches[0] * 4; + extBuffer.num_planes = 1; + break; + default: + break; + } + img_info.pBuf = malloc(size + base_addr_align); + img_info.pUserBase = (uint8_t*)((((uint64_t)(img_info.pBuf) + base_addr_align-1)/base_addr_align)*base_addr_align); + + extBuffer.pixel_format = img_info.fourcc; + extBuffer.width = img_info.pic_width; + extBuffer.height = img_info.pic_height; + extBuffer.data_size = size; + + extBuffer.num_buffers = 1; + extBuffer.buffers = &(img_info.ptrb); + extBuffer.buffers[0] = (uintptr_t)(img_info.pUserBase); + + extBuffer.flags = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR; + va_status = vaCreateSurfaces(va_dpy, img_info.rtformat, img_info.pic_width,img_info.pic_height,p_surface_id, 1, surfaceAttrib, 3); + CHECK_VASTATUS(va_status, "vaCreateSurfaces"); + } + return va_status; +} + + +/* Load yuv frame to NV12/YUY2/YV12/ARGB surface*/ +static VAStatus +upload_yuv_frame_to_yuv_surface(FILE *fp, + VASurfaceID surface_id) +{ + VAStatus va_status; + VAImage surface_image; + unsigned char *y_src = NULL; + unsigned char *u_src = NULL; + unsigned char *v_src = NULL; + unsigned char *y_dst = NULL; + unsigned char *u_dst = NULL; + unsigned char *v_dst = NULL; + void *surface_p = NULL; + uint32_t frame_size, row; + size_t n_items; + unsigned char * newImageBuffer = NULL; + va_status = vaSyncSurface (va_dpy,surface_id); + CHECK_VASTATUS(va_status, "vaSyncSurface"); + + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + CHECK_VASTATUS(va_status, "vaDeriveImage"); + + va_status = vaMapBuffer(va_dpy, surface_image.buf, &surface_p); + CHECK_VASTATUS(va_status, "vaMapBuffer"); + + if (surface_image.format.fourcc == VA_FOURCC_NV12){ + frame_size = surface_image.width * surface_image.height * 3 / 2; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + + do { + n_items = fread(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + y_src = newImageBuffer; + u_src = newImageBuffer + surface_image.width * surface_image.height; + y_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + u_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[1]); + + /* Y plane, directly copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width); + y_dst += surface_image.pitches[0]; + y_src += surface_image.width; + } + + /* UV plane */ + for (row = 0; row < surface_image.height / 2; row++) { + memcpy(u_dst, u_src, surface_image.width); + u_src += surface_image.width; + u_dst += surface_image.pitches[1]; + } + } else if (surface_image.format.fourcc == VA_FOURCC_YUY2) { + frame_size = surface_image.width * surface_image.height * 2; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + + do { + n_items = fread(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + y_src = newImageBuffer; + y_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + + /* plane 0, directly copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width * 2); + y_src += surface_image.width * 2; + y_dst += surface_image.pitches[0]; + } + }else if (surface_image.format.fourcc == VA_FOURCC_YV12) { + frame_size = surface_image.width * surface_image.height * 3 / 2; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + + do { + n_items = fread(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + y_src = newImageBuffer; + v_src = newImageBuffer + surface_image.width * surface_image.height; + u_src = newImageBuffer + surface_image.width * surface_image.height * 5 / 4; + y_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + v_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[1]); + u_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[2]); + /* Y plane, directly copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width); + y_dst += surface_image.pitches[0]; + y_src += surface_image.width; + } + for (row = 0; row < surface_image.height /2; row ++){ + memcpy(v_dst, v_src, surface_image.width/2); + memcpy(u_dst, u_src, surface_image.width/2); + v_src += surface_image.width/2; + u_src += surface_image.width/2; + v_dst += surface_image.pitches[1]; + u_dst += surface_image.pitches[2]; + } + }else if (surface_image.format.fourcc == VA_FOURCC_ARGB) { + frame_size = surface_image.width * surface_image.height * 4; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + + do { + n_items = fread(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + y_src = newImageBuffer; + y_dst = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + + /* plane 0, directly copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width * 4); + y_src += surface_image.width * 4; + y_dst += surface_image.pitches[0]; + } + } + + if (newImageBuffer){ + free(newImageBuffer); + newImageBuffer = NULL; + } + + va_status = vaUnmapBuffer(va_dpy, surface_image.buf); + CHECK_VASTATUS(va_status, "vaUnmapBuffer"); + va_status = vaDestroyImage(va_dpy, surface_image.image_id); + CHECK_VASTATUS(va_status, "vaDestroyImage"); + return VA_STATUS_SUCCESS; +} + +static VAStatus +store_yuv_surface_to_nv12_file(FILE *fp, + VASurfaceID surface_id) +{ + VAStatus va_status; + VAImage surface_image; + void *surface_p = NULL; + unsigned char *y_src = NULL; + unsigned char *u_src = NULL; + unsigned char *y_dst = NULL; + unsigned char *u_dst = NULL; + uint32_t row; + int32_t n_items; + unsigned char * newImageBuffer = NULL; + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + CHECK_VASTATUS(va_status, "vaDeriveImage"); + + va_status = vaMapBuffer(va_dpy, surface_image.buf, &surface_p); + CHECK_VASTATUS(va_status, "vaMapBuffer"); + + /* store the surface to one nv12 file */ + uint32_t y_size = surface_image.width * surface_image.height; + + newImageBuffer = (unsigned char*)malloc(y_size * 3 / 2); + assert(newImageBuffer); + + /* stored as YV12 format */ + y_dst = newImageBuffer; + u_dst = newImageBuffer + y_size; + + y_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + u_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[1]); + + /* Y plane copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width); + y_src += surface_image.pitches[0]; + y_dst += surface_image.width; + } + + /* UV plane copy */ + for (row = 0; row < surface_image.height / 2; row++) { + memcpy(u_dst, u_src, surface_image.width); + u_dst += surface_image.width; + u_src += surface_image.pitches[1]; + } + /* write frame to file */ + do { + n_items = fwrite(newImageBuffer, y_size * 3 / 2, 1, fp); + } while (n_items != 1); + + if (newImageBuffer){ + free(newImageBuffer); + newImageBuffer = NULL; + } + + va_status = vaUnmapBuffer(va_dpy, surface_image.buf); + CHECK_VASTATUS(va_status, "vaUnmapBuffer"); + va_status = vaDestroyImage(va_dpy, surface_image.image_id); + CHECK_VASTATUS(va_status, "vaDestroyImage"); + + return VA_STATUS_SUCCESS; +} + +static VAStatus +store_packed_yuv_surface_to_yuy2_file(FILE *fp, + VASurfaceID surface_id) +{ + VAStatus va_status; + VAImage surface_image; + void *surface_p = NULL; + unsigned char *y_src = NULL; + unsigned char *y_dst = NULL; + uint32_t row; + int32_t n_items; + unsigned char * newImageBuffer = NULL; + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + CHECK_VASTATUS(va_status, "vaDeriveImage"); + + va_status = vaMapBuffer(va_dpy, surface_image.buf, &surface_p); + CHECK_VASTATUS(va_status, "vaMapBuffer"); + + /* store the surface to one YUY2 file */ + uint32_t frame_size = surface_image.width * surface_image.height * 2; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + memset(newImageBuffer, 0, frame_size); + + /* stored as YUY2 or UYVY format */ + y_dst = newImageBuffer; + y_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + + /* Plane 0 copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width * 2); + y_src += surface_image.pitches[0]; + y_dst += surface_image.width * 2; + } + + /* write frame to file */ + do { + n_items = fwrite(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + if (newImageBuffer){ + free(newImageBuffer); + newImageBuffer = NULL; + } + + va_status = vaUnmapBuffer(va_dpy, surface_image.buf); + CHECK_VASTATUS(va_status, "vaUnmapBuffer"); + va_status = vaDestroyImage(va_dpy, surface_image.image_id); + CHECK_VASTATUS(va_status, "vaDestroyImage"); + + return VA_STATUS_SUCCESS; +} + +/* Store YV12 surface to yv12 file */ +static VAStatus +store_yuv_surface_to_yv12_file(FILE *fp, + VASurfaceID surface_id) +{ + VAStatus va_status; + VAImage surface_image; + void *surface_p = NULL; + unsigned char *y_src, *u_src, *v_src; + unsigned char *y_dst, *u_dst, *v_dst; + uint32_t row; + int32_t n_items; + unsigned char * newImageBuffer = NULL; + + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + CHECK_VASTATUS(va_status, "vaDeriveImage"); + + va_status = vaMapBuffer(va_dpy, surface_image.buf, &surface_p); + CHECK_VASTATUS(va_status, "vaMapBuffer"); + uint32_t y_size = surface_image.width * surface_image.height; + uint32_t u_size = y_size/4; + + newImageBuffer = (unsigned char*)malloc(y_size * 3 / 2); + assert(newImageBuffer); + + /* stored as YV12 format */ + y_dst = newImageBuffer; + v_dst = newImageBuffer + y_size; + u_dst = newImageBuffer + y_size + u_size; + + y_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + v_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[1]); + u_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[2]); + + /* Y plane copy */ + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width); + y_src += surface_image.pitches[0]; + y_dst += surface_image.width; + } + + /* UV plane copy */ + for (row = 0; row < surface_image.height /2; row ++){ + memcpy(v_dst, v_src, surface_image.width/2); + memcpy(u_dst, u_src, surface_image.width/2); + v_dst += surface_image.width/2; + u_dst += surface_image.width/2; + v_src += surface_image.pitches[1]; + u_src += surface_image.pitches[2]; + } + /* write frame to file */ + do { + n_items = fwrite(newImageBuffer, y_size * 3 / 2, 1, fp); + } while (n_items != 1); + if (newImageBuffer){ + free(newImageBuffer); + newImageBuffer = NULL; + } + + vaUnmapBuffer(va_dpy, surface_image.buf); + vaDestroyImage(va_dpy, surface_image.image_id); + + return VA_STATUS_SUCCESS; +} + + +static VAStatus +store_rgb_surface_to_rgb_file(FILE *fp, VASurfaceID surface_id) +{ + VAStatus va_status; + VAImage surface_image; + void *surface_p = NULL; + unsigned char *y_src; + unsigned char *y_dst; + uint32_t frame_size, row; + int32_t n_items; + unsigned char * newImageBuffer = NULL; + + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + CHECK_VASTATUS(va_status, "vaDeriveImage"); + + va_status = vaMapBuffer(va_dpy, surface_image.buf, &surface_p); + CHECK_VASTATUS(va_status, "vaMapBuffer"); + + frame_size = surface_image.width * surface_image.height * 4; + newImageBuffer = (unsigned char*)malloc(frame_size); + assert(newImageBuffer); + y_dst = newImageBuffer; + + y_src = (unsigned char *)((unsigned char*)surface_p + surface_image.offsets[0]); + + for (row = 0; row < surface_image.height; row++) { + memcpy(y_dst, y_src, surface_image.width * 4); + y_src += surface_image.pitches[0]; + y_dst += surface_image.width * 4; + } + + /* write frame to file */ + do { + n_items = fwrite(newImageBuffer, frame_size, 1, fp); + } while (n_items != 1); + + if (newImageBuffer){ + free(newImageBuffer); + newImageBuffer = NULL; + } + + vaUnmapBuffer(va_dpy, surface_image.buf); + vaDestroyImage(va_dpy, surface_image.image_id); + + return VA_STATUS_SUCCESS; +} + +static VAStatus +store_yuv_surface_to_file(FILE *fp, + VASurfaceID surface_id,uint32_t out_fourcc) +{ + if (out_fourcc == VA_FOURCC_NV12) { + return store_yuv_surface_to_nv12_file(fp, surface_id); + } else if (out_fourcc == VA_FOURCC_YUY2) { + return store_packed_yuv_surface_to_yuy2_file(fp, surface_id); + } else if (out_fourcc == VA_FOURCC_YV12) { + return store_yuv_surface_to_yv12_file(fp, surface_id); + } else if (out_fourcc == VA_FOURCC_ARGB) { + return store_rgb_surface_to_rgb_file(fp, surface_id); + } else { + printf("Not supported YUV fourcc for output !!!\n"); + return VA_STATUS_ERROR_INVALID_SURFACE; + } +} + +static VAStatus +video_frame_process( VASurfaceID in_surface_id, + VASurfaceID *out_surface_ids) +{ + VAStatus va_status; + VAProcPipelineParameterBuffer pipeline_param; + VARectangle surface_region, output_region; + VABufferID pipeline_param_buf_id = VA_INVALID_ID; + /* Fill pipeline buffer */ + memset(&pipeline_param, 0, sizeof(pipeline_param)); + pipeline_param.surface = in_surface_id; + if(g_dst_count > 1 && g_scale_again == 0) + { + pipeline_param.additional_outputs = &out_surface_ids[1]; + pipeline_param.num_additional_outputs = g_dst_count -1; + } + uint32_t input_crop = 0; + read_value_uint32(g_config_file_fd, "SRC_SURFACE_CROP", &input_crop); + if(input_crop == 0){ + surface_region.x = 0; + surface_region.y = 0; + surface_region.width = g_src_info.pic_width; + surface_region.height = g_src_info.pic_height; + }else{ + //do the input crop + read_value_int16(g_config_file_fd, "SRC_CROP_LEFT_X", &surface_region.x); + read_value_int16(g_config_file_fd, "SRC_CROP_TOP_Y", &surface_region.y); + read_value_uint16(g_config_file_fd, "SRC_CROP_WIDTH", &surface_region.width); + read_value_uint16(g_config_file_fd, "SRC_CROP_HEIGHT", &surface_region.height); + } + + uint32_t output_crop = 0; + read_value_uint32(g_config_file_fd, "DST_SURFACE_CROP", &output_crop); + if(output_crop == 0){ + output_region.x = 0; + output_region.y = 0; + output_region.width = g_dst_info[0].pic_width; + output_region.height = g_dst_info[0].pic_height; + }else{ + //do the output crop + read_value_int16(g_config_file_fd, "DST_CROP_LEFT_X", &output_region.x); + read_value_int16(g_config_file_fd, "DST_CROP_TOP_Y", &output_region.y); + read_value_uint16(g_config_file_fd, "DST_CROP_WIDTH", &output_region.width); + read_value_uint16(g_config_file_fd, "DST_CROP_HEIGHT", &output_region.height); + } + pipeline_param.surface_region = &surface_region; + pipeline_param.output_region = &output_region; + pipeline_param.output_background_color = 0xff000000; + va_status = vaCreateBuffer(va_dpy, + context_id, + VAProcPipelineParameterBufferType, + sizeof(pipeline_param), + 1, + &pipeline_param, + &pipeline_param_buf_id); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + + va_status = vaBeginPicture(va_dpy, + context_id, + out_surface_ids[0]); + CHECK_VASTATUS(va_status, "vaBeginPicture"); + + va_status = vaRenderPicture(va_dpy, + context_id, + &pipeline_param_buf_id, + 1); + CHECK_VASTATUS(va_status, "vaRenderPicture"); + + va_status = vaEndPicture(va_dpy, context_id); + CHECK_VASTATUS(va_status, "vaEndPicture"); + + if (pipeline_param_buf_id != VA_INVALID_ID){ + vaDestroyBuffer(va_dpy,pipeline_param_buf_id); + CHECK_VASTATUS(va_status, "vaDestroyBuffer"); + } + if(g_scale_again == 1) + { + printf("begin to scale the 16align out as input\n"); + memset(&pipeline_param, 0, sizeof(pipeline_param)); + pipeline_param.surface = out_surface_ids[0]; + + va_status = vaCreateBuffer(va_dpy, + context_id, + VAProcPipelineParameterBufferType, + sizeof(pipeline_param), + 1, + &pipeline_param, + &pipeline_param_buf_id); + CHECK_VASTATUS(va_status, "vaCreateBuffer"); + va_status = vaBeginPicture(va_dpy, context_id, out_surface_ids[1]); + CHECK_VASTATUS(va_status, "vaBeginPicture"); + va_status = vaRenderPicture(va_dpy, context_id,&pipeline_param_buf_id, 1); + CHECK_VASTATUS(va_status, "vaRenderPicture"); + va_status = vaEndPicture(va_dpy, context_id); + CHECK_VASTATUS(va_status, "vaEndPicture"); + if (pipeline_param_buf_id != VA_INVALID_ID){ + vaDestroyBuffer(va_dpy,pipeline_param_buf_id); + CHECK_VASTATUS(va_status, "vaDestroyBuffer"); + } + } + + return va_status; +} + +static VAStatus +vpp_context_create() +{ + VAStatus va_status = VA_STATUS_SUCCESS; + int32_t j; + + /* VA driver initialization */ + #if 0 + x11_display = XOpenDisplay("intel-CoffeeLake-Client-Platform:2"); + if (NULL == x11_display) { + printf("Error: Can't connect X server! %s %s(line %d)\n", __FILE__, __func__, __LINE__); + return false; + } + va_dpy = vaGetDisplay(x11_display); + #endif + va_dpy = va_open_display(); + int32_t major_ver, minor_ver; + va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); + assert(va_status == VA_STATUS_SUCCESS); + + /* Check whether VPP is supported by driver */ + VAEntrypoint entrypoints[5]; + int32_t num_entrypoints; + num_entrypoints = vaMaxNumEntrypoints(va_dpy); + va_status = vaQueryConfigEntrypoints(va_dpy, + VAProfileNone, + entrypoints, + &num_entrypoints); + CHECK_VASTATUS(va_status, "vaQueryConfigEntrypoints"); + + for (j = 0; j < num_entrypoints; j++) { + if (entrypoints[j] == VAEntrypointVideoProc) + break; + } + + if (j == num_entrypoints) { + printf("VPP is not supported by driver\n"); + assert(0); + } + + /* Create surface/config/context for VPP pipeline */ + va_status = create_surface(g_src_info,&g_in_surface_id); + CHECK_VASTATUS(va_status, "vaCreateSurfaces for input"); + + for(uint32_t i = 0; i< g_dst_count; i++) + { + va_status = create_surface(g_dst_info[i],&g_out_surface_ids[i]); + } + CHECK_VASTATUS(va_status, "vaCreateSurfaces for output"); + VAConfigAttrib attrib; + attrib.type = VAConfigAttribRTFormat; + attrib.value = VA_RT_FORMAT_YUV420; + + va_status = vaCreateConfig(va_dpy, + VAProfileNone, + VAEntrypointVideoProc, + &attrib, + 1, + &config_id); + CHECK_VASTATUS(va_status, "vaCreateConfig"); + + va_status = vaCreateContext(va_dpy, + config_id, + g_dst_info[0].pic_width, + g_dst_info[0].pic_height, + VA_PROGRESSIVE, + g_out_surface_ids, + g_dst_count, + &context_id); + CHECK_VASTATUS(va_status, "vaCreateContext"); + return va_status; +} + +static void +vpp_context_destroy() +{ + VAStatus va_status; + + /* Release resource */ + va_status = vaDestroySurfaces(va_dpy, &g_in_surface_id, 1); + CHECK_VASTATUS(va_status, "vaDestroySurfaces"); + VPP_FREE(g_src_info.pBuf); + for(uint32_t index = 0; index < g_dst_count; index++){ + vaDestroySurfaces(va_dpy, &g_out_surface_ids[index], 1); + CHECK_VASTATUS(va_status, "vaDestroySurfaces"); + VPP_FREE(g_dst_info[index].pBuf); + } + VPP_FREE(g_dst_info); + VPP_FREE(g_out_surface_ids); + vaDestroyContext(va_dpy, context_id); + CHECK_VASTATUS(va_status, "vaDestroyContext"); + vaDestroyConfig(va_dpy, config_id); + CHECK_VASTATUS(va_status, "vaDestroyConfig"); + vaTerminate(va_dpy); + CHECK_VASTATUS(va_status, "vaTerminate"); +#if 0 + XCloseDisplay(x11_display); + x11_display = NULL; +#endif + va_close_display(va_dpy); +} + +static int8_t +parse_fourcc_and_format(char *str, uint32_t *fourcc, uint32_t *format) +{ + uint32_t tfourcc = VA_FOURCC('N', 'V', '1', '2'); + uint32_t tformat = VA_RT_FORMAT_YUV420; + + if (!strcmp(str, "NV12")){ + tfourcc = VA_FOURCC('N', 'V', '1', '2'); + } else if(!strcmp(str, "YUY2")) { + tfourcc = VA_FOURCC('Y', 'U', 'Y', '2'); + } else if(!strcmp(str, "YV12")) { + tfourcc = VA_FOURCC('Y', 'V', '1', '2'); + }else if(!strcmp(str, "ARGB")) { + tfourcc = VA_FOURCC('A', 'R', 'G', 'B'); + }else{ + printf("Not supported format: %s! Currently only support following format: %s for this sample\n", + str, "NV12, YUY2,YV12,ARGB"); + assert(0); + } + + if (fourcc) + *fourcc = tfourcc; + + if (format) + *format = tformat; + + return 0; +} + +static int8_t +parse_memtype_format(char *str,uint32_t *dst_memtype) +{ + uint32_t tmemtype = VA_SURFACE_ATTRIB_MEM_TYPE_VA; + + if (!strcmp(str, "VA")){ + tmemtype = VA_SURFACE_ATTRIB_MEM_TYPE_VA; + } else if(!strcmp(str, "CPU")){ + tmemtype = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR; + }else{ + printf("Not supported format: %s! Currently only support following format: %s\n", + str, "VA,CPU"); + assert(0); + } + if(dst_memtype) + *dst_memtype = tmemtype; + return 0; +} + + +static int8_t +parse_basic_parameters() +{ + char str[MAX_LEN]; + + /* Read src frame file information */ + read_value_string(g_config_file_fd, "SRC_FILE_NAME", g_src_info.file_name); + read_value_uint32(g_config_file_fd, "SRC_FRAME_WIDTH", &g_src_info.pic_width); + read_value_uint32(g_config_file_fd, "SRC_FRAME_HEIGHT", &g_src_info.pic_height); + read_value_string(g_config_file_fd, "SRC_FRAME_FORMAT", str); + parse_fourcc_and_format(str, &g_src_info.fourcc, &g_src_info.rtformat); + read_value_string(g_config_file_fd, "SRC_SURFACE_MEMORY_TYPE", str); + parse_memtype_format(str,&g_src_info.memtype); + read_value_uint32(g_config_file_fd, "SRC_SURFACE_CPU_ALIGN_MODE", &g_src_info.align_mode); + + read_value_uint32(g_config_file_fd, "2ND_SCALE", &g_scale_again); + + /* Read dst frame file information */ + read_value_uint32(g_config_file_fd, "DST_NUMBER", &g_dst_count); + g_out_surface_ids = (VASurfaceID*)malloc(g_dst_count * sizeof(VASurfaceID)); + g_dst_info = (VPP_ImageInfo *)malloc(g_dst_count * sizeof(VPP_ImageInfo)); + for(uint32_t i = 0; i< g_dst_count; i++) + { + char dst_file_name[MAX_LEN]; + char dst_frame_width[MAX_LEN]; + char dst_frame_height[MAX_LEN]; + char dst_frame_format[MAX_LEN]; + char dst_memtype[MAX_LEN]; + char dst_align_mode[MAX_LEN]; + sprintf(dst_file_name,"DST_FILE_NAME_%d",i+1); + sprintf(dst_frame_width,"DST_FRAME_WIDTH_%d",i+1); + sprintf(dst_frame_height,"DST_FRAME_HEIGHT_%d",i+1); + sprintf(dst_frame_format,"DST_FRAME_FORMAT_%d",i+1); + sprintf(dst_memtype,"DST_SURFACE_MEMORY_TYPE_%d",i+1); + sprintf(dst_align_mode,"DST_SURFACE_CPU_ALIGN_MODE_%d",i+1); + read_value_string(g_config_file_fd, dst_file_name, g_dst_info[i].file_name); + read_value_uint32(g_config_file_fd, dst_frame_width, &g_dst_info[i].pic_width); + read_value_uint32(g_config_file_fd, dst_frame_height,&g_dst_info[i].pic_height); + read_value_string(g_config_file_fd, dst_frame_format, str); + parse_fourcc_and_format(str, &g_dst_info[i].fourcc, &g_dst_info[i].rtformat); + read_value_string(g_config_file_fd, dst_memtype, str); + parse_memtype_format(str,&g_dst_info[i].memtype); + read_value_uint32(g_config_file_fd, dst_align_mode, &g_dst_info[i].align_mode); + } + read_value_uint32(g_config_file_fd, "FRAME_SUM", &g_frame_count); + return 0; +} + +static void +print_help() +{ + printf("The app is used to test the usrptr and 1:N output and crop feature.\n"); + printf("Cmd Usage: ./vppscaling_n_out_usrptr process_scaling_n_out_usrptr.cfg\n"); + printf("The configure file process_scaling_n_out_usrptr.cfg is used to configure the para.\n"); + printf("You can refer process_scaling_n_out_usrptr.cfg.template for each para meaning and create the configure file.\n"); +} + +int32_t main(int32_t argc, char *argv[]) +{ + VAStatus va_status; + uint32_t i; + + if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + print_help(); + return -1; + } + + + /* Parse the configure file for video process*/ + strncpy(g_config_file_name, argv[1], MAX_LEN); + g_config_file_name[MAX_LEN - 1] = '\0'; + + if (NULL == (g_config_file_fd = fopen(g_config_file_name, "r"))){ + printf("Open configure file %s failed!\n",g_config_file_name); + assert(0); + } + + /* Parse basic parameters */ + if (parse_basic_parameters()){ + printf("Parse parameters in configure file error\n"); + assert(0); + } + + va_status = vpp_context_create(); + if (va_status != VA_STATUS_SUCCESS) { + printf("vpp context create failed \n"); + assert(0); + } + + /* Video frame fetch, process and store */ + if (NULL == (g_src_info.file_fd = fopen(g_src_info.file_name, "r"))){ + printf("Open SRC_FILE_NAME: %s failed, please specify it in config file: %s !\n", + g_src_info.file_name, g_config_file_name); + assert(0); + } + for(uint32_t index = 0; index < g_dst_count; index++){ + if (NULL == (g_dst_info[index].file_fd = fopen(g_dst_info[index].file_name, "w"))){ + printf("Open DST_FILE_NAME: %s failed, please specify it in config file: %s !\n", + g_dst_info[index].file_name, g_config_file_name); + assert(0); + } + } + + printf("\nStart to process, ...\n"); + struct timespec Pre_time; + struct timespec Cur_time; + unsigned int duration = 0; + clock_gettime(CLOCK_MONOTONIC, &Pre_time); + + for (i = 0; i < g_frame_count; i ++){ + upload_yuv_frame_to_yuv_surface(g_src_info.file_fd,g_in_surface_id); + video_frame_process(g_in_surface_id, g_out_surface_ids); + //first sync surface to check the process ready + va_status = vaSyncSurface (va_dpy,g_out_surface_ids[g_dst_count - 1]); + CHECK_VASTATUS(va_status, "vaSyncSurface"); + for(uint32_t index = 0; index < g_dst_count; index++){ + store_yuv_surface_to_file(g_dst_info[index].file_fd, g_out_surface_ids[index],g_dst_info[index].fourcc); + } + } + + clock_gettime(CLOCK_MONOTONIC, &Cur_time); + duration = (Cur_time.tv_sec - Pre_time.tv_sec) * 1000; + if (Cur_time.tv_nsec > Pre_time.tv_nsec) { + duration += (Cur_time.tv_nsec - Pre_time.tv_nsec) / 1000000; + } else { + duration += (Cur_time.tv_nsec + 1000000000 - Pre_time.tv_nsec) / 1000000 - 1000; + } + + printf("Finish processing, performance: \n" ); + printf("%d frames processed in: %d ms, ave time = %d ms\n",g_frame_count, duration, duration/g_frame_count); + + if (g_src_info.file_fd) + fclose(g_src_info.file_fd); + for(uint32_t index = 0; index < g_dst_count; index++){ + if (g_dst_info[index].file_fd) + fclose(g_dst_info[index].file_fd); + } + if (g_config_file_fd) + fclose(g_config_file_fd); + + vpp_context_destroy(); + + return 0; +}