diff --git a/cpp/uvc/src/uvc_example.cpp b/cpp/uvc/src/uvc_example.cpp index 017140357..92c85caf3 100644 --- a/cpp/uvc/src/uvc_example.cpp +++ b/cpp/uvc/src/uvc_example.cpp @@ -35,7 +35,8 @@ extern "C" { std::atomic quitEvent(false); std::shared_ptr inputQueue{nullptr}; -std::shared_ptr outputQueue; +std::shared_ptr videoBitstreamQueue{nullptr}; +std::shared_ptr stillBitstreamQueue{nullptr}; /* Necessary for and only used by signal handler. */ static struct events *sigint_events; @@ -48,49 +49,30 @@ void signalHandler(int signum) { events_stop(sigint_events); } -// Custom host node for saving video data -class VideoSaver : public dai::node::CustomNode { - public: - VideoSaver() : fileHandle("video.encoded", std::ios::binary) { - if(!fileHandle.is_open()) { - throw std::runtime_error("Could not open video.encoded for writing"); - } - } - - ~VideoSaver() { - if(fileHandle.is_open()) { - fileHandle.close(); - } - } - - std::shared_ptr processGroup(std::shared_ptr message) override { - if(!fileHandle.is_open()) return nullptr; - // Get raw data and write to file - auto frame = message->get("data"); - unsigned char* frameData = frame->getData().data(); - size_t frameSize = frame->getData().size(); - std::cout << "Storing frame of size: " << frameSize << std::endl; - fileHandle.write(reinterpret_cast(frameData), frameSize); +extern "C" void depthai_uvc_get_buffer(struct video_source *s, struct video_buffer *buf, bool still) { + unsigned int frame_size, size; + uint8_t *f; - // Don't send anything back - return nullptr; + if(quitEvent) { + std::cout << "depthai_uvc_get_buffer(): Stopping capture due to quit event." << std::endl; + return; } - private: - std::ofstream fileHandle; -}; + if(still) { + std::cout << "depthai_uvc_get_buffer(): Sending latest full-resolution frame as still image." << std::endl; -extern "C" void depthai_uvc_get_buffer(struct video_source *s, struct video_buffer *buf) { - unsigned int frame_size, size; - uint8_t *f; + auto frame = stillBitstreamQueue->get(); - if(quitEvent) { - std::cout << "depthai_uvc_get_buffer(): Stopping capture due to quit event." << std::endl; + f = frame->getData().data(); + frame_size = frame->getData().size(); + size = std::min(frame_size, buf->size); + memcpy(buf->mem, f, size); + buf->bytesused = size; return; - } + } - auto frame = outputQueue->get(); + auto frame = videoBitstreamQueue->get(); if(frame == nullptr) { std::cerr << "depthai_uvc_get_buffer(): No frame available." << std::endl; return; @@ -176,13 +158,20 @@ int main() { // Create nodes auto camRgb = pipeline.create()->build(socket); inputQueue = camRgb->inputControl.createInputQueue(); - auto output = camRgb->requestOutput(std::make_pair(1920, 1080), dai::ImgFrame::Type::NV12); - - // Create video encoder node - auto encoded = pipeline.create(); - encoded->setDefaultProfilePreset(30, dai::VideoEncoderProperties::Profile::MJPEG); - output->link(encoded->input); - outputQueue = encoded->bitstream.createOutputQueue(1, false); + auto videoSource = camRgb->requestOutput(std::make_pair(1920, 1080), dai::ImgFrame::Type::NV12); + + // Encode the center-cropped HD stream for continuous UVC video. + auto videoEncoder = pipeline.create(); + videoEncoder->setDefaultProfilePreset(30, dai::VideoEncoderProperties::Profile::MJPEG); + videoSource->link(videoEncoder->input); + videoBitstreamQueue = videoEncoder->bitstream.createOutputQueue(1, false); + + auto stillSource = camRgb->requestOutput(std::make_pair(3840, 2160), dai::ImgFrame::Type::NV12); + // Encode full-resolution frames for UVC still-image requests. + auto stillEncoder = pipeline.create(); + stillEncoder->setDefaultProfilePreset(4, dai::VideoEncoderProperties::Profile::MJPEG); + stillSource->link(stillEncoder->input); + stillBitstreamQueue = stillEncoder->bitstream.createOutputQueue(1, false); // Start pipeline pipeline.start(); diff --git a/cpp/uvc/uvc-gadget b/cpp/uvc/uvc-gadget index 860b0b506..a2280d0b8 160000 --- a/cpp/uvc/uvc-gadget +++ b/cpp/uvc/uvc-gadget @@ -1 +1 @@ -Subproject commit 860b0b506bf2e17662458d5c642eaba5bfc07831 +Subproject commit a2280d0b829302361fae6899b2a6017d4d9f80a6 diff --git a/cpp/uvc/uvc-start.sh b/cpp/uvc/uvc-start.sh index cbf9a3233..eaec06fe5 100644 --- a/cpp/uvc/uvc-start.sh +++ b/cpp/uvc/uvc-start.sh @@ -25,6 +25,8 @@ fi MANUF="Luxonis" PRODUCT="Luxonis UVC Camera" UDC=$(ls /sys/class/udc | head -n1) # will identify the 'first' UDC +STILL_WIDTH="3840" +STILL_HEIGHT="2160" log "=== Detecting platform:" log " product : $PRODUCT" @@ -66,6 +68,8 @@ remove_uvc_gadget() { rm functions/uvc.0/control/class/ss/* 2>/dev/null rmdir functions/uvc.0/control/header/* 2>/dev/null + rmdir functions/uvc.0/control/extensions/* 2>/dev/null + rmdir functions/uvc.0 2>/dev/null popd >/dev/null @@ -95,7 +99,7 @@ create_frame() { mkdir -p "$wdir" echo "$WIDTH" > "$wdir/wWidth" echo "$HEIGHT" > "$wdir/wHeight" - echo $(( WIDTH * HEIGHT * 2 )) > "$wdir/dwMaxVideoFrameBufferSize" + echo $(( STILL_WIDTH * STILL_HEIGHT * 2 )) > "$wdir/dwMaxVideoFrameBufferSize" cat < "$wdir/dwFrameInterval" 333333 EOF @@ -113,6 +117,9 @@ create_uvc() { pushd "$GADGET/g1" >/dev/null mkdir "functions/$FUNCTION" + # write to fule function_name PRODUCT var + echo "Luxonis UVC Camera" > "functions/$FUNCTION/function_name" + # create_frame "$FUNCTION" 640 360 uncompressed u # create_frame "$FUNCTION" 1280 720 uncompressed u # create_frame "$FUNCTION" 320 180 uncompressed u @@ -121,6 +128,14 @@ create_uvc() { # create_frame "$FUNCTION" 640 360 mjpeg m mkdir "functions/$FUNCTION/streaming/header/h" + if [ -w "functions/$FUNCTION/streaming/header/h/still_width" ] && \ + [ -w "functions/$FUNCTION/streaming/header/h/still_height" ]; then + echo "$STILL_WIDTH" > "functions/$FUNCTION/streaming/header/h/still_width" + echo "$STILL_HEIGHT" > "functions/$FUNCTION/streaming/header/h/still_height" + else + log " Still frame configfs attributes not present, using kernel defaults" + fi + cd "functions/$FUNCTION/streaming/header/h" # ln -s ../../uncompressed/u ln -s ../../mjpeg/m