6.3.5.6.1. Framebuffer标准接口的使用

标准framebuffer接口操作示例:

int main()
{
    int fd = 0;
    struct fb_var_screeninfo vi;
    struct fb_fix_screeninfo fi;

    void *fb_ptr = NULL;

    fd = open("/dev/fb0", O_RDWR);
    if (fd < 0) {
        printf("failed to open fb0\n");
        return -1;
    }

    // 获取可变屏幕参数,如分辨率,像素格式
    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
        printf("failed to get fb0 info\n");
        close(fd);
        return -1;
    }

    // 获取屏幕固定参数,包括显存起始物理地址、显存大小和行间距
    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
        printf("failed to get fb0 info\n");
        close(fd);
        return -1;
    }

    // framebuffer 缓冲区映射到用户空间:
    fb_ptr = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (fb_ptr == MAP_FAILED) {
        printf("failed to mmap framebuffer\n");
        close(fd);
        return -1;
    }

    // 用户可以直接把要显示到屏幕的图像数据写入fb_ptr即可
    ........
    ........

    munmap(fb_ptr, fi.smem_len);
    close(fd);

    return 0;
}

6.3.5.6.2. AICFB扩展接口的使用

代码详见 source/zx/test-fb/fb_test.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2020-2021 ZX Technology Co., Ltd.
 * Authors:  Matteo <duanmt@zx.com>
 */

#include "base.h"
#include "zx_fb.h"

/* Global macro and variables */

#define AICFB_LAYER_MAX_NUM 2
#define FB_DEV "/dev/fb0"

static const char sopts[] = "nscflLaAkKedw:h:m:v:u";
static const struct option lopts[] = {
    {"get_layer_num",   no_argument, NULL, 'n'},
    {"get_screen_size", no_argument, NULL, 's'},
    {"get_layer_cap",   no_argument, NULL, 'c'},
    {"get_fb_layer",    no_argument, NULL, 'f'},
    {"get_layer",       no_argument, NULL, 'l'},
    {"set_layer",       no_argument, NULL, 'L'},
    {"get_alpha",       no_argument, NULL, 'a'},
    {"set_alpha",     required_argument, NULL, 'A'},
    {"get_ck_cfg",      no_argument, NULL, 'k'},
    {"set_ck_cfg",      no_argument, NULL, 'K'},
    {"enable",      no_argument, NULL, 'e'},
    {"disable",     no_argument, NULL, 'd'},
    {"id",        required_argument, NULL, 'i'},
    {"width",     required_argument, NULL, 'w'},
    {"height",    required_argument, NULL, 'h'},
    {"mode",      required_argument, NULL, 'm'},
    {"value",     required_argument, NULL, 'v'},
    {"usage",       no_argument, NULL, 'u'},
    {0, 0, 0, 0}
};

/* Functions */

void usage(char *program)
{
    printf("Usage: %s [options]: \n", program);
    printf("\t -n, --get_layer_num \n");
    printf("\t -s, --get_screen_size \n");
    printf("\t -c, --get_layer_cap \n");
    printf("\t -f, --get_fb_layer \n");
    printf("\t -l, --get_layer \n");
    printf("\t -L, --set_layer\tneed other options: -i x -e/d -w y -h z\n");
    printf("\t -a, --get_alpha \n");
    printf("\t -A, --set_alpha\tneed other options: -e/d -m x -v y \n");
    printf("\t -k, --get_ck_cfg \n");
    printf("\t -K, --set_ck_cfg\tneed other options: -e/d -v x \n");
    printf("\t -e, --enable \n");
    printf("\t -d, --disable \n");
    printf("\t -i, --id\t\tneed an integer argument of Layer ID\n");
    printf("\t -w, --width\t\tneed an integer argument\n");
    printf("\t -h, --height\t\tneed an integer argument\n");
    printf("\t -m, --mode\t\tneed an integer argument\n");
    printf("\t -v, --value\t\tneed an integer argument\n");
    printf("\t -u, --usage \n");
    printf("\n");
    printf("Example: %s -l\n", program);
    printf("Example: %s -L -i 1 -e -w 800 -h 480\n", program);
    printf("Example: %s -A -e -w 800 -h 480\n", program);
    printf("Example: %s -K -e -v 0x3F\n", program);
}

/* Open a device file to be needed. */
int device_open(char *_fname, int _flag)
{
    s32 fd = -1;

    fd = open(_fname, _flag);
    if (fd < 0) {
        ERR("Failed to open %s", _fname);
        exit(0);
    }
    return fd;
}

