@@ -177,6 +177,20 @@ class GSafePtr
177
177
GSafePtr& operator =(const T*); // = disabled
178
178
};
179
179
180
+ class ScopeGuardGstMapInfo
181
+ {
182
+ GstBuffer* buf_;
183
+ GstMapInfo* info_;
184
+ public:
185
+ ScopeGuardGstMapInfo (GstBuffer* buf, GstMapInfo* info)
186
+ : buf_(buf), info_(info)
187
+ {}
188
+ ~ScopeGuardGstMapInfo ()
189
+ {
190
+ gst_buffer_unmap (buf_, info_);
191
+ }
192
+ };
193
+
180
194
} // namespace
181
195
182
196
/* !
@@ -300,7 +314,6 @@ class GStreamerCapture CV_FINAL : public IVideoCapture
300
314
static void newPad (GstElement * /* elem*/ , GstPad *pad, gpointer data);
301
315
302
316
protected:
303
- bool determineFrameDims (CV_OUT Size& sz, CV_OUT gint& channels, CV_OUT bool & isOutputByteBuffer);
304
317
bool isPipelinePlaying ();
305
318
void startPipeline ();
306
319
void stopPipeline ();
@@ -369,72 +382,68 @@ bool GStreamerCapture::grabFrame()
369
382
bool GStreamerCapture::retrieveFrame (int , OutputArray dst)
370
383
{
371
384
if (!sample)
385
+ {
372
386
return false ;
373
- Size sz;
374
- gint channels = 0 ;
375
- bool isOutputByteBuffer = false ;
376
- if (!determineFrameDims (sz, channels, isOutputByteBuffer))
377
- return false ;
387
+ }
378
388
379
- // gstreamer expects us to handle the memory at this point
380
- // so we can just wrap the raw buffer and be done with it
381
- GstBuffer* buf = gst_sample_get_buffer (sample); // no lifetime transfer
382
- if (!buf)
383
- return false ;
384
- GstMapInfo info = {};
385
- if (!gst_buffer_map (buf, &info, GST_MAP_READ))
389
+ GstCaps* frame_caps = gst_sample_get_caps (sample); // no lifetime transfer
390
+ if (!frame_caps)
386
391
{
387
- // something weird went wrong here. abort. abort.
388
- CV_WARN (" Failed to map GStreamer buffer to system memory" );
392
+ CV_LOG_ERROR (NULL , " GStreamer: gst_sample_get_caps() returns NULL" );
389
393
return false ;
390
394
}
391
395
392
- try
396
+ if (! GST_CAPS_IS_SIMPLE (frame_caps))
393
397
{
394
- Mat src;
395
- if (isOutputByteBuffer)
396
- src = Mat (Size (info.size , 1 ), CV_8UC1, info.data );
397
- else
398
- src = Mat (sz, CV_MAKETYPE (CV_8U, channels), info.data );
399
- CV_Assert (src.isContinuous ());
400
- src.copyTo (dst);
398
+ // bail out in no caps
399
+ CV_LOG_ERROR (NULL , " GStreamer: GST_CAPS_IS_SIMPLE(frame_caps) check is failed" );
400
+ return false ;
401
401
}
402
- catch (...)
402
+
403
+ GstVideoInfo info = {};
404
+ gboolean video_info_res = gst_video_info_from_caps (&info, frame_caps);
405
+ if (!video_info_res)
403
406
{
404
- gst_buffer_unmap (buf, &info);
405
- throw ;
407
+ CV_Error (Error::StsError, " GStreamer: gst_video_info_from_caps() is failed. Can't handle unknown layout" );
406
408
}
407
- gst_buffer_unmap (buf, &info);
408
-
409
- return true ;
410
- }
411
409
412
- bool GStreamerCapture::determineFrameDims (Size &sz, gint& channels, bool & isOutputByteBuffer)
413
- {
414
- GstCaps * frame_caps = gst_sample_get_caps (sample); // no lifetime transfer
415
-
416
- // bail out in no caps
417
- if (!GST_CAPS_IS_SIMPLE (frame_caps))
410
+ int frame_width = GST_VIDEO_INFO_WIDTH (&info);
411
+ int frame_height = GST_VIDEO_INFO_HEIGHT (&info);
412
+ if (frame_width <= 0 || frame_height <= 0 )
413
+ {
414
+ CV_LOG_ERROR (NULL , " GStreamer: Can't query frame size from GStreamer sample" );
418
415
return false ;
416
+ }
419
417
420
418
GstStructure* structure = gst_caps_get_structure (frame_caps, 0 ); // no lifetime transfer
421
-
422
- // bail out if width or height are 0
423
- if (!gst_structure_get_int (structure, " width" , &width)
424
- || !gst_structure_get_int (structure, " height" , &height))
419
+ if (!structure)
425
420
{
426
- CV_WARN ( " Can't query frame size from GStreeamer buffer " );
421
+ CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'structure'-0 from GStreamer sample " );
427
422
return false ;
428
423
}
429
424
430
- sz = Size (width, height);
431
-
432
425
const gchar* name_ = gst_structure_get_name (structure);
433
426
if (!name_)
427
+ {
428
+ CV_LOG_ERROR (NULL , " GStreamer: Can't query 'name' from GStreamer sample" );
434
429
return false ;
430
+ }
435
431
std::string name = toLowerCase (std::string (name_));
436
432
437
- // we support 11 types of data:
433
+ // gstreamer expects us to handle the memory at this point
434
+ // so we can just wrap the raw buffer and be done with it
435
+ GstBuffer* buf = gst_sample_get_buffer (sample); // no lifetime transfer
436
+ if (!buf)
437
+ return false ;
438
+ GstMapInfo map_info = {};
439
+ if (!gst_buffer_map (buf, &map_info, GST_MAP_READ))
440
+ {
441
+ CV_LOG_ERROR (NULL , " GStreamer: Failed to map GStreamer buffer to system memory" );
442
+ return false ;
443
+ }
444
+ ScopeGuardGstMapInfo map_guard (buf, &map_info); // call gst_buffer_unmap(buf, &map_info) on scope leave
445
+
446
+ // we support these types of data:
438
447
// video/x-raw, format=BGR -> 8bit, 3 channels
439
448
// video/x-raw, format=GRAY8 -> 8bit, 1 channel
440
449
// video/x-raw, format=UYVY -> 8bit, 2 channel
@@ -448,50 +457,117 @@ bool GStreamerCapture::determineFrameDims(Size &sz, gint& channels, bool& isOutp
448
457
// image/jpeg -> 8bit, mjpeg: buffer_size x 1 x 1
449
458
// bayer data is never decoded, the user is responsible for that
450
459
// everything is 8 bit, so we just test the caps for bit depth
460
+ Size sz = Size (frame_width, frame_height);
461
+ guint n_planes = GST_VIDEO_INFO_N_PLANES (&info);
451
462
if (name == " video/x-raw" )
452
463
{
453
464
const gchar* format_ = gst_structure_get_string (structure, " format" );
454
465
if (!format_)
466
+ {
467
+ CV_LOG_ERROR (NULL , " GStreamer: Can't query 'format' of 'video/x-raw'" );
455
468
return false ;
469
+ }
456
470
std::string format = toUpperCase (std::string (format_));
457
471
458
472
if (format == " BGR" )
459
473
{
460
- channels = 3 ;
474
+ CV_CheckEQ ((int )n_planes, 1 , " " );
475
+ size_t step = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0 );
476
+ CV_CheckGE (step, (size_t )frame_width * 3 , " " );
477
+ Mat src (sz, CV_8UC3, map_info.data + GST_VIDEO_INFO_PLANE_OFFSET (&info, 0 ), step);
478
+ src.copyTo (dst);
479
+ return true ;
461
480
}
462
- else if (format == " UYVY " || format == " YUY2 " || format == " YVYU " )
481
+ else if (format == " GRAY8 " )
463
482
{
464
- channels = 2 ;
483
+ CV_CheckEQ ((int )n_planes, 1 , " " );
484
+ size_t step = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0 );
485
+ CV_CheckGE (step, (size_t )frame_width, " " );
486
+ Mat src (sz, CV_8UC1, map_info.data + GST_VIDEO_INFO_PLANE_OFFSET (&info, 0 ), step);
487
+ src.copyTo (dst);
488
+ return true ;
465
489
}
466
- else if (format == " NV12 " || format == " NV21 " || format == " YV12 " || format == " I420 " )
490
+ else if (format == " UYVY " || format == " YUY2 " || format == " YVYU " )
467
491
{
468
- channels = 1 ;
469
- sz.height = sz.height * 3 / 2 ;
492
+ CV_CheckEQ ((int )n_planes, 1 , " " );
493
+ size_t step = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0 );
494
+ CV_CheckGE (step, (size_t )frame_width * 2 , " " );
495
+ Mat src (sz, CV_8UC2, map_info.data + GST_VIDEO_INFO_PLANE_OFFSET (&info, 0 ), step);
496
+ src.copyTo (dst);
497
+ return true ;
498
+ }
499
+ else if (format == " NV12" || format == " NV21" )
500
+ {
501
+ CV_CheckEQ ((int )n_planes, 2 , " " );
502
+ size_t stepY = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0 );
503
+ CV_CheckGE (stepY, (size_t )frame_width, " " );
504
+ size_t stepUV = GST_VIDEO_INFO_PLANE_STRIDE (&info, 1 );
505
+ CV_CheckGE (stepUV, (size_t )frame_width, " " );
506
+ size_t offsetY = GST_VIDEO_INFO_PLANE_OFFSET (&info, 0 );
507
+ size_t offsetUV = GST_VIDEO_INFO_PLANE_OFFSET (&info, 1 );
508
+ if (stepY != stepUV || (offsetUV - offsetY) != (stepY * frame_height))
509
+ {
510
+ dst.create (Size (frame_width, frame_height * 3 / 2 ), CV_8UC1);
511
+ Mat dst_ = dst.getMat ();
512
+ Mat srcY (sz, CV_8UC1, map_info.data + offsetY, stepY);
513
+ Mat srcUV (Size (frame_width, frame_height / 2 ), CV_8UC1, map_info.data + offsetUV, stepUV);
514
+ srcY.copyTo (dst_ (Rect (0 , 0 , frame_width, frame_height)));
515
+ srcUV.copyTo (dst_ (Rect (0 , frame_height, frame_width, frame_height / 2 )));
516
+ }
517
+ else
518
+ {
519
+ Mat src (Size (frame_width, frame_height * 3 / 2 ), CV_8UC1, map_info.data + offsetY, stepY);
520
+ src.copyTo (dst);
521
+ }
522
+ return true ;
470
523
}
471
- else if (format == " GRAY8 " )
524
+ else if (format == " YV12 " || format == " I420 " )
472
525
{
473
- channels = 1 ;
526
+ CV_CheckEQ ((int )n_planes, 3 , " " );
527
+ size_t step0 = GST_VIDEO_INFO_PLANE_STRIDE (&info, 0 );
528
+ CV_CheckGE (step0, (size_t )frame_width, " " );
529
+ size_t step1 = GST_VIDEO_INFO_PLANE_STRIDE (&info, 1 );
530
+ CV_CheckGE (step1, (size_t )frame_width / 2 , " " );
531
+ size_t step2 = GST_VIDEO_INFO_PLANE_STRIDE (&info, 2 );
532
+ CV_CheckGE (step2, (size_t )frame_width / 2 , " " );
533
+
534
+ size_t offset0 = GST_VIDEO_INFO_PLANE_OFFSET (&info, 0 );
535
+ size_t offset1 = GST_VIDEO_INFO_PLANE_OFFSET (&info, 1 );
536
+ size_t offset2 = GST_VIDEO_INFO_PLANE_OFFSET (&info, 2 );
537
+ {
538
+ dst.create (Size (frame_width, frame_height * 3 / 2 ), CV_8UC1);
539
+ Mat dst_ = dst.getMat ();
540
+ Mat srcY (sz, CV_8UC1, map_info.data + offset0, step0);
541
+ Size sz2 (frame_width / 2 , frame_height / 2 );
542
+ Mat src1 (sz2, CV_8UC1, map_info.data + offset1, step1);
543
+ Mat src2 (sz2, CV_8UC1, map_info.data + offset2, step2);
544
+ srcY.copyTo (dst_ (Rect (0 , 0 , frame_width, frame_height)));
545
+ src1.copyTo (Mat (sz2, CV_8UC1, dst_.ptr <uchar>(frame_height)));
546
+ src2.copyTo (Mat (sz2, CV_8UC1, dst_.ptr <uchar>(frame_height) + src1.total ()));
547
+ }
548
+ return true ;
474
549
}
475
550
else
476
551
{
477
- CV_Error_ (Error::StsNotImplemented, (" Unsupported GStreamer format: %s" , format.c_str ()));
552
+ CV_Error_ (Error::StsNotImplemented, (" Unsupported GStreamer 'video/x-raw' format: %s" , format.c_str ()));
478
553
}
479
554
}
480
555
else if (name == " video/x-bayer" )
481
556
{
482
- channels = 1 ;
557
+ CV_CheckEQ ((int )n_planes, 0 , " " );
558
+ Mat src = Mat (sz, CV_8UC1, map_info.data );
559
+ src.copyTo (dst);
560
+ return true ;
483
561
}
484
562
else if (name == " image/jpeg" )
485
563
{
486
- // the correct size will be set once the first frame arrives
487
- channels = 1 ;
488
- isOutputByteBuffer = true ;
489
- }
490
- else
491
- {
492
- CV_Error_ (Error::StsNotImplemented, (" Unsupported GStreamer layer type: %s" , name.c_str ()));
564
+ CV_CheckEQ ((int )n_planes, 0 , " " );
565
+ Mat src = Mat (Size (map_info.size , 1 ), CV_8UC1, map_info.data );
566
+ src.copyTo (dst);
567
+ return true ;
493
568
}
494
- return true ;
569
+
570
+ CV_Error_ (Error::StsNotImplemented, (" Unsupported GStreamer layer type: %s" , name.c_str ()));
495
571
}
496
572
497
573
bool GStreamerCapture::isPipelinePlaying ()
0 commit comments