openharmony 使用uvc库获取摄像头数据使用nativewindow显示

界面代码:

XComponent({ id: 'xcomponentId', type: 'texture', libraryname: 'entry' })
            .width(800)
            .height(500)

Natvie代码:

1、头文件

//NativeWindow
#include <ace/xcomponent/native_interface_xcomponent.h>
#include <cstdint>
#include <native_window/external_window.h>
#include <sys/mman.h>

//UVC
#include "libuvc.h"
#include "stdio.h"
#include "string.h"

2、关键变量

OHNativeWindow* nativeWindow = nullptr;
BufferHandle* bufferHandle = nullptr;
// 初始化 OH_NativeXComponent_Callback
OH_NativeXComponent_Callback callback;
void* mappedAddr = nullptr;
int winwidth = 640;
int winheigh = 480;
OHNativeWindowBuffer* buffer = nullptr;
int fenceFd;
// 设置刷新区域,如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
Region region{nullptr, 0};

3、注册回调函数,通过回调函数获取nativewindow,可以在

static napi_value Init(napi_env env, napi_value exports)中调用
// 定义回调函数
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{
    
    // 可获取 OHNativeWindow 实例
    nativeWindow = static_cast<OHNativeWindow*>(window);
    OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","surface create get nativewindow");
    // ...
}
void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window)
{
    // 可获取 OHNativeWindow 实例
    nativeWindow = static_cast<OHNativeWindow*>(window);
    // ...
}
void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window)
{
    // 可获取 OHNativeWindow 实例
    nativeWindow = static_cast<OHNativeWindow*>(window);
    // ...
}
void DispatchTouchEventCB(OH_NativeXComponent* component, void* window)
{
    // 可获取 OHNativeWindow 实例
    nativeWindow = static_cast<OHNativeWindow*>(window);
    OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","DispatchTouchEventCB get nativewindow");
    // ...
}


static napi_value nativewin_init(napi_env env,  napi_value exports)
{
#if NATIVE_WINDOW_
    napi_value exportInstance = nullptr;
    // 用来解析出被wrap了NativeXComponent指针的属性
    napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
    
    OH_NativeXComponent *nativeXComponent = nullptr;
    // 通过napi_unwrap接口,解析出NativeXComponent的实例指针
    napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
    
    // 获取XComponentId
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
    
    callback.OnSurfaceCreated = OnSurfaceCreatedCB;
    callback.OnSurfaceChanged = OnSurfaceChangedCB;
    callback.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
    callback.DispatchTouchEvent = DispatchTouchEventCB;
    
    // 注册回调函数
    OH_NativeXComponent_RegisterCallback(nativeXComponent, &callback);
    

    return nullptr;

#endif
}

4、设置nativewindow窗口


void SetNativeWindow(OHNativeWindow* nativeWindow, uint64_t width,  uint64_t height)
{
    if(nativeWindow!=nullptr)
    {
        // 设置 OHNativeWindowBuffer 的宽高
        int32_t code = SET_BUFFER_GEOMETRY;
        
        // 这里的nativeWindow是从上一步骤中的回调函数中获得的
        winwidth = width;
        winheigh = height;
        int32_t bufferHeight = static_cast<int32_t>(height );
        int32_t bufferWidth = static_cast<int32_t>(width );
        int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, bufferWidth, bufferHeight);
        if(ret<0)
        {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","SetNativeWindow fail %{public}d",ret);
        } else {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","SetNativeWindow  ok");
        }
    } else {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","get NativeWindow fail");
    }
}

5、显示一帧数据  可以放到uvc的回调函数中

void draw_init(OHNativeWindow* nativeWindow,BufferHandle* bufferHandle,int32_t *bgrdata)
{
    if(nativeWindow!=nullptr)
    {
        //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","draw_init");
        // 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 OHNativeWindowBuffer 实例
        OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd);
        if(buffer == nullptr)
        {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","RequestBuffer fail");
            return;
        }
        else
        {
            //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","RequestBuffer ok");
        }
        // 通过 OH_NativeWindow_GetBufferHandleFromNative 获取 buffer 的 handle
        bufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
        if(bufferHandle == nullptr)
        {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","GetBufferHandle fail");
            return;
        }
        else
        {
            //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","GetBufferHandle ok");
        }
        // 使用内存映射函数mmap将bufferHandle对应的共享内存映射到用户空间,可以通过映射出来的虚拟地址向bufferHandle中写入图像数据
        // bufferHandle->virAddr是bufferHandle在共享内存中的起始地址,bufferHandle->size是bufferHandle在共享内存中的内存占用大小
        mappedAddr = mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0);
        if (mappedAddr == MAP_FAILED) {
            // mmap failed
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap failed");
            return;
        } else {
            //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap ok");
        }
        static uint32_t value = 0xFF;
        int32_t index = 0;
        uint32_t *pixel = static_cast<uint32_t *>(mappedAddr); // 使用mmap获取到的地址来访问内存
        for (uint32_t x = 0; x < winwidth ; x++) {
            for (uint32_t y = 0;  y < winheigh; y++) {
                *pixel++ = bgrdata[index++];
            }
        }
        // 设置刷新区域,如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
        // 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。
        OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);
        //OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);

        // 内存使用完记得去掉内存映射
        int result = munmap(mappedAddr, bufferHandle->size);
        if (result == -1) {
            // munmap failed
        }
        //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap write ok");

    } else {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","draw_init fail");
    }

}

