先安裝 libx11, libuvc, libusb 程式庫:
sudo apt install libx11-dev libuvc-dev libusb-1.0-0-dev
// 原始程式 xwin.cpp
#include "libuvc/libuvc.h"
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/cursorfont.h>
#include <signal.h>
class XwinClient {
private:
const char *title = "usbCamera";
int x, y, width, height;
Display *display= nullptr;
XImage *bg = nullptr;
Window winX;
GC gCtx;
Colormap cmap;
XColor fgColor;
public:
XColor greyColor, blackColor, whiteColor ,redColor, greenColor, blueColor, yellowColor;
~XwinClient(){
if (bg) XDestroyImage(bg);
if (display) {
XFreeColormap(display, cmap);
XFreeGC(display, gCtx);
XDestroyWindow(display, winX);
XCloseDisplay(display);
}
}
XwinClient(int w = 800, int h = 600) {
width = w;
height = h;
display = XOpenDisplay(getenv("DISPLAY"));
winX = XCreateSimpleWindow(display,
XDefaultRootWindow(display), 0, 0, w, h, 0,
WhitePixel(display, 0),
BlackPixel(display, 0)
);
gCtx = XCreateGC(display, winX, 0, 0);
cmap = DefaultColormap(display, 0);
auto setColorMap = [this] (XColor &c, const char *str){
XParseColor(display, cmap, str, &c);
XAllocColor(display, cmap, &c);
};
setColorMap(whiteColor , "#FFFFFF");
setColorMap(blackColor , "#000000");
setColorMap(redColor , "#FF0000");
setColorMap(greenColor , "#00FF00");
setColorMap(blueColor , "#0000FF");
setColorMap(greyColor , "#7f7f7f");
setColorMap(yellowColor, "#FFFF00");
fgColor = redColor;
XSetForeground(display, gCtx, fgColor.pixel);
XSetStandardProperties(display, winX, title,
"Icon", None, nullptr, 0, nullptr
);
XSelectInput(display, winX,
StructureNotifyMask |
ExposureMask |
ButtonPressMask |
KeyPressMask |
PointerMotionMask
);
XMapWindow(display, winX);
XEvent evt;
while (true) {
usleep(1000);
XEvent evt;
XNextEvent(display, &evt);
if(evt.type == Expose && evt.xexpose.count == 0) break;
}
XFlush(display);
XClearWindow(display, winX);
XSync(display, false);
}
void setImageBGR(void *rgb888, int sw, int sh, int sch) {
while (true) {
if (bg) XDestroyImage(bg);
bg = nullptr;
XWindowAttributes info;
XGetWindowAttributes(display, winX, &info);
Visual *v = info.visual;
width = info.width;
height = info.height;
int depth = info.depth;
int dline= 4 * width;
void *fb_bg = malloc(dline * height);
if (fb_bg == nullptr) break;
bg = XCreateImage(display, v, depth, ZPixmap, 0, (char *)fb_bg, width, height, 32, 0);
if (bg == nullptr) {
free(fb_bg);
break;
}
uint8_t *dst = (uint8_t *)fb_bg;
uint8_t *src = (uint8_t *)rgb888;
float xs = (float)sw / width ;
float ys = (float)sh / height;
int sline = sch * sw;
int pSize = sline * sh;
int zch = (sch > 4) ? 4 : sch;
int vk = 0;
float fy = 0;
for(int y = 0; y < height && vk < pSize; y ++) {
int ld = 0;
int ls = 0;
float fx = 0;
for (int x = 0; x < width && ls < sline; x ++) {
dst[ld] = src[ls];
for(int z = 1; z < zch; z ++) {
dst[ld + z] = src[ls + z];
}
ld += 4;
fx += xs;
ls = sch * (int)fx;
}
dst += dline;
fy += ys;
vk = sline * (int)fy;
src = (uint8_t *)rgb888 + vk;
}
XPutImage(display, winX, gCtx, bg, 0, 0, 0, 0, width, height);
break;
}
XSync(display, false);
}
};
XwinClient x11 = XwinClient(800, 600);
void screenUpdate(uvc_frame_t *frame, void *user) {
static bool active = false;
int length = 2 * frame->width * frame->height;
if (active || frame->frame_format != UVC_FRAME_FORMAT_YUYV || length != frame->data_bytes) return;
active = true;
auto int8bits = [](float v) { return (v < 0) ? 0 : (v > 255) ? 0xff : (uint8_t)v; };
uint8_t *yuyv = (uint8_t *)(frame->data);
uint8_t *dest = (uint8_t *)user;
for (int i = 0; i < length; i += 4) {
int y0 = *yuyv;
int u = yuyv[1];
int y1 = yuyv[2];
int v = yuyv[3];
dest[2] = int8bits(y0 + 1.13983 * (v - 128));// R -> b0
dest[1] = int8bits(y0 - 0.39465 * (u - 128) - 0.5806 * (v - 128));//G -> g0
dest[0] = int8bits(y0 + 2.03211 * (u - 128));// B -> r0
dest[5] = int8bits(y1 + 1.13983 * (v - 128));// R -> b1
dest[4] = int8bits(y1 - 0.39465 * (u - 128) - 0.5806 * (v - 128));//G -> g1
dest[3] = int8bits(y1 + 2.03211 * (u - 128));// B -> r1
yuyv += 4;
dest += 6;
}
x11.setImageBGR(user, frame->width , frame->height, 3);
active = false;
}
int main(int argc, char **argv) {
printf("press Ctrl+C to exit.\n");
signal(SIGINT, [](int sig) { exit(1); });
uvc_context_t *ctx;
printf("uvc init ...\n");
if (uvc_init(&ctx, NULL) == UVC_SUCCESS) {
uvc_device_t *dev;
printf("find device ...\n");
if (uvc_find_device(ctx, &dev, 0, 0, NULL) == UVC_SUCCESS) {
uvc_device_handle_t *handle;
printf("open device ...\n");
if (uvc_open(dev, &handle) == UVC_SUCCESS) {
const uvc_format_desc_t *descriptor = uvc_get_format_descs(handle);
const uvc_frame_desc_t *hFrame = descriptor->frame_descs;
int fps = 10000000 / hFrame->dwDefaultFrameInterval;
uvc_stream_ctrl_t controller;
uvc_frame_format fmt = UVC_FRAME_FORMAT_YUYV;
uvc_error_t result = uvc_get_stream_ctrl_format_size(handle, &controller, fmt, hFrame->wWidth, hFrame->wHeight, 10);
if (result == UVC_SUCCESS) {
uint8_t *image = new uint8_t[hFrame->wWidth * hFrame->wHeight * 3];
uvc_start_streaming(handle, &controller, screenUpdate, image, 0);
sleep(10);
uvc_stop_streaming(handle);
delete [] image;
}
uvc_close(handle);
}
uvc_unref_device(dev);
}
uvc_exit(ctx);
}
}
編譯並執行, 因為開啟 /dev/video 需要權限, 暫時用 sudo 來執行:
g++ xwin.cpp -lX11 -luvc -lusb-1.0 && sudo ./a.out
後記:使用原始 libuvc 及 libusb 程式庫編譯
1. 先到官網下載 libusb 及 libuvc 原始程式庫
https://github.com/libuvc/libuvc/tags
https://github.com/libusb/libusb/releases
2. 在專案根目錄內, 編輯一個檔案: CMakeLists.txt
3. 在專案根目錄內, 另建三個目錄: lib/ src/ build/
4. 將上述原始程式庫解壓縮, 放到專案 lib/ 目錄內
5. 在專案根目錄 src/ 目錄內, 再建一個目錄命名為 libuvc/, 只要放入 libuvc_config.h 檔案
6. 建一個空檔案命名為 libuvc_config.h 放到 src/libuvc/ 目錄內: src/libuvc/libuvc_config.h
7. 編輯主程式放到專案 src/ 目錄內: src/xwin.cpp
8. 切換到專案目錄下 build/ 執行 cmake .. 產生 Makefile
9. 編譯時, 在專案 build/ 目錄內執行 make 就會產生可執行檔 xwin, 暫時可用 root 權限或用 sudo ./xwin 來執行
# 檔案 CMakeLists.txt 內容
cmake_minimum_required(VERSION 3.16)
project(xwin)
set(topDir ${PROJECT_SOURCE_DIR})
set(uvcDir ${topDir}/lib/libuvc-0.0.7)
set(usbDir ${topDir}/lib/libusb-1.0.27)
set(uvcSource
${uvcDir}/src/device.c
${uvcDir}/src/init.c
${uvcDir}/src/stream.c
)
set(usbSource
${usbDir}/libusb/core.c
${usbDir}/libusb/descriptor.c
${usbDir}/libusb/hotplug.c
${usbDir}/libusb/io.c
${usbDir}/libusb/strerror.c
${usbDir}/libusb/sync.c
${usbDir}/libusb/os/events_posix.c
${usbDir}/libusb/os/threads_posix.c
${usbDir}/libusb/os/linux_usbfs.c
${usbDir}/libusb/os/linux_netlink.c
)
include_directories(
$(topDir)/src
${uvcDir}/include
${usbDir}/android
${usbDir}/libusb
${usbDir}/libusb/os
)
add_library(uvcdriver
${uvcSource}
${usbSource}
)
add_executable(xwin
${topDir}/src/xwin.cpp
)
target_link_libraries(xwin
X11
uvcdriver
)