int get_layer_num(int fd)
{
    int ret = 0;
    struct aicfb_layer_num n = {0};

    ret = ioctl(fd, AICFB_GET_LAYER_NUM, &n);
    if (ret < 0) {
        ERR("ioctl() return %d\n", ret);
    }
    else {
        printf("The number of video layer: %d\n", n.vi_num);
        printf("The number of UI layer: %d\n", n.ui_num);
    }
    return ret;
}

int get_layer_cap(int fd)
{
    int i;
    int ret = 0;
    struct aicfb_layer_capability cap = {0};

    for (i = 0; i < AICFB_LAYER_MAX_NUM; i++) {
        memset(&cap, 0, sizeof(struct aicfb_layer_capability));
        cap.layer_id = i;
        ret = ioctl(fd, AICFB_GET_LAYER_CAPABILITY, &cap);
        if (ret < 0) {
            ERR("ioctl() return %d\n", ret);
            return ret;
        }

        printf("\n--------------- Layer %d ---------------\n", i);
        printf("Type: %s\n", cap.layer_type == AICFB_LAYER_TYPE_VIDEO ?
            "Video" : "UI");
        printf("Max Width: %d (%#x)\n", cap.max_width, cap.max_width);
        printf("Max height: %d (%#x)\n", cap.max_height, cap.max_height);
        printf("Flag: %#x\n", cap.cap_flags);
        printf("---------------------------------------\n");
    }
    return ret;
}

int get_one_layer_cfg(int fd, int cmd, int id)
{
    int ret = 0;
    struct aicfb_layer_data layer = {0};

    layer.layer_id = id;
    ret = ioctl(fd, cmd, &layer);
    if (ret < 0) {
        ERR("ioctl() return %d\n", ret);
        return ret;
    }

    printf("\n--------------- Layer %d ---------------\n", layer.layer_id);
    printf("Enable: %d\n", layer.enable);
    printf("Layer ID: %d\n", layer.layer_id);
    printf("Rect ID: %d\n", layer.rect_id);
    printf("Scale Size: w %d, h %d\n",
        layer.scale_size.width, layer.scale_size.height);
    printf("Position: x %d, y %d\n", layer.pos.x, layer.pos.y);
    printf("Buffer: \n");
    printf("\tPixel format: %#x\n", layer.buf.format);
    printf("\tPhysical addr: %#x %#x %#x\n",
        layer.buf.phy_addr[0], layer.buf.phy_addr[1],
        layer.buf.phy_addr[2]);
    printf("\tStride: %d %d %d\n", layer.buf.stride[0],
        layer.buf.stride[1], layer.buf.stride[2]);
    printf("\tSize: w %d, h %d\n",
        layer.buf.size.width, layer.buf.size.height);
    printf("\tCrop enable: %d\n", layer.buf.crop_en);
    printf("\tCrop: x %d, y %d, w %d, h %d\n",
        layer.buf.crop.x, layer.buf.crop.y,
        layer.buf.crop.width, layer.buf.crop.height);
    printf("\tFlag: %#x\n", layer.buf.buf_flags);
    printf("---------------------------------------\n");

    return 0;
}

int get_fb_layer_cfg(int fd)
{
    return get_one_layer_cfg(fd, AICFB_GET_FB_LAYER_CONFIG, 0);
}

int get_layer_cfg(int fd)
{
    int i;

    for (i = 0; i < AICFB_LAYER_MAX_NUM; i++)
        get_one_layer_cfg(fd, AICFB_GET_LAYER_CONFIG, i);

    return 0;
}

int set_layer_cfg(int fd, int id, int enable, int width, int height)
{
    int ret = 0;
    struct aicfb_layer_data layer = {0};

    if ((id < 0) || (enable < 0) || (width < 0) || (height < 0)) {
        ERR("Invalid argument.\n");
        return -1;
    }

    layer.layer_id = id;
    layer.enable = enable;
    layer.scale_size.width = layer.buf.size.width;
    layer.scale_size.height = layer.buf.size.height;
    ret = ioctl(fd, AICFB_UPDATE_LAYER_CONFIG, &layer);
    if (ret < 0)
        ERR("ioctl() return %d\n", ret);

    return ret;
}

