libuvc get still image
libuvc get still image
1.环境:
rck@ubuntu:~/code/libuvc$ uname -a
Linux ubuntu 5.15.0-101-generic #111~20.04.1-Ubuntu SMP Mon Mar 11 15:44:43 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
rck@ubuntu:~/code/libuvc$
2.使用方法2获取still image,libuvc暂时只支持到方法2获取still image

UVC 负载数据头 Payload Header Information


判断bmHeaderInfo的BIT1与BIT5 来抓取一帧still image。
3.安装libuvc的依赖libusb
#通过包管理器安装
sudo apt-get install libusb-1.0-0-dev
#或者通过源码安装
git clone https://github.com/libusb/libusb.git
cd libusb
./bootstrap.sh
./configure --disable-udev
make
make install
4.libuvc 下载与编译
#通过包管理器安装
sudo apt-get install libuvc-dev
#或者通过源码安装
git clone https://github.com/libuvc/libuvc.git
cd libuvc
mkdir -p build/
cd build
cmake ..
make
make install
5.测试验证
5.1测试源码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libuvc/libuvc.h>
// 处理视频帧数据的回调函数
void frame_callback(uvc_frame_t *frame, void *user_data) {
// 在这里可以对视频帧进行进一步处理或展示
// 这里只简单打印帧的大小和时间戳
//printf("%d Frame Size: %lu bytes,%dx%d\n", frame->sequence,frame->data_bytes,frame->width,frame->height);
// 处理帧数据,这里可以保存为图片文件
if(*(int*)(user_data) ){
char file_name[64] = {0};
sprintf(file_name,"/tmp/%d-%lu.jpg",frame->sequence,frame->data_bytes);
FILE *fp = fopen(file_name, "wb");
if (fp) {
fwrite(frame->data, 1, frame->data_bytes, fp);
fclose(fp);
}
*(int*)(user_data) = 0;
}
}
int main() {
uvc_context_t *ctx;
uvc_device_t *dev;
uvc_device_handle_t *devh;
uvc_stream_ctrl_t ctrl;
uvc_still_ctrl_t still_ctrl;
int transfer_id;
printf("------Compile time is %s--------------\n",__TIME__);
// 初始化 UVC 上下文
uvc_error_t res = uvc_init(&ctx, NULL);
if (res < 0) {
fprintf(stderr, "Failed to initialize UVC: %s\n", uvc_strerror(res));
return res;
}
// 查找并打开第一个 UVC 设备
res = uvc_find_device(ctx, &dev, 0, 0, NULL);
if (res < 0) {
fprintf(stderr, "Failed to find UVC device: %s\n", uvc_strerror(res));
uvc_exit(ctx);
return res;
}
printf("[%s:%d]----------------here----------\r\n",__func__,__LINE__);
// 打开设备
res = uvc_open(dev, &devh);
if (res < 0) {
fprintf(stderr, "Failed to open UVC device: %s\n", uvc_strerror(res));
uvc_unref_device(dev);
uvc_exit(ctx);
return res;
}
printf("[%s:%d]----------------here----------\r\n",__func__,__LINE__);
// uvc_print_diag(devh, stderr);
printf("[%s:%d]------------11----here----------\r\n",__func__,__LINE__);
// 获取默认的视频流控制参数
res = uvc_get_stream_ctrl_format_size(devh, &ctrl, UVC_FRAME_FORMAT_MJPEG, 1280, 720, 30);
// res = uvc_get_stream_ctrl_format_size(devh, &ctrl, UVC_FRAME_FORMAT_MJPEG, 1920, 1080, 30);
if (res < 0) {
fprintf(stderr, "Failed to get stream control: %s\n", uvc_strerror(res));
uvc_close(devh);
uvc_unref_device(dev);
uvc_exit(ctx);
return res;
}
// res = uvc_get_still_ctrl_format_size(devh,&ctrl,&still_ctrl,3264,2448);
// res = uvc_get_still_ctrl_format_size(devh,&ctrl,&still_ctrl,640,480);
// res = uvc_get_still_ctrl_format_size(devh,&ctrl,&still_ctrl,2048,1536);
// res = uvc_get_still_ctrl_format_size(devh,&ctrl,&still_ctrl,2592,1944);
res = uvc_get_still_ctrl_format_size(devh,&ctrl,&still_ctrl,2560,1920);
if (res < 0) {
fprintf(stderr, "Failed to get still control: %s\n", uvc_strerror(res));
uvc_close(devh);
uvc_unref_device(dev);
uvc_exit(ctx);
return res;
}
printf("bFormatIndex = %d\n bFrameIndex=%d\n bCompressionIndex=%d\n dwMaxVideoFrameSize=%d\n dwMaxPayloadTransferSize=%d\n bInterfaceNumber=%d\n",\
still_ctrl.bFormatIndex,\
still_ctrl.bFrameIndex,\
still_ctrl.bCompressionIndex,\
still_ctrl.dwMaxVideoFrameSize,\
still_ctrl.dwMaxPayloadTransferSize,\
still_ctrl.bInterfaceNumber);
// printf("[%s:%d]----------------here----------\r\n",__func__,__LINE__);
// 启动视频流
static int myctl = 0;
res = uvc_start_streaming(devh, &ctrl, frame_callback, (void*)&myctl, 0);
if (res < 0) {
fprintf(stderr, "Failed to start streaming: %s\n", uvc_strerror(res));
uvc_close(devh);
uvc_unref_device(dev);
uvc_exit(ctx);
return res;
}
sleep(1);
printf("[%s:%d]-----------uvc_trigger_still----------\r\n",__func__,__LINE__);
res = uvc_trigger_still(devh,&still_ctrl);
if (res < 0) {
fprintf(stderr, "Failed to trigger still control: %s\n", uvc_strerror(res));
uvc_close(devh);
uvc_unref_device(dev);
uvc_exit(ctx);
return res;
}
//让应用程序继续运行一段时间
sleep(2);
// 停止视频流
uvc_stop_streaming(devh);
// 关闭设备和 UVC 上下文
uvc_close(devh);
uvc_unref_device(dev);
uvc_exit(ctx);
return 0;
}
5.2修改libuvc/src/stream.c 用于still image 测试与保存
如下patch有047920bcd-stream-c.patch
diff --git a/src/stream.c b/src/stream.c
index 89dac69..e2be00b 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -686,6 +686,9 @@ void _uvc_swap_buffers(uvc_stream_handle_t *strmh) {
strmh->pts = 0;
}
+
+static int still_print = 0;
+
/** @internal
* @brief Process a payload transfer
*
@@ -747,6 +750,32 @@ void _uvc_process_payload(uvc_stream_handle_t *strmh, uint8_t *payload, size_t p
header_info = payload[1];
+ //如果是still image data 通知线程把数据存下来
+ if (header_info & 0x20 ) {
+ //打印still image 前16字节,可以在12与13字节看到,mjpeg 的标志0xff 0xd8
+ if( !still_print){
+ still_print = 1;
+ printf("still image first 16 bytes:");
+ for(int i =0;i<16;i++){
+ printf("0x%02x ",payload[i]);
+ }
+ printf("\n");
+ }
+
+ //打印still image 最后16字节,可以在最后字节看到,mjpeg 的标志0xff 0xd9
+ if(header_info & (1 << 1) ){
+ printf("still image last 16 bytes:");
+ for(int i =0;i<16;i++){
+ printf("0x%02x ",payload[payload_len -16 + i]);
+ }
+ printf("\n");
+ still_print = 0;
+ //置位保存still image
+ *(int*)(strmh->user_ptr) = 1;
+ }
+
+ }
+
if (header_info & 0x40) {
UVC_DEBUG("bad packet: error bit set");
return;
@@ -785,6 +814,7 @@ void _uvc_process_payload(uvc_stream_handle_t *strmh, uint8_t *payload, size_t p
if (data_len > 0) {
if (strmh->got_bytes + data_len > strmh->cur_ctrl.dwMaxVideoFrameSize)
data_len = strmh->cur_ctrl.dwMaxVideoFrameSize - strmh->got_bytes; /* Avoid overflow. */
+
memcpy(strmh->outbuf + strmh->got_bytes, payload + header_len, data_len);
strmh->got_bytes += data_len;
if (header_info & (1 << 1) || strmh->got_bytes == strmh->cur_ctrl.dwMaxVideoFrameSize) {
@@ -1105,7 +1135,6 @@ uvc_error_t uvc_stream_start(
strmh->fid = 0;
strmh->pts = 0;
strmh->last_scr = 0;
-
frame_desc = uvc_find_frame_desc_stream(strmh, ctrl->bFormatIndex, ctrl->bFrameIndex);
if (!frame_desc) {
ret = UVC_ERROR_INVALID_PARAM;
@@ -1232,7 +1261,6 @@ uvc_error_t uvc_stream_start(
( void* ) strmh, 5000 );
}
}
-
strmh->user_cb = cb;
strmh->user_ptr = user_ptr;
5.3 打上pacth 重新编译
cd build/
rm -rf ./*
cmake ..
make && sudo make install
gcc ../libuvc-still-test.c -o libuvc-still-test -luvc -L. -I./include
测试使用King Jim desk shot DK800 的USB Camera,still image 参数有:
#使用UsbTreeView.exe 获取的still image信息
#USBTreeView - USB中文网 - https://www.usbzh.com/article/detail-749.html
---------- Still Image Frame Type Descriptor ----------
bLength : 0x16 (22 bytes)
bDescriptorType : 0x24 (Video Streaming Interface)
bDescriptorSubtype : 0x03 (Still Image Frame Type)
bEndpointAddress : 0x00 (no endpoint)
bNumImageSizePatterns : 0x04 (4 Image Size Patterns)
1: wWidth x wHeight : 0x0CC0 x 0x0990 (3264 x 2448)
2: wWidth x wHeight : 0x0280 x 0x01E0 (640 x 480)
3: wWidth x wHeight : 0x0800 x 0x0600 (2048 x 1536)
4: wWidth x wHeight : 0x0A00 x 0x0780 (2560 x 1920)
bNumCompressionPattern : 0x00
Data (HexDump) : 16 24 03 00 04 C0 0C 90 09 80 02 E0 01 00 08 00 .$..............
06 00 0A 80 07 00
5.4 测试在/tmp/ 生成mjpeg文件
sudo ./libuvc-still-test

//TODO:还无法抓取3264 x 2448分辨率的still image,使用这个分辨率抓到都是错乱的数据,还需分析debug!
参考资料:
一个简单的 UVC 应用程序的示例代码_uvc应用层代码-CSDN博客 - https://blog.csdn.net/weixin_37787043/article/details/134422521
Leo手把手教你C语言使用libuvc对USB摄像头进行开发-CSDN博客 - https://blog.csdn.net/weixin_44709134/article/details/131430383
UVC 静态图片抓取 - USB中文网 - https://www.usbzh.com/article/detail-83.html
END!
浙公网安备 33010602011771号