@@ -444,20 +444,29 @@ void LibCameraWorker::processCompletedRequest(Request *request) {
444444
445445 const std::map<const Stream *, FrameBuffer *> &buffers = request->buffers ();
446446
447- // Return buffer to free list (with lock)
448- {
447+ if (request-> status () == Request::RequestCancelled) {
448+ // Return buffer to free list (with lock)
449449 std::lock_guard<std::mutex> lock (mBufferMutex );
450450 for (auto it = buffers.begin (); it != buffers.end (); ++it) {
451451 mBuffersInFlight .erase (it->second );
452452 mFreeBuffers .push_back (it->second );
453453 }
454- }
455-
456- if (request->status () == Request::RequestCancelled) {
457454 return ;
458455 }
459456
457+ // IMPORTANT: Convert image BEFORE returning buffer to free list!
458+ // Otherwise the buffer may be reused before we read it
460459 QImage preview = convertBufferToImage (buffers);
460+
461+ // Now return buffer to free list (with lock)
462+ {
463+ std::lock_guard<std::mutex> lock (mBufferMutex );
464+ for (auto it = buffers.begin (); it != buffers.end (); ++it) {
465+ mBuffersInFlight .erase (it->second );
466+ mFreeBuffers .push_back (it->second );
467+ }
468+ }
469+
461470 if (!preview.isNull ()) {
462471 Q_EMIT frameReady (preview);
463472 }
@@ -477,134 +486,228 @@ QImage LibCameraWorker::convertBufferToImage(
477486 const Stream *stream = it->first ;
478487 FrameBuffer *buffer = it->second ;
479488
480- const FrameBuffer::Plane &plane = buffer->planes ().front ();
481- void *memory =
482- mmap (NULL , plane.length , PROT_READ, MAP_SHARED, plane.fd .get (), 0 );
483-
484- if (memory == MAP_FAILED) {
485- qDebug () << " [ERROR] Failed to mmap framebuffer memory:"
486- << strerror (errno);
489+ const StreamConfiguration &cfg = stream->configuration ();
490+ Span<const FrameBuffer::Plane> planes = buffer->planes ();
491+
492+ // Log buffer metadata for debugging
493+ const FrameMetadata &metadata = buffer->metadata ();
494+ qDebug () << " [DEBUG] convertBufferToImage: format=" << QString::fromStdString (cfg.pixelFormat .toString ())
495+ << " size=" << cfg.size .width << " x" << cfg.size .height
496+ << " stride=" << cfg.stride
497+ << " num_planes=" << planes.size ()
498+ << " metadata_status=" << metadata.status ;
499+
500+ for (size_t i = 0 ; i < planes.size () && i < metadata.planes ().size (); i++) {
501+ qDebug () << " [DEBUG] Plane" << i << " : fd=" << planes[i].fd .get ()
502+ << " offset=" << planes[i].offset
503+ << " length=" << planes[i].length
504+ << " bytesused=" << metadata.planes ()[i].bytesused ;
505+ }
506+
507+ // Check if buffer has data
508+ if (metadata.planes ().empty () || metadata.planes ()[0 ].bytesused == 0 ) {
509+ qDebug () << " [ERROR] Buffer is empty - bytesused=0" ;
487510 return QImage ();
488511 }
489512
490- const StreamConfiguration &cfg = stream->configuration ();
491-
492- if (cfg.pixelFormat == libcamera::formats::RGB888) {
493- QImage temp (static_cast <const uchar *>(memory),
494- cfg.size .width ,
495- cfg.size .height ,
496- cfg.stride ,
497- QImage::Format_BGR888);
498- qDebug () << " [DEBUG] RGB888 conversion complete, image size:" << temp.size ();
499- image = temp.copy ();
500- } else if (cfg.pixelFormat == libcamera::formats::BGR888) {
501- QImage temp (static_cast <const uchar *>(memory),
502- cfg.size .width ,
503- cfg.size .height ,
504- cfg.stride ,
505- QImage::Format_BGR888);
506- qDebug () << " [DEBUG] BGR888 conversion complete, image size:" << temp.size ();
507- image = temp.copy ();
508- } else if (cfg.pixelFormat == libcamera::formats::MJPEG) {
509- size_t size = buffer->metadata ().planes ()[0 ].bytesused ;
510- image.loadFromData (static_cast <const uchar *>(memory), static_cast <int >(size), " JPEG" );
511- qDebug () << " [DEBUG] MJPEG conversion complete, image size:" << image.size ();
512- } else if (cfg.pixelFormat == libcamera::formats::YUYV) {
513- // Use OpenCV for YUYV to RGB conversion
514- cv::Mat yuyv (cfg.size .height , cfg.size .width , CV_8UC2,
515- const_cast <void *>(memory), cfg.stride );
516- cv::Mat rgb;
517- cv::cvtColor (yuyv, rgb, cv::COLOR_YUV2RGB_YUYV);
513+ // For single-plane formats (RGB, BGR, MJPEG, YUYV)
514+ if (cfg.pixelFormat == libcamera::formats::RGB888 ||
515+ cfg.pixelFormat == libcamera::formats::BGR888 ||
516+ cfg.pixelFormat == libcamera::formats::MJPEG ||
517+ cfg.pixelFormat == libcamera::formats::YUYV) {
518+
519+ const FrameBuffer::Plane &plane = planes.front ();
520+ void *memory = mmap (NULL , plane.length , PROT_READ, MAP_SHARED,
521+ plane.fd .get (), plane.offset );
522+
523+ if (memory == MAP_FAILED) {
524+ qDebug () << " [ERROR] Failed to mmap framebuffer memory:" << strerror (errno);
525+ return QImage ();
526+ }
527+
528+ if (cfg.pixelFormat == libcamera::formats::RGB888) {
529+ QImage temp (static_cast <const uchar *>(memory),
530+ cfg.size .width ,
531+ cfg.size .height ,
532+ cfg.stride ,
533+ QImage::Format_BGR888);
534+ qDebug () << " [DEBUG] RGB888 conversion complete, image size:" << temp.size ();
535+ image = temp.copy ();
536+ } else if (cfg.pixelFormat == libcamera::formats::BGR888) {
537+ QImage temp (static_cast <const uchar *>(memory),
538+ cfg.size .width ,
539+ cfg.size .height ,
540+ cfg.stride ,
541+ QImage::Format_BGR888);
542+ qDebug () << " [DEBUG] BGR888 conversion complete, image size:" << temp.size ();
543+ image = temp.copy ();
544+ } else if (cfg.pixelFormat == libcamera::formats::MJPEG) {
545+ size_t size = metadata.planes ()[0 ].bytesused ;
546+ image.loadFromData (static_cast <const uchar *>(memory), static_cast <int >(size), " JPEG" );
547+ qDebug () << " [DEBUG] MJPEG conversion complete, image size:" << image.size ();
548+ } else if (cfg.pixelFormat == libcamera::formats::YUYV) {
549+ cv::Mat yuyv (cfg.size .height , cfg.size .width , CV_8UC2,
550+ const_cast <void *>(memory), cfg.stride );
551+ cv::Mat rgb;
552+ cv::cvtColor (yuyv, rgb, cv::COLOR_YUV2RGB_YUYV);
553+
554+ image = QImage (rgb.data , rgb.cols , rgb.rows , rgb.step ,
555+ QImage::Format_RGB888).copy ();
556+ qDebug () << " [DEBUG] YUYV conversion complete (OpenCV), image size:" << image.size ();
557+ }
558+
559+ munmap (memory, plane.length );
518560
519- image = QImage (rgb.data , rgb.cols , rgb.rows , rgb.step ,
520- QImage::Format_RGB888).copy ();
521- qDebug () << " [DEBUG] YUYV conversion complete (OpenCV), image size:" << image.size ();
522561 } else if (cfg.pixelFormat == libcamera::formats::YUV420) {
523562 // Use OpenCV for YUV420 (I420) to RGB conversion
524563 unsigned int width = cfg.size .width ;
525564 unsigned int height = cfg.size .height ;
526565 unsigned int stride = cfg.stride ;
527566
528- Span<const FrameBuffer::Plane> planes = buffer->planes ();
529-
530567 qDebug () << " [DEBUG] YUV420: width=" << width << " height=" << height
531568 << " stride=" << stride << " num_planes=" << planes.size ();
532569
570+ // Check if all planes share the same fd (contiguous buffer with offsets)
571+ bool samefd = true ;
533572 if (planes.size () >= 3 ) {
534- // Multi-plane format - map each plane separately and copy to contiguous buffer
573+ int fd0 = planes[0 ].fd .get ();
574+ for (size_t i = 1 ; i < planes.size (); i++) {
575+ if (planes[i].fd .get () != fd0) {
576+ samefd = false ;
577+ break ;
578+ }
579+ }
580+ }
581+ qDebug () << " [DEBUG] YUV420: planes share same fd:" << samefd;
582+
583+ if (planes.size () >= 3 && samefd) {
584+ // All planes share same fd - map once with the total size
585+ // Calculate total size needed
586+ size_t totalSize = 0 ;
587+ for (size_t i = 0 ; i < planes.size (); i++) {
588+ size_t endOffset = planes[i].offset + planes[i].length ;
589+ if (endOffset > totalSize) totalSize = endOffset;
590+ }
591+
592+ qDebug () << " [DEBUG] YUV420 same-fd: totalSize=" << totalSize;
593+
594+ void *memory = mmap (NULL , totalSize, PROT_READ, MAP_SHARED,
595+ planes[0 ].fd .get (), 0 );
596+
597+ if (memory == MAP_FAILED) {
598+ qDebug () << " [ERROR] Failed to mmap YUV420 buffer:" << strerror (errno);
599+ return QImage ();
600+ }
601+
602+ // Get pointers to each plane using their offsets
603+ const uint8_t *yData = static_cast <const uint8_t *>(memory) + planes[0 ].offset ;
604+ const uint8_t *uData = static_cast <const uint8_t *>(memory) + planes[1 ].offset ;
605+ const uint8_t *vData = static_cast <const uint8_t *>(memory) + planes[2 ].offset ;
606+
607+ // Debug: Check first bytes of each plane
608+ qDebug () << " [DEBUG] Y plane first byte:" << (int )yData[0 ];
609+ qDebug () << " [DEBUG] U plane first byte:" << (int )uData[0 ];
610+ qDebug () << " [DEBUG] V plane first byte:" << (int )vData[0 ];
611+
612+ // Create contiguous I420 buffer for OpenCV
613+ size_t ySize = stride * height;
614+ size_t uvStride = stride / 2 ;
615+ size_t uvSize = uvStride * (height / 2 );
616+ std::vector<uint8_t > i420Buffer (ySize + 2 * uvSize);
617+
618+ memcpy (i420Buffer.data (), yData, ySize);
619+ memcpy (i420Buffer.data () + ySize, uData, uvSize);
620+ memcpy (i420Buffer.data () + ySize + uvSize, vData, uvSize);
621+
622+ cv::Mat yuv (height * 3 / 2 , stride, CV_8UC1, i420Buffer.data ());
623+ cv::Mat rgb;
624+ cv::cvtColor (yuv, rgb, cv::COLOR_YUV2RGB_I420);
625+
626+ if (stride != width) {
627+ rgb = rgb (cv::Rect (0 , 0 , width, height)).clone ();
628+ }
629+
630+ image = QImage (rgb.data , rgb.cols , rgb.rows , rgb.step ,
631+ QImage::Format_RGB888).copy ();
632+
633+ qDebug () << " [DEBUG] YUV420 same-fd conversion complete, image size:" << image.size ();
634+
635+ munmap (memory, totalSize);
636+
637+ } else if (planes.size () >= 3 ) {
638+ // Different fds for each plane - map each separately
535639 void *yMem = mmap (NULL , planes[0 ].length , PROT_READ, MAP_SHARED,
536- planes[0 ].fd .get (), 0 );
640+ planes[0 ].fd .get (), planes[ 0 ]. offset );
537641 void *uMem = mmap (NULL , planes[1 ].length , PROT_READ, MAP_SHARED,
538- planes[1 ].fd .get (), 0 );
642+ planes[1 ].fd .get (), planes[ 1 ]. offset );
539643 void *vMem = mmap (NULL , planes[2 ].length , PROT_READ, MAP_SHARED,
540- planes[2 ].fd .get (), 0 );
644+ planes[2 ].fd .get (), planes[ 2 ]. offset );
541645
542646 if (yMem == MAP_FAILED || uMem == MAP_FAILED || vMem == MAP_FAILED) {
543- qDebug () << " [ERROR] Failed to mmap YUV420 planes" ;
647+ qDebug () << " [ERROR] Failed to mmap YUV420 planes: " << strerror (errno) ;
544648 if (yMem != MAP_FAILED) munmap (yMem, planes[0 ].length );
545649 if (uMem != MAP_FAILED) munmap (uMem, planes[1 ].length );
546650 if (vMem != MAP_FAILED) munmap (vMem, planes[2 ].length );
547- munmap (memory, plane.length );
548651 return QImage ();
549652 }
550653
551- // Create contiguous I420 buffer for OpenCV
552- // I420 layout: Y plane (height rows), U plane (height/2 rows), V plane (height/2 rows)
553654 size_t ySize = stride * height;
554655 size_t uvStride = stride / 2 ;
555656 size_t uvSize = uvStride * (height / 2 );
556657 std::vector<uint8_t > i420Buffer (ySize + 2 * uvSize);
557658
558- // Copy Y plane
559659 memcpy (i420Buffer.data (), yMem, ySize);
560- // Copy U plane
561660 memcpy (i420Buffer.data () + ySize, uMem, uvSize);
562- // Copy V plane
563661 memcpy (i420Buffer.data () + ySize + uvSize, vMem, uvSize);
564662
565- // Create OpenCV Mat wrapping the I420 data (height * 3/2 rows, stride columns)
566663 cv::Mat yuv (height * 3 / 2 , stride, CV_8UC1, i420Buffer.data ());
567664 cv::Mat rgb;
568665 cv::cvtColor (yuv, rgb, cv::COLOR_YUV2RGB_I420);
569666
570- // Crop to actual width if stride != width
571667 if (stride != width) {
572668 rgb = rgb (cv::Rect (0 , 0 , width, height)).clone ();
573669 }
574670
575671 image = QImage (rgb.data , rgb.cols , rgb.rows , rgb.step ,
576672 QImage::Format_RGB888).copy ();
577673
578- qDebug () << " [DEBUG] YUV420 multi-plane conversion complete (OpenCV) " ;
674+ qDebug () << " [DEBUG] YUV420 multi-fd conversion complete, image size: " << image. size () ;
579675
580676 munmap (yMem, planes[0 ].length );
581677 munmap (uMem, planes[1 ].length );
582678 munmap (vMem, planes[2 ].length );
583679
584680 } else {
585- // Single plane - all data contiguous, use OpenCV directly
586- // The buffer contains Y, U, V planes contiguously
587- cv::Mat yuv (height * 3 / 2 , stride, CV_8UC1,
588- const_cast <void *>(memory));
681+ // Single plane - all data contiguous
682+ const FrameBuffer::Plane &plane = planes.front ();
683+ void *memory = mmap (NULL , plane.length , PROT_READ, MAP_SHARED,
684+ plane.fd .get (), plane.offset );
685+
686+ if (memory == MAP_FAILED) {
687+ qDebug () << " [ERROR] Failed to mmap YUV420 buffer:" << strerror (errno);
688+ return QImage ();
689+ }
690+
691+ cv::Mat yuv (height * 3 / 2 , stride, CV_8UC1, const_cast <void *>(memory));
589692 cv::Mat rgb;
590693 cv::cvtColor (yuv, rgb, cv::COLOR_YUV2RGB_I420);
591694
592- // Crop to actual width if stride != width
593695 if (stride != width) {
594696 rgb = rgb (cv::Rect (0 , 0 , width, height)).clone ();
595697 }
596698
597699 image = QImage (rgb.data , rgb.cols , rgb.rows , rgb.step ,
598700 QImage::Format_RGB888).copy ();
599701
600- qDebug () << " [DEBUG] YUV420 single-plane conversion complete (OpenCV)" ;
702+ qDebug () << " [DEBUG] YUV420 single-plane conversion complete, image size:" << image.size ();
703+
704+ munmap (memory, plane.length );
601705 }
602706 } else {
603707 qDebug () << " [ERROR] Unsupported pixel format:"
604708 << QString::fromStdString (cfg.pixelFormat .toString ());
605709 }
606710
607- munmap (memory, plane.length );
608711 break ;
609712 }
610713
0 commit comments