int get_layer_alpha(int fd)
{
    int i;
    int ret = 0;
    struct aicfb_alpha_config alpha = {0};

    for (i = 1; i < AICFB_LAYER_MAX_NUM; i++) {
        alpha.layer_id = i;
        ret = ioctl(fd, AICFB_GET_ALPHA_CONFIG, &alpha);
        if (ret < 0) {
            ERR("ioctl() return %d\n", ret);
            return ret;
        }

        printf("\n--------------- Layer %d ---------------\n", i);
        printf("Alpha enable: %d\n", alpha.enable);
        printf("Alpla mode: %d (0, pixel; 1, global; 2, mix)\n",
            alpha.mode);
        printf("Alpha value: %d (%#x)\n", alpha.value, alpha.value);
        printf("---------------------------------------\n");
    }

    return 0;
}

int set_layer_alpha(int fd, int enable, int mode, int val)
{
    int ret = 0;
    struct aicfb_alpha_config alpha = {0};

    if ((enable < 0) || (mode < 0) || (val < 0)) {
        ERR("Invalid argument.\n");
        return -1;
    }

    alpha.layer_id = 1;
    alpha.enable = enable;
    alpha.mode = mode;
    alpha.value = val;
    ret = ioctl(fd, AICFB_UPDATE_ALPHA_CONFIG, &alpha);
    if (ret < 0)
        ERR("ioctl() return %d\n", ret);

    return ret;
}

int get_ck_cfg(int fd)
{
    int i;
    int ret = 0;
    struct aicfb_ck_config ck = {0};

    for (i = 1; i < AICFB_LAYER_MAX_NUM; i++) {
        ck.layer_id = i;
        ret = ioctl(fd, AICFB_GET_CK_CONFIG, &ck);
        if (ret < 0) {
            ERR("ioctl() return %d\n", ret);
            return ret;
        }

        printf("\n--------------- Layer %d ---------------\n", i);
        printf("Color key enable: %d\n", ck.enable);
        printf("Color key value: R %#x, G %#x, B %#x\n",
            (ck.value >> 16) & 0xFF, (ck.value >> 8) & 0xFF,
            ck.value & 0xFF);
        printf("---------------------------------------\n");
    }
    return 0;
}

int set_ck_cfg(int fd, int enable, int val)
{
    int ret = 0;
    struct aicfb_ck_config ck = {0};

    if ((enable < 0) || (val < 0)) {
        ERR("Invalid argument.\n");
        return -1;
    }

    ck.layer_id = 1;
    ck.enable = enable;
    ck.value = val;
    ret = ioctl(fd, AICFB_UPDATE_CK_CONFIG, &ck);
    if (ret < 0)
        ERR("ioctl() return %d\n", ret);

    return ret;
}

int get_screen_size(int fd)
{
    int ret = 0;
    struct aic_size s = {0};

    ret = ioctl(fd, AICFB_GET_SCREEN_SIZE, &s);
    if (ret < 0) {
        ERR("ioctl() return %d\n", ret);
        return ret;
    }

    printf("Screen width: %d (%#x)\n", s.width, s.width);
    printf("Screen height: %d (%#x)\n", s.height, s.height);
    return 0;
}

int main(int argc, char **argv)
{
    int dev_fd = -1;
    int ret = 0;
    int c = 0;
    int layer_id = 0;
    int enable = 0;
    int mode = 0;
    int width = 0;
    int height = 0;
    int value = 0;

    dev_fd = device_open(FB_DEV, O_RDWR);
    if (dev_fd < 0) {
        ERR("Failed to open %s, return %d\n", FB_DEV, dev_fd);
        return -1;
    }

    while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
        switch (c) {
        case 'n':
            ret = get_layer_num(dev_fd);
            goto end;
        case 's':
            ret = get_screen_size(dev_fd);
            goto end;
        case 'c':
            ret = get_layer_cap(dev_fd);
            goto end;
        case 'f':
            ret = get_fb_layer_cfg(dev_fd);
            goto end;
        case 'l':
            ret = get_layer_cfg(dev_fd);
            goto end;
        case 'a':
            ret = get_layer_alpha(dev_fd);
            goto end;
        case 'k':
            ret = get_ck_cfg(dev_fd);
            goto end;
        case 'e':
            enable = 1;
            continue;
        case 'd':
            enable = 0;
            continue;
        case 'i':
            layer_id = str2int(optarg);
            continue;
        case 'w':
            width = str2int(optarg);
            continue;
        case 'h':
            height = str2int(optarg);
            continue;
        case 'm':
            mode = str2int(optarg);
            continue;
        case 'v':
            value = str2int(optarg);
            continue;
        case 'u':
            usage(argv[0]);
            goto end;
        default:
            continue;
        }
    }

    optind = 0;
    while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
        switch (c) {
        case 'L':
            ret = set_layer_cfg(dev_fd, layer_id, enable, width, height);
            goto end;
        case 'A':
            ret = set_layer_alpha(dev_fd, enable, mode, value);
            goto end;
        case 'K':
            ret = set_ck_cfg(dev_fd, enable, value);
            goto end;
        default:
            continue;
        }
    }