6、uvc设备打开及注册回到函数

bool s_cbSignal=false;
void uvc_callback(uvc_frame_t *frame, void *ptr);


void uvc_start()
{
    uvc_context_t *m_ctx = nullptr;
    uvc_device_t *m_dev = nullptr;/*!< uvc设备结构*/
    uvc_device_handle_t *m_devh = nullptr;/*!< 打开uvc设备的句柄*/
    int fps = 25;
    uvc_frame_format frame_format = UVC_FRAME_FORMAT_MJPEG;
    uvc_error_t res = uvc_init(&m_ctx, nullptr);
    int deviceWidth = 640;
    int deviceHeight = 480;
    
    if (res < 0)
    {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc init fail");
    } else {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc init ok");
    }
    int VID=0xxxx,PID=0xxxx;
    res = uvc_find_device( m_ctx, &m_dev, VID, PID, nullptr);
    if (res < 0)
    {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc find fail");
    } else {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc find ok");
        res = uvc_open(m_dev, &m_devh);
        if (res < 0)
        {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc open fail%{public}d",res);
        } else {
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc open ok");
            uvc_stream_ctrl_t ctrl;
            res = uvc_get_stream_ctrl_format_size( m_devh, &ctrl, frame_format, deviceWidth, deviceHeight, fps);
            uvc_print_stream_ctrl(&ctrl, stderr);
            if (res < 0)
            {
                OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc get stream mode fail");
            } else {
                OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc get stream mode ok");

                void* user_ptr = nullptr;
                res = uvc_start_streaming(m_devh, &ctrl, uvc_callback, user_ptr, 0);
                if(res < 0)
                {
                    OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_start_streaming error");
                } else {
                    OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_start_streaming ok");
                }
                struct timeval tpstart,tpend;
                float timeuse;
                gettimeofday(&tpstart,nullptr);
                while(!s_cbSignal)
                {
                    gettimeofday(&tpend,nullptr);
                    timeuse=(1000000*(tpend.tv_sec-tpstart.tv_sec) + tpend.tv_usec-tpstart.tv_usec)/1000000.0;
                    if(timeuse > 2)
                    {
                        //printf("等待回调超时\n");
                        return ;
                    }
                }
                return ;

            }
        }
    }
}


void uvc_callback(uvc_frame_t *frame, void *ptr)
{
    
    
    //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc getting stream");
    s_cbSignal = true;
#if 1
    static int g_current_bufNum = 0;
    if(g_current_bufNum >= 3)
    {
        g_current_bufNum = 0;
    }
    (void)ptr;
    uvc_frame_t *bgr;
    uvc_error_t ret = UVC_ERROR_OTHER;

    bgr = uvc_allocate_frame(frame->width * frame->height * 3);
    if (!bgr)
    {
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc allocate error");
        return;
    }
    //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_allocate_frame");
    try {
        ret = uvc_any2rgb(frame, bgr);//RGB
        if (ret)
        {
            uvc_perror(ret, "uvc_any2rgb(face)");
            uvc_free_frame(bgr);
            OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc any2rgb");
            return;
        }
        //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_any2rgb");
    } catch (...)
    {
        uvc_perror(ret, "uvc_any2rgb(face) catch");
        uvc_free_frame(bgr);
        OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc any2rgb catch");
        return;
    }

    //"frame width=%{public}d height=%{public}d format=%{public}d size=%{public}d",
    //bgr->width,bgr->height,bgr->frame_format,bgr->data_bytes);

    static uint32_t value = 0xFF;
    int32_t index = 0;
    int32_t indexrgb = 0;
    
    for (uint32_t x = 0; x < winwidth ; x++) {
        for (uint32_t y = 0;  y < winheigh; y++) {
            value = *((uint8_t *)(bgr->data)+indexrgb++); value+=(*((uint8_t *)(bgr->data)+indexrgb++)<<8);value+=(*((uint8_t *)(bgr->data)+indexrgb++)<<16);
            bmpdata[index++] = value;
        }
    }
    draw_init(nativeWindow,bufferHandle,bmpdata);
    //memcpy(bmpdata,bgr->data,bgr->data_bytes);
    uvc_free_frame(bgr);
    
    
    //OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_free_frame");
#endif
}

需要依赖的库

target_link_libraries(entry PUBLIC libace_napi.z.so  libhilog_ndk.z.so )
target_link_libraries(entry PUBLIC libace_ndk.z.so libuvc.so)
target_link_libraries(entry PUBLIC libnative_buffer.so libnative_window.so)
上一篇:【R安装】VSCODE安装及R语言环境配置-VSCODE上配置R语言环境


下一篇:mac上的建议xftp 工具