@ -192,6 +192,19 @@ public:
}
} ;
class ScopeGuardGstVideoFrame
{
GstVideoFrame * frame_ ;
public :
ScopeGuardGstVideoFrame ( GstVideoFrame * frame )
: frame_ ( frame )
{ }
~ ScopeGuardGstVideoFrame ( )
{
gst_video_frame_unmap ( frame_ ) ;
}
} ;
} // namespace
/*!
@ -651,8 +664,28 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst)
CV_Error ( Error : : StsError , " GStreamer: gst_video_info_from_caps() is failed. Can't handle unknown layout " ) ;
}
int frame_width = GST_VIDEO_INFO_WIDTH ( & info ) ;
int frame_height = GST_VIDEO_INFO_HEIGHT ( & info ) ;
// gstreamer expects us to handle the memory at this point
// so we can just wrap the raw buffer and be done with it
GstBuffer * buf = gst_sample_get_buffer ( sample ) ; // no lifetime transfer
if ( ! buf )
return false ;
// at this point, the gstreamer buffer may contain a video meta with special
// stride and plane locations. We __must__ consider in order to correctly parse
// the data. The gst_video_frame_map will parse the meta for us, or default to
// regular strides/offsets if no meta is present.
GstVideoFrame frame = { } ;
GstMapFlags flags = static_cast < GstMapFlags > ( GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF ) ;
if ( ! gst_video_frame_map ( & frame , & info , buf , flags ) )
{
CV_LOG_ERROR ( NULL , " GStreamer: Failed to map GStreamer buffer to system memory " ) ;
return false ;
}
ScopeGuardGstVideoFrame frame_guard ( & frame ) ; // call gst_video_frame_unmap(&frame) on scope leave
int frame_width = GST_VIDEO_FRAME_COMP_WIDTH ( & frame , 0 ) ;
int frame_height = GST_VIDEO_FRAME_COMP_HEIGHT ( & frame , 0 ) ;
if ( frame_width < = 0 | | frame_height < = 0 )
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query frame size from GStreamer sample " ) ;
@ -674,19 +707,6 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst)
}
std : : string name = toLowerCase ( std : : string ( name_ ) ) ;
// gstreamer expects us to handle the memory at this point
// so we can just wrap the raw buffer and be done with it
GstBuffer * buf = gst_sample_get_buffer ( sample ) ; // no lifetime transfer
if ( ! buf )
return false ;
GstMapInfo map_info = { } ;
if ( ! gst_buffer_map ( buf , & map_info , GST_MAP_READ ) )
{
CV_LOG_ERROR ( NULL , " GStreamer: Failed to map GStreamer buffer to system memory " ) ;
return false ;
}
ScopeGuardGstMapInfo map_guard ( buf , & map_info ) ; // call gst_buffer_unmap(buf, &map_info) on scope leave
// we support these types of data:
// video/x-raw, format=BGR -> 8bit, 3 channels
// video/x-raw, format=GRAY8 -> 8bit, 1 channel
@ -703,10 +723,11 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst)
// video/x-raw, format={BGRA, RGBA, BGRx, RGBx} -> 8bit, 4 channels
// bayer data is never decoded, the user is responsible for that
Size sz = Size ( frame_width , frame_height ) ;
guint n_planes = GST_VIDEO_INFO_N_PLANES ( & info ) ;
guint n_planes = GST_VIDEO_FRAME_N_PLANES ( & frame ) ;
if ( name = = " video/x-raw " )
{
const gchar * format_ = gst_structure_get_string ( structure , " format " ) ;
const gchar * format_ = frame . info . finfo - > name ;
if ( ! format_ )
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'format' of 'video/x-raw' " ) ;
@ -717,97 +738,83 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst)
if ( format = = " BGR " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
size_t step = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step , ( size_t ) frame_width * 3 , " " ) ;
Mat src ( sz , CV_8UC3 , map_info . data + GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) , step ) ;
Mat src ( sz , CV_8UC3 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " GRAY8 " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
size_t step = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
Mat src ( sz , CV_8UC1 , map_info . data + GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) , step ) ;
Mat src ( sz , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " GRAY16_LE " | | format = = " GRAY16_BE " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
size_t step = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
Mat src ( sz , CV_16UC1 , map_info . data + GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) , step ) ;
Mat src ( sz , CV_16UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " BGRA " | | format = = " RGBA " | | format = = " BGRX " | | format = = " RGBX " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
size_t step = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
Mat src ( sz , CV_8UC4 , map_info . data + GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) , step ) ;
Mat src ( sz , CV_8UC4 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " UYVY " | | format = = " YUY2 " | | format = = " YVYU " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
size_t step = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step , ( size_t ) frame_width * 2 , " " ) ;
Mat src ( sz , CV_8UC2 , map_info . data + GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) , step ) ;
Mat src ( sz , CV_8UC2 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " NV12 " | | format = = " NV21 " )
{
CV_CheckEQ ( ( int ) n_planes , 2 , " " ) ;
size_t stepY = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t stepY = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( stepY , ( size_t ) frame_width , " " ) ;
size_t stepUV = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 1 ) ;
size_t stepUV = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 1 ) ;
CV_CheckGE ( stepUV , ( size_t ) frame_width , " " ) ;
size_t offsetY = GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) ;
size_t offsetUV = GST_VIDEO_INFO_PLANE_OFFSET ( & info , 1 ) ;
if ( stepY ! = stepUV | | ( offsetUV - offsetY ) ! = ( stepY * frame_height ) )
{
dst . create ( Size ( frame_width , frame_height * 3 / 2 ) , CV_8UC1 ) ;
Mat dst_ = dst . getMat ( ) ;
Mat srcY ( sz , CV_8UC1 , map_info . data + offsetY , stepY ) ;
Mat srcUV ( Size ( frame_width , frame_height / 2 ) , CV_8UC1 , map_info . data + offsetUV , stepUV ) ;
srcY . copyTo ( dst_ ( Rect ( 0 , 0 , frame_width , frame_height ) ) ) ;
srcUV . copyTo ( dst_ ( Rect ( 0 , frame_height , frame_width , frame_height / 2 ) ) ) ;
}
else
{
Mat src ( Size ( frame_width , frame_height * 3 / 2 ) , CV_8UC1 , map_info . data + offsetY , stepY ) ;
src . copyTo ( dst ) ;
}
dst . create ( Size ( frame_width , frame_height * 3 / 2 ) , CV_8UC1 ) ;
Mat dst_ = dst . getMat ( ) ;
Mat srcY ( sz , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , stepY ) ;
Mat srcUV ( Size ( frame_width , frame_height / 2 ) , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 1 ) , stepUV ) ;
srcY . copyTo ( dst_ ( Rect ( 0 , 0 , frame_width , frame_height ) ) ) ;
srcUV . copyTo ( dst_ ( Rect ( 0 , frame_height , frame_width , frame_height / 2 ) ) ) ;
return true ;
}
else if ( format = = " YV12 " | | format = = " I420 " )
{
CV_CheckEQ ( ( int ) n_planes , 3 , " " ) ;
size_t step0 = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 0 ) ;
size_t step0 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
CV_CheckGE ( step0 , ( size_t ) frame_width , " " ) ;
size_t step1 = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 1 ) ;
size_t step1 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 1 ) ;
CV_CheckGE ( step1 , ( size_t ) frame_width / 2 , " " ) ;
size_t step2 = GST_VIDEO_INFO_PLANE_STRIDE ( & info , 2 ) ;
size_t step2 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 2 ) ;
CV_CheckGE ( step2 , ( size_t ) frame_width / 2 , " " ) ;
size_t offset0 = GST_VIDEO_INFO_PLANE_OFFSET ( & info , 0 ) ;
size_t offset1 = GST_VIDEO_INFO_PLANE_OFFSET ( & info , 1 ) ;
size_t offset2 = GST_VIDEO_INFO_PLANE_OFFSET ( & info , 2 ) ;
{
dst . create ( Size ( frame_width , frame_height * 3 / 2 ) , CV_8UC1 ) ;
Mat dst_ = dst . getMat ( ) ;
Mat srcY ( sz , CV_8UC1 , map_info . data + offset0 , step0 ) ;
Size sz2 ( frame_width / 2 , frame_height / 2 ) ;
Mat src1 ( sz2 , CV_8UC1 , map_info . data + offset1 , step1 ) ;
Mat src2 ( sz2 , CV_8UC1 , map_info . data + offset2 , step2 ) ;
srcY . copyTo ( dst_ ( Rect ( 0 , 0 , frame_width , frame_height ) ) ) ;
src1 . copyTo ( Mat ( sz2 , CV_8UC1 , dst_ . ptr < uchar > ( frame_height ) ) ) ;
src2 . copyTo ( Mat ( sz2 , CV_8UC1 , dst_ . ptr < uchar > ( frame_height ) + src1 . total ( ) ) ) ;
}
dst . create ( Size ( frame_width , frame_height * 3 / 2 ) , CV_8UC1 ) ;
Mat dst_ = dst . getMat ( ) ;
Mat srcY ( sz , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step0 ) ;
Size sz2 ( frame_width / 2 , frame_height / 2 ) ;
Mat src1 ( sz2 , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 1 ) , step1 ) ;
Mat src2 ( sz2 , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 2 ) , step2 ) ;
srcY . copyTo ( dst_ ( Rect ( 0 , 0 , frame_width , frame_height ) ) ) ;
src1 . copyTo ( Mat ( sz2 , CV_8UC1 , dst_ . ptr < uchar > ( frame_height ) ) ) ;
src2 . copyTo ( Mat ( sz2 , CV_8UC1 , dst_ . ptr < uchar > ( frame_height ) + src1 . total ( ) ) ) ;
return true ;
}
else
@ -818,14 +825,14 @@ bool GStreamerCapture::retrieveVideoFrame(int, OutputArray dst)
else if ( name = = " video/x-bayer " )
{
CV_CheckEQ ( ( int ) n_planes , 0 , " " ) ;
Mat src = Mat ( sz , CV_8UC1 , map_info . data ) ;
Mat src = Mat ( sz , CV_8UC1 , frame . map [ 0 ] . data ) ;
src . copyTo ( dst ) ;
return true ;
}
else if ( name = = " image/jpeg " )
{
CV_CheckEQ ( ( int ) n_planes , 0 , " " ) ;
Mat src = Mat ( Size ( map_info . size , 1 ) , CV_8UC1 , map_info . data ) ;
Mat src = Mat ( Size ( frame . map [ 0 ] . size , 1 ) , CV_8UC1 , frame . map [ 0 ] . data ) ;
src . copyTo ( dst ) ;
return true ;
}