end:
    if (dev_fd > 0)
        close(dev_fd);

    return ret;
}

6.3.5.6.3. DMA-BUF的使用

代码详见 source/zx/test-dma-buf/

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2020-2021 ZX Technology Co., Ltd.
 * Authors:  Matteo <duanmt@zx.com>
 */

#include <zx/sample_base.h>
#include <linux/dma-buf.h>
#include <linux/dma-heap.h>
#include <video/zx_fb.h>

/* Global macro and variables */

#define AICFB_VID_BUF_NUM   2
#define AIC_CMA_BUF_MAX     (8 * 1024 * 1024)
#define DMA_HEAP_DEV        "/dev/dma_heap/reserved"
#define FB_DEV          "/dev/fb0"

static const char sopts[] = "m:w:h:f:i:u";
static const struct option lopts[] = {
    {"width",     required_argument, NULL, 'w'},
    {"height",    required_argument, NULL, 'h'},
    {"format",    required_argument, NULL, 'f'},
    {"input",     required_argument, NULL, 'i'},
    {"usage",       no_argument, NULL, 'u'},
    {0, 0, 0, 0}
};

struct video_data_format {
    enum aic_pixel_format format;
    char f_str[12];
    int y_shift;
    int u_shift;
    int v_shift;
};

struct video_data_format g_vformat[] = {
    {AIC_FMT_YUV420P, "yuv420p", 0, 2, 2},
    {AIC_FMT_YUV422P, "yuv422p", 0, 1, 1},
    {AIC_FMT_YUV444P, "yuv444p", 0, 0, 0},

    {AIC_FMT_MAX, "", 0, 0, 0}
};

struct video_plane {
    int fd;
    char *buf;
    int len;
};

struct video_buf {
    struct video_plane y;
    struct video_plane u;
    struct video_plane v;
};

struct aicfb_video_layer {
    int w;
    int h;
    struct video_data_format *f;
    struct video_buf vbuf[AICFB_VID_BUF_NUM];
};

static int g_fb_fd = -1;
static struct aicfb_video_layer g_vlayer = {0};

/* Functions */

void usage(char *program)
{
    printf("Usage: %s [options]: \n", program);
    printf("\t -w, --width\t\tneed an integer argument\n");
    printf("\t -h, --height\t\tneed an integer argument\n");
    printf("\t -f, --format\t\tvideo format, yuv420p etc\n");
    printf("\t -i, --input\t\tneed a file name \n");
    printf("\t -u, --usage \n");
    printf("\n");
    printf("Example: %s -w 480 -h 320 -f yuv420p -i my.yuv\n", program);
}

/* Open a device file to be needed. */
int device_open(char *_fname, int _flag)
{
    s32 fd = -1;

    fd = open(_fname, _flag);
    if (fd < 0) {
        ERR("Failed to open %s", _fname);
        exit(0);
    }
    return fd;
}

int vidbuf_request_one(struct video_plane *plane, int len, int fd)
{
    int ret;
    char *buf = NULL;
    struct dma_heap_allocation_data data = {0};

    if ((len < 0) || (len > AIC_CMA_BUF_MAX)) {
        ERR("Invalid len %d\n", len);
        return -1;
    }

    data.fd = 0;
    data.len = len;
    data.fd_flags = O_RDWR;
    data.heap_flags = 0;
    ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
    if (ret < 0) {
        ERR("ioctl() failed! errno: %d[%s]\n", errno, strerror(errno));
        return -1;
    }

    DBG("Get dma_heap fd: %d\n", data.fd);
    plane->fd = data.fd;

    buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, data.fd, 0);
    if (buf == MAP_FAILED) {
        ERR("mmap() failed! errno: %d[%s]\n", errno, strerror(errno));
        return -1;
    }
    DBG("Get virtual address: %p\n", buf);
    plane->buf = buf;
    plane->len = len;
    return 0;
}

void aic_fb_open(void)
{
    if (g_fb_fd > 0)
        return;

    g_fb_fd = device_open(FB_DEV, O_RDWR);
    if (g_fb_fd < 0)
        ERR("Failed to open %s. errno: %d[%s]\n",
            FB_DEV, errno, strerror(errno));
}

int set_ui_layer_alpha(int val)
{
    int ret = 0;
    struct aicfb_alpha_config alpha = {0};

    alpha.layer_id = 1;
    alpha.enable = 1;
    alpha.mode = 1;
    alpha.value = val;
    ret = ioctl(g_fb_fd, AICFB_UPDATE_ALPHA_CONFIG, &alpha);
    if (ret < 0)
        ERR("ioctl() failed! errno: %d[%s]\n", errno, strerror(errno));

    return ret;
}

int vidbuf_request(struct aicfb_video_layer *vlayer)
{
    int i, j;
    int heap_fd = -1;
    int y_frame = vlayer->w * vlayer->h;

    heap_fd = device_open(DMA_HEAP_DEV, O_RDWR);
    if (heap_fd < 0) {
        ERR("Failed to open %s, errno: %d[%s]\n",
            DMA_HEAP_DEV, errno, strerror(errno));
        return -1;
    }

    /* Prepare two group buffer for video player,
       and each group has three planes: y, u, v. */
    for (i = 0; i < AICFB_VID_BUF_NUM; i++) {
        struct video_plane *p = (struct video_plane *)&vlayer->vbuf[i];
        int *shift = &vlayer->f->y_shift;
        for (j = 0; j < AICFB_PLANE_NUM; j++, p++)
            vidbuf_request_one(p, y_frame >> shift[j], heap_fd);
    }

    close(heap_fd);
    return 0;
}

void vidbuf_release(struct aicfb_video_layer *vlayer)
{
    int i, j;

    for (i = 0; i < AICFB_VID_BUF_NUM; i++) {
        struct video_plane *p = (struct video_plane *)&vlayer->vbuf[i];
        for (j = 0; j < AICFB_PLANE_NUM; j++, p++) {
            if (munmap(p->buf, p->len) < 0)
                ERR("munmap() failed! errno: %d[%s]\n",
                    errno, strerror(errno));
        }
    }
}

void vidbuf_dmabuf_begin(struct aicfb_video_layer *vlayer)
{
    int i, j;
    struct aicfb_dmabuf_fd fds = {0};

    for (i = 0; i < AICFB_VID_BUF_NUM; i++) {
        struct video_plane *plane = (struct video_plane *)&vlayer->vbuf[i];
        for (j = 0; j < AICFB_PLANE_NUM; j++, plane++) {
            fds.fd = plane->fd;
            if (ioctl(g_fb_fd, AICFB_GET_DMABUF, &fds) < 0)
                ERR("ioctl() failed! err %d[%s]\n",
                    errno, strerror(errno));
        }
    }
}

void vidbuf_dmabuf_end(struct aicfb_video_layer *vlayer)
{
    int i, j;
    struct aicfb_dmabuf_fd fds = {0};

    for (i = 0; i < AICFB_VID_BUF_NUM; i++) {
        struct video_plane *plane = (struct video_plane *)&vlayer->vbuf[i];
        for (j = 0; j < AICFB_PLANE_NUM; j++, plane++) {
            fds.fd = plane->fd;
            if (ioctl(g_fb_fd, AICFB_PUT_DMABUF, &fds) < 0)
                ERR("ioctl() failed! err %d[%s]\n",
                    errno, strerror(errno));
        }
    }
}

void video_layer_set(struct aicfb_video_layer *vlayer, int index)
{
    struct aicfb_layer_data layer = {0};
    struct video_buf *vbuf = &vlayer->vbuf[index];

    layer.layer_id = 0;
    layer.enable = 1;
    layer.scale_size.width = vlayer->w * 4;
    layer.scale_size.height = vlayer->h * 4;
    layer.pos.x = 10;
    layer.pos.y = 10;
    layer.buf.size.width = vlayer->w;
    layer.buf.size.height = vlayer->h;
    layer.buf.format = vlayer->f->format;
    layer.buf.dmabuf_fd[0] = vbuf->y.fd;
    layer.buf.dmabuf_fd[1] = vbuf->u.fd;
    layer.buf.dmabuf_fd[2] = vbuf->v.fd;
    layer.buf.stride[0] = vlayer->w;
    layer.buf.stride[1] = vlayer->w >> 1;
    layer.buf.stride[2] = vlayer->w >> 1;

    if (ioctl(g_fb_fd, AICFB_UPDATE_LAYER_CONFIG, &layer) < 0)
        ERR("ioctl() failed! err %d[%s]\n", errno, strerror(errno));
}

