最近在處理 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 就失效了的問題.
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; }
-
留言列表