| /* |
| * Copyright (c) 2009 Intel Corporation. All Rights Reserved. |
| * |
| * 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, sub license, 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 (including the |
| * next paragraph) 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 NON-INFRINGEMENT. |
| * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. |
| */ |
| |
| #define _GNU_SOURCE 1 |
| #include "va.h" |
| #include "va_backend.h" |
| #include "va_trace.h" |
| #include "va_fool.h" |
| |
| #include <assert.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <dlfcn.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <time.h> |
| |
| /* |
| * Do dummy decode/encode, ignore the input data |
| * In order to debug memory leak or low performance issues, we need to isolate driver problems |
| * We export env "VA_FOOL", with which, we can do fake decode/encode: |
| * |
| * LIBVA_FOOL_DECODE: |
| * . if set, decode does nothing, but fill in some YUV data |
| * LIBVA_FOOL_ENCODE=<clip name>: |
| * . if set, encode does nothing, but fill in the coded buffer from a H264 clip. |
| * . VA CONTEXT/CONFIG/SURFACE will call into drivers, but VA Buffer creation is done in here |
| * . Bypass all "vaBeginPic/vaRenderPic/vaEndPic" |
| * LIBVA_FOOL_POSTP: |
| * . if set, do nothing for vaPutSurface |
| */ |
| |
| |
| /* global settings */ |
| |
| /* LIBVA_FOOL_DECODE/LIBVA_FOOL_ENCODE/LIBVA_FOOL_POSTP */ |
| int fool_decode = 0; |
| int fool_encode = 0; |
| int fool_postp = 0; |
| |
| |
| |
| #define NAL_BUF_SIZE 65536 // maximum NAL unit size |
| #define RING_BUF_SIZE 8192 // input ring buffer size, MUST be a power of two! |
| #define MAX_FRAME 16 |
| #define SLICE_NUM 4 |
| |
| #define FOOL_CONTEXT_MAX 4 |
| /* per context settings */ |
| static struct _fool_context { |
| VADisplay dpy; /* should use context as the key */ |
| |
| VAProfile fool_profile; /* current profile for buffers */ |
| VAEntrypoint fool_entrypoint; /* current entrypoint */ |
| |
| FILE *fool_fp_codedclip; /* load a clip from disk for fooling encode*/ |
| char *frame_buf; |
| |
| /* all buffers with same type share one malloc-ed memory |
| * bufferID = (buffer numbers with the same type << 8) || type |
| * the malloc-ed memory can be find by fool_buf[bufferID & 0xff] |
| * the size is ignored here |
| */ |
| char *fool_buf[VABufferTypeMax]; /* memory of fool buffers */ |
| unsigned int fool_buf_size[VABufferTypeMax]; /* size of memory of fool buffers */ |
| unsigned int fool_buf_count[VABufferTypeMax]; /* count of created buffers */ |
| VAContextID context; |
| } fool_context[FOOL_CONTEXT_MAX] = { {0} }; /* trace five context at the same time */ |
| |
| #define FOOL_DECODE(idx) (fool_decode && (fool_context[idx].fool_entrypoint == VAEntrypointVLD)) |
| #define FOOL_ENCODE(idx) \ |
| (fool_encode \ |
| && (fool_context[idx].fool_entrypoint == VAEntrypointEncSlice) \ |
| && (fool_context[idx].fool_profile >= VAProfileH264Baseline) \ |
| && (fool_context[idx].fool_profile <= VAProfileH264High)) |
| |
| |
| |
| #define DPY2INDEX(dpy) \ |
| int idx; \ |
| \ |
| for (idx = 0; idx < FOOL_CONTEXT_MAX; idx++) \ |
| if (fool_context[idx].dpy == dpy) \ |
| break; \ |
| \ |
| if (idx == FOOL_CONTEXT_MAX) \ |
| return 0; /* let driver go */ |
| |
| |
| /* Prototype declarations (functions defined in va.c) */ |
| |
| void va_errorMessage(const char *msg, ...); |
| void va_infoMessage(const char *msg, ...); |
| |
| int va_parseConfig(char *env, char *env_value); |
| |
| VAStatus vaBufferInfo( |
| VADisplay dpy, |
| VAContextID context, /* in */ |
| VABufferID buf_id, /* in */ |
| VABufferType *type, /* out */ |
| unsigned int *size, /* out */ |
| unsigned int *num_elements /* out */ |
| ); |
| |
| VAStatus vaLockSurface(VADisplay dpy, |
| VASurfaceID surface, |
| unsigned int *fourcc, /* following are output argument */ |
| unsigned int *luma_stride, |
| unsigned int *chroma_u_stride, |
| unsigned int *chroma_v_stride, |
| unsigned int *luma_offset, |
| unsigned int *chroma_u_offset, |
| unsigned int *chroma_v_offset, |
| unsigned int *buffer_name, |
| void **buffer |
| ); |
| |
| VAStatus vaUnlockSurface(VADisplay dpy, |
| VASurfaceID surface |
| ); |
| |
| |
| void va_FoolInit(VADisplay dpy) |
| { |
| char env_value[1024]; |
| int fool_index = 0; |
| |
| for (fool_index = 0; fool_index < FOOL_CONTEXT_MAX; fool_index++) |
| if (fool_context[fool_index].dpy == 0) |
| break; |
| |
| if (fool_index == FOOL_CONTEXT_MAX) |
| return; |
| |
| if (va_parseConfig("LIBVA_FOOL_POSTP", NULL) == 0) { |
| fool_postp = 1; |
| va_infoMessage("LIBVA_FOOL_POSTP is on, dummy vaPutSurface\n"); |
| } |
| |
| |
| if (va_parseConfig("LIBVA_FOOL_DECODE", NULL) == 0) { |
| fool_decode = 1; |
| va_infoMessage("LIBVA_FOOL_DECODE is on, dummy decode\n"); |
| } |
| |
| |
| if (va_parseConfig("LIBVA_FOOL_ENCODE", &env_value[0]) == 0) { |
| fool_context[fool_index].fool_fp_codedclip = fopen(env_value, "r"); |
| |
| if (fool_context[fool_index].fool_fp_codedclip) { |
| fool_encode = 1; |
| } else |
| fool_encode = 0; |
| |
| if (fool_encode) /* malloc the buffer for fake clip */ |
| fool_context[fool_index].frame_buf = malloc(MAX_FRAME*SLICE_NUM*NAL_BUF_SIZE*sizeof(char)); |
| |
| if (fool_context[fool_index].frame_buf == NULL) |
| fool_encode = 0; |
| |
| if (fool_encode) |
| va_infoMessage("LIBVA_FOOL_ENCODE is on, dummy encode\n"); |
| } |
| |
| if (fool_encode || fool_decode) |
| fool_context[fool_index].dpy = dpy; |
| } |
| |
| |
| int va_FoolEnd(VADisplay dpy) |
| { |
| int i; |
| |
| DPY2INDEX(dpy); |
| |
| for (i = 0; i < VABufferTypeMax; i++) {/* free memory */ |
| if (fool_context[idx].fool_buf[i]) |
| free(fool_context[idx].fool_buf[i]); |
| } |
| if (fool_context[idx].fool_fp_codedclip) |
| fclose(fool_context[idx].fool_fp_codedclip); |
| |
| if (fool_context[idx].frame_buf) |
| free(fool_context[idx].frame_buf); |
| |
| memset(&fool_context[idx], sizeof(struct _fool_context), 0); |
| return 0; |
| } |
| |
| int va_FoolCodedBuf(VADisplay dpy) |
| { |
| /* do nothing */ |
| return 0; |
| } |
| |
| |
| int va_FoolCreateConfig( |
| VADisplay dpy, |
| VAProfile profile, |
| VAEntrypoint entrypoint, |
| VAConfigAttrib *attrib_list, |
| int num_attribs, |
| VAConfigID *config_id /* out */ |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| /* call into driver level to allocate real context/surface/buffers, etc */ |
| fool_context[idx].fool_profile = profile; |
| fool_context[idx].fool_entrypoint = entrypoint; |
| return 0; |
| } |
| |
| static int yuvgen_planar( |
| int width, int height, |
| unsigned char *Y_start, int Y_pitch, |
| unsigned char *U_start, int U_pitch, |
| unsigned char *V_start, int V_pitch, |
| int UV_interleave, int box_width, int row_shift, |
| int field |
| ) |
| { |
| int row; |
| |
| /* copy Y plane */ |
| for (row=0;row<height;row++) { |
| unsigned char *Y_row = Y_start + row * Y_pitch; |
| int jj, xpos, ypos; |
| |
| ypos = (row / box_width) & 0x1; |
| |
| /* fill garbage data into the other field */ |
| if (((field == VA_TOP_FIELD) && (row &1)) |
| || ((field == VA_BOTTOM_FIELD) && ((row &1)==0))) { |
| memset(Y_row, 0xff, width); |
| continue; |
| } |
| |
| for (jj=0; jj<width; jj++) { |
| xpos = ((row_shift + jj) / box_width) & 0x1; |
| |
| if ((xpos == 0) && (ypos == 0)) |
| Y_row[jj] = 0xeb; |
| if ((xpos == 1) && (ypos == 1)) |
| Y_row[jj] = 0xeb; |
| |
| if ((xpos == 1) && (ypos == 0)) |
| Y_row[jj] = 0x10; |
| if ((xpos == 0) && (ypos == 1)) |
| Y_row[jj] = 0x10; |
| } |
| } |
| |
| /* copy UV data */ |
| for( row =0; row < height/2; row++) { |
| unsigned short value = 0x80; |
| |
| /* fill garbage data into the other field */ |
| if (((field == VA_TOP_FIELD) && (row &1)) |
| || ((field == VA_BOTTOM_FIELD) && ((row &1)==0))) { |
| value = 0xff; |
| } |
| |
| if (UV_interleave) { |
| unsigned short *UV_row = (unsigned short *)(U_start + row * U_pitch); |
| |
| memset(UV_row, value, width); |
| } else { |
| unsigned char *U_row = U_start + row * U_pitch; |
| unsigned char *V_row = V_start + row * V_pitch; |
| |
| memset (U_row,value,width/2); |
| memset (V_row,value,width/2); |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| int va_FoolCreateSurfaces( |
| VADisplay dpy, |
| int width, |
| int height, |
| int format, |
| int num_surfaces, |
| VASurfaceID *surfaces /* out */ |
| ) |
| { |
| int i; |
| unsigned int fourcc; /* following are output argument */ |
| unsigned int luma_stride; |
| unsigned int chroma_u_stride; |
| unsigned int chroma_v_stride; |
| unsigned int luma_offset; |
| unsigned int chroma_u_offset; |
| unsigned int chroma_v_offset; |
| unsigned int buffer_name; |
| void *buffer = NULL; |
| unsigned char *Y_data, *U_data, *V_data; |
| |
| int box_width = num_surfaces/2; |
| int row_shift = 0; |
| VAStatus va_status; |
| |
| DPY2INDEX(dpy); |
| |
| if (FOOL_DECODE(idx)) { |
| /* call into driver level to allocate real context/surface/buffers, etc |
| * fill in the YUV data, will be overwrite if it is encode context |
| */ |
| for (i = 0; i < num_surfaces; i++) { |
| /* fool decoder: fill with auto-generated YUV data */ |
| va_status = vaLockSurface(dpy, surfaces[i], &fourcc, |
| &luma_stride, &chroma_u_stride, &chroma_v_stride, |
| &luma_offset, &chroma_u_offset, &chroma_v_offset, |
| &buffer_name, &buffer); |
| |
| if (va_status != VA_STATUS_SUCCESS) |
| return 0; |
| |
| if (!buffer) { |
| vaUnlockSurface(dpy, surfaces[i]); |
| return 0; |
| } |
| |
| Y_data = buffer; |
| |
| /* UV should be same for NV12 */ |
| U_data = buffer + chroma_u_offset; |
| V_data = buffer + chroma_v_offset; |
| |
| yuvgen_planar(width, height, |
| Y_data, luma_stride, |
| U_data, chroma_v_stride, |
| V_data, chroma_v_stride, |
| (fourcc==VA_FOURCC_NV12), |
| box_width, row_shift, 0); |
| |
| vaUnlockSurface(dpy, surfaces[i]); |
| |
| row_shift++; |
| if (row_shift==(2*box_width)) |
| row_shift= 0; |
| } |
| return 0; /* the return value is ignored */ |
| } |
| return 0; /* the return value is ignored */ |
| } |
| |
| VAStatus va_FoolCreateBuffer ( |
| VADisplay dpy, |
| VAContextID context, /* in */ |
| VABufferType type, /* in */ |
| unsigned int size, /* in */ |
| unsigned int num_elements, /* in */ |
| void *data, /* in */ |
| VABufferID *buf_id /* out */ |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) { /* fool buffer creation */ |
| int new_size = size * num_elements; |
| |
| if (type == VAEncCodedBufferType) /* only a VACodedBufferSegment */ |
| new_size = sizeof(VACodedBufferSegment); |
| |
| if (fool_context[idx].fool_buf_size[type] == 0) |
| fool_context[idx].fool_buf[type] = calloc(1, new_size); |
| else if (fool_context[idx].fool_buf_size[type] <= new_size) |
| fool_context[idx].fool_buf[type] = realloc(fool_context[idx].fool_buf, new_size); |
| |
| if (fool_context[idx].fool_buf[type] == NULL) { |
| va_FoolEnd(dpy); |
| return 0; /* let driver go */ |
| } |
| |
| /* because we ignore the vaRenderPicture, |
| * all buffers with same type share same real memory |
| * bufferID = (buffer count << 8) | type |
| */ |
| fool_context[idx].fool_buf_count[type]++; |
| *buf_id = (fool_context[idx].fool_buf_count[type] << 8) | type; |
| |
| return 1; /* don't call into driver */ |
| } |
| |
| return 0; /* let driver go ... */ |
| } |
| |
| VAStatus va_FoolMapBuffer ( |
| VADisplay dpy, |
| VABufferID buf_id, /* in */ |
| void **pbuf /* out */ |
| ) |
| { |
| VABufferType type; |
| unsigned int size; |
| unsigned int num_elements; |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) { /* fool buffer creation */ |
| unsigned int buf_idx = buf_id & 0xff; |
| |
| /* Image buffer? */ |
| vaBufferInfo(dpy, fool_context[idx].context, buf_id, &type, &size, &num_elements); |
| if (type == VAImageBufferType && FOOL_ENCODE(idx)) |
| return 0; |
| |
| /* buf_id is the buffer type */ |
| if (fool_context[idx].fool_buf[buf_idx] != NULL) |
| *pbuf = fool_context[idx].fool_buf[buf_idx]; |
| else |
| *pbuf = NULL; |
| |
| /* expect APP to MapBuffer when get the the coded data */ |
| if (*pbuf && (buf_idx == VAEncCodedBufferType)) { /* it is coded buffer */ |
| /* read from a clip */ |
| va_FoolGetFrame(fool_context[idx].fool_fp_codedclip, |
| fool_context[idx].frame_buf); |
| *pbuf = fool_context[idx].frame_buf; |
| } |
| return 1; /* don't call into driver */ |
| } |
| |
| return 0; /* let driver go ... */ |
| } |
| |
| |
| int va_FoolBeginPicture( |
| VADisplay dpy, |
| VAContextID context, |
| VASurfaceID render_target |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) { |
| if (fool_context[idx].context == 0) |
| fool_context[idx].context = context; |
| return 1; /* don't call into driver level */ |
| } |
| |
| return 0; /* let driver go ... */ |
| } |
| |
| int va_FoolRenderPicture( |
| VADisplay dpy, |
| VAContextID context, |
| VABufferID *buffers, |
| int num_buffers |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) |
| return 1; /* don't call into driver level */ |
| |
| return 0; /* let driver go ... */ |
| } |
| |
| |
| int va_FoolEndPicture( |
| VADisplay dpy, |
| VAContextID context |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| /* don't call into driver level */ |
| |
| /* do real fooling operation here */ |
| |
| /* only support H264 encoding currently */ |
| if (FOOL_ENCODE(idx)) { |
| /* expect vaMapBuffer will handle it |
| * or else, need to save the codedbuf ID, |
| * and fool encode it here |
| */ |
| /* va_FoolCodedBuf(dpy); */ |
| return 1; /* don't call into driver level */ |
| } |
| |
| if (FOOL_DECODE(idx)) |
| return 1; /* don't call into driver level */ |
| |
| return 0; /* let driver go ... */ |
| } |
| |
| int va_FoolSyncSurface( |
| VADisplay dpy, |
| VASurfaceID render_target |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| /*Fill in black and white squares. */ |
| if (FOOL_DECODE(idx) || FOOL_DECODE(idx)) |
| return 1; |
| |
| return 0; |
| |
| } |
| |
| VAStatus va_FoolUnmapBuffer( |
| VADisplay dpy, |
| VABufferID buf_id /* in */ |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) |
| return 1; /* fool buffer creation */ |
| |
| return 0; |
| } |
| |
| VAStatus va_FoolQuerySubpictureFormats( |
| VADisplay dpy, |
| VAImageFormat *format_list, |
| unsigned int *flags, |
| unsigned int *num_formats |
| ) |
| { |
| DPY2INDEX(dpy); |
| |
| if (FOOL_ENCODE(idx) || FOOL_DECODE(idx)) { |
| if (num_formats) |
| *num_formats = 0; |
| return 1; |
| } |
| return 0; |
| } |
| |