int main(int argc, char *argv[])
{
    AVCodecContext* video_dec_ctx = NULL;
    AVCodec* video_dec = NULL;
    AVPacket pkt;
    AVFrame *frame = NULL;
    int read_eos = 0;
    int decode_count = 0;
    int render_count = 0;
    int video_stream_index = -1, i;
    uint8_t *frame_copy = NULL;
    FILE *dump_yuv = NULL;

    // parse command line parameters
    process_cmdline(argc, argv);
    if (!input_file) {
        ERROR("no input file specified\n");
        return -1;
    }

    // libav* init
    av_register_all();

    // open input file
    AVFormatContext* pFormat = NULL;
    if (avformat_open_input(&pFormat, input_file, NULL, NULL) < 0) {
        ERROR("fail to open input file: %s by avformat\n", input_file);
        return -1;
    }
    if (avformat_find_stream_info(pFormat, NULL) < 0) {
        ERROR("fail to find out stream info\n");
        return -1;
    }
    av_dump_format(pFormat,0,input_file,0);

    // find out video stream
    for (i = 0; i < pFormat->nb_streams; i++) {
        if (pFormat->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_dec_ctx = pFormat->streams[i]->codec;
            video_stream_index = i;
            break;
        }
    }
    ASSERT(video_dec_ctx && video_stream_index>=0);

    // open video codec
    video_dec = avcodec_find_decoder(video_dec_ctx->codec_id);
    video_dec_ctx->coder_type = render_mode ? render_mode -1 : render_mode; // specify output frame type
    if (avcodec_open2(video_dec_ctx, video_dec, NULL) < 0) {
        ERROR("fail to open codec\n");
        return -1;
    }

    // decode frames one by one
    av_init_packet(&pkt);
    while (1) {
        if(read_eos == 0 && av_read_frame(pFormat, &pkt) < 0) {
            read_eos = 1;
        }
        if (read_eos) {
            pkt.data = NULL;
            pkt.size = 0;
        }

        if (pkt.stream_index == video_stream_index) {
            frame = av_frame_alloc();
            int got_picture = 0,ret = 0;
            ret = avcodec_decode_video2(video_dec_ctx, frame, &got_picture, &pkt);
            if (ret < 0) { // decode fail (or decode finished)
                DEBUG("exit ...\n");
                break;
            }

            if (read_eos && ret>=0 && !got_picture) {
                DEBUG("ret=%d, exit ...\n", ret);
                break; // eos has been processed
            }

            decode_count++;
            if (got_picture) {
                switch (render_mode) {
                case 0: // dump raw video frame to disk file
                case 1: { // draw raw frame data as texture
                    // assumed I420 format
                    int height[3] = {video_dec_ctx->height, video_dec_ctx->height/2, video_dec_ctx->height/2};
                    int width[3] = {video_dec_ctx->width, video_dec_ctx->width/2, video_dec_ctx->width/2};
                    int plane, row;

                    if (render_mode == 0) {
                        if (!dump_yuv) {
                            char out_file[256];
                            sprintf(out_file, "./dump_%dx%d.I420", video_dec_ctx->width, video_dec_ctx->height);
                            dump_yuv = fopen(out_file, "ab");
                            if (!dump_yuv) {
                                ERROR("fail to create file for dumped yuv data\n");
                                return -1;
                            }
                        }
                        for (plane=0; plane<3; plane++) {
                            for (row = 0; row<height[plane]; row++)
                                fwrite(frame->data[plane]+ row*frame->linesize[plane], width[plane], 1, dump_yuv);
                        }
                    } else {
                        // glTexImage2D  doesn't handle pitch, make a copy of video data
                        frame_copy = malloc(video_dec_ctx->height * video_dec_ctx->width * 3 / 2);
                        unsigned char* ptr = frame_copy;

                        for (plane=0; plane<3; plane++) {
                            for (row=0; row<height[plane]; row++) {
                                memcpy(ptr, frame->data[plane]+row*frame->linesize[plane], width[plane]);
                                ptr += width[plane];
                            }
                        }

                        drawVideo((uintptr_t)frame_copy, 0, video_dec_ctx->width, video_dec_ctx->height, 0);
                    }
                }
                    break;
                case 2: // draw video frame as texture with drm handle
                case 3: // draw video frame as texture with dma_buf handle
                    drawVideo((uintptr_t)frame->data[0], render_mode -1, video_dec_ctx->width, video_dec_ctx->height, (uintptr_t)frame->data[1]);
                    break;
                default:
                    break;
                }
                render_count++;
            }
        }
    }

    if (frame)
        av_frame_free(&frame);
    if (frame_copy)
        free(frame_copy);
    if (dump_yuv)
        fclose(dump_yuv);
    deinit_egl();
    PRINTF("decode %s ok, decode_count=%d, render_count=%d\n", input_file, decode_count, render_count);

    return 0;
}
int egl_lock_surface(void)
{
	fprintf(stderr, "TEST_EXTENSIONS : EGL_KHR_lock_surface & EGL_KHR_lock_surface2 \n");
	int ret = 1;
	int i = 0;

	EGLImageKHR image;
	GLuint image_texture;
	int move_x = 0;
	int move_y = 0;
	int direction_x = 1;
	int direction_y = 1;
	

	/* CREATE CONTEXT STRUCTURE */
	X11Context* x11_ctx = (X11Context*)malloc(sizeof(X11Context));
	EglContext* egl_ctx = (EglContext*)malloc(sizeof(EglContext));
	RenderingContext* gles_ctx = (RenderingContext*)malloc(sizeof(RenderingContext));
	PFNEGLCREATEIMAGEKHRPROC p_eglCreateImageKHR;
	PFNEGLDESTROYIMAGEKHRPROC p_eglDestroyImageKHR;
	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC p_glEGLImageTargetTexture2DOES;
	
	/* Initialize native x11 */
	if(!init_x11_native(x11_ctx))
		goto finish;

	/* Initialize egl */
	if(!init_egl(x11_ctx, egl_ctx))
		goto finish;
		
	if(!is_supported(egl_ctx->egl_display,"EGL_KHR_lock_surface"))
	{	
		/* EGL_KHR_lock_surface is not supporect */
		fprintf(stderr, "EGL_KHR_lock_surface is not supported\n");
		goto finish;
	}

	/* Create native pixmap */
	int xDefaultScreen = DefaultScreen(x11_ctx->native_display);
	int xDefaultDepth = XDefaultDepth( x11_ctx->native_display, xDefaultScreen );
	Pixmap pixmap = XCreatePixmap(x11_ctx->native_display,x11_ctx->native_window, TEX_W, TEX_H, xDefaultDepth);
	if(pixmap == None)
	{
		fprintf(stderr, "FAIL to XCreatePixmap \n");
		goto finish;
	}
	
	PFNEGLLOCKSURFACEKHRPROC p_eglLockSurfaceKHR = (PFNEGLLOCKSURFACEKHRPROC)eglGetProcAddress( "eglLockSurfaceKHR" );
	PFNEGLUNLOCKSURFACEKHRPROC p_eglUnlockSurfaceKHR = (PFNEGLUNLOCKSURFACEKHRPROC)eglGetProcAddress( "eglUnlockSurfaceKHR" );
	if(!p_eglLockSurfaceKHR || !p_eglUnlockSurfaceKHR)
	{
		fprintf(stderr, "EGL_KHR_lock_surface is not supported \n");
		goto finish;
	}

	/* Create eglPixmapSurface */
	if(!create_lockable_pixmap_surface(egl_ctx, pixmap))
	{
		fprintf(stderr, "FAIL to Create PixmapSurface \n");
		goto finish;
	}

	/* Create eglImage and texture */
	if(!create_egl_image_texture(pixmap, &image_texture, egl_ctx))
	{
		fprintf(stderr, "FAIL to Create eglImage \n");
		goto finish;
	}

	/* vertices, color, texture coordinate info */
	static GLfloat vertices[] = {  -1.0,  1.0, 0.0,
									1.0,  1.0, 0.0,
								   -1.0, -1.0, 0.0, 
									1.0, -1.0, 0.0};

	static GLfloat colors[] = { 1.0f, 1.0f, 1.0f, 1.0f, 			
								1.0f, 1.0f, 1.0f, 1.0f,
								1.0f, 1.0f, 1.0f, 1.0f, 			
								1.0f, 1.0f, 1.0f, 1.0f};

	static GLfloat texcoord[] = {0.0, 1.0,
								 1.0, 1.0,
								 0.0, 0.0,
								 1.0, 0.0};
								 
	EGLint lock_surface_attrib [] = {EGL_MAP_PRESERVE_PIXELS_KHR, EGL_FALSE, 
									EGL_LOCK_USAGE_HINT_KHR, EGL_READ_SURFACE_BIT_KHR | EGL_WRITE_SURFACE_BIT_KHR,
									EGL_NONE};	
	unsigned char* p_eglimg_data = NULL;

	if(!init_gles(vertices, colors, texcoord, gles_ctx))
	{
		fprintf(stderr, "FAIL TO INIT GLES\n");
		goto finish;
	}

	/* Query Lockable Surface infomation */
	if(!eglMakeCurrent(egl_ctx->egl_display, egl_ctx->pixmap_sur, egl_ctx->pixmap_sur, egl_ctx->pixmap_ctx))
		goto finish;
		
	if(!p_eglLockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur, lock_surface_attrib ))
	{
		p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur );
		if(!eglMakeCurrent(egl_ctx->egl_display, egl_ctx->wnd_sur, egl_ctx->wnd_sur, egl_ctx->wnd_ctx))
			goto finish;

		if(!p_eglLockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur, lock_surface_attrib ))
		{
			fprintf(stderr, "FAIL to p_eglLockSurfaceKHR %x \n", eglGetError());	
			p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur );
		}
	}
	EGLint data[7];
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PITCH_KHR, &data[0]);
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_ORIGIN_KHR, &data[1]);
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PIXEL_RED_OFFSET_KHR, &data[2]);
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR , &data[3]);
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR, &data[4]);
	eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR , &data[5]);
	fprintf(stderr, "eglSurface Infomation \n");
	fprintf(stderr, " EGL_BITMAP_PITCH_KHR %d\n EGL_BITMAP_ORIGIN_KHR 0x%x\n EGL_BITMAP_PIXEL_RED_OFFSET_KHR %d\n EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR %d\n ", data[0], data[1], data[2], data[3]);
	fprintf(stderr, "EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR %d\n EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR %d\n", data[4], data[5]);
	
	if(is_supported(egl_ctx->egl_display, "EGL_KHR_lock_surface2"))
	{
		eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur,  EGL_BITMAP_PIXEL_SIZE_KHR , &data[6]);
		fprintf(stderr, " EGL_BITMAP_PIXEL_SIZE_KHR %d\n", data[6]);
	}
	
	if(!p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur ))
	{
		fprintf(stderr, "FAIL to eglUnlockSurfaceKHR %x \n", eglGetError());
		goto finish;
	}

	for(i=0; i<FRAME; i++)
	{
		/* MakeCurrent eglPixmapSurface */
		if(!eglMakeCurrent(egl_ctx->egl_display, egl_ctx->pixmap_sur, egl_ctx->pixmap_sur, egl_ctx->pixmap_ctx))
			goto finish;
			
		if(!p_eglLockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur, lock_surface_attrib ))
		{
			p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur );
			/* MakeCurrent eglWindowSurface */
			if(!eglMakeCurrent(egl_ctx->egl_display, egl_ctx->wnd_sur, egl_ctx->wnd_sur, egl_ctx->wnd_ctx))
				goto finish;

			if(!p_eglLockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur, lock_surface_attrib ))
			{
				fprintf(stderr, "FAIL to p_eglLockSurfaceKHR %x \n", eglGetError());	
				p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur );
			}
		}
		if(!eglQuerySurface(egl_ctx->egl_display, egl_ctx->pixmap_sur, EGL_BITMAP_POINTER_KHR, (EGLint *) &p_eglimg_data))
		{
				fprintf(stderr, "FAIL to query surface %x \n", eglGetError());
				p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur );
				goto finish;
		}

		if(p_eglimg_data == NULL)
		{
			fprintf(stderr, "eglQuerySurface return NULL for locksurface \n");
			goto finish;
		}

		if(i%30 < 10)
			update_eglimg( p_eglimg_data, 'R', TEX_W, TEX_H, 4 );
		else if(i%30 >= 10 && i%30 <20)
			update_eglimg( p_eglimg_data, 'G', TEX_W, TEX_H, 4 );
		else
			update_eglimg( p_eglimg_data, 'B', TEX_W, TEX_H, 4 );
			
		if(!p_eglUnlockSurfaceKHR( egl_ctx->egl_display, egl_ctx->pixmap_sur ))
		{
			fprintf(stderr, "FAIL to eglUnlockSurfaceKHR %x \n", eglGetError());
			goto finish;
		}
		
		/* MakeCurrent eglWindowSurface */
		if(!eglMakeCurrent(egl_ctx->egl_display, egl_ctx->wnd_sur, egl_ctx->wnd_sur, egl_ctx->wnd_ctx))
			goto finish;
		
		/* Draw on to eglWindowSurface */
		glClearColor(1.0, 1.0, 1.0, 1.0);
		glClear(GL_COLOR_BUFFER_BIT);
		glViewport(move_x,move_y, RECT_W, RECT_H );
		glBindTexture(GL_TEXTURE_2D, image_texture);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		eglSwapBuffers(egl_ctx->egl_display, egl_ctx->wnd_sur);

		if(direction_x == 1)
			move_x += SPEED;
		else 
			move_x -= SPEED;
			
		if(direction_y == 1)
			move_y += SPEED;
		else
			move_y -= SPEED;

		if(move_x+RECT_W > x11_ctx->width || move_x < 0)
			direction_x = - direction_x;
		if(move_y+RECT_H > x11_ctx->height || move_y < 0)
			direction_y = - direction_y;
		if(!eglSwapBuffers(egl_ctx->egl_display, egl_ctx->wnd_sur))
			goto finish;
	}
	destroy_egl_image_texture(&image_texture, egl_ctx);			
finish:
	/* Deinit gl */
	deinit_gles(gles_ctx);
	
	/* Deinit egl */
	deinit_egl(egl_ctx);
	
	/* Deinit native x11 */
	deinit_x11_native(x11_ctx);

	if(x11_ctx)
		free(x11_ctx);
	if(egl_ctx)
		free(egl_ctx);
	if(gles_ctx)
		free(gles_ctx);

	return ret;
}