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

image

UVC 负载数据头 Payload Header Information

image

image

判断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

image

//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!

posted @ 2024-04-07 14:59  Yaction  阅读(586)  评论(0)    收藏  举报