close


最近在處理 embedded linux system 開機直屏/橫屏 logo, 想來想去, 最後決定在 user-space 處理 logo, 而非 kernel. 我在網路上 google 搜尋/測試了半天, 好像只有 fbv 比較好用. 但我最後卻選擇不用 fbv ... 等等 utilities, 自己寫一個 jepg2fb.c (Jpeg to Framebuffer) 程式. Why? 原因在於 fbv ... 等等 utilities 使用 memory-map IO 處理 framebuffer, 但是在 qt-app 使用 eglfs 開啟之後, framebuffer 的 memory-map IO 就失效了. 因此我只好自己寫一個 jepg2fb.c, 使用 read/write system call. 

 

使用 Jpeg Library: 

 

如何使用 jpeg library? 讀者可以參考 
https://android.googlesource.com/platform/external/jpeg/+/b937fcf1c310758b28d2d19bb0e10c5f881365f9/example.c

. 事實上我也是參考這個 example.c

 

I. 首先你當然必須要 include #include <jpeglib.h> . 編譯 Makefile 需加上 -ljpeg. 

 

II. 定義 my_error_exit(), 如下: 


struct my_error_mgr {
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void)
my_error_exit (j_common_ptr cinfo) {
    my_error_ptr myerr;
    myerr = (my_error_ptr) cinfo->err;
    (*cinfo->err->output_message) (cinfo);
    longjmp(myerr->setjmp_buffer, 1);
}



III.   初始化  struct jpeg_decompress_struct cinfo . 

 

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 1;
    }
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, infile);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_calc_output_dimensions(&cinfo);
    cinfo.two_pass_quantize = FALSE;
    cinfo.dither_mode = JDITHER_ORDERED;
    if (!cinfo.quantize_colors)        cinfo.desired_number_of_colors = 216;
    cinfo.dct_method = JDCT_FASTEST;
    cinfo.do_fancy_upsampling = FALSE;

 

IV. setjmp() / longjmp()

 

setjmp() 與 longjmp() 函數可以讓程式往回跳到函數呼叫堆疊中的某個函數中, 就像是一種跨函數的 goto.

參閱

https://blog.gtwang.org/programming/c-setjmp-longjmp-function-tutorial/

 

IV.  jpeg_start_decompress()


    jpeg_start_decompress(&cinfo);
    assert(3 == cinfo.output_components);
    row_stride = cinfo.output_width * cinfo.output_components;

 

V.  jpeg_read_scanlines()

    while (cinfo.output_scanline < cinfo.output_height) {
        unsigned int scanline = cinfo.output_scanline;

        jpeg_read_scanlines(&cinfo, &buffer, 1);
        p = (unsigned char *)buffer;
        for (i = 0; i < (int)cinfo.output_width; i++) {
            ...
        }
    }
 

VI. jpeg cleanup. 

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);


 

 

取得 Linux framebuffer FBIOGET_VSCREENINFO 資訊: 

 

    struct fb_var_screeninfo vinfo = {0};
    struct fb_fix_screeninfo finfo = {0};
 

    fb = open("/dev/fb0", O_RDWR);
    ioctl(fb, FBIOGET_FSCREENINFO, &finfo);
    ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);

    DEFAULT_OSD_WIDTH = vinfo.xres;
    DEFAULT_OSD_HEIGHT = vinfo.yres;
    bits_per_pixel = vinfo.bits_per_pixel;
    printf("(%s %d) bits_per_pixel=%d\n", __FILE__, __LINE__, bits_per_pixel);
    screensize = DEFAULT_OSD_WIDTH * DEFAULT_OSD_HEIGHT * vinfo.bits_per_pixel / 8;
    printf("(%s %d) screensize(%dx%d)=%ld bytes\n", __FILE__, __LINE__, DEFAULT_OSD_WIDTH, DEFAULT_OSD_HEIGHT, screensize);
 

 

Linux framebuffer read / write: 

 

對於 framebuffer 做 read / write 之前記得 lseek(). 


    lseek(fb, 0, SEEK_SET);
    read(fb, (void *)osd_addr, screensize);
    lseek(fb, 0, SEEK_SET);
    write(fb, (void *)osd_addr, screensize);

 

Output jpeg buffer: 


Output jpeg buffer 我寫在 screenJpeg() 這個函數. 內容請讀者自己看. 

int screenJpeg(int x, int y, const char *path);

 

參數 x, y 是座標.

其中 (x, y) = (-1, -1) 代表水平與垂直置中. 

 

範例: 

 

上述所有東東加起來就是筆者的範例程式了. 

 