void vidbuf_cpu_begin(struct video_buf *vbuf)
{
    int i;
    struct dma_buf_sync flag;
    struct video_plane *p = (struct video_plane *)vbuf;

    for (i = 0; i < AICFB_PLANE_NUM; i++, p++) {
        flag.flags = DMA_BUF_SYNC_WRITE | DMA_BUF_SYNC_START;
        if (ioctl(p->fd, DMA_BUF_IOCTL_SYNC, &flag) < 0)
            ERR("ioctl() failed! err %d[%s]\n",
                errno, strerror(errno));
    }
}

void vidbuf_cpu_end(struct video_buf *vbuf)
{
    int i;
    struct dma_buf_sync flag;
    struct video_plane *p = (struct video_plane *)vbuf;

    for (i = 0; i < AICFB_PLANE_NUM; i++, p++) {
        flag.flags = DMA_BUF_SYNC_WRITE | DMA_BUF_SYNC_END;
        if (ioctl(p->fd, DMA_BUF_IOCTL_SYNC, &flag) < 0)
            ERR("ioctl() failed! err %d[%s]\n",
                errno, strerror(errno));
    }
}

int vidbuf_read(struct aicfb_video_layer *vlayer, int index, int fd)
{
    int i, ret;
    static int frame_cnt = 0;
    struct video_plane *p = (struct video_plane *)&vlayer->vbuf[index];

    if (frame_cnt == 0)
        lseek(fd, 0, SEEK_SET);

    for (i = 0; i < AICFB_PLANE_NUM; i++, p++) {
        DBG("Frame %d - %d, offset %ld, len %d\n", frame_cnt, i,
             lseek(fd, 0, SEEK_CUR), p->len);
        ret = read(fd, p->buf, p->len);
        if (ret != p->len) {
            ERR("read(%d) return %d. errno: %d[%s]\n", p->len,
                ret, errno, strerror(errno));
            return -1;
        }
    }
    frame_cnt++;
    return ret;
}

int format_parse(char *str)
{
    int i;

    for (i = 0; g_vformat[i].format != AIC_FMT_MAX; i++) {
        if (strncasecmp(g_vformat[i].f_str, str, strlen(str)) == 0)
            return i;
    }

    ERR("Invalid format: %s\n", str);
    return 0;
}

int main(int argc, char **argv)
{
    int vid_fd = -1;
    int ret = 0;
    int c = 0;
    int fsize = 0;
    int index = 0;

    DBG("Compile time: %s\n", __TIME__);
    while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
        switch (c) {
        case 'w':
            g_vlayer.w = str2int(optarg);
            continue;
        case 'h':
            g_vlayer.h = str2int(optarg);
            continue;
        case 'f':
            g_vlayer.f = &g_vformat[format_parse(optarg)];
            break;
        case 'i':
            vid_fd = device_open(optarg, O_RDONLY);
            if (vid_fd < 0) {
                ERR("Failed to open %s. errno: %d[%s]\n",
                    optarg, errno, strerror(errno));
                return -1;
            }
            fsize = lseek(vid_fd, 0, SEEK_END);
            DBG("open(%s) %d, size %d\n", optarg, vid_fd, fsize);
            break;
        case 'u':
            usage(argv[0]);
            return 0;
        default:
            break;
        }
    }

    aic_fb_open();
    set_ui_layer_alpha(128);
    vidbuf_request(&g_vlayer);
    vidbuf_dmabuf_begin(&g_vlayer);

    do {
        struct video_buf *vbuf = &g_vlayer.vbuf[index];

        vidbuf_cpu_begin(vbuf);
        ret = vidbuf_read(&g_vlayer, index, vid_fd);
        vidbuf_cpu_end(vbuf);
        if (ret < 0)
            break;

        video_layer_set(&g_vlayer, index);
        index = !index;
        usleep(40000);
        if (lseek(vid_fd, 0, SEEK_CUR) == fsize)
            break;
    } while (1);

    vidbuf_dmabuf_end(&g_vlayer);
    vidbuf_release(&g_vlayer);

    if (vid_fd > 0)
        close(vid_fd);
    if (g_fb_fd > 0)
        close (g_fb_fd);
    return ret;
}