| /* |
| * Copyright (c) 2007-2008 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. |
| */ |
| |
| /* |
| * it is a real program to show how VAAPI encoding work, |
| * It does H264 element stream level encoding on auto-generated YUV data |
| * |
| * gcc -o h264encode h264encode -lva -lva-x11 |
| * ./h264encode -w <width> -h <height> -n <frame_num> |
| * |
| */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <getopt.h> |
| #include <X11/Xlib.h> |
| |
| #include <unistd.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include <assert.h> |
| |
| #include <va/va.h> |
| #include <va/va_x11.h> |
| |
| #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); \ |
| } |
| |
| #include "loadsurface.h" |
| |
| #define SURFACE_NUM 18 /* 16 surfaces for src, 2 surface for reconstructed/reference */ |
| |
| static Display *x11_display; |
| static VADisplay va_dpy; |
| static VAContextID context_id; |
| static VASurfaceID surface_id[SURFACE_NUM]; |
| static Window display_win = 0; |
| static int win_width; |
| static int win_height; |
| |
| static int coded_fd; |
| static char coded_file[256]; |
| |
| #define CODEDBUF_NUM 5 |
| static VABufferID coded_buf[CODEDBUF_NUM]; |
| |
| static int frame_display = 0; /* display the frame during encoding */ |
| static int frame_width=352, frame_height=288; |
| static int frame_rate = 30; |
| static int frame_count = 400; |
| static int intra_count = 30; |
| static int frame_bitrate = 8000000; /* 8M */ |
| static int initial_qp = 15; |
| static int minimal_qp = 0; |
| |
| static int upload_source_YUV_once_for_all() |
| { |
| VAImage surface_image; |
| void *surface_p=NULL, *U_start,*V_start; |
| VAStatus va_status; |
| int box_width=8; |
| int row_shift=0; |
| int i; |
| |
| for (i=0; i<SURFACE_NUM-2; i++) { |
| printf("\rLoading data into surface %d.....", i); |
| upload_surface(va_dpy, surface_id[i], box_width, row_shift, 0); |
| |
| row_shift++; |
| if (row_shift==(2*box_width)) row_shift= 0; |
| } |
| printf("\n", i); |
| |
| return 0; |
| } |
| |
| |
| static int save_coded_buf(VABufferID coded_buf, int current_frame, int frame_skipped) |
| { |
| void *coded_p=NULL; |
| int coded_size,coded_offset,wrt_size; |
| VAStatus va_status; |
| |
| va_status = vaMapBuffer(va_dpy,coded_buf,&coded_p); |
| CHECK_VASTATUS(va_status,"vaMapBuffer"); |
| |
| coded_size = *((unsigned long *) coded_p); /* first DWord is the coded video size */ |
| coded_offset = *((unsigned long *) (coded_p + 4)); /* second DWord is byte offset */ |
| |
| wrt_size = write(coded_fd,coded_p+coded_offset,coded_size); |
| if (wrt_size != coded_size) { |
| fprintf(stderr, "Trying to write %d bytes, but actual %d bytes\n", |
| coded_size, wrt_size); |
| exit(1); |
| } |
| vaUnmapBuffer(va_dpy,coded_buf); |
| |
| printf("\r "); /* return back to startpoint */ |
| switch (current_frame % 4) { |
| case 0: |
| printf("|"); |
| break; |
| case 1: |
| printf("/"); |
| break; |
| case 2: |
| printf("-"); |
| break; |
| case 3: |
| printf("\\"); |
| break; |
| } |
| printf("%08d", current_frame); |
| if (current_frame % intra_count == 0) |
| printf("(I)"); |
| else |
| printf("(P)"); |
| |
| printf("(%06d bytes coded)",coded_size); |
| if (frame_skipped) |
| printf("(SKipped)"); |
| printf(" "); |
| |
| return; |
| } |
| |
| |
| static int display_surface(int frame_id, int *exit_encode) |
| { |
| Window win = display_win; |
| XEvent event; |
| VAStatus va_status; |
| |
| if (win == 0) { /* display reconstructed surface */ |
| win_width = frame_width; |
| win_height = frame_height; |
| |
| win = XCreateSimpleWindow(x11_display, RootWindow(x11_display, 0), 0, 0, |
| frame_width, frame_height, 0, 0, WhitePixel(x11_display, 0)); |
| XMapWindow(x11_display, win); |
| XSync(x11_display, False); |
| |
| display_win = win; |
| } |
| |
| va_status = vaPutSurface(va_dpy, surface_id[frame_id], win, |
| 0,0, frame_width, frame_height, |
| 0,0, win_width, win_height, |
| NULL,0,0); |
| |
| *exit_encode = 0; |
| while(XPending(x11_display)) { |
| XNextEvent(x11_display, &event); |
| |
| /* bail on any focused key press */ |
| if(event.type == KeyPress) { |
| *exit_encode = 1; |
| break; |
| } |
| |
| /* rescale the video to fit the window */ |
| if(event.type == ConfigureNotify) { |
| win_width = event.xconfigure.width; |
| win_height = event.xconfigure.height; |
| } |
| } |
| |
| return; |
| } |
| |
| enum { |
| SH_LEVEL_1=10, |
| SH_LEVEL_1B=11, |
| SH_LEVEL_2=20, |
| SH_LEVEL_3=30, |
| SH_LEVEL_31=31, |
| SH_LEVEL_32=32, |
| SH_LEVEL_4=40, |
| SH_LEVEL_5=50 |
| }; |
| |
| static int do_h264_encoding(void) |
| { |
| VAEncPictureParameterBufferH264 pic_h264; |
| VAEncSliceParameterBuffer slice_h264; |
| VAStatus va_status; |
| VABufferID seq_param_buf, pic_param_buf, slice_param_buf; |
| int codedbuf_size; |
| VASurfaceStatus surface_status; |
| int src_surface, dst_surface, ref_surface; |
| int codedbuf_idx = 0; |
| int frame_skipped = 0; |
| int i; |
| |
| |
| va_status = vaCreateSurfaces(va_dpy,frame_width, frame_height, |
| VA_RT_FORMAT_YUV420, SURFACE_NUM, &surface_id[0]); |
| CHECK_VASTATUS(va_status, "vaCreateSurfaces"); |
| |
| /* upload RAW YUV data into all surfaces */ |
| upload_source_YUV_once_for_all(); |
| |
| codedbuf_size = (frame_width * frame_height * 400) / (16*16); |
| |
| for (i = 0; i < CODEDBUF_NUM; i++) { |
| /* create coded buffer once for all |
| * other VA buffers which won't be used again after vaRenderPicture. |
| * so APP can always vaCreateBuffer for every frame |
| * but coded buffer need to be mapped and accessed after vaRenderPicture/vaEndPicture |
| * so VA won't maintain the coded buffer |
| */ |
| va_status = vaCreateBuffer(va_dpy,context_id,VAEncCodedBufferType, |
| codedbuf_size, 1, NULL, &coded_buf[i]); |
| CHECK_VASTATUS(va_status,"vaBeginPicture"); |
| } |
| |
| src_surface = 0; |
| /* the last two frames are reference/reconstructed frame */ |
| dst_surface = SURFACE_NUM - 1; |
| ref_surface = SURFACE_NUM - 2; |
| |
| for (i = 0; i < frame_count; i++) { |
| va_status = vaBeginPicture(va_dpy, context_id, surface_id[src_surface]); |
| CHECK_VASTATUS(va_status,"vaBeginPicture"); |
| |
| if (i == 0) { |
| VAEncSequenceParameterBufferH264 seq_h264 = {0}; |
| VABufferID seq_param_buf; |
| |
| seq_h264.level_idc = SH_LEVEL_3; |
| seq_h264.picture_width_in_mbs = frame_width / 16; |
| seq_h264.picture_height_in_mbs = frame_height / 16; |
| seq_h264.bits_per_second = frame_bitrate; |
| seq_h264.frame_rate = frame_rate; |
| seq_h264.initial_qp = initial_qp; |
| seq_h264.min_qp = minimal_qp; |
| seq_h264.basic_unit_size = 0; |
| seq_h264.intra_period = intra_count; |
| |
| va_status = vaCreateBuffer(va_dpy, context_id, |
| VAEncSequenceParameterBufferType, |
| sizeof(seq_h264),1,&seq_h264,&seq_param_buf); |
| CHECK_VASTATUS(va_status,"vaCreateBuffer");; |
| |
| va_status = vaRenderPicture(va_dpy,context_id, &seq_param_buf, 1); |
| CHECK_VASTATUS(va_status,"vaRenderPicture");; |
| } |
| |
| |
| pic_h264.reference_picture = surface_id[ref_surface]; |
| pic_h264.reconstructed_picture= surface_id[dst_surface]; |
| pic_h264.coded_buf = coded_buf[codedbuf_idx]; |
| pic_h264.picture_width = frame_width; |
| pic_h264.picture_height = frame_height; |
| pic_h264.last_picture = (i==frame_count); |
| |
| va_status = vaCreateBuffer(va_dpy, context_id,VAEncPictureParameterBufferType, |
| sizeof(pic_h264),1,&pic_h264,&pic_param_buf); |
| CHECK_VASTATUS(va_status,"vaCreateBuffer");; |
| |
| va_status = vaRenderPicture(va_dpy,context_id, &pic_param_buf, 1); |
| CHECK_VASTATUS(va_status,"vaRenderPicture"); |
| |
| /* one frame, one slice */ |
| slice_h264.start_row_number = 0; |
| slice_h264.slice_height = frame_height/16; /* Measured by MB */ |
| slice_h264.slice_flags.bits.is_intra = ((i % intra_count) == 0); |
| slice_h264.slice_flags.bits.disable_deblocking_filter_idc = 0; |
| va_status = vaCreateBuffer(va_dpy,context_id,VAEncSliceParameterBufferType, |
| sizeof(slice_h264),1,&slice_h264,&slice_param_buf); |
| CHECK_VASTATUS(va_status,"vaCreateBuffer");; |
| |
| va_status = vaRenderPicture(va_dpy,context_id, &slice_param_buf, 1); |
| CHECK_VASTATUS(va_status,"vaRenderPicture"); |
| |
| va_status = vaEndPicture(va_dpy,context_id); |
| CHECK_VASTATUS(va_status,"vaEndPicture");; |
| |
| va_status = vaSyncSurface(va_dpy, surface_id[src_surface]); |
| CHECK_VASTATUS(va_status,"vaSyncSurface"); |
| |
| surface_status = 0; |
| va_status = vaQuerySurfaceStatus(va_dpy, surface_id[src_surface],&surface_status); |
| frame_skipped = (surface_status & VASurfaceSkipped); |
| |
| save_coded_buf(coded_buf[codedbuf_idx], i, frame_skipped); |
| |
| /* should display reconstructed frame, but just diplay source frame */ |
| if (frame_display) { |
| int exit_encode = 0; |
| |
| display_surface(src_surface, &exit_encode); |
| if (exit_encode) |
| frame_count = i; |
| } |
| |
| /* use next surface */ |
| src_surface++; |
| if (src_surface == (SURFACE_NUM - 2)) |
| src_surface = 0; |
| |
| /* use next codedbuf */ |
| codedbuf_idx++; |
| if (codedbuf_idx == (CODEDBUF_NUM - 1)) |
| codedbuf_idx = 0; |
| |
| /* if a frame is skipped, current frame still use last reference frame */ |
| if (frame_skipped == 0) { |
| /* swap ref/dst */ |
| int tmp = dst_surface; |
| dst_surface = ref_surface; |
| ref_surface = tmp; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int main(int argc,char **argv) |
| { |
| VAEntrypoint entrypoints[5]; |
| int num_entrypoints,slice_entrypoint; |
| VAConfigAttrib attrib[2]; |
| VAConfigID config_id; |
| int major_ver, minor_ver; |
| VAStatus va_status; |
| char c; |
| |
| strcpy(coded_file, "/tmp/demo.264"); |
| while ((c =getopt(argc,argv,"w:h:n:p:f:r:q:s:o:d?") ) != EOF) { |
| switch (c) { |
| case 'w': |
| frame_width = atoi(optarg); |
| break; |
| case 'h': |
| frame_height = atoi(optarg); |
| break; |
| case 'n': |
| frame_count = atoi(optarg); |
| break; |
| case 'p': |
| intra_count = atoi(optarg); |
| break; |
| case 'f': |
| frame_rate = atoi(optarg); |
| break; |
| case 'b': |
| frame_bitrate = atoi(optarg); |
| break; |
| case 'q': |
| initial_qp = atoi(optarg); |
| break; |
| case 's': |
| minimal_qp = atoi(optarg); |
| break; |
| case 'd': |
| frame_display = 1; |
| break; |
| case 'o': |
| strcpy(coded_file, optarg); |
| break; |
| case ':': |
| case '?': |
| printf("./h264encode <options>\n"); |
| printf(" -w -h: resolution\n"); |
| printf(" -n frame number\n"); |
| printf(" -d display the source frame\n"); |
| printf(" -p P frame count between two I frames\n"); |
| printf(" -f frame rate\n"); |
| printf(" -r bit rate\n"); |
| printf(" -q initial QP\n"); |
| printf(" -s maximum QP\n"); |
| printf(" -o coded file\n"); |
| exit(0); |
| } |
| } |
| |
| x11_display = XOpenDisplay(":0.0"); |
| assert(x11_display); |
| |
| va_dpy = vaGetDisplay(x11_display); |
| va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); |
| CHECK_VASTATUS(va_status, "vaInitialize"); |
| |
| vaQueryConfigEntrypoints(va_dpy, VAProfileH264Baseline, entrypoints, |
| &num_entrypoints); |
| for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; slice_entrypoint++) { |
| if (entrypoints[slice_entrypoint] == VAEntrypointEncSlice) |
| break; |
| } |
| if (slice_entrypoint == num_entrypoints) { |
| /* not find Slice entry point */ |
| assert(0); |
| } |
| |
| /* find out the format for the render target, and rate control mode */ |
| attrib[0].type = VAConfigAttribRTFormat; |
| attrib[1].type = VAConfigAttribRateControl; |
| vaGetConfigAttributes(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, |
| &attrib[0], 2); |
| if ((attrib[0].value & VA_RT_FORMAT_YUV420) == 0) { |
| /* not find desired YUV420 RT format */ |
| assert(0); |
| } |
| if ((attrib[1].value & VA_RC_VBR) == 0) { |
| /* Can't find matched RC mode */ |
| printf("VBR mode doesn't found, exit\n"); |
| assert(0); |
| } |
| attrib[0].value = VA_RT_FORMAT_YUV420; /* set to desired RT format */ |
| attrib[1].value = VA_RC_VBR; /* set to desired RC mode */ |
| |
| va_status = vaCreateConfig(va_dpy, VAProfileH264Baseline, VAEntrypointEncSlice, |
| &attrib[0], 2,&config_id); |
| CHECK_VASTATUS(va_status, "vaCreateConfig"); |
| |
| va_status = vaCreateSurfaces(va_dpy,frame_width, frame_height, |
| VA_RT_FORMAT_YUV420, SURFACE_NUM, &surface_id[0]); |
| CHECK_VASTATUS(va_status, "vaCreateSurfaces"); |
| |
| /* Create a context for this decode pipe */ |
| va_status = vaCreateContext(va_dpy, config_id, |
| frame_width, ((frame_height+15)/16)*16, |
| VA_PROGRESSIVE,&surface_id[0],SURFACE_NUM,&context_id); |
| CHECK_VASTATUS(va_status, "vaCreateContext"); |
| |
| /* store coded data into a file */ |
| coded_fd = open(coded_file,O_CREAT|O_RDWR, 0); |
| if (coded_fd == -1) { |
| printf("Open file %s failed, exit\n", coded_file); |
| exit(1); |
| } |
| |
| printf("Coded %d frames, %dx%d, save the coded file into %s\n", |
| frame_count, frame_width, frame_height, coded_file); |
| do_h264_encoding(); |
| |
| printf("\n\n"); |
| |
| vaDestroySurfaces(va_dpy,&surface_id[0],SURFACE_NUM); |
| vaDestroyConfig(va_dpy,config_id); |
| vaDestroyContext(va_dpy,context_id); |
| |
| vaTerminate(va_dpy); |
| |
| XCloseDisplay(x11_display); |
| |
| return 0; |
| } |