jpeg2fb.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdint.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <assert.h>
#include <setjmp.h>

#include <pthread.h>
#include <jpeglib.h>
#include <setjmp.h>

#define ARGB(a, r, g, b)    ( (a << 24) | (r << 16) | (g << 8) | (b << 0) )
#define RGB565(r, g, b)        ((b >> 3) << 11)| ((g >> 2) << 5)| ((r >> 3) << 0)
#define MAKEFOURCC(ch0, ch1, ch2, ch3)    (\
    (unsigned int)(unsigned char)(ch0 << 0) |\
    (unsigned int)(unsigned char)(ch1 << 8) |\
    (unsigned int)(unsigned char)(ch2 << 16) |\
    (unsigned int)(unsigned char)(ch3 << 24) \
)

struct my_error_mgr {
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;

static int DEFAULT_OSD_WIDTH = 720;
static int DEFAULT_OSD_HEIGHT = 480;
static unsigned int *osd_addr = 0;

METHODDEF(void)
my_error_exit (j_common_ptr cinfo) {
    my_error_ptr myerr;
    myerr = (my_error_ptr) cinfo->err;
    (*cinfo->err->output_message) (cinfo);
    longjmp(myerr->setjmp_buffer, 1);
}
static char *FileName(const char *path) {
    int i, j = -1, len;
    char szTmp[1024] = {0};
    static char name[256] = {0};

    if(!path)                    return 0;
    strcpy(szTmp, path);
    len = strlen(szTmp);
    if(0 >= len || 255 <= len)    return 0;
    if(szTmp[len - 1] == '/') {
        szTmp[len - 1] = 0;
        len = strlen(szTmp);
    }
    for (i = 0; i < len; i++) {
        if(szTmp[i] == '/')
            j = i;
    }
    strcpy(name, &szTmp[j + 1]);
    return name;
}
static char *FileExtension(const char *path) {
    int i, j = -1, len;
    char szTmp[1024] = {0};
    static char ext[256] = {0};
    char *name;

    if(!path)        return 0;
    name = FileName(path);
    if(!name)        return 0;
    strcpy(szTmp, name);
    len = strlen(szTmp);
    if(0 > len || 255 <= len)
        return 0;
    for (i = 0; i < len; i++) {
        if(szTmp[i] == '.')
            j = i;
    }
    if (j == -1 || j == 0) {
        ext[0] = 0;
        return ext;
    }
    strcpy(ext, &szTmp[j + 1]);
    return ext;
}
static int screenJpeg(int x, int y, const char *path) {
    volatile unsigned int *oa = (volatile unsigned int *)osd_addr;
    FILE *infile;
    int len = 0;
    char ext[256];
    int filesize = 0;
    int i;
    int w, h;

    unsigned char r, g, b;
    unsigned char *p;
    unsigned char *buffer = 0;
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;
    int row_stride;
    struct stat lbuf;

    if(!path)                        return 1;
    if ((len = strlen(path)) < 5)    return 1;
    if(stat(path, &lbuf) < 0) {
        printf("(%s %d) stat fail, <%s>\n", __FILE__, __LINE__, path);
        return 1;
    }
    if(!S_ISREG(lbuf.st_mode)) {
        printf("(%s %d) not regular file, <%s>\n", __FILE__, __LINE__, path);
        return 1;
    }
    strcpy(ext, FileExtension(path));
    if(strcasecmp("jpg", ext) && strcasecmp("jpeg", ext)) {
        printf("(%s %d) file extension not jpg/jpeg\n", __FILE__, __LINE__);
        return 1;
    }
    infile = fopen(path, "rb");
    if(!infile) {
        printf("(%s %d) fopen fail, <%s>\n", __FILE__, __LINE__, path);
        return 1;
    }
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 1;
    }
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, infile);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_calc_output_dimensions(&cinfo);
    //cinfo.scale_num = 1;
    //cinfo.scale_denom = 1;
    cinfo.two_pass_quantize = FALSE;
    cinfo.dither_mode = JDITHER_ORDERED;
    if (!cinfo.quantize_colors)
        cinfo.desired_number_of_colors = 216;
    cinfo.dct_method = JDCT_FASTEST;
    cinfo.do_fancy_upsampling = FALSE;
    printf("(%s %d) jpeg-image dimension=%dx%d .\n", __FILE__, __LINE__, cinfo.image_width, cinfo.image_height);
    if (DEFAULT_OSD_WIDTH < cinfo.image_width || DEFAULT_OSD_HEIGHT < cinfo.image_height) {
        printf("(%s %d) jpeg-dimension is larger than screen-dimension.\n", __FILE__, __LINE__);
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 1;
    }
    if(-1 == x && -1 == y) {
        x = (DEFAULT_OSD_WIDTH - cinfo.image_width) / 2;
        y = (DEFAULT_OSD_HEIGHT - cinfo.image_height) / 2;
    }
    jpeg_start_decompress(&cinfo);
    assert(3 == cinfo.output_components);
    row_stride = cinfo.output_width * cinfo.output_components;
    buffer = (unsigned char *)malloc(row_stride);
    if (!buffer) {
        jpeg_finish_decompress(&cinfo);
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 1;
    }
    while (cinfo.output_scanline < cinfo.output_height) {
        unsigned int scanline = cinfo.output_scanline;

        jpeg_read_scanlines(&cinfo, &buffer, 1);
        p = (unsigned char *)buffer;
        for (i = 0; i < (int)cinfo.output_width; i++) {
            r = *p, p++;
            g = *p, p++;
            b = *p, p++;
            if(scanline + y < DEFAULT_OSD_HEIGHT && x + i < DEFAULT_OSD_WIDTH) {
                *(oa + (scanline + y) * DEFAULT_OSD_WIDTH + i + x) = ARGB(0, r, g, b);
            }
        }
    }
    free(buffer), buffer = 0;
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
}
int main(int argc, char*argv[]) {
    int fb = 0;
    int bits_per_pixel = 0;
    long int screensize = 0;
    char path[256] = {0};
    ssize_t len = 0;
    struct fb_var_screeninfo vinfo = {0};
    struct fb_fix_screeninfo finfo = {0};

    if(2 != argc) {
        printf("usage: jpeg2fb [JPEG-FILE]\n");
        return 1;
    }
    if(strncasecmp("jpg", FileExtension(argv[1]), 3) != 0 && strncasecmp("jpeg", FileExtension(argv[1]), 4) != 0) {
        printf("(%s %d) File extension not jpg/jpeg. \n", __FILE__, __LINE__);
        return 2;
    }
    strcpy(path, argv[1]);

    fb = open("/dev/fb0", O_RDWR);
    if (!fb)
        printf("(%s %d) open() fail\n", __FILE__, __LINE__), exit(1);  
    if (ioctl(fb, FBIOGET_FSCREENINFO, &finfo)) {
        printf("(%s %d) FBIOGET_FSCREENINFO fail. \n", __FILE__, __LINE__);
        close(fb), fb = -1;
        return -1;
    }
    if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("(%s %d) FBIOGET_VSCREENINFO fail. \n", __FILE__, __LINE__);
        close(fb), fb = -1;
        return -1;
    }
    DEFAULT_OSD_WIDTH = vinfo.xres;
    DEFAULT_OSD_HEIGHT = vinfo.yres;
    bits_per_pixel = vinfo.bits_per_pixel;
    printf("(%s %d) bits_per_pixel=%d\n", __FILE__, __LINE__, bits_per_pixel);
    screensize = DEFAULT_OSD_WIDTH * DEFAULT_OSD_HEIGHT * vinfo.bits_per_pixel / 8;
    printf("(%s %d) screensize(%dx%d)=%ld bytes\n", __FILE__, __LINE__, DEFAULT_OSD_WIDTH, DEFAULT_OSD_HEIGHT, screensize);
    osd_addr = (unsigned int *)malloc(screensize);
    if (!osd_addr) {
        close(fb), fb = -1;
        return -1;
    }
    lseek(fb, 0, SEEK_SET);
    len = read(fb, (void *)osd_addr, screensize);
    if (-1 == len) {
        printf("(%s %d) read() fail\n", __FILE__, __LINE__);
        free(osd_addr), osd_addr = 0;
        close(fb), fb = -1;
        return -1;
    }
    if (0 != screenJpeg(-1, -1, path)) {
        printf("(%s %d) screenJpeg() fail\n", __FILE__, __LINE__);
        free(osd_addr), osd_addr = 0;
        close(fb), fb = -1;
        return -1;
    }
    //sleep(1);
    lseek(fb, 0, SEEK_SET);
    len = write(fb, (void *)osd_addr, screensize);
    if (-1 == len) {
        printf("(%s %d) write() fail\n", __FILE__, __LINE__);
        free(osd_addr), osd_addr = 0;
        close(fb), fb = -1;
        return -1;
    }
    //vinfo.yoffset = 1, ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo);
    //vinfo.yoffset = 0, ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo);
    free(osd_addr), osd_addr = 0;
    close(fb), fb = -1;
    printf("(%s %d) Decompress jpeg to framebuffer success. \n", __FILE__, __LINE__);
    return 0;
}

 

不錯, 這支程式測試起來效能還不錯. 又沒有 qt-app 在使用 eglfs 開啟之後, framebuffer 的 memory-map IO 就失效了的問題. 

 

 

 

 

 

 

 

z.png
Email: jasonc@mail2000.com.tw . 請尊重原創, 使用圖文時載明出處. 謝謝. 

 

(Finished)


 

 

 

 

* FF_INPUT_BUFFER_PADDING_SIZE renamed as AV_INPUT_BUFFER_PADDING_SIZE: 
  https://github.com/twitter/vireo/issues/11

 

* Bluealsa HFP test:
  https://github.com/nokia/rcm-bluez/blob/master/client/bluez-5.43/unit/test-hfp.c

 

* bluetooth LE HOGP profile的代碼大致實現流程
  https://read01.com/5QzNGd.html

 

* bluez SCO-tester
  https://github.com/nokia/rcm-bluez/blob/master/client/bluez-5.43/tools/sco-tester.c

 

* linux keyboard mouse event simulation
  https://www.itread01.com/p/1383928.html

 

* Linux's uinput usage tutorial – Virtual Gamepad
   https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

 

* Virtual Mouse Linux Kernel Driver
   http://shyuanliang.blogspot.com/2012/09/virtual-mouse-linux-kernel-driver.html

 

* Uinput mouse movements
  https://www.kernel.org/doc/html/v4.12/input/uinput.html

 

* Exploring /dev/input
  https://thehackerdiary.wordpress.com/2017/04/21/exploring-devinput-1/ 
  https://gist.github.com/uobikiemukot/457338b890e96babf60b

 

* Bluetooth BLE client 
  https://github.com/jjjsmit/BluetoothBLEClient

 

* How to install X11 on my own Linux Buildroot system? 
  https://unix.stackexchange.com/questions/70931/how-to-install-x11-on-my-own-linux-buildroot-system

 

* Archlinux Kodi 
  https://wiki.archlinux.org/index.php/Kodi 
  https://github.com/taxigps/xbmc/blob/master/docs/README.Linux.md 
  http://patchwork.ozlabs.org/project/buildroot/patch/20200202173333.305860-2-bernd.kuhls@t-online.de/ 
  https://github.com/xbmc

 

* Kodi-addon 
  https://github.com/topics/kodi-addon 
   https://github.com/xbmc
  https://github.com/XBMC-Addons?page=1 
  https://github.com/xbmc/xbmc/blob/Jarvis/system/keymaps/joystick.xml.sample 
  https://gist.github.com/grimpy/47555717914e65198df806177eafdafc
  https://kodi.wiki/view/Keymap#Customizing_keymaps_through_the_GUI

 

* Alsa dsnloop, multiple capture
  https://sourceforge.net/p/alsa/mailman/message/23634390/ 
  https://gist.githubusercontent.com/albanpeignier/104902/raw/913ab5ba41b04735f84fb41072e21060778f1679/alsa-record-example.c 
  https://gist.github.com/ghedo/963382/98f730d61dad5b6fdf0c4edb7a257c5f9700d83b
   https://gist.github.com/ghedo/963382

 

* btusb.c 
  https://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-4.8/tree/drivers/bluetooth/btusb.c

 

* Ffmpeg muxing 
  https://ffmpeg.org/doxygen/3.3/muxing_8c-example.html#a68

 

* PCM audio manipulation - volume control and mixing  
  https://www.microchip.com/forums/m932509.aspx


* Wav RIFF header
  http://www.topherlee.com/software/pcm-tut-wavformat.html

 

* Buildroot 架構分析 
  https://hugh712.gitbooks.io/buildroot/content/buildroot.html

 

* Linux 程序通訊之: 記憶體共享(Shared Memory) 
  https://www.itread01.com/content/1542579799.html

 

* libavformat/output-example.c 
  https://ffmpeg.org/doxygen/0.6/output-example_8c-source.html

 

* Pixelcade API 
  https://pixelcade.org/api/
   https://pixelcade.org/coinops/

 

* USB Descriptor and Request Parser
  https://eleccelerator.com/usbdescreqparser/

 

* ALSA PCM naming 
  https://www.alsa-project.org/wiki/Asoundrc

 

* Gadget Hid 
  https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt

 

* 从蓝牙PCM发送双向信号到环回以在linphone中使用它 
  https://xbuba.com/questions/54785307

 

* Bluetooth Class of Device-Service Generator
  http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html

 

* Bluetooth agent test 
  https://android.googlesource.com/platform/external/bluetooth/bluez/+/froyo-release/test/agent.c
  http://ops9.blogspot.com/2013/10/bluez.html

 

* Emulate Bluetooth HID (Keyboard, Gamepad, ...) From linux BlueZ
  https://github.com/007durgesh219/BTGamepad

 

* How to develop opengl es gles 2.0 applications on linux
  https://stackoverflow.com/questions/3809236/how-to-develop-opengl-es-gles-2-0-applications-on-linux

 

* drmtoy
  https://github.com/w23/drmtoy

 

* drm_clone.c 
  https://git.ti.com/cgit/glsdk/example-applications/tree/drm-tests/drm_clone.c

 

* [C++] PPM影像檔輸出 (PPM image format output) 
  https://cg2010studio.com/2011/05/20/ppm-image-format-output/
  https://www.cs.swarthmore.edu/~soni/cs35/f13/Labs/extras/01/ppm_info.html
  https://stackoverflow.com/questions/3781549/how-to-convert-16-bit-rgb-frame-buffer-to-a-viewable-format

 

* c++ - 寫入內存緩衝區,而不是使用libjpeg文件? 
  https://hant-kb.kutu66.com/c++/post_1343599

 

* kmsgrab.c 
  https://www.ffmpeg.org/doxygen/4.0/kmsgrab_8c_source.html

 

* drm_clone.c 
  https://git.ti.com/cgit/glsdk/example-applications/tree/drm-tests/drm_clone.c

 

* Tutorial about USB HID Report Descriptors 
  https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/
  https://people.csail.mit.edu/albert/bluez-intro/index.html

 

* GStreamer 開發經驗
  https://thebigdoc.readthedocs.io/en/latest/gstreamer/index.html

 

* Multimedia User Guide(Rockchip Linux) 
  http://blog.iotwrt.com/media/2017/05/04/VIDEO/

 

* libpng example.c 
  https://fossies.org/linux/libpng/example.c

 

* gstreamer demo 
  http://trac.gateworks.com/wiki/buildroot/gstreamer

 

* gstreamer rotate 
  https://gstreamer.freedesktop.org/documentation/geometrictransform/rotate.html?gi-language=c

 

* Qt Tcp 
  https://blog.csdn.net/demowolf/article/details/5598879

 

 

drm2png.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdint.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <signal.h>
#include <assert.h>
#include <regex.h>

#ifdef __cplusplus
extern  "C" {
#endif // __cplusplus

#include <drm/drm.h>
#include <xf86drmMode.h>
#include <xf86drm.h>
#include <libdrm/drm_fourcc.h>
#include <png.h>
#include <zlib.h>

#ifdef __cplusplus
}
#endif // __cplusplus

#include <pthread.h>
#include <setjmp.h>

#define DRI_DRM_DEVICE    "/dev/dri/card0"
#define MAX_FBS 16

#define MODESET_PREPARE(fd)    \
    do { if (0 != modeset_prepare(fd))    printf("(%s %d) modeset_prepare() fail\n", __FILE__, __LINE__), close(fd), fd = -1, exit(1); } while(0)

struct modeset_dev {
    struct modeset_dev *next;
    uint32_t width;
    uint32_t height;
    uint32_t stride;
    uint32_t size;
    uint32_t handle;
    uint8_t *map;
    struct _drmModeModeInfo mode;
    uint32_t fb;
    uint32_t conn;
    uint32_t crtc;
    struct _drmModeCrtc *saved_crtc;
    int dma_buf_fd;
};
static struct modeset_dev *modeset_list = NULL;

static char *stripCrlf(char *line) {
    int len = 0;

    if(line == 0)        return 0;
    len = strlen(line);
    if(len > 1000)        return line;
    if(len <= 0)        return line;
    if (line[len - 1] == '\n' || line[len - 1] == '\r')
        line[len - 1] = 0;
    len = strlen(line);
    if (len > 0)
        if (line[len - 1] == '\n' || line[len - 1] == '\r')
            line[len - 1] = 0;
    return line;
}
static char *FileName(const char *path) {
    int i, j = -1, len;
    char szTmp[1024] = {0};
    static char name[256] = {0};

    if(!path)                        return 0;
    strcpy(szTmp, path);
    len = strlen(szTmp);
    if(0 >= len || 255 <= len)        return 0;
    if(szTmp[len - 1] == '/') {
        szTmp[len - 1] = 0;
        len = strlen(szTmp);
    }
    for (i = 0; i < len; i++) {
        if(szTmp[i] == '/')
            j = i;
    }
    strcpy(name, &szTmp[j + 1]);
    return name;
}
static char *FileExtension(const char *path) {
    int i, j = -1, len;
    char szTmp[1024] = {0};
    static char ext[256] = {0};
    char *name;

    if(!path)        return 0;
    name = FileName(path);
    if(!name)        return 0;
    strcpy(szTmp, name);
    len = strlen(szTmp);
    if(0 > len || 255 <= len)
        return 0;
    for (i = 0; i < len; i++) {
        if(szTmp[i] == '.')
            j = i;
    }
    if (-1 == j || 0 == j) {
        ext[0] = 0;
        return ext;
    }
    strcpy(ext, &szTmp[j + 1]);
    return ext;
}
static int modeset_find_crtc(int fd, drmModeRes *res, struct _drmModeConnector *conn, struct modeset_dev *dev) {
    struct _drmModeEncoder *enc = 0;
    unsigned int i, j;
    int32_t crtc;
    struct modeset_dev *iter;

    if (0 == res)    return -ENOENT;
    if (0 == conn)    return -ENOENT;
    if (0 == dev)    return -ENOENT;
    // first try the currently conected encoder+crtc 
    if (conn->encoder_id)
        enc = drmModeGetEncoder(fd, conn->encoder_id);
    if (enc) {
        if (enc->crtc_id) {
            crtc = enc->crtc_id;
            for (iter = modeset_list; iter; iter = iter->next) {
                if (iter->crtc == crtc) {
                    crtc = -1;
                    break;
                }
            }
            if (crtc >= 0) {
                drmModeFreeEncoder(enc);
                dev->crtc = crtc;
                return 0;
            }
        }
        drmModeFreeEncoder(enc);
    }
    // If the connector is not currently bound to an encoder or if the
    // encoder+crtc is already used by another connector (actually unlikely
    // but lets be safe), iterate all other available encoders to find a matching CRTC. 
    for (i = 0; i < conn->count_encoders; ++i) {
        enc = drmModeGetEncoder(fd, conn->encoders[i]);
        if (!enc) {
            printf("(%s %d) cannot retrieve encoder %u:%u (%d): %m\n", __FILE__, __LINE__, i, conn->encoders[i], errno);
            continue;
        }
        // iterate all global CRTCs
        for (j = 0; j < res->count_crtcs; ++j) {
            // check whether this CRTC works with the encoder
            if (!(enc->possible_crtcs & (1 << j)))
                continue;
            // check that no other device already uses this CRTC
            crtc = res->crtcs[j];
            for (iter = modeset_list; iter; iter = iter->next) {
                if (iter->crtc == crtc) {
                    crtc = -1;
                    break;
                }
            }
            // we have found a CRTC, so save it and return
            if (crtc >= 0) {
                drmModeFreeEncoder(enc);
                dev->crtc = crtc;
                return 0;
            }
        }
        drmModeFreeEncoder(enc);
    }
    printf("(%s %d) cannot find suitable CRTC for connector %u\n", __FILE__, __LINE__, conn->connector_id);
    return -ENOENT;
}
static int modeset_setup_dev(int fd, drmModeRes *res, struct _drmModeConnector *conn, struct modeset_dev *dev) {
    int ret, found = 0, count_fbs = 0, dma_buf_fd, I, len, i, j;
    drmModeFBPtr fb;
    drmModePlaneResPtr planes;
    drmModePlanePtr plane;
    uint32_t fbs[MAX_FBS] = {0};
    regex_t Regx;
    char Pattern[128] = {0}, line[128] = {0}, num[64] = {0};
    regmatch_t Match[64];
    FILE *f = 0;
    struct stat lbuf;
    struct _drmModeModeInfo *mode = 0;
    uint16_t w = 0, h = 0;

    if (0 == res)        return -ENOENT;
    if (0 == conn)        return -ENOENT;
    if (0 == dev)        return -ENOENT;
    // check if a monitor is connected
    if (DRM_MODE_CONNECTED != conn->connection) {
        printf("(%s %d) ignoring unused connector %u\n", __FILE__, __LINE__, conn->connector_id);
        return -ENOENT;
    }
    // check if there is at least one valid mode
    if (0 == conn->count_modes) {
        printf("(%s %d) no valid mode for connector %u\n", __FILE__, __LINE__, conn->connector_id);
        return -EFAULT;
    }
    strcpy(Pattern, "^([0-9]{1,})x([0-9]{1,})p([0-9]{1,})$"), regcomp(&Regx, Pattern, REG_EXTENDED);
    f = fopen("/sys/class/drm/card0-HDMI-A-1/mode", "r");
    if (0 != f) {
        fgets(line, sizeof(line), f);
        stripCrlf(line);
        if (0 == regexec(&Regx, line, sizeof(Match) / sizeof(regmatch_t), Match, 0)) {
            I = 1, len = Match[I].rm_eo - Match[I].rm_so, memcpy(num, line + Match[I].rm_so, len), num[len] = 0;
            w = atoi(num);
            I = 2, len = Match[I].rm_eo - Match[I].rm_so, memcpy(num, line + Match[I].rm_so, len), num[len] = 0;
            h = atoi(num);
        }
        fclose(f), f = 0;
    }
    regfree(&Regx);
    mode = &conn->modes[0];
    if (0 == w && 0 == h)
        printf("(%s %d) 0 == w && 0 == h\n", __FILE__, __LINE__);
    else {
        for (i = 0; i < conn->count_modes; i++)
            if (w == conn->modes[i].hdisplay && h == conn->modes[i].vdisplay) {
                mode = &conn->modes[i];
                break;
            }
    }
    memcpy(&dev->mode, mode, sizeof(dev->mode));
    dev->width = mode->hdisplay;
    dev->height = mode->vdisplay;
    printf("(%s %d) mode for connector %u is %ux%u\n", __FILE__, __LINE__, conn->connector_id, dev->width, dev->height);
    // find a crtc for this connector
    ret = modeset_find_crtc(fd, res, conn, dev);
    if (0 != ret) {
        printf("(%s %d) no valid crtc for connector %u\n", __FILE__, __LINE__, conn->connector_id);
        return ret;
    }
    planes = drmModeGetPlaneResources(fd);
    if (planes) {
        for (i = 0; i < planes->count_planes; i++) {
            plane = drmModeGetPlane(fd, planes->planes[i]);
            if (0 == plane)
                continue;
            if (plane->fb_id) {
                found = 0;
                for (j = 0; j < count_fbs; j++) {
                    if (fbs[j] == plane->fb_id) {
                        found = 1;
                        break;
                    }
                }
                if (0 == found) {
                    if (count_fbs == MAX_FBS) {
                    } else
                        fbs[count_fbs++] = plane->fb_id;
                }
            }
            drmModeFreePlane(plane);
        }
        drmModeFreePlaneResources(planes);
    }
    dev->fb = fbs[0];    // plane->fb_id
    fb = drmModeGetFB(fd, fbs[0]);
    if (0 == fb) {
        printf("(%s %d) drmModeGetFB() == 0\n", __FILE__, __LINE__);
        return -EFAULT;
    }
    dev->height = fb->height, dev->stride = fb->pitch, dev->size = fb->height * fb->pitch, dev->handle = fb->handle;
    drmPrimeHandleToFD(fd, fb->handle, O_RDONLY, &dev->dma_buf_fd);
    dev->size = fb->height * fb->pitch;
    dev->map = mmap(0, dev->size, PROT_READ, MAP_SHARED, dev->dma_buf_fd, 0);
    if (MAP_FAILED == dev->map) {
        printf("(%s %d) MAP_FAILED\n", __FILE__, __LINE__);
        drmModeFreeFB(fb), fb = 0;
        return -EFAULT;
    }
    drmModeFreeFB(fb);
    return 0;
}
static int modeset_prepare(int fd) {
    struct _drmModeRes *res;
    struct _drmModeConnector *conn;
    struct modeset_dev *dev;
    int i, ret;

    // retrieve resources
    res = drmModeGetResources(fd);
    if (0 == res) {
        printf("(%s %d) cannot retrieve DRM resources (%d): %m\n", __FILE__, __LINE__, errno);
        return -errno;
    }
    // iterate all connectors
    for (i = 0; i < res->count_connectors; ++i) {
        // get information for each connector
        conn = drmModeGetConnector(fd, res->connectors[i]);
        if (!conn) {
            printf("(%s %d) cannot retrieve DRM connector %u:%u (%d): %m\n", __FILE__, __LINE__, i, res->connectors[i], errno);
            continue;
        }
        // create a device structure
        dev = malloc(sizeof(*dev));
        memset(dev, 0, sizeof(*dev));
        dev->conn = conn->connector_id;
        // call helper function to prepare this connector
        ret = modeset_setup_dev(fd, res, conn, dev);
        if (ret) {
            if (ret != -ENOENT) {
                errno = -ret;
                printf("(%s %d) cannot setup device for connector %u:%u (%d): %m\n", __FILE__, __LINE__, i, res->connectors[i], errno);
            }
            free(dev);
            drmModeFreeConnector(conn);
            continue;
        }
        // free connector data and link device into global list
        drmModeFreeConnector(conn);
        dev->next = modeset_list;
        modeset_list = dev;
    }
    // free resources again
    drmModeFreeResources(res);
    return 0;
}
static void modeset_cleanup(int fd) {
    struct modeset_dev *iter;
    struct drm_mode_destroy_dumb dreq;

    while (modeset_list) {
        // remove from global list
        iter = modeset_list;
        modeset_list = iter->next;
        // restore saved CRTC configuration
        //drmModeSetCrtc(fd, iter->saved_crtc->crtc_id,iter->saved_crtc->buffer_id, 
        //    iter->saved_crtc->x, iter->saved_crtc->y, &iter->conn, 1, &iter->saved_crtc->mode);
        drmModeFreeCrtc(iter->saved_crtc);
        // unmap buffer
        munmap(iter->map, iter->size), free(iter);
    }
}
static int BGRA2RGBA(png_bytep buffer, int length) {
    uint32_t *p;
    uint8_t swp[4] = {0}, tmp = 0;
    int i = 0;

    if (0 == length)                    return -1;
    if (0 != (length % 4))                return -1;
    if (0 == (p = (uint32_t *)buffer))    return -1;
    for (; i < length / 4; i++) {
        memcpy(swp, p, 4);
        tmp = swp[0], swp[0] = swp[2], swp[2] = tmp;
        memcpy(p, swp, 4);
        p++;
    }
    return 0;
}
int main(int argc, char*argv[]) {
    char path[256] = {0};
    const char *card = DRI_DRM_DEVICE;
    int fd, i, N = 0, row_stride, y, color_type = PNG_COLOR_TYPE_RGB;
    struct _drmStats stats;
    struct modeset_dev *iter, *front;
    FILE *outfile = 0;
    png_bytep png_buffer = 0, png_bufferp = 0;
    png_structp png_ptr;
    png_infop info_ptr;

    if(2 != argc)                    printf("(%s %d) usage: drm2png [PNG-FILE]\n", __FILE__, __LINE__), exit(1);
    if(0 != strncasecmp("png", FileExtension(argv[1]), 3))
        printf("(%s %d) File extension not png. \n", __FILE__, __LINE__), exit(1);
    strcpy(path, argv[1]);
    if (!drmAvailable())            printf("(%s %d) DRM driver is currently not loaded\n", __FILE__, __LINE__), exit(1);
    assert(-1 != (fd = open(card, O_RDWR | O_CLOEXEC)));
    drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1), drmGetStats(fd, &stats);
    // prepare all connectors and CRTCs
    MODESET_PREPARE(fd);
    // perform actual modesetting on each found connector+CRTC
    for (iter = modeset_list; iter; iter = iter->next)        iter->saved_crtc = drmModeGetCrtc(fd, iter->crtc);
    N = 0;
    for (iter = modeset_list; iter; iter = iter->next)        N++;
    if (0 == N)                        printf("(%s %d) 0 == N\n", __FILE__, __LINE__), close(fd), fd = -1, exit(1);
    front = modeset_list;
    assert(0 != (png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)));
    assert(0 != (info_ptr = png_create_info_struct(png_ptr)));
    assert(0 == setjmp(png_jmpbuf(png_ptr)));
    assert(0 != (outfile = fopen(path, "wb")));
    png_init_io(png_ptr, outfile);
    if (4 == (front->stride / front->width))    color_type = PNG_COLOR_TYPE_RGBA;
    png_set_IHDR(png_ptr, info_ptr, front->width, front->height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    png_write_info(png_ptr, info_ptr);
    png_buffer = (png_bytep)malloc(front->size); pthread_cleanup_push((void (*)(void *))free, (void *)png_buffer); memcpy(png_buffer, front->map, front->size);
    if (4 == (front->stride / front->width))
        BGRA2RGBA(png_buffer, front->size);
    png_bufferp = png_buffer, row_stride = front->stride;
    for (y = 0; y < front->height; y++)
        png_write_row(png_ptr, png_bufferp), png_bufferp += row_stride;
    pthread_cleanup_pop(1);
    png_write_end(png_ptr, NULL), png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(outfile);
    modeset_cleanup(fd), close(fd), fd = -1;
    printf("(%s %d) '%s' successfully saved. \n", __FILE__, __LINE__, path);
    return 0;
}

 

 

 

 

 

 

 

 

 
arrow
arrow

    Lexra 發表在 痞客邦 留言(0) 人氣()