2010-05-12 01:44:00 +08:00
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
2011-04-24 05:24:41 +08:00
// Copyright (C) 2008, 2011, Nils Hasler, all rights reserved.
2010-05-12 01:44:00 +08:00
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
2013-03-21 17:17:34 +08:00
/*!
* \ file cap_gstreamer . cpp
* \ author Nils Hasler < hasler @ mpi - inf . mpg . de >
* Max - Planck - Institut Informatik
* \ author Dirk Van Haerenborgh < vhdirk @ gmail . com >
*
* \ brief Use GStreamer to read / write video
*/
2010-05-12 01:44:00 +08:00
# include "precomp.hpp"
2019-06-20 18:51:57 +08:00
# include <opencv2/core/utils/logger.hpp>
# include <opencv2/core/utils/filesystem.hpp>
2018-04-16 20:59:25 +08:00
# include <iostream>
2010-05-12 01:44:00 +08:00
# include <string.h>
2020-10-02 09:48:15 +08:00
# include <thread>
2019-06-20 18:51:57 +08:00
2010-05-12 01:44:00 +08:00
# include <gst/gst.h>
2013-03-21 17:17:34 +08:00
# include <gst/gstbuffer.h>
2010-05-12 01:44:00 +08:00
# include <gst/video/video.h>
2021-12-17 03:43:02 +08:00
# include <gst/audio/audio.h>
2010-05-12 01:44:00 +08:00
# include <gst/app/gstappsink.h>
# include <gst/app/gstappsrc.h>
# include <gst/riff/riff-media.h>
2013-03-21 17:17:34 +08:00
# include <gst/pbutils/missing-plugins.h>
2013-03-26 16:27:39 +08:00
# define VERSION_NUM(major, minor, micro) (major * 1000000 + minor * 1000 + micro)
# define FULL_GST_VERSION VERSION_NUM(GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO)
# include <gst/pbutils/encoding-profile.h>
2013-03-21 17:17:34 +08:00
//#include <gst/base/gsttypefindhelper.h>
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
# define CV_WARN(...) CV_LOG_WARNING(NULL, "OpenCV | GStreamer warning: " << __VA_ARGS__)
2010-05-12 01:44:00 +08:00
2017-11-23 20:03:20 +08:00
# define COLOR_ELEM "videoconvert"
2015-08-17 02:01:22 +08:00
# define COLOR_ELEM_NAME COLOR_ELEM
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
# define CV_GST_FORMAT(format) (format)
namespace cv {
static void toFraction ( double decimal , CV_OUT int & numerator , CV_OUT int & denominator ) ;
static void handleMessage ( GstElement * pipeline ) ;
namespace {
2019-07-04 05:55:30 +08:00
# if defined __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-function"
# endif
2019-06-20 18:51:57 +08:00
template < typename T > static inline void GSafePtr_addref ( T * ptr )
2017-08-20 05:51:02 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ptr )
g_object_ref_sink ( ptr ) ;
2017-08-20 05:51:02 +08:00
}
2019-06-20 18:51:57 +08:00
template < typename T > static inline void GSafePtr_release ( T * * pPtr ) ;
template < > inline void GSafePtr_release < GError > ( GError * * pPtr ) { g_clear_error ( pPtr ) ; }
template < > inline void GSafePtr_release < GstElement > ( GstElement * * pPtr ) { if ( pPtr ) { gst_object_unref ( G_OBJECT ( * pPtr ) ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstElementFactory > ( GstElementFactory * * pPtr ) { if ( pPtr ) { gst_object_unref ( G_OBJECT ( * pPtr ) ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstPad > ( GstPad * * pPtr ) { if ( pPtr ) { gst_object_unref ( G_OBJECT ( * pPtr ) ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstCaps > ( GstCaps * * pPtr ) { if ( pPtr ) { gst_caps_unref ( * pPtr ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstBuffer > ( GstBuffer * * pPtr ) { if ( pPtr ) { gst_buffer_unref ( * pPtr ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstSample > ( GstSample * * pPtr ) { if ( pPtr ) { gst_sample_unref ( * pPtr ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstBus > ( GstBus * * pPtr ) { if ( pPtr ) { gst_object_unref ( G_OBJECT ( * pPtr ) ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstMessage > ( GstMessage * * pPtr ) { if ( pPtr ) { gst_message_unref ( * pPtr ) ; * pPtr = NULL ; } }
2020-10-02 09:48:15 +08:00
template < > inline void GSafePtr_release < GMainLoop > ( GMainLoop * * pPtr ) { if ( pPtr ) { g_main_loop_unref ( * pPtr ) ; * pPtr = NULL ; } }
2019-06-20 18:51:57 +08:00
template < > inline void GSafePtr_release < GstEncodingVideoProfile > ( GstEncodingVideoProfile * * pPtr ) { if ( pPtr ) { gst_encoding_profile_unref ( * pPtr ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_release < GstEncodingContainerProfile > ( GstEncodingContainerProfile * * pPtr ) { if ( pPtr ) { gst_object_unref ( G_OBJECT ( * pPtr ) ) ; * pPtr = NULL ; } }
template < > inline void GSafePtr_addref < char > ( char * pPtr ) ; // declaration only. not defined. should not be used
template < > inline void GSafePtr_release < char > ( char * * pPtr ) { if ( pPtr ) { g_free ( * pPtr ) ; * pPtr = NULL ; } }
2019-07-04 05:55:30 +08:00
# if defined __clang__
# pragma clang diagnostic pop
# endif
2019-06-20 18:51:57 +08:00
template < typename T >
class GSafePtr
{
protected :
T * ptr ;
public :
inline GSafePtr ( ) CV_NOEXCEPT : ptr ( NULL ) { }
inline ~ GSafePtr ( ) CV_NOEXCEPT { release ( ) ; }
inline void release ( ) CV_NOEXCEPT
{
#if 0
printf ( " release: %s:%d: %p \n " , CV__TRACE_FUNCTION , __LINE__ , ptr ) ;
if ( ptr ) {
printf ( " refcount: %d \n " , ( int ) GST_OBJECT_REFCOUNT_VALUE ( ptr ) ) ; \
}
2017-08-20 05:51:02 +08:00
# endif
2019-06-20 18:51:57 +08:00
if ( ptr )
GSafePtr_release < T > ( & ptr ) ;
}
inline operator T * ( ) CV_NOEXCEPT { return ptr ; }
inline operator /*const*/ T * ( ) const CV_NOEXCEPT { return ( T * ) ptr ; } // there is no const correctness in Gst C API
2019-07-19 06:12:01 +08:00
T * get ( ) { CV_Assert ( ptr ) ; return ptr ; }
/*const*/ T * get ( ) const { CV_Assert ( ptr ) ; return ( T * ) ptr ; } // there is no const correctness in Gst C API
2019-06-20 18:51:57 +08:00
2019-07-19 06:12:01 +08:00
const T * operator - > ( ) const { CV_Assert ( ptr ) ; return ptr ; }
2019-06-20 18:51:57 +08:00
inline operator bool ( ) const CV_NOEXCEPT { return ptr ! = NULL ; }
inline bool operator ! ( ) const CV_NOEXCEPT { return ptr = = NULL ; }
2019-07-19 06:12:01 +08:00
T * * getRef ( ) { CV_Assert ( ptr = = NULL ) ; return & ptr ; }
2019-06-20 18:51:57 +08:00
inline GSafePtr & reset ( T * p ) CV_NOEXCEPT // pass result of functions with "transfer floating" ownership
{
//printf("reset: %s:%d: %p\n", CV__TRACE_FUNCTION, __LINE__, p);
release ( ) ;
if ( p )
{
GSafePtr_addref < T > ( p ) ;
ptr = p ;
}
return * this ;
}
2015-12-09 22:08:19 +08:00
2019-06-20 18:51:57 +08:00
inline GSafePtr & attach ( T * p ) CV_NOEXCEPT // pass result of functions with "transfer full" ownership
{
//printf("attach: %s:%d: %p\n", CV__TRACE_FUNCTION, __LINE__, p);
release ( ) ; ptr = p ; return * this ;
}
inline T * detach ( ) CV_NOEXCEPT { T * p = ptr ; ptr = NULL ; return p ; }
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
inline void swap ( GSafePtr & o ) CV_NOEXCEPT { std : : swap ( ptr , o . ptr ) ; }
private :
GSafePtr ( const GSafePtr & ) ; // = disabled
GSafePtr & operator = ( const T * ) ; // = disabled
} ;
2013-03-21 17:17:34 +08:00
2021-02-24 16:40:42 +08:00
class ScopeGuardGstMapInfo
{
GstBuffer * buf_ ;
GstMapInfo * info_ ;
public :
ScopeGuardGstMapInfo ( GstBuffer * buf , GstMapInfo * info )
: buf_ ( buf ) , info_ ( info )
{ }
~ ScopeGuardGstMapInfo ( )
{
gst_buffer_unmap ( buf_ , info_ ) ;
}
} ;
2022-05-23 10:08:38 +08:00
class ScopeGuardGstVideoFrame
{
GstVideoFrame * frame_ ;
public :
ScopeGuardGstVideoFrame ( GstVideoFrame * frame )
: frame_ ( frame )
{ }
~ ScopeGuardGstVideoFrame ( )
{
gst_video_frame_unmap ( frame_ ) ;
}
} ;
2019-06-20 18:51:57 +08:00
} // namespace
2012-11-19 20:44:23 +08:00
2013-03-21 17:17:34 +08:00
/*!
* \ brief The gst_initializer class
* Initializes gstreamer once in the whole process
2013-03-21 23:11:53 +08:00
*/
class gst_initializer
2012-11-19 20:44:23 +08:00
{
public :
2019-06-20 18:51:57 +08:00
static gst_initializer & init ( )
2012-11-19 20:44:23 +08:00
{
2019-06-20 18:51:57 +08:00
static gst_initializer g_init ;
if ( g_init . isFailed )
CV_Error ( Error : : StsError , " Can't initialize GStreamer " ) ;
return g_init ;
2012-11-19 20:44:23 +08:00
}
private :
2019-06-20 18:51:57 +08:00
bool isFailed ;
bool call_deinit ;
2020-10-02 09:48:15 +08:00
bool start_loop ;
GSafePtr < GMainLoop > loop ;
std : : thread thread ;
2019-06-20 18:51:57 +08:00
gst_initializer ( ) :
isFailed ( false )
2012-11-19 20:44:23 +08:00
{
2019-06-20 18:51:57 +08:00
call_deinit = utils : : getConfigurationParameterBool ( " OPENCV_VIDEOIO_GSTREAMER_CALL_DEINIT " , false ) ;
2020-10-02 09:48:15 +08:00
start_loop = utils : : getConfigurationParameterBool ( " OPENCV_VIDEOIO_GSTREAMER_START_MAINLOOP " , false ) ;
2019-06-20 18:51:57 +08:00
GSafePtr < GError > err ;
2021-10-02 09:07:25 +08:00
gboolean gst_init_res = gst_init_check ( NULL , NULL , err . getRef ( ) ) ;
if ( ! gst_init_res )
2019-06-20 18:51:57 +08:00
{
2021-10-02 09:07:25 +08:00
CV_WARN ( " Can't initialize GStreamer: " < < ( err ? err - > message : " <unknown reason> " ) ) ;
2019-06-20 18:51:57 +08:00
isFailed = true ;
return ;
}
2018-04-16 20:59:25 +08:00
guint major , minor , micro , nano ;
gst_version ( & major , & minor , & micro , & nano ) ;
2018-05-23 15:48:13 +08:00
if ( GST_VERSION_MAJOR ! = major )
2018-04-16 20:59:25 +08:00
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " incompatible GStreamer version " ) ;
isFailed = true ;
return ;
}
2020-10-02 09:48:15 +08:00
if ( start_loop )
{
loop . attach ( g_main_loop_new ( NULL , FALSE ) ) ;
thread = std : : thread ( [ this ] ( ) {
g_main_loop_run ( loop ) ;
} ) ;
}
2019-06-20 18:51:57 +08:00
}
~ gst_initializer ( )
{
if ( call_deinit )
{
// Debug leaks: GST_LEAKS_TRACER_STACK_TRACE=1 GST_DEBUG="GST_TRACER:7" GST_TRACERS="leaks"
gst_deinit ( ) ;
2018-04-16 20:59:25 +08:00
}
2020-10-02 09:48:15 +08:00
if ( start_loop )
{
g_main_loop_quit ( loop ) ;
thread . join ( ) ;
}
2012-11-19 20:44:23 +08:00
}
} ;
2019-06-20 18:51:57 +08:00
inline static
std : : string get_gst_propname ( int propId )
2010-05-12 01:44:00 +08:00
{
2018-04-16 20:59:25 +08:00
switch ( propId )
{
case CV_CAP_PROP_BRIGHTNESS : return " brightness " ;
case CV_CAP_PROP_CONTRAST : return " contrast " ;
case CV_CAP_PROP_SATURATION : return " saturation " ;
case CV_CAP_PROP_HUE : return " hue " ;
2019-06-20 18:51:57 +08:00
default : return std : : string ( ) ;
2018-04-16 20:59:25 +08:00
}
}
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
inline static
bool is_gst_element_exists ( const std : : string & name )
2018-04-16 20:59:25 +08:00
{
2019-06-20 18:51:57 +08:00
GSafePtr < GstElementFactory > testfac ; testfac . attach ( gst_element_factory_find ( name . c_str ( ) ) ) ;
return ( bool ) testfac ;
2018-04-16 20:59:25 +08:00
}
2010-05-12 01:44:00 +08:00
2021-03-01 23:51:04 +08:00
static void find_hw_element ( const GValue * item , gpointer va_type )
{
GstElement * element = GST_ELEMENT ( g_value_get_object ( item ) ) ;
const gchar * name = g_type_name ( G_OBJECT_TYPE ( element ) ) ;
if ( name ) {
std : : string name_lower = toLowerCase ( name ) ;
if ( name_lower . find ( " vaapi " ) ! = std : : string : : npos ) {
* ( int * ) va_type = VIDEO_ACCELERATION_VAAPI ;
} else if ( name_lower . find ( " mfx " ) ! = std : : string : : npos | | name_lower . find ( " msdk " ) ! = std : : string : : npos ) {
* ( int * ) va_type = VIDEO_ACCELERATION_MFX ;
} else if ( name_lower . find ( " d3d11 " ) ! = std : : string : : npos ) {
* ( int * ) va_type = VIDEO_ACCELERATION_D3D11 ;
}
}
}
2018-04-16 20:59:25 +08:00
//==================================================================================================
2010-05-12 01:44:00 +08:00
2018-04-24 19:29:01 +08:00
class GStreamerCapture CV_FINAL : public IVideoCapture
2018-04-16 20:59:25 +08:00
{
private :
2019-06-20 18:51:57 +08:00
GSafePtr < GstElement > pipeline ;
GSafePtr < GstElement > v4l2src ;
GSafePtr < GstElement > sink ;
GSafePtr < GstSample > sample ;
GSafePtr < GstCaps > caps ;
2021-12-17 03:43:02 +08:00
gint videoStream ;
gint audioStream ;
2014-09-10 14:38:43 +08:00
gint64 duration ;
2014-09-25 18:40:54 +08:00
gint width ;
gint height ;
double fps ;
2017-07-05 18:45:08 +08:00
bool isPosFramesSupported ;
bool isPosFramesEmulated ;
gint64 emulatedFrameNumber ;
2021-12-17 03:43:02 +08:00
gint outputAudioFormat ;
gint audioBaseIndex ;
gint nAudioChannels ;
gint audioSamplesPerSecond ;
Mat audioFrame ;
2018-04-16 20:59:25 +08:00
2021-03-01 23:51:04 +08:00
VideoAccelerationType va_type ;
int hw_device ;
2018-04-16 20:59:25 +08:00
public :
GStreamerCapture ( ) ;
2019-06-20 18:51:57 +08:00
virtual ~ GStreamerCapture ( ) CV_OVERRIDE ;
2018-04-24 19:29:01 +08:00
virtual bool grabFrame ( ) CV_OVERRIDE ;
virtual bool retrieveFrame ( int /*unused*/ , OutputArray dst ) CV_OVERRIDE ;
2021-12-17 03:43:02 +08:00
bool grabAudioFrame ( ) ;
bool retrieveVideoFrame ( int /*unused*/ , OutputArray dst ) ;
bool retrieveAudioFrame ( int /*unused*/ , OutputArray dst ) ;
2018-04-24 19:29:01 +08:00
virtual double getProperty ( int propId ) const CV_OVERRIDE ;
virtual bool setProperty ( int propId , double value ) CV_OVERRIDE ;
2019-06-20 18:51:57 +08:00
virtual bool isOpened ( ) const CV_OVERRIDE { return ( bool ) pipeline ; }
2018-09-16 04:54:03 +08:00
virtual int getCaptureDomain ( ) CV_OVERRIDE { return cv : : CAP_GSTREAMER ; }
2021-03-01 23:51:04 +08:00
bool open ( int id , const cv : : VideoCaptureParameters & params ) ;
bool open ( const String & filename_ , const cv : : VideoCaptureParameters & params ) ;
2018-04-16 20:59:25 +08:00
static void newPad ( GstElement * /*elem*/ , GstPad * pad , gpointer data ) ;
2021-12-17 03:43:02 +08:00
bool configureHW ( const cv : : VideoCaptureParameters & ) ;
bool configureStreams ( const cv : : VideoCaptureParameters & ) ;
bool setAudioProperties ( const cv : : VideoCaptureParameters & ) ;
2018-04-16 20:59:25 +08:00
protected :
bool isPipelinePlaying ( ) ;
void startPipeline ( ) ;
void stopPipeline ( ) ;
void restartPipeline ( ) ;
void setFilter ( const char * prop , int type , int v1 , int v2 ) ;
void removeFilter ( const char * filter ) ;
2010-05-12 01:44:00 +08:00
} ;
2011-04-23 15:27:04 +08:00
2018-04-16 20:59:25 +08:00
GStreamerCapture : : GStreamerCapture ( ) :
2021-12-17 03:43:02 +08:00
videoStream ( 0 ) ,
audioStream ( - 1 ) ,
2019-06-20 18:51:57 +08:00
duration ( - 1 ) , width ( - 1 ) , height ( - 1 ) , fps ( - 1 ) ,
2018-04-16 20:59:25 +08:00
isPosFramesSupported ( false ) ,
isPosFramesEmulated ( false ) ,
2021-12-17 03:43:02 +08:00
emulatedFrameNumber ( - 1 ) ,
outputAudioFormat ( CV_16S ) ,
audioBaseIndex ( 1 ) ,
nAudioChannels ( 0 ) ,
audioSamplesPerSecond ( 44100 )
2021-03-01 23:51:04 +08:00
, va_type ( VIDEO_ACCELERATION_NONE )
, hw_device ( - 1 )
2018-04-16 20:59:25 +08:00
{
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : close
* Closes the pipeline and destroys all instances
*/
2018-04-16 20:59:25 +08:00
GStreamerCapture : : ~ GStreamerCapture ( )
2010-05-12 01:44:00 +08:00
{
2013-03-21 17:17:34 +08:00
if ( isPipelinePlaying ( ) )
2018-04-16 20:59:25 +08:00
stopPipeline ( ) ;
2019-06-20 18:51:57 +08:00
if ( pipeline & & GST_IS_ELEMENT ( pipeline . get ( ) ) )
2018-04-16 20:59:25 +08:00
{
2019-06-20 18:51:57 +08:00
gst_element_set_state ( pipeline , GST_STATE_NULL ) ;
pipeline . release ( ) ;
2013-03-21 17:17:34 +08:00
}
2010-05-12 01:44:00 +08:00
}
2021-12-17 03:43:02 +08:00
bool GStreamerCapture : : configureHW ( const cv : : VideoCaptureParameters & params )
{
if ( params . has ( CAP_PROP_HW_ACCELERATION ) )
{
va_type = params . get < VideoAccelerationType > ( CAP_PROP_HW_ACCELERATION ) ;
}
if ( params . has ( CAP_PROP_HW_DEVICE ) )
{
hw_device = params . get < int > ( CAP_PROP_HW_DEVICE ) ;
if ( va_type = = VIDEO_ACCELERATION_NONE & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Invalid usage of CAP_PROP_HW_DEVICE without requested H/W acceleration. Bailout " ) ;
return false ;
}
if ( va_type = = VIDEO_ACCELERATION_ANY & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Invalid usage of CAP_PROP_HW_DEVICE with 'ANY' H/W acceleration. Bailout " ) ;
return false ;
}
if ( hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: CAP_PROP_HW_DEVICE is not supported. Specify -1 (auto) value. Bailout " ) ;
return false ;
}
}
return true ;
}
bool GStreamerCapture : : configureStreams ( const cv : : VideoCaptureParameters & params )
{
if ( params . has ( CAP_PROP_VIDEO_STREAM ) )
{
double value = params . get < double > ( CAP_PROP_VIDEO_STREAM ) ;
if ( value = = - 1 | | value = = 0 )
videoStream = static_cast < gint > ( value ) ;
else
{
CV_LOG_ERROR ( NULL , " VIDEOIO/MSMF: CAP_PROP_VIDEO_STREAM parameter value is invalid/unsupported: " < < value ) ;
return false ;
}
}
if ( params . has ( CAP_PROP_AUDIO_STREAM ) )
{
double value = params . get < double > ( CAP_PROP_AUDIO_STREAM ) ;
if ( value = = - 1 | | value > - 1 )
audioStream = static_cast < gint > ( value ) ;
else
{
CV_LOG_ERROR ( NULL , " VIDEOIO/MSMF: CAP_PROP_AUDIO_STREAM parameter value is invalid/unsupported: " < < value ) ;
return false ;
}
}
return true ;
}
bool GStreamerCapture : : setAudioProperties ( const cv : : VideoCaptureParameters & params )
{
if ( params . has ( CAP_PROP_AUDIO_DATA_DEPTH ) )
{
gint value = static_cast < gint > ( params . get < double > ( CAP_PROP_AUDIO_DATA_DEPTH ) ) ;
if ( value ! = CV_8S & & value ! = CV_16S & & value ! = CV_32S & & value ! = CV_32F )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/MSMF: CAP_PROP_AUDIO_DATA_DEPTH parameter value is invalid/unsupported: " < < value ) ;
return false ;
}
else
{
outputAudioFormat = value ;
}
}
if ( params . has ( CAP_PROP_AUDIO_SAMPLES_PER_SECOND ) )
{
int value = static_cast < int > ( params . get < double > ( CAP_PROP_AUDIO_SAMPLES_PER_SECOND ) ) ;
if ( value < 0 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/MSMF: CAP_PROP_AUDIO_SAMPLES_PER_SECOND parameter can't be negative: " < < value ) ;
return false ;
}
else
{
audioSamplesPerSecond = value ;
}
}
return true ;
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : grabFrame
* \ return
2022-04-30 13:40:07 +08:00
* Grabs a sample from the pipeline , awaiting consumation by retrieveFrame .
2013-03-21 17:17:34 +08:00
* The pipeline is started if it was not running yet
*/
2018-04-16 20:59:25 +08:00
bool GStreamerCapture : : grabFrame ( )
2010-05-12 01:44:00 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ! pipeline | | ! GST_IS_ELEMENT ( pipeline . get ( ) ) )
2011-04-13 15:15:35 +08:00
return false ;
2010-05-12 01:44:00 +08:00
2013-03-21 17:17:34 +08:00
// start the pipeline if it was not in playing state yet
2019-06-20 18:51:57 +08:00
if ( ! this - > isPipelinePlaying ( ) )
2013-03-21 17:17:34 +08:00
this - > startPipeline ( ) ;
// bail out if EOS
2019-06-20 18:51:57 +08:00
if ( gst_app_sink_is_eos ( GST_APP_SINK ( sink . get ( ) ) ) )
2011-04-13 15:15:35 +08:00
return false ;
2019-06-20 18:51:57 +08:00
sample . attach ( gst_app_sink_pull_sample ( GST_APP_SINK ( sink . get ( ) ) ) ) ;
if ( ! sample )
2013-03-21 17:17:34 +08:00
return false ;
2017-07-05 18:45:08 +08:00
if ( isPosFramesEmulated )
emulatedFrameNumber + + ;
2021-12-17 03:43:02 +08:00
if ( audioStream > = 0 )
return grabAudioFrame ( ) ;
2011-04-13 15:15:35 +08:00
return true ;
2010-05-12 01:44:00 +08:00
}
2021-12-17 03:43:02 +08:00
bool GStreamerCapture : : grabAudioFrame ( )
2010-05-12 01:44:00 +08:00
{
2021-12-17 03:43:02 +08:00
GstCaps * frame_caps = gst_sample_get_caps ( sample ) ; // no lifetime transfer
if ( ! frame_caps )
{
CV_LOG_ERROR ( NULL , " GStreamer: gst_sample_get_caps() returns NULL " ) ;
return false ;
}
if ( ! GST_CAPS_IS_SIMPLE ( frame_caps ) )
{
// bail out in no caps
CV_LOG_ERROR ( NULL , " GStreamer: GST_CAPS_IS_SIMPLE(frame_caps) check is failed " ) ;
return false ;
}
GstAudioInfo info = { } ;
gboolean audio_info_res = gst_audio_info_from_caps ( & info , frame_caps ) ;
if ( ! audio_info_res )
{
CV_Error ( Error : : StsError , " GStreamer: gst_audio_info_from_caps() is failed. Can't handle unknown layout " ) ;
}
int bpf = GST_AUDIO_INFO_BPF ( & info ) ;
2021-12-21 22:32:19 +08:00
CV_CheckGT ( bpf , 0 , " " ) ;
2021-12-17 03:43:02 +08:00
GstStructure * structure = gst_caps_get_structure ( frame_caps , 0 ) ; // no lifetime transfer
if ( ! structure )
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'structure'-0 from GStreamer sample " ) ;
return false ;
}
const gchar * name_ = gst_structure_get_name ( structure ) ;
if ( ! name_ )
2021-02-24 16:40:42 +08:00
{
2021-12-17 03:43:02 +08:00
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'name' from GStreamer sample " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2021-02-24 16:40:42 +08:00
}
2021-12-17 03:43:02 +08:00
std : : string name = toLowerCase ( std : : string ( name_ ) ) ;
2010-05-12 01:44:00 +08:00
2021-12-17 03:43:02 +08:00
GstBuffer * buf = gst_sample_get_buffer ( sample ) ;
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 ) ;
if ( name = = " audio/x-raw " )
{
const gchar * format_ = gst_structure_get_string ( structure , " format " ) ;
if ( ! format_ )
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'format' of 'video/x-raw' " ) ;
return false ;
}
std : : string format = toUpperCase ( std : : string ( format_ ) ) ;
cv : : Mat data ;
if ( format = = " S8 " )
{
Mat ( map_info . size / bpf , nAudioChannels , CV_8S , map_info . data ) . copyTo ( audioFrame ) ;
return true ;
}
if ( format = = " S16LE " )
{
Mat ( map_info . size / bpf , nAudioChannels , CV_16S , map_info . data ) . copyTo ( audioFrame ) ;
return true ;
}
if ( format = = " S32LE " )
{
Mat ( map_info . size / bpf , nAudioChannels , CV_32S , map_info . data ) . copyTo ( audioFrame ) ;
return true ;
}
if ( format = = " F32LE " )
{
Mat ( map_info . size / bpf , nAudioChannels , CV_32F , map_info . data ) . copyTo ( audioFrame ) ;
return true ;
}
CV_Error_ ( Error : : StsNotImplemented , ( " Unsupported GStreamer audio format: %s " , format . c_str ( ) ) ) ;
}
CV_Error_ ( Error : : StsNotImplemented , ( " Unsupported GStreamer layer type: %s " , name . c_str ( ) ) ) ;
}
bool GStreamerCapture : : retrieveAudioFrame ( int index , OutputArray dst )
{
CV_Check ( index , index > = audioBaseIndex & & index < audioBaseIndex + nAudioChannels , " " ) ;
index - = audioBaseIndex ;
CV_CheckType ( outputAudioFormat ,
outputAudioFormat = = CV_8S | |
outputAudioFormat = = CV_16S | |
outputAudioFormat = = CV_32S | |
outputAudioFormat = = CV_32F ,
" " ) ;
dst . create ( 1 , audioFrame . rows , outputAudioFormat ) ;
Mat data = dst . getMat ( ) ;
switch ( outputAudioFormat )
{
case CV_8S :
for ( int i = 0 ; i < audioFrame . rows ; i + + )
data . at < char > ( i ) = audioFrame . at < char > ( i , index ) ;
return true ;
case CV_16S :
for ( int i = 0 ; i < audioFrame . rows ; i + + )
data . at < short > ( i ) = audioFrame . at < short > ( i , index ) ;
return true ;
case CV_32S :
for ( int i = 0 ; i < audioFrame . rows ; i + + )
data . at < int > ( i ) = audioFrame . at < int > ( i , index ) ;
return true ;
case CV_32F :
for ( int i = 0 ; i < audioFrame . rows ; i + + )
data . at < float > ( i ) = audioFrame . at < float > ( i , index ) ;
return true ;
}
dst . release ( ) ;
return false ;
}
bool GStreamerCapture : : retrieveVideoFrame ( int , OutputArray dst )
{
2021-02-24 16:40:42 +08:00
GstCaps * frame_caps = gst_sample_get_caps ( sample ) ; // no lifetime transfer
if ( ! frame_caps )
2013-03-21 17:17:34 +08:00
{
2021-02-24 16:40:42 +08:00
CV_LOG_ERROR ( NULL , " GStreamer: gst_sample_get_caps() returns NULL " ) ;
2018-04-16 20:59:25 +08:00
return false ;
}
2021-02-24 16:40:42 +08:00
if ( ! GST_CAPS_IS_SIMPLE ( frame_caps ) )
2018-04-16 20:59:25 +08:00
{
2021-02-24 16:40:42 +08:00
// bail out in no caps
CV_LOG_ERROR ( NULL , " GStreamer: GST_CAPS_IS_SIMPLE(frame_caps) check is failed " ) ;
return false ;
2018-04-16 20:59:25 +08:00
}
2021-02-24 16:40:42 +08:00
GstVideoInfo info = { } ;
gboolean video_info_res = gst_video_info_from_caps ( & info , frame_caps ) ;
if ( ! video_info_res )
2019-06-20 18:51:57 +08:00
{
2021-02-24 16:40:42 +08:00
CV_Error ( Error : : StsError , " GStreamer: gst_video_info_from_caps() is failed. Can't handle unknown layout " ) ;
2019-06-20 18:51:57 +08:00
}
2018-04-16 20:59:25 +08:00
2022-05-23 10:08:38 +08:00
// 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 ) ;
2021-02-24 16:40:42 +08:00
if ( frame_width < = 0 | | frame_height < = 0 )
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query frame size from GStreamer sample " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2021-02-24 16:40:42 +08:00
}
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
GstStructure * structure = gst_caps_get_structure ( frame_caps , 0 ) ; // no lifetime transfer
2021-02-24 16:40:42 +08:00
if ( ! structure )
2019-06-20 18:51:57 +08:00
{
2021-02-24 16:40:42 +08:00
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'structure'-0 from GStreamer sample " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2019-06-20 18:51:57 +08:00
}
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
const gchar * name_ = gst_structure_get_name ( structure ) ;
if ( ! name_ )
2021-02-24 16:40:42 +08:00
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'name' from GStreamer sample " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2021-02-24 16:40:42 +08:00
}
2019-06-20 18:51:57 +08:00
std : : string name = toLowerCase ( std : : string ( name_ ) ) ;
2010-05-12 01:44:00 +08:00
2021-02-24 16:40:42 +08:00
// we support these types of data:
2018-04-16 20:59:25 +08:00
// video/x-raw, format=BGR -> 8bit, 3 channels
// video/x-raw, format=GRAY8 -> 8bit, 1 channel
// video/x-raw, format=UYVY -> 8bit, 2 channel
// video/x-raw, format=YUY2 -> 8bit, 2 channel
// video/x-raw, format=YVYU -> 8bit, 2 channel
// video/x-raw, format=NV12 -> 8bit, 1 channel (height is 1.5x larger than true height)
// video/x-raw, format=NV21 -> 8bit, 1 channel (height is 1.5x larger than true height)
// video/x-raw, format=YV12 -> 8bit, 1 channel (height is 1.5x larger than true height)
// video/x-raw, format=I420 -> 8bit, 1 channel (height is 1.5x larger than true height)
// video/x-bayer -> 8bit, 1 channel
// image/jpeg -> 8bit, mjpeg: buffer_size x 1 x 1
2021-08-17 00:20:10 +08:00
// video/x-raw, format=GRAY16_LE (BE) -> 16 bit, 1 channel
// video/x-raw, format={BGRA, RGBA, BGRx, RGBx} -> 8bit, 4 channels
2018-04-16 20:59:25 +08:00
// bayer data is never decoded, the user is responsible for that
2021-02-24 16:40:42 +08:00
Size sz = Size ( frame_width , frame_height ) ;
2022-05-23 10:08:38 +08:00
guint n_planes = GST_VIDEO_FRAME_N_PLANES ( & frame ) ;
2019-06-20 18:51:57 +08:00
if ( name = = " video/x-raw " )
2018-04-16 20:59:25 +08:00
{
2022-05-23 10:08:38 +08:00
const gchar * format_ = frame . info . finfo - > name ;
2019-06-20 18:51:57 +08:00
if ( ! format_ )
2021-02-24 16:40:42 +08:00
{
CV_LOG_ERROR ( NULL , " GStreamer: Can't query 'format' of 'video/x-raw' " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2021-02-24 16:40:42 +08:00
}
2019-06-20 18:51:57 +08:00
std : : string format = toUpperCase ( std : : string ( format_ ) ) ;
if ( format = = " BGR " )
2013-03-21 17:17:34 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step , ( size_t ) frame_width * 3 , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src ( sz , CV_8UC3 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
2021-02-24 16:40:42 +08:00
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " GRAY8 " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src ( sz , CV_8UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
2021-02-24 16:40:42 +08:00
src . copyTo ( dst ) ;
return true ;
2013-03-21 17:17:34 +08:00
}
2021-08-17 00:20:10 +08:00
else if ( format = = " GRAY16_LE " | | format = = " GRAY16_BE " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-08-17 00:20:10 +08:00
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src ( sz , CV_16UC1 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
2021-08-17 00:20:10 +08:00
src . copyTo ( dst ) ;
return true ;
}
else if ( format = = " BGRA " | | format = = " RGBA " | | format = = " BGRX " | | format = = " RGBX " )
{
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-08-17 00:20:10 +08:00
CV_CheckGE ( step , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src ( sz , CV_8UC4 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
2021-08-17 00:20:10 +08:00
src . copyTo ( dst ) ;
return true ;
}
2019-06-20 18:51:57 +08:00
else if ( format = = " UYVY " | | format = = " YUY2 " | | format = = " YVYU " )
2013-03-21 17:17:34 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 1 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step , ( size_t ) frame_width * 2 , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src ( sz , CV_8UC2 , GST_VIDEO_FRAME_PLANE_DATA ( & frame , 0 ) , step ) ;
2021-02-24 16:40:42 +08:00
src . copyTo ( dst ) ;
return true ;
2013-03-21 17:17:34 +08:00
}
2021-02-24 16:40:42 +08:00
else if ( format = = " NV12 " | | format = = " NV21 " )
2018-04-16 20:59:25 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 2 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t stepY = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( stepY , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
size_t stepUV = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 1 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( stepUV , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
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 ) ) ) ;
2021-02-24 16:40:42 +08:00
return true ;
2018-04-16 20:59:25 +08:00
}
2021-02-24 16:40:42 +08:00
else if ( format = = " YV12 " | | format = = " I420 " )
2018-04-16 20:59:25 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 3 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step0 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 0 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step0 , ( size_t ) frame_width , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step1 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 1 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step1 , ( size_t ) frame_width / 2 , " " ) ;
2022-05-23 10:08:38 +08:00
size_t step2 = GST_VIDEO_FRAME_PLANE_STRIDE ( & frame , 2 ) ;
2021-02-24 16:40:42 +08:00
CV_CheckGE ( step2 , ( size_t ) frame_width / 2 , " " ) ;
2022-05-23 10:08:38 +08:00
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 ( ) ) ) ;
2021-02-24 16:40:42 +08:00
return true ;
2013-03-21 17:17:34 +08:00
}
2019-06-20 18:51:57 +08:00
else
{
2021-02-24 16:40:42 +08:00
CV_Error_ ( Error : : StsNotImplemented , ( " Unsupported GStreamer 'video/x-raw' format: %s " , format . c_str ( ) ) ) ;
2019-06-20 18:51:57 +08:00
}
2011-04-13 15:15:35 +08:00
}
2019-06-20 18:51:57 +08:00
else if ( name = = " video/x-bayer " )
2017-11-02 23:27:30 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 0 , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src = Mat ( sz , CV_8UC1 , frame . map [ 0 ] . data ) ;
2021-02-24 16:40:42 +08:00
src . copyTo ( dst ) ;
return true ;
2016-03-03 00:22:49 +08:00
}
2019-06-20 18:51:57 +08:00
else if ( name = = " image/jpeg " )
2018-04-16 20:59:25 +08:00
{
2021-02-24 16:40:42 +08:00
CV_CheckEQ ( ( int ) n_planes , 0 , " " ) ;
2022-05-23 10:08:38 +08:00
Mat src = Mat ( Size ( frame . map [ 0 ] . size , 1 ) , CV_8UC1 , frame . map [ 0 ] . data ) ;
2021-02-24 16:40:42 +08:00
src . copyTo ( dst ) ;
return true ;
2019-06-20 18:51:57 +08:00
}
2021-02-24 16:40:42 +08:00
CV_Error_ ( Error : : StsNotImplemented , ( " Unsupported GStreamer layer type: %s " , name . c_str ( ) ) ) ;
2010-05-12 01:44:00 +08:00
}
2021-12-17 03:43:02 +08:00
bool GStreamerCapture : : retrieveFrame ( int index , OutputArray dst )
{
if ( index < 0 )
return false ;
if ( ! sample )
return false ;
if ( index = = 0 )
{
CV_CheckGE ( videoStream , 0 , " No video stream configured " ) ;
return retrieveVideoFrame ( index , dst ) ;
}
else if ( index > = audioBaseIndex )
{
CV_CheckGE ( audioStream , 0 , " No audio stream configured " ) ;
return retrieveAudioFrame ( index , dst ) ;
}
CV_LOG_ERROR ( NULL , " GStreamer(retrive): unrecognized index= " < < index ) ;
return false ;
}
2018-04-16 20:59:25 +08:00
bool GStreamerCapture : : isPipelinePlaying ( )
2010-05-12 01:44:00 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ! pipeline | | ! GST_IS_ELEMENT ( pipeline . get ( ) ) )
2019-01-14 18:33:38 +08:00
{
CV_WARN ( " GStreamer: pipeline have not been created " ) ;
return false ;
}
2013-03-21 17:17:34 +08:00
GstState current , pending ;
GstClockTime timeout = 5 * GST_SECOND ;
2018-04-16 20:59:25 +08:00
GstStateChangeReturn ret = gst_element_get_state ( pipeline , & current , & pending , timeout ) ;
if ( ! ret )
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " unable to query pipeline state " ) ;
2013-03-21 17:17:34 +08:00
return false ;
}
return current = = GST_STATE_PLAYING ;
}
/*!
* \ brief CvCapture_GStreamer : : startPipeline
* Start the pipeline by setting it to the playing state
*/
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : startPipeline ( )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ! pipeline | | ! GST_IS_ELEMENT ( pipeline . get ( ) ) )
2019-01-14 18:33:38 +08:00
{
CV_WARN ( " GStreamer: pipeline have not been created " ) ;
return ;
}
GstStateChangeReturn status = gst_element_set_state ( pipeline , GST_STATE_PLAYING ) ;
2014-09-10 14:38:43 +08:00
if ( status = = GST_STATE_CHANGE_ASYNC )
{
// wait for status update
2015-08-15 23:30:08 +08:00
status = gst_element_get_state ( pipeline , NULL , NULL , GST_CLOCK_TIME_NONE ) ;
2014-09-10 14:38:43 +08:00
}
if ( status = = GST_STATE_CHANGE_FAILURE )
{
handleMessage ( pipeline ) ;
2019-06-20 18:51:57 +08:00
pipeline . release ( ) ;
CV_WARN ( " unable to start pipeline " ) ;
2011-04-13 15:15:35 +08:00
return ;
}
2010-05-12 01:44:00 +08:00
2017-07-05 18:45:08 +08:00
if ( isPosFramesEmulated )
emulatedFrameNumber = 0 ;
2013-03-21 17:17:34 +08:00
handleMessage ( pipeline ) ;
}
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : stopPipeline ( )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ! pipeline | | ! GST_IS_ELEMENT ( pipeline . get ( ) ) )
2019-01-14 18:33:38 +08:00
{
CV_WARN ( " GStreamer: pipeline have not been created " ) ;
return ;
}
2019-06-20 18:51:57 +08:00
if ( gst_element_set_state ( pipeline , GST_STATE_NULL ) = = GST_STATE_CHANGE_FAILURE )
2018-04-16 20:59:25 +08:00
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " unable to stop pipeline " ) ;
pipeline . release ( ) ;
2011-04-13 15:15:35 +08:00
}
2013-03-21 17:17:34 +08:00
}
2010-05-12 01:44:00 +08:00
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : restartPipeline
* Restart the pipeline
*/
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : restartPipeline ( )
2013-03-21 17:17:34 +08:00
{
handleMessage ( pipeline ) ;
2010-05-12 01:44:00 +08:00
2013-03-21 17:17:34 +08:00
this - > stopPipeline ( ) ;
this - > startPipeline ( ) ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : setFilter
* \ param prop the property name
* \ param type glib property type
* \ param v1 the value
* \ param v2 second value of property type requires it , else NULL
* Filter the output formats by setting appsink caps properties
*/
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : setFilter ( const char * prop , int type , int v1 , int v2 )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( ! caps | | ! ( GST_IS_CAPS ( caps . get ( ) ) ) )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( type = = G_TYPE_INT )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_new_simple ( " video/x-raw " , " format " , G_TYPE_STRING , " BGR " , prop , type , v1 , NULL ) ) ;
2013-03-21 17:17:34 +08:00
}
2011-04-13 15:15:35 +08:00
else
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_new_simple ( " video/x-raw " , " format " , G_TYPE_STRING , " BGR " , prop , type , v1 , v2 , NULL ) ) ;
2013-03-21 17:17:34 +08:00
}
}
else
{
2019-06-20 18:51:57 +08:00
if ( ! gst_caps_is_writable ( caps . get ( ) ) )
caps . attach ( gst_caps_make_writable ( caps . detach ( ) ) ) ;
if ( type = = G_TYPE_INT )
{
2013-03-21 17:17:34 +08:00
gst_caps_set_simple ( caps , prop , type , v1 , NULL ) ;
2019-06-20 18:51:57 +08:00
}
else
{
2013-03-21 17:17:34 +08:00
gst_caps_set_simple ( caps , prop , type , v1 , v2 , NULL ) ;
}
2011-04-13 15:15:35 +08:00
}
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
gst_app_sink_set_caps ( GST_APP_SINK ( sink . get ( ) ) , caps ) ;
GST_LOG ( " filtering with caps: % " GST_PTR_FORMAT , caps . get ( ) ) ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : removeFilter
* \ param filter filter to remove
* remove the specified filter from the appsink template caps
*/
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : removeFilter ( const char * filter )
2010-05-12 01:44:00 +08:00
{
2011-04-13 15:15:35 +08:00
if ( ! caps )
return ;
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
if ( ! gst_caps_is_writable ( caps . get ( ) ) )
caps . attach ( gst_caps_make_writable ( caps . detach ( ) ) ) ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
GstStructure * s = gst_caps_get_structure ( caps , 0 ) ; // no lifetime transfer
2011-04-13 15:15:35 +08:00
gst_structure_remove_field ( s , filter ) ;
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
gst_app_sink_set_caps ( GST_APP_SINK ( sink . get ( ) ) , caps ) ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : newPad link dynamic padd
* \ param pad
* \ param data
* decodebin creates pads based on stream information , which is not known upfront
* on receiving the pad - added signal , we connect it to the colorspace conversion element
*/
2018-04-16 20:59:25 +08:00
void GStreamerCapture : : newPad ( GstElement * , GstPad * pad , gpointer data )
2010-05-12 01:44:00 +08:00
{
2019-06-20 18:51:57 +08:00
GSafePtr < GstPad > sinkpad ;
GstElement * color = ( GstElement * ) data ;
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
sinkpad . attach ( gst_element_get_static_pad ( color , " sink " ) ) ;
if ( ! sinkpad ) {
CV_WARN ( " no pad named sink " ) ;
2013-03-21 17:17:34 +08:00
return ;
}
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
gst_pad_link ( pad , sinkpad . get ( ) ) ;
2018-04-16 20:59:25 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
2019-06-20 18:51:57 +08:00
* \ brief Create GStreamer pipeline
2013-03-21 17:17:34 +08:00
* \ param filename Filename to open in case of CV_CAP_GSTREAMER_FILE
2015-05-23 21:26:18 +08:00
* \ return boolean . Specifies if opening was successful .
2013-03-21 17:17:34 +08:00
*
2019-06-20 18:51:57 +08:00
* In case of camera ' index ' , a pipeline is constructed as follows :
2013-03-21 17:17:34 +08:00
* v4l2src ! autoconvert ! appsink
*
*
* The ' filename ' parameter is not limited to filesystem paths , and may be one of the following :
*
* - a normal filesystem path :
* e . g . video . avi or / path / to / video . avi or C : \ \ video . avi
* - an uri :
* e . g . file : ///path/to/video.avi or rtsp:///path/to/stream.asf
* - a gstreamer pipeline description :
* e . g . videotestsrc ! videoconvert ! appsink
* the appsink name should be either ' appsink0 ' ( the default ) or ' opencvsink '
*
2019-06-20 18:51:57 +08:00
* GStreamer will not drop frames if the grabbing interval larger than the framerate period .
* To support dropping for live streams add appsink ' drop ' parameter into your custom pipeline .
2013-03-21 17:17:34 +08:00
*
* The pipeline will only be started whenever the first frame is grabbed . Setting pipeline properties
* is really slow if we need to restart the pipeline over and over again .
*
*/
2021-03-01 23:51:04 +08:00
bool GStreamerCapture : : open ( int id , const cv : : VideoCaptureParameters & params )
2010-05-12 01:44:00 +08:00
{
2018-08-06 22:11:23 +08:00
gst_initializer : : init ( ) ;
2018-04-16 20:59:25 +08:00
if ( ! is_gst_element_exists ( " v4l2src " ) )
return false ;
std : : ostringstream desc ;
2018-08-06 22:11:23 +08:00
desc < < " v4l2src device=/dev/video " < < id
2018-04-16 20:59:25 +08:00
< < " ! " < < COLOR_ELEM
2019-06-20 18:51:57 +08:00
< < " ! appsink drop=true " ;
2021-03-01 23:51:04 +08:00
return open ( desc . str ( ) , params ) ;
2018-04-16 20:59:25 +08:00
}
2010-05-12 01:44:00 +08:00
2021-03-01 23:51:04 +08:00
bool GStreamerCapture : : open ( const String & filename_ , const cv : : VideoCaptureParameters & params )
2018-04-16 20:59:25 +08:00
{
2012-11-19 20:44:23 +08:00
gst_initializer : : init ( ) ;
2021-12-17 03:43:02 +08:00
if ( ! configureHW ( params ) )
2021-03-01 23:51:04 +08:00
{
2021-12-17 03:43:02 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: can't configure HW encoding/decoding support " ) ;
return false ;
2021-03-01 23:51:04 +08:00
}
2021-12-17 03:43:02 +08:00
if ( ! configureStreams ( params ) )
2021-03-01 23:51:04 +08:00
{
2021-12-17 03:43:02 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: can't configure streams " ) ;
return false ;
}
if ( ( videoStream > = 0 & & audioStream > = 0 ) | | ( videoStream < 0 & & audioStream < 0 ) )
{
CV_LOG_ERROR ( NULL , " GStreamer backend supports audio-only or video-only capturing. Only one of the properties CAP_PROP_AUDIO_STREAM= " < < audioStream < < " and CAP_PROP_VIDEO_STREAM= " < < videoStream < < " should be >= 0 " ) ;
return false ;
}
if ( audioStream > 0 )
{
CV_LOG_ERROR ( NULL , " GStreamer backend supports the first audio stream only. CAP_PROP_AUDIO_STREAM= " < < audioStream ) ;
return false ;
}
if ( ! setAudioProperties ( params ) )
{
CV_LOG_WARNING ( NULL , " GStreamer: can't configure audio properties " ) ;
return false ;
2021-03-01 23:51:04 +08:00
}
2019-06-20 18:51:57 +08:00
const gchar * filename = filename_ . c_str ( ) ;
2018-04-16 20:59:25 +08:00
2014-09-16 21:05:36 +08:00
bool file = false ;
2011-04-13 15:15:35 +08:00
bool manualpipeline = false ;
2019-06-20 18:51:57 +08:00
GSafePtr < char > uri ;
GSafePtr < GstElement > uridecodebin ;
GSafePtr < GstElement > color ;
2014-09-10 14:38:43 +08:00
GstStateChangeReturn status ;
2013-03-21 17:17:34 +08:00
2021-12-17 03:43:02 +08:00
GSafePtr < GstElement > convert ;
GSafePtr < GstElement > resample ;
2013-03-21 17:17:34 +08:00
// test if we have a valid uri. If so, open it with an uridecodebin
// else, we might have a file or a manual pipeline.
// if gstreamer cannot parse the manual pipeline, we assume we were given and
// ordinary file path.
2019-11-09 19:19:23 +08:00
CV_LOG_INFO ( NULL , " OpenCV | GStreamer: " < < filename ) ;
2015-12-09 22:08:19 +08:00
if ( ! gst_uri_is_valid ( filename ) )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( utils : : fs : : exists ( filename_ ) )
2015-12-09 22:08:19 +08:00
{
2019-11-09 19:19:23 +08:00
GSafePtr < GError > err ;
uri . attach ( gst_filename_to_uri ( filename , err . getRef ( ) ) ) ;
2019-06-20 18:51:57 +08:00
if ( uri )
2014-09-16 21:05:36 +08:00
{
file = true ;
}
else
{
2021-10-08 10:07:04 +08:00
CV_WARN ( " Error opening file: " < < filename < < " ( " < < ( err ? err - > message : " <unknown reason> " ) < < " ) " ) ;
2011-04-13 15:15:35 +08:00
return false ;
}
2013-03-21 17:17:34 +08:00
}
else
{
2019-06-20 18:51:57 +08:00
GSafePtr < GError > err ;
uridecodebin . attach ( gst_parse_launch ( filename , err . getRef ( ) ) ) ;
2021-10-08 10:07:04 +08:00
if ( ! uridecodebin )
2014-09-16 21:05:36 +08:00
{
2021-10-08 10:07:04 +08:00
CV_WARN ( " Error opening bin: " < < ( err ? err - > message : " <unknown reason> " ) ) ;
2011-04-13 15:15:35 +08:00
return false ;
}
manualpipeline = true ;
}
2015-08-15 23:30:08 +08:00
}
else
{
2019-06-20 18:51:57 +08:00
uri . attach ( g_strdup ( filename ) ) ;
2011-04-13 15:15:35 +08:00
}
2019-11-09 19:19:23 +08:00
CV_LOG_INFO ( NULL , " OpenCV | GStreamer: mode - " < < ( file ? " FILE " : manualpipeline ? " MANUAL " : " URI " ) ) ;
2013-03-21 17:17:34 +08:00
bool element_from_uri = false ;
2019-06-20 18:51:57 +08:00
if ( ! uridecodebin )
2013-03-21 17:17:34 +08:00
{
2021-12-17 03:43:02 +08:00
if ( videoStream > = 0 )
2013-03-21 17:17:34 +08:00
{
2021-12-17 03:43:02 +08:00
// At this writing, the v4l2 element (and maybe others too) does not support caps renegotiation.
// This means that we cannot use an uridecodebin when dealing with v4l2, since setting
// capture properties will not work.
// The solution (probably only until gstreamer 1.2) is to make an element from uri when dealing with v4l2.
GSafePtr < gchar > protocol_ ; protocol_ . attach ( gst_uri_get_protocol ( uri ) ) ;
CV_Assert ( protocol_ ) ;
std : : string protocol = toLowerCase ( std : : string ( protocol_ . get ( ) ) ) ;
if ( protocol = = " v4l2 " )
{
uridecodebin . reset ( gst_element_make_from_uri ( GST_URI_SRC , uri . get ( ) , " src " , NULL ) ) ;
CV_Assert ( uridecodebin ) ;
element_from_uri = true ;
}
else
{
uridecodebin . reset ( gst_element_factory_make ( " uridecodebin " , NULL ) ) ;
CV_Assert ( uridecodebin ) ;
g_object_set ( G_OBJECT ( uridecodebin . get ( ) ) , " uri " , uri . get ( ) , NULL ) ;
}
2015-08-15 23:30:08 +08:00
}
2021-12-17 03:43:02 +08:00
else if ( audioStream > = 0 )
2015-08-15 23:30:08 +08:00
{
2019-06-20 18:51:57 +08:00
uridecodebin . reset ( gst_element_factory_make ( " uridecodebin " , NULL ) ) ;
CV_Assert ( uridecodebin ) ;
g_object_set ( G_OBJECT ( uridecodebin . get ( ) ) , " uri " , uri . get ( ) , NULL ) ;
2013-03-21 17:17:34 +08:00
}
2019-06-20 18:51:57 +08:00
if ( ! uridecodebin )
2015-08-15 23:30:08 +08:00
{
2018-04-16 20:59:25 +08:00
CV_WARN ( " Can not parse GStreamer URI bin " ) ;
2011-05-07 03:06:33 +08:00
return false ;
}
2011-04-13 15:15:35 +08:00
}
2015-08-15 23:30:08 +08:00
if ( manualpipeline )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
GstIterator * it = gst_bin_iterate_elements ( GST_BIN ( uridecodebin . get ( ) ) ) ;
2013-06-13 17:16:33 +08:00
2015-08-17 02:01:22 +08:00
gboolean done = false ;
2013-06-13 17:16:33 +08:00
GValue value = G_VALUE_INIT ;
2015-08-15 23:30:08 +08:00
while ( ! done )
{
2019-06-20 18:51:57 +08:00
GstElement * element = NULL ;
GSafePtr < gchar > name ;
2015-08-15 23:30:08 +08:00
switch ( gst_iterator_next ( it , & value ) )
{
2014-09-23 23:02:37 +08:00
case GST_ITERATOR_OK :
2015-08-15 23:30:08 +08:00
element = GST_ELEMENT ( g_value_get_object ( & value ) ) ;
2019-06-20 18:51:57 +08:00
name . attach ( gst_element_get_name ( element ) ) ;
2015-08-15 23:30:08 +08:00
if ( name )
{
if ( strstr ( name , " opencvsink " ) ! = NULL | | strstr ( name , " appsink " ) ! = NULL )
{
2019-06-20 18:51:57 +08:00
sink . attach ( GST_ELEMENT ( gst_object_ref ( element ) ) ) ;
2015-08-15 23:30:08 +08:00
}
2015-08-17 02:01:22 +08:00
else if ( strstr ( name , COLOR_ELEM_NAME ) ! = NULL )
2015-08-15 23:30:08 +08:00
{
2019-06-20 18:51:57 +08:00
color . attach ( GST_ELEMENT ( gst_object_ref ( element ) ) ) ;
2015-08-17 02:25:36 +08:00
}
else if ( strstr ( name , " v4l " ) ! = NULL )
{
2019-06-20 18:51:57 +08:00
v4l2src . attach ( GST_ELEMENT ( gst_object_ref ( element ) ) ) ;
2015-08-15 23:30:08 +08:00
}
2019-06-20 18:51:57 +08:00
name . release ( ) ;
2015-08-17 02:25:36 +08:00
done = sink & & color & & v4l2src ;
2013-06-13 17:16:33 +08:00
}
2015-08-15 23:30:08 +08:00
g_value_unset ( & value ) ;
break ;
2013-06-13 17:16:33 +08:00
case GST_ITERATOR_RESYNC :
2015-08-15 23:30:08 +08:00
gst_iterator_resync ( it ) ;
break ;
2013-06-13 17:16:33 +08:00
case GST_ITERATOR_ERROR :
case GST_ITERATOR_DONE :
2015-08-15 23:30:08 +08:00
done = TRUE ;
break ;
}
2011-04-24 17:48:19 +08:00
}
2013-06-13 17:16:33 +08:00
gst_iterator_free ( it ) ;
2015-08-15 23:30:08 +08:00
if ( ! sink )
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " cannot find appsink in manual pipeline " ) ;
2013-03-21 17:17:34 +08:00
return false ;
}
2015-08-17 02:01:22 +08:00
2019-06-20 18:51:57 +08:00
pipeline . swap ( uridecodebin ) ;
2013-03-21 17:17:34 +08:00
}
else
{
2019-06-20 18:51:57 +08:00
pipeline . reset ( gst_pipeline_new ( NULL ) ) ;
CV_Assert ( pipeline ) ;
sink . reset ( gst_element_factory_make ( " appsink " , NULL ) ) ;
CV_Assert ( sink ) ;
2021-12-17 03:43:02 +08:00
if ( videoStream > = 0 )
{
// videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert)
//automatically selects the correct colorspace conversion based on caps.
color . reset ( gst_element_factory_make ( COLOR_ELEM , NULL ) ) ;
CV_Assert ( color ) ;
2013-03-21 17:17:34 +08:00
2021-12-17 03:43:02 +08:00
gst_bin_add_many ( GST_BIN ( pipeline . get ( ) ) , uridecodebin . get ( ) , color . get ( ) , sink . get ( ) , NULL ) ;
2019-06-20 18:51:57 +08:00
2021-12-17 03:43:02 +08:00
if ( element_from_uri )
{
if ( ! gst_element_link ( uridecodebin , color . get ( ) ) )
{
CV_WARN ( " GStreamer(video): cannot link color -> sink " ) ;
pipeline . release ( ) ;
return false ;
}
}
else
{
g_signal_connect ( uridecodebin , " pad-added " , G_CALLBACK ( newPad ) , color . get ( ) ) ;
}
if ( ! gst_element_link ( color . get ( ) , sink . get ( ) ) )
2015-08-15 23:30:08 +08:00
{
2021-12-17 03:43:02 +08:00
CV_WARN ( " GStreamer(video): cannot link color -> sink " ) ;
2019-06-20 18:51:57 +08:00
pipeline . release ( ) ;
2013-03-21 17:17:34 +08:00
return false ;
}
2015-08-15 23:30:08 +08:00
}
2021-12-17 03:43:02 +08:00
else if ( audioStream > = 0 )
2015-08-15 23:30:08 +08:00
{
2021-12-17 03:43:02 +08:00
convert . reset ( gst_element_factory_make ( " audioconvert " , NULL ) ) ;
resample . reset ( gst_element_factory_make ( " audioresample " , NULL ) ) ;
2010-05-12 01:44:00 +08:00
2021-12-17 03:43:02 +08:00
gst_bin_add_many ( GST_BIN ( pipeline . get ( ) ) , uridecodebin . get ( ) , convert . get ( ) , resample . get ( ) , sink . get ( ) , NULL ) ;
if ( ! gst_element_link_many ( convert . get ( ) , resample . get ( ) , sink . get ( ) , NULL ) )
{
CV_WARN ( " GStreamer(audio): cannot link convert -> resample -> sink " ) ;
pipeline . release ( ) ;
return false ;
}
g_signal_connect ( uridecodebin , " pad-added " , G_CALLBACK ( newPad ) , convert . get ( ) ) ;
2011-04-24 17:48:19 +08:00
}
2011-04-13 15:15:35 +08:00
}
2010-05-12 01:44:00 +08:00
2019-06-20 18:51:57 +08:00
if ( ! manualpipeline | | strstr ( filename , " max-buffers= " ) = = NULL )
{
//TODO: is 1 single buffer really high enough?
gst_app_sink_set_max_buffers ( GST_APP_SINK ( sink . get ( ) ) , 1 ) ;
}
2020-12-10 21:07:28 +08:00
if ( ! manualpipeline )
{
gst_base_sink_set_sync ( GST_BASE_SINK ( sink . get ( ) ) , FALSE ) ;
}
2013-03-21 17:17:34 +08:00
//do not emit signals: all calls will be synchronous and blocking
2019-06-20 18:51:57 +08:00
gst_app_sink_set_emit_signals ( GST_APP_SINK ( sink . get ( ) ) , FALSE ) ;
2021-12-17 03:43:02 +08:00
if ( videoStream > = 0 )
{
caps . attach ( gst_caps_from_string ( " video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}; image/jpeg " ) ) ;
}
else if ( audioStream > = 0 )
{
std : : string audioFormat ;
switch ( outputAudioFormat )
{
case CV_8S :
audioFormat = " S8 " ;
break ;
case CV_16S :
audioFormat = " S16LE " ;
break ;
case CV_32S :
audioFormat = " S32LE " ;
break ;
case CV_32F :
audioFormat = " F32LE " ;
break ;
default :
audioFormat = " S16LE " ;
break ;
}
std : : string stringCaps = " audio/x-raw, format=(string) " + audioFormat + " , rate=(int) " + std : : to_string ( audioSamplesPerSecond ) + " , channels=(int){1, 2}, layout=(string)interleaved " ;
caps . attach ( gst_caps_from_string ( stringCaps . c_str ( ) ) ) ;
}
2017-07-15 01:11:30 +08:00
2019-06-20 18:51:57 +08:00
if ( manualpipeline )
{
GSafePtr < GstCaps > peer_caps ;
GSafePtr < GstPad > sink_pad ;
sink_pad . attach ( gst_element_get_static_pad ( sink , " sink " ) ) ;
peer_caps . attach ( gst_pad_peer_query_caps ( sink_pad , NULL ) ) ;
2017-07-15 01:11:30 +08:00
if ( ! gst_caps_can_intersect ( caps , peer_caps ) ) {
2021-08-17 00:20:10 +08:00
caps . attach ( gst_caps_from_string ( " video/x-raw, format=(string){UYVY,YUY2,YVYU,NV12,NV21,YV12,I420,BGRA,RGBA,BGRx,RGBx,GRAY16_LE,GRAY16_BE} " ) ) ;
2019-06-20 18:51:57 +08:00
CV_Assert ( caps ) ;
2017-07-15 01:11:30 +08:00
}
}
2019-06-20 18:51:57 +08:00
gst_app_sink_set_caps ( GST_APP_SINK ( sink . get ( ) ) , caps ) ;
caps . release ( ) ;
2011-04-24 17:48:19 +08:00
2014-09-10 14:38:43 +08:00
{
2019-06-20 18:51:57 +08:00
GST_DEBUG_BIN_TO_DOT_FILE ( GST_BIN ( pipeline . get ( ) ) , GST_DEBUG_GRAPH_SHOW_ALL , " pipeline-init " ) ;
2018-04-16 20:59:25 +08:00
2019-06-20 18:51:57 +08:00
status = gst_element_set_state ( GST_ELEMENT ( pipeline . get ( ) ) ,
2015-08-15 23:30:08 +08:00
file ? GST_STATE_PAUSED : GST_STATE_PLAYING ) ;
2014-09-10 14:38:43 +08:00
if ( status = = GST_STATE_CHANGE_ASYNC )
{
// wait for status update
2015-08-15 23:30:08 +08:00
status = gst_element_get_state ( pipeline , NULL , NULL , GST_CLOCK_TIME_NONE ) ;
2014-09-10 14:38:43 +08:00
}
if ( status = = GST_STATE_CHANGE_FAILURE )
{
2019-06-20 18:51:57 +08:00
GST_DEBUG_BIN_TO_DOT_FILE ( GST_BIN ( pipeline . get ( ) ) , GST_DEBUG_GRAPH_SHOW_ALL , " pipeline-error " ) ;
2014-09-10 14:38:43 +08:00
handleMessage ( pipeline ) ;
2019-06-20 18:51:57 +08:00
pipeline . release ( ) ;
CV_WARN ( " unable to start pipeline " ) ;
2014-09-10 14:38:43 +08:00
return false ;
}
2019-06-20 18:51:57 +08:00
GSafePtr < GstPad > pad ;
pad . attach ( gst_element_get_static_pad ( sink , " sink " ) ) ;
2014-09-25 18:40:54 +08:00
2019-06-20 18:51:57 +08:00
GSafePtr < GstCaps > buffer_caps ;
buffer_caps . attach ( gst_pad_get_current_caps ( pad ) ) ;
2014-09-25 18:40:54 +08:00
2021-12-17 03:43:02 +08:00
if ( videoStream > = 0 )
2014-09-25 18:51:34 +08:00
{
2021-12-17 03:43:02 +08:00
GstFormat format ;
2014-09-25 18:40:54 +08:00
2021-12-17 03:43:02 +08:00
format = GST_FORMAT_DEFAULT ;
if ( ! gst_element_query_duration ( sink , format , & duration ) )
{
handleMessage ( pipeline ) ;
CV_WARN ( " unable to query duration of stream " ) ;
duration = - 1 ;
}
2014-09-25 18:40:54 +08:00
2021-12-17 03:43:02 +08:00
handleMessage ( pipeline ) ;
2014-09-25 18:40:54 +08:00
2021-12-17 03:43:02 +08:00
const GstStructure * structure = gst_caps_get_structure ( buffer_caps , 0 ) ; // no lifetime transfer
if ( ! gst_structure_get_int ( structure , " width " , & width ) | |
! gst_structure_get_int ( structure , " height " , & height ) )
{
CV_WARN ( " cannot query video width/height " ) ;
}
gint num = 0 , denom = 1 ;
if ( ! gst_structure_get_fraction ( structure , " framerate " , & num , & denom ) )
{
CV_WARN ( " cannot query video fps " ) ;
}
2017-07-05 18:45:08 +08:00
2021-12-17 03:43:02 +08:00
fps = ( double ) num / ( double ) denom ;
2019-01-14 18:33:38 +08:00
2017-07-05 18:45:08 +08:00
{
2021-12-17 03:43:02 +08:00
GstFormat format_ ;
gint64 value_ = - 1 ;
gboolean status_ ;
format_ = GST_FORMAT_DEFAULT ;
status_ = gst_element_query_position ( sink , CV_GST_FORMAT ( format_ ) , & value_ ) ;
if ( ! status_ | | value_ ! = 0 | | duration < 0 )
{
CV_WARN ( " Cannot query video position: status= " < < status_ < < " , value= " < < value_ < < " , duration= " < < duration ) ;
isPosFramesSupported = false ;
isPosFramesEmulated = true ;
emulatedFrameNumber = 0 ;
}
else
isPosFramesSupported = true ;
}
}
else if ( audioStream > = 0 )
{
GstAudioInfo info = { } ;
if ( gst_audio_info_from_caps ( & info , buffer_caps ) )
{
nAudioChannels = GST_AUDIO_INFO_CHANNELS ( & info ) ;
audioSamplesPerSecond = GST_AUDIO_INFO_RATE ( & info ) ;
2017-07-05 18:45:08 +08:00
}
else
2021-12-17 03:43:02 +08:00
{
CV_WARN ( " cannot query audio nChannels and SamplesPerSecond " ) ;
}
2017-07-05 18:45:08 +08:00
}
2019-06-20 18:51:57 +08:00
GST_DEBUG_BIN_TO_DOT_FILE ( GST_BIN ( pipeline . get ( ) ) , GST_DEBUG_GRAPH_SHOW_ALL , " pipeline " ) ;
2014-09-10 14:38:43 +08:00
}
2021-03-01 23:51:04 +08:00
std : : vector < int > unused_params = params . getUnused ( ) ;
for ( int key : unused_params ) {
if ( ! setProperty ( key , params . get < double > ( key ) ) ) {
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: can't set property " < < key ) ;
return false ;
}
}
if ( pipeline )
{
VideoAccelerationType actual_va_type = VIDEO_ACCELERATION_NONE ;
GstIterator * iter = gst_bin_iterate_recurse ( GST_BIN ( pipeline . get ( ) ) ) ;
gst_iterator_foreach ( iter , find_hw_element , ( gpointer ) & actual_va_type ) ;
gst_iterator_free ( iter ) ;
if ( va_type ! = VIDEO_ACCELERATION_NONE & & va_type ! = VIDEO_ACCELERATION_ANY )
{
if ( va_type ! = actual_va_type )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Can't select requested video acceleration through CAP_PROP_HW_ACCELERATION: "
< < va_type < < " (actual is " < < actual_va_type < < " ). Bailout " ) ;
return false ;
}
}
else
{
va_type = actual_va_type ;
}
}
2011-04-13 15:15:35 +08:00
return true ;
2010-05-12 01:44:00 +08:00
}
2011-04-24 05:24:41 +08:00
2013-03-21 17:17:34 +08:00
/*!
2018-02-14 00:28:11 +08:00
* \ brief CvCapture_GStreamer : : getProperty retrieve the requested property from the pipeline
2013-03-21 17:17:34 +08:00
* \ param propId requested property
* \ return property value
*
2018-02-14 00:28:11 +08:00
* There are two ways the properties can be retrieved . For seek - based properties we can query the pipeline .
2019-12-26 19:45:03 +08:00
* For frame - based properties , we use the caps of the last receivef sample . This means that some properties
2013-03-21 17:17:34 +08:00
* are not available until a first frame was received
*/
2018-04-16 20:59:25 +08:00
double GStreamerCapture : : getProperty ( int propId ) const
2010-05-12 01:44:00 +08:00
{
2013-03-21 17:17:34 +08:00
GstFormat format ;
gint64 value ;
gboolean status ;
2010-05-12 01:44:00 +08:00
2013-03-21 17:17:34 +08:00
if ( ! pipeline ) {
CV_WARN ( " GStreamer: no pipeline " ) ;
2015-08-17 02:25:36 +08:00
return 0 ;
2011-04-23 15:27:04 +08:00
}
2011-04-13 15:15:35 +08:00
2019-06-20 18:51:57 +08:00
switch ( propId )
{
2013-03-21 17:17:34 +08:00
case CV_CAP_PROP_POS_MSEC :
2020-12-08 05:18:47 +08:00
CV_LOG_ONCE_WARNING ( NULL , " OpenCV | GStreamer: CAP_PROP_POS_MSEC property result may be unrealiable: "
" https://github.com/opencv/opencv/issues/19025 " ) ;
2013-03-21 17:17:34 +08:00
format = GST_FORMAT_TIME ;
2019-06-20 18:51:57 +08:00
status = gst_element_query_position ( sink . get ( ) , CV_GST_FORMAT ( format ) , & value ) ;
2013-03-21 17:17:34 +08:00
if ( ! status ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2013-03-21 17:17:34 +08:00
CV_WARN ( " GStreamer: unable to query position of stream " ) ;
2015-08-17 02:25:36 +08:00
return 0 ;
2011-04-13 15:15:35 +08:00
}
return value * 1e-6 ; // nano seconds to milli seconds
case CV_CAP_PROP_POS_FRAMES :
2017-07-05 18:45:08 +08:00
if ( ! isPosFramesSupported )
{
if ( isPosFramesEmulated )
return emulatedFrameNumber ;
return 0 ; // TODO getProperty() "unsupported" value should be changed
}
2011-04-13 15:15:35 +08:00
format = GST_FORMAT_DEFAULT ;
2019-06-20 18:51:57 +08:00
status = gst_element_query_position ( sink . get ( ) , CV_GST_FORMAT ( format ) , & value ) ;
2013-03-21 17:17:34 +08:00
if ( ! status ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2011-04-13 15:15:35 +08:00
CV_WARN ( " GStreamer: unable to query position of stream " ) ;
2015-08-17 02:25:36 +08:00
return 0 ;
2011-04-13 15:15:35 +08:00
}
return value ;
case CV_CAP_PROP_POS_AVI_RATIO :
format = GST_FORMAT_PERCENT ;
2019-06-20 18:51:57 +08:00
status = gst_element_query_position ( sink . get ( ) , CV_GST_FORMAT ( format ) , & value ) ;
2013-03-21 17:17:34 +08:00
if ( ! status ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2011-04-13 15:15:35 +08:00
CV_WARN ( " GStreamer: unable to query position of stream " ) ;
2015-08-17 02:25:36 +08:00
return 0 ;
2011-04-13 15:15:35 +08:00
}
return ( ( double ) value ) / GST_FORMAT_PERCENT_MAX ;
2014-09-25 18:40:54 +08:00
case CV_CAP_PROP_FRAME_WIDTH :
2013-03-21 17:17:34 +08:00
return width ;
2014-09-25 18:40:54 +08:00
case CV_CAP_PROP_FRAME_HEIGHT :
2013-03-21 17:17:34 +08:00
return height ;
2014-09-25 18:40:54 +08:00
case CV_CAP_PROP_FPS :
return fps ;
2011-04-13 15:15:35 +08:00
case CV_CAP_PROP_FRAME_COUNT :
2014-09-10 14:38:43 +08:00
return duration ;
2011-04-13 15:15:35 +08:00
case CV_CAP_PROP_BRIGHTNESS :
case CV_CAP_PROP_CONTRAST :
case CV_CAP_PROP_SATURATION :
case CV_CAP_PROP_HUE :
2015-08-17 02:25:36 +08:00
if ( v4l2src )
{
2019-06-20 18:51:57 +08:00
std : : string propName = get_gst_propname ( propId ) ;
2018-04-16 20:59:25 +08:00
if ( ! propName . empty ( ) )
2015-08-17 02:25:36 +08:00
{
2018-04-16 20:59:25 +08:00
gint32 val = 0 ;
2019-06-20 18:51:57 +08:00
g_object_get ( G_OBJECT ( v4l2src . get ( ) ) , propName . c_str ( ) , & val , NULL ) ;
2018-04-16 20:59:25 +08:00
return static_cast < double > ( val ) ;
2015-08-17 02:25:36 +08:00
}
}
2011-04-13 15:15:35 +08:00
break ;
2021-03-01 23:51:04 +08:00
case CAP_PROP_HW_ACCELERATION :
return static_cast < double > ( va_type ) ;
case CAP_PROP_HW_DEVICE :
return static_cast < double > ( hw_device ) ;
2011-04-23 15:27:04 +08:00
case CV_CAP_GSTREAMER_QUEUE_LENGTH :
2018-04-16 20:59:25 +08:00
if ( ! sink )
{
CV_WARN ( " there is no sink yet " ) ;
return 0 ;
2011-04-24 17:48:19 +08:00
}
2019-06-20 18:51:57 +08:00
return gst_app_sink_get_max_buffers ( GST_APP_SINK ( sink . get ( ) ) ) ;
2021-12-17 03:43:02 +08:00
case CAP_PROP_AUDIO_TOTAL_CHANNELS :
return nAudioChannels ;
case CAP_PROP_AUDIO_SAMPLES_PER_SECOND :
return audioSamplesPerSecond ;
case CAP_PROP_AUDIO_DATA_DEPTH :
return outputAudioFormat ;
case CAP_PROP_AUDIO_BASE_INDEX :
return audioBaseIndex ;
2011-04-13 15:15:35 +08:00
default :
2019-06-20 18:51:57 +08:00
CV_WARN ( " unhandled property: " < < propId ) ;
2011-04-13 15:15:35 +08:00
break ;
}
2013-03-21 17:17:34 +08:00
2015-08-17 02:25:36 +08:00
return 0 ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
* \ brief CvCapture_GStreamer : : setProperty
* \ param propId
* \ param value
* \ return success
* Sets the desired property id with val . If the pipeline is running ,
* it is briefly stopped and started again after the property was set
*/
2018-04-16 20:59:25 +08:00
bool GStreamerCapture : : setProperty ( int propId , double value )
2010-05-12 01:44:00 +08:00
{
2018-04-16 20:59:25 +08:00
const GstSeekFlags flags = ( GstSeekFlags ) ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ) ;
2010-05-12 01:44:00 +08:00
2018-04-16 20:59:25 +08:00
if ( ! pipeline )
{
CV_WARN ( " no pipeline " ) ;
2011-04-13 15:15:35 +08:00
return false ;
}
2010-05-12 01:44:00 +08:00
2013-03-21 17:17:34 +08:00
bool wasPlaying = this - > isPipelinePlaying ( ) ;
if ( wasPlaying )
this - > stopPipeline ( ) ;
2018-04-16 20:59:25 +08:00
switch ( propId )
{
2011-04-13 15:15:35 +08:00
case CV_CAP_PROP_POS_MSEC :
2019-06-20 18:51:57 +08:00
if ( ! gst_element_seek_simple ( GST_ELEMENT ( pipeline . get ( ) ) , GST_FORMAT_TIME ,
2013-03-21 17:17:34 +08:00
flags , ( gint64 ) ( value * GST_MSECOND ) ) ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2011-04-13 15:15:35 +08:00
CV_WARN ( " GStreamer: unable to seek " ) ;
}
2017-07-05 18:45:08 +08:00
else
{
if ( isPosFramesEmulated )
{
if ( value = = 0 )
{
emulatedFrameNumber = 0 ;
return true ;
}
else
{
isPosFramesEmulated = false ; // reset frame counter emulation
}
}
}
2011-04-13 15:15:35 +08:00
break ;
case CV_CAP_PROP_POS_FRAMES :
2017-07-05 18:45:08 +08:00
{
if ( ! isPosFramesSupported )
{
if ( isPosFramesEmulated )
{
if ( value = = 0 )
{
restartPipeline ( ) ;
emulatedFrameNumber = 0 ;
return true ;
}
}
return false ;
2018-04-16 20:59:25 +08:00
CV_WARN ( " unable to seek " ) ;
2017-07-05 18:45:08 +08:00
}
2019-06-20 18:51:57 +08:00
if ( ! gst_element_seek_simple ( GST_ELEMENT ( pipeline . get ( ) ) , GST_FORMAT_DEFAULT ,
2013-03-21 17:17:34 +08:00
flags , ( gint64 ) value ) ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2011-04-13 15:15:35 +08:00
CV_WARN ( " GStreamer: unable to seek " ) ;
2017-07-05 18:45:08 +08:00
break ;
2011-04-13 15:15:35 +08:00
}
2017-07-05 18:45:08 +08:00
// wait for status update
gst_element_get_state ( pipeline , NULL , NULL , GST_CLOCK_TIME_NONE ) ;
return true ;
}
2011-04-13 15:15:35 +08:00
case CV_CAP_PROP_POS_AVI_RATIO :
2019-06-20 18:51:57 +08:00
if ( ! gst_element_seek_simple ( GST_ELEMENT ( pipeline . get ( ) ) , GST_FORMAT_PERCENT ,
2013-03-21 17:17:34 +08:00
flags , ( gint64 ) ( value * GST_FORMAT_PERCENT_MAX ) ) ) {
2017-07-05 18:45:08 +08:00
handleMessage ( pipeline ) ;
2011-04-13 15:15:35 +08:00
CV_WARN ( " GStreamer: unable to seek " ) ;
}
2017-07-05 18:45:08 +08:00
else
{
if ( isPosFramesEmulated )
{
if ( value = = 0 )
{
emulatedFrameNumber = 0 ;
return true ;
}
else
{
isPosFramesEmulated = false ; // reset frame counter emulation
}
}
}
2011-04-13 15:15:35 +08:00
break ;
case CV_CAP_PROP_FRAME_WIDTH :
if ( value > 0 )
setFilter ( " width " , G_TYPE_INT , ( int ) value , 0 ) ;
else
removeFilter ( " width " ) ;
break ;
case CV_CAP_PROP_FRAME_HEIGHT :
if ( value > 0 )
setFilter ( " height " , G_TYPE_INT , ( int ) value , 0 ) ;
else
removeFilter ( " height " ) ;
break ;
case CV_CAP_PROP_FPS :
if ( value > 0 ) {
2019-06-20 18:51:57 +08:00
int num = 0 , denom = 1 ;
toFraction ( value , num , denom ) ;
2013-03-21 17:17:34 +08:00
setFilter ( " framerate " , GST_TYPE_FRACTION , value , denom ) ;
2011-04-13 15:15:35 +08:00
} else
removeFilter ( " framerate " ) ;
break ;
case CV_CAP_PROP_BRIGHTNESS :
case CV_CAP_PROP_CONTRAST :
case CV_CAP_PROP_SATURATION :
case CV_CAP_PROP_HUE :
2015-08-17 02:25:36 +08:00
if ( v4l2src )
{
2019-06-20 18:51:57 +08:00
std : : string propName = get_gst_propname ( propId ) ;
2018-04-16 20:59:25 +08:00
if ( ! propName . empty ( ) )
2015-08-17 02:25:36 +08:00
{
2018-04-16 20:59:25 +08:00
gint32 val = cv : : saturate_cast < gint32 > ( value ) ;
2019-06-20 18:51:57 +08:00
g_object_set ( G_OBJECT ( v4l2src . get ( ) ) , propName . c_str ( ) , & val , NULL ) ;
2015-08-17 02:25:36 +08:00
return true ;
}
}
2018-04-16 20:59:25 +08:00
return false ;
2011-04-13 15:15:35 +08:00
case CV_CAP_PROP_GAIN :
case CV_CAP_PROP_CONVERT_RGB :
break ;
2021-03-01 23:51:04 +08:00
case cv : : CAP_PROP_HW_ACCELERATION :
return false ; // open-only
case cv : : CAP_PROP_HW_DEVICE :
return false ; // open-only
2011-04-23 15:27:04 +08:00
case CV_CAP_GSTREAMER_QUEUE_LENGTH :
2018-04-16 20:59:25 +08:00
{
2011-04-23 15:27:04 +08:00
if ( ! sink )
2018-04-16 20:59:25 +08:00
{
CV_WARN ( " there is no sink yet " ) ;
return false ;
}
2019-06-20 18:51:57 +08:00
gst_app_sink_set_max_buffers ( GST_APP_SINK ( sink . get ( ) ) , ( guint ) value ) ;
2018-04-16 20:59:25 +08:00
return true ;
}
2011-04-13 15:15:35 +08:00
default :
CV_WARN ( " GStreamer: unhandled property " ) ;
}
2013-03-21 17:17:34 +08:00
if ( wasPlaying )
this - > startPipeline ( ) ;
2011-04-13 15:15:35 +08:00
return false ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
2010-05-12 01:44:00 +08:00
2021-03-01 23:51:04 +08:00
Ptr < IVideoCapture > createGStreamerCapture_file ( const String & filename , const cv : : VideoCaptureParameters & params )
2018-04-16 20:59:25 +08:00
{
Ptr < GStreamerCapture > cap = makePtr < GStreamerCapture > ( ) ;
2021-03-01 23:51:04 +08:00
if ( cap & & cap - > open ( filename , params ) )
2018-04-16 20:59:25 +08:00
return cap ;
return Ptr < IVideoCapture > ( ) ;
}
2010-05-12 01:44:00 +08:00
2021-03-01 23:51:04 +08:00
Ptr < IVideoCapture > createGStreamerCapture_cam ( int index , const cv : : VideoCaptureParameters & params )
2018-04-16 20:59:25 +08:00
{
Ptr < GStreamerCapture > cap = makePtr < GStreamerCapture > ( ) ;
2021-03-01 23:51:04 +08:00
if ( cap & & cap - > open ( index , params ) )
2018-04-16 20:59:25 +08:00
return cap ;
return Ptr < IVideoCapture > ( ) ;
2010-05-12 01:44:00 +08:00
}
2013-03-21 17:17:34 +08:00
2018-04-16 20:59:25 +08:00
//==================================================================================================
2013-03-21 17:17:34 +08:00
/*!
* \ brief The CvVideoWriter_GStreamer class
2019-06-20 18:51:57 +08:00
* Use GStreamer to write video
2013-03-21 17:17:34 +08:00
*/
class CvVideoWriter_GStreamer : public CvVideoWriter
{
public :
2018-07-27 23:41:39 +08:00
CvVideoWriter_GStreamer ( )
2020-12-06 05:28:07 +08:00
: ipl_depth ( CV_8U )
, input_pix_fmt ( 0 ) , num_frames ( 0 ) , framerate ( 0 )
2021-03-01 23:51:04 +08:00
, va_type ( VIDEO_ACCELERATION_NONE ) , hw_device ( 0 )
2018-07-27 23:41:39 +08:00
{
}
2019-07-19 06:12:01 +08:00
virtual ~ CvVideoWriter_GStreamer ( ) CV_OVERRIDE
{
try
{
close ( ) ;
}
catch ( const std : : exception & e )
{
CV_WARN ( " C++ exception in writer destructor: " < < e . what ( ) ) ;
}
catch ( . . . )
{
CV_WARN ( " Unknown exception in writer destructor. Ignore " ) ;
}
}
2013-03-21 17:17:34 +08:00
2018-09-16 04:54:03 +08:00
int getCaptureDomain ( ) const CV_OVERRIDE { return cv : : CAP_GSTREAMER ; }
2019-03-29 22:52:22 +08:00
bool open ( const std : : string & filename , int fourcc ,
2021-03-01 23:51:04 +08:00
double fps , const Size & frameSize , const VideoWriterParameters & params ) ;
2019-01-14 18:33:38 +08:00
void close ( ) ;
bool writeFrame ( const IplImage * image ) CV_OVERRIDE ;
2020-12-06 05:28:07 +08:00
int getIplDepth ( ) const { return ipl_depth ; }
2021-03-01 23:51:04 +08:00
virtual double getProperty ( int ) const CV_OVERRIDE ;
2013-03-21 17:17:34 +08:00
protected :
const char * filenameToMimetype ( const char * filename ) ;
2019-06-20 18:51:57 +08:00
GSafePtr < GstElement > pipeline ;
GSafePtr < GstElement > source ;
2020-12-06 05:28:07 +08:00
int ipl_depth ;
2013-03-21 17:17:34 +08:00
int input_pix_fmt ;
int num_frames ;
double framerate ;
2019-06-20 18:51:57 +08:00
2021-03-01 23:51:04 +08:00
VideoAccelerationType va_type ;
int hw_device ;
2019-06-20 18:51:57 +08:00
void close_ ( ) ;
2013-03-21 17:17:34 +08:00
} ;
/*!
* \ brief CvVideoWriter_GStreamer : : close
* ends the pipeline by sending EOS and destroys the pipeline and all
* elements afterwards
*/
2019-06-20 18:51:57 +08:00
void CvVideoWriter_GStreamer : : close_ ( )
2013-03-21 17:17:34 +08:00
{
2014-09-10 14:38:43 +08:00
GstStateChangeReturn status ;
2013-03-21 23:11:53 +08:00
if ( pipeline )
{
2014-09-16 17:57:44 +08:00
handleMessage ( pipeline ) ;
2014-09-10 14:38:43 +08:00
2019-07-19 06:12:01 +08:00
if ( ! ( bool ) source )
{
CV_WARN ( " No source in GStreamer pipeline. Ignore " ) ;
}
else if ( gst_app_src_end_of_stream ( GST_APP_SRC ( source . get ( ) ) ) ! = GST_FLOW_OK )
2014-09-16 17:57:44 +08:00
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " Cannot send EOS to GStreamer pipeline " ) ;
2014-09-16 17:57:44 +08:00
}
2019-06-20 18:51:57 +08:00
else
2014-09-16 17:57:44 +08:00
{
2019-06-20 18:51:57 +08:00
//wait for EOS to trickle down the pipeline. This will let all elements finish properly
GSafePtr < GstBus > bus ; bus . attach ( gst_element_get_bus ( pipeline ) ) ;
if ( bus )
2018-07-27 23:41:39 +08:00
{
2019-06-20 18:51:57 +08:00
GSafePtr < GstMessage > msg ; msg . attach ( gst_bus_timed_pop_filtered ( bus , GST_CLOCK_TIME_NONE , ( GstMessageType ) ( GST_MESSAGE_ERROR | GST_MESSAGE_EOS ) ) ) ;
if ( ! msg | | GST_MESSAGE_TYPE ( msg . get ( ) ) = = GST_MESSAGE_ERROR )
{
CV_WARN ( " Error during VideoWriter finalization " ) ;
handleMessage ( pipeline ) ;
}
}
else
{
CV_WARN ( " can't get GstBus " ) ;
2018-07-27 23:41:39 +08:00
}
2014-09-16 17:57:44 +08:00
}
2014-09-10 14:38:43 +08:00
status = gst_element_set_state ( pipeline , GST_STATE_NULL ) ;
if ( status = = GST_STATE_CHANGE_ASYNC )
{
// wait for status update
GstState st1 ;
GstState st2 ;
status = gst_element_get_state ( pipeline , & st1 , & st2 , GST_CLOCK_TIME_NONE ) ;
}
if ( status = = GST_STATE_CHANGE_FAILURE )
{
handleMessage ( pipeline ) ;
2019-06-20 18:51:57 +08:00
CV_WARN ( " Unable to stop writer pipeline " ) ;
2014-09-10 14:38:43 +08:00
}
2013-03-21 17:17:34 +08:00
}
}
2019-06-20 18:51:57 +08:00
void CvVideoWriter_GStreamer : : close ( )
{
close_ ( ) ;
source . release ( ) ;
pipeline . release ( ) ;
2021-03-01 23:51:04 +08:00
va_type = VIDEO_ACCELERATION_NONE ;
hw_device = - 1 ;
2019-06-20 18:51:57 +08:00
}
2013-03-21 17:17:34 +08:00
/*!
2019-06-20 18:51:57 +08:00
* \ brief filenameToMimetype
2013-03-21 17:17:34 +08:00
* \ param filename
* \ return mimetype
2019-06-20 18:51:57 +08:00
* Returns a container mime type for a given filename by looking at it ' s extension
2013-03-21 17:17:34 +08:00
*/
const char * CvVideoWriter_GStreamer : : filenameToMimetype ( const char * filename )
{
//get extension
2019-06-20 18:51:57 +08:00
const char * ext_ = strrchr ( filename , ' . ' ) ;
if ( ! ext_ | | ext_ = = filename )
return NULL ;
ext_ + = 1 ; //exclude the dot
std : : string ext ( ext_ ) ;
ext = toLowerCase ( ext ) ;
2013-03-21 17:17:34 +08:00
// return a container mime based on the given extension.
// gstreamer's function returns too much possibilities, which is not useful to us
//return the appropriate mime
2019-06-20 18:51:57 +08:00
if ( ext = = " avi " )
return " video/x-msvideo " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " mkv " | | ext = = " mk3d " | | ext = = " webm " )
return " video/x-matroska " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " wmv " )
return " video/x-ms-asf " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " mov " )
return " video/x-quicktime " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " ogg " | | ext = = " ogv " )
return " application/ogg " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " rm " )
return " vnd.rn-realmedia " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " swf " )
return " application/x-shockwave-flash " ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ext = = " mp4 " )
return " video/x-quicktime, variant=(string)iso " ;
2013-03-21 17:17:34 +08:00
//default to avi
2019-06-20 18:51:57 +08:00
return " video/x-msvideo " ;
2013-03-21 17:17:34 +08:00
}
/*!
* \ brief CvVideoWriter_GStreamer : : open
* \ param filename filename to output to
* \ param fourcc desired codec fourcc
* \ param fps desired framerate
* \ param frameSize the size of the expected frames
2021-03-01 23:51:04 +08:00
* \ param params other parameters
2013-03-21 17:17:34 +08:00
* \ return success
*
* We support 2 modes of operation . Either the user enters a filename and a fourcc
2019-06-20 18:51:57 +08:00
* code , or enters a manual pipeline description like in CvVideoCapture_GStreamer .
2013-03-21 17:17:34 +08:00
* In the latter case , we just push frames on the appsink with appropriate caps .
* In the former case , we try to deduce the correct container from the filename ,
* and the correct encoder from the fourcc profile .
*
* If the file extension did was not recognize , an avi container is used
*
*/
2019-03-29 22:52:22 +08:00
bool CvVideoWriter_GStreamer : : open ( const std : : string & filename , int fourcc ,
2020-12-06 05:28:07 +08:00
double fps , const cv : : Size & frameSize ,
2021-03-01 23:51:04 +08:00
const VideoWriterParameters & params )
2013-03-21 17:17:34 +08:00
{
// check arguments
2019-06-20 18:51:57 +08:00
CV_Assert ( ! filename . empty ( ) ) ;
CV_Assert ( fps > 0 ) ;
CV_Assert ( frameSize . width > 0 & & frameSize . height > 0 ) ;
2013-03-21 17:17:34 +08:00
2021-03-01 23:51:04 +08:00
const bool is_color = params . get ( VIDEOWRITER_PROP_IS_COLOR , true ) ;
const int depth = params . get ( VIDEOWRITER_PROP_DEPTH , CV_8U ) ;
if ( params . has ( VIDEOWRITER_PROP_HW_ACCELERATION ) )
{
va_type = params . get < VideoAccelerationType > ( VIDEOWRITER_PROP_HW_ACCELERATION ) ;
}
if ( params . has ( VIDEOWRITER_PROP_HW_DEVICE ) )
{
hw_device = params . get < int > ( VIDEOWRITER_PROP_HW_DEVICE ) ;
if ( va_type = = VIDEO_ACCELERATION_NONE & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Invalid usage of VIDEOWRITER_PROP_HW_DEVICE without requested H/W acceleration. Bailout " ) ;
return false ;
}
if ( va_type = = VIDEO_ACCELERATION_ANY & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Invalid usage of VIDEOWRITER_PROP_HW_DEVICE with 'ANY' H/W acceleration. Bailout " ) ;
return false ;
}
if ( hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: VIDEOWRITER_PROP_HW_DEVICE is not supported. Specify -1 (auto) value. Bailout " ) ;
return false ;
}
}
if ( params . warnUnusedParameters ( ) )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: unsupported parameters in VideoWriter, see logger INFO channel for details " ) ;
return false ;
}
2013-03-21 17:17:34 +08:00
// init gstreamer
gst_initializer : : init ( ) ;
// init vars
2019-06-20 18:51:57 +08:00
GSafePtr < GstElement > file ;
GSafePtr < GstElement > encodebin ;
2013-03-21 17:17:34 +08:00
bool manualpipeline = true ;
int bufsize = 0 ;
2019-06-20 18:51:57 +08:00
GSafePtr < GError > err ;
2013-03-21 17:17:34 +08:00
GstStateChangeReturn stateret ;
2019-06-20 18:51:57 +08:00
GSafePtr < GstCaps > caps ;
2013-03-21 17:17:34 +08:00
2014-09-16 21:05:36 +08:00
GstIterator * it = NULL ;
2014-09-23 18:00:53 +08:00
gboolean done = FALSE ;
2014-09-25 18:51:34 +08:00
2013-03-21 17:17:34 +08:00
// we first try to construct a pipeline from the given string.
// if that fails, we assume it is an ordinary filename
2019-06-20 18:51:57 +08:00
encodebin . attach ( gst_parse_launch ( filename . c_str ( ) , err . getRef ( ) ) ) ;
manualpipeline = ( bool ) encodebin ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( manualpipeline )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
if ( err )
{
CV_WARN ( " error opening writer pipeline: " < < err - > message ) ;
if ( encodebin )
{
gst_element_set_state ( encodebin , GST_STATE_NULL ) ;
}
handleMessage ( encodebin ) ;
encodebin . release ( ) ;
return false ;
}
it = gst_bin_iterate_sources ( GST_BIN ( encodebin . get ( ) ) ) ;
2013-06-13 17:16:33 +08:00
2019-06-20 18:51:57 +08:00
while ( ! done )
{
GValue value = G_VALUE_INIT ;
GSafePtr < gchar > name ;
GstElement * element = NULL ;
2013-06-13 17:16:33 +08:00
switch ( gst_iterator_next ( it , & value ) ) {
case GST_ITERATOR_OK :
2019-06-20 18:51:57 +08:00
element = GST_ELEMENT ( g_value_get_object ( & value ) ) ; // no lifetime transfer
name . attach ( gst_element_get_name ( element ) ) ;
if ( name )
{
if ( strstr ( name . get ( ) , " opencvsrc " ) ! = NULL | | strstr ( name . get ( ) , " appsrc " ) ! = NULL )
{
source . attach ( GST_ELEMENT ( gst_object_ref ( element ) ) ) ;
2013-06-13 17:16:33 +08:00
done = TRUE ;
}
}
2019-06-20 18:51:57 +08:00
g_value_unset ( & value ) ;
2013-06-13 17:16:33 +08:00
break ;
case GST_ITERATOR_RESYNC :
gst_iterator_resync ( it ) ;
break ;
case GST_ITERATOR_ERROR :
case GST_ITERATOR_DONE :
done = TRUE ;
break ;
}
2013-03-21 17:17:34 +08:00
}
2013-06-13 17:16:33 +08:00
gst_iterator_free ( it ) ;
2013-06-12 22:58:16 +08:00
2013-03-21 17:17:34 +08:00
if ( ! source ) {
2018-04-16 20:59:25 +08:00
CV_WARN ( " GStreamer: cannot find appsrc in manual pipeline \n " ) ;
2013-03-21 17:17:34 +08:00
return false ;
}
2019-06-20 18:51:57 +08:00
pipeline . swap ( encodebin ) ;
2013-03-21 17:17:34 +08:00
}
else
{
2019-06-20 18:51:57 +08:00
err . release ( ) ;
pipeline . reset ( gst_pipeline_new ( NULL ) ) ;
2013-03-21 17:17:34 +08:00
// we just got a filename and a fourcc code.
// first, try to guess the container from the filename
//proxy old non existing fourcc ids. These were used in previous opencv versions,
//but do not even exist in gstreamer any more
if ( fourcc = = CV_FOURCC ( ' M ' , ' P ' , ' 1 ' , ' V ' ) ) fourcc = CV_FOURCC ( ' M ' , ' P ' , ' G ' , ' 1 ' ) ;
if ( fourcc = = CV_FOURCC ( ' M ' , ' P ' , ' 2 ' , ' V ' ) ) fourcc = CV_FOURCC ( ' M ' , ' P ' , ' G ' , ' 2 ' ) ;
if ( fourcc = = CV_FOURCC ( ' D ' , ' R ' , ' A ' , ' C ' ) ) fourcc = CV_FOURCC ( ' d ' , ' r ' , ' a ' , ' c ' ) ;
2014-09-16 21:05:36 +08:00
2013-03-21 17:17:34 +08:00
//create encoder caps from fourcc
2019-06-20 18:51:57 +08:00
GSafePtr < GstCaps > videocaps ;
videocaps . attach ( gst_riff_create_video_caps ( fourcc , NULL , NULL , NULL , NULL , NULL ) ) ;
if ( ! videocaps )
{
CV_WARN ( " OpenCV backend does not support passed FOURCC value " ) ;
2018-04-16 20:59:25 +08:00
return false ;
2013-03-21 17:17:34 +08:00
}
//create container caps from file extension
2019-06-20 18:51:57 +08:00
const char * mime = filenameToMimetype ( filename . c_str ( ) ) ;
if ( ! mime )
{
CV_WARN ( " OpenCV backend does not support this file type (extension): " < < filename ) ;
2018-04-16 20:59:25 +08:00
return false ;
2013-03-21 17:17:34 +08:00
}
2013-03-26 16:27:39 +08:00
2019-06-20 18:51:57 +08:00
//create pipeline elements
encodebin . reset ( gst_element_factory_make ( " encodebin " , NULL ) ) ;
GSafePtr < GstCaps > containercaps ;
GSafePtr < GstEncodingContainerProfile > containerprofile ;
GSafePtr < GstEncodingVideoProfile > videoprofile ;
containercaps . attach ( gst_caps_from_string ( mime ) ) ;
2013-03-21 17:17:34 +08:00
//create encodebin profile
2019-06-20 18:51:57 +08:00
containerprofile . attach ( gst_encoding_container_profile_new ( " container " , " container " , containercaps . get ( ) , NULL ) ) ;
videoprofile . reset ( gst_encoding_video_profile_new ( videocaps . get ( ) , NULL , NULL , 1 ) ) ;
gst_encoding_container_profile_add_profile ( containerprofile . get ( ) , ( GstEncodingProfile * ) videoprofile . get ( ) ) ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
g_object_set ( G_OBJECT ( encodebin . get ( ) ) , " profile " , containerprofile . get ( ) , NULL ) ;
2014-09-16 21:05:36 +08:00
2019-06-20 18:51:57 +08:00
source . reset ( gst_element_factory_make ( " appsrc " , NULL ) ) ;
file . reset ( gst_element_factory_make ( " filesink " , NULL ) ) ;
g_object_set ( G_OBJECT ( file . get ( ) ) , " location " , ( const char * ) filename . c_str ( ) , NULL ) ;
2013-03-21 17:17:34 +08:00
}
2019-06-20 18:51:57 +08:00
int fps_num = 0 , fps_denom = 1 ;
toFraction ( fps , fps_num , fps_denom ) ;
2016-03-19 01:02:19 +08:00
if ( fourcc = = CV_FOURCC ( ' M ' , ' J ' , ' P ' , ' G ' ) & & frameSize . height = = 1 )
{
2020-12-06 05:28:07 +08:00
CV_Assert ( depth = = CV_8U ) ;
ipl_depth = IPL_DEPTH_8U ;
2016-04-14 19:11:26 +08:00
input_pix_fmt = GST_VIDEO_FORMAT_ENCODED ;
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_new_simple ( " image/jpeg " ,
" framerate " , GST_TYPE_FRACTION , int ( fps_num ) , int ( fps_denom ) ,
NULL ) ) ;
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
2016-03-19 01:02:19 +08:00
}
2019-06-20 18:51:57 +08:00
else if ( is_color )
2013-03-21 17:17:34 +08:00
{
2020-12-06 05:28:07 +08:00
CV_Assert ( depth = = CV_8U ) ;
ipl_depth = IPL_DEPTH_8U ;
2013-03-21 17:17:34 +08:00
input_pix_fmt = GST_VIDEO_FORMAT_BGR ;
bufsize = frameSize . width * frameSize . height * 3 ;
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_new_simple ( " video/x-raw " ,
" format " , G_TYPE_STRING , " BGR " ,
" width " , G_TYPE_INT , frameSize . width ,
" height " , G_TYPE_INT , frameSize . height ,
" framerate " , GST_TYPE_FRACTION , gint ( fps_num ) , gint ( fps_denom ) ,
NULL ) ) ;
CV_Assert ( caps ) ;
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
CV_Assert ( caps ) ;
2013-03-21 17:17:34 +08:00
}
2020-12-06 05:28:07 +08:00
else if ( ! is_color & & depth = = CV_8U )
2013-03-21 17:17:34 +08:00
{
2020-12-06 05:28:07 +08:00
ipl_depth = IPL_DEPTH_8U ;
2013-03-21 17:17:34 +08:00
input_pix_fmt = GST_VIDEO_FORMAT_GRAY8 ;
bufsize = frameSize . width * frameSize . height ;
2019-06-20 18:51:57 +08:00
caps . attach ( gst_caps_new_simple ( " video/x-raw " ,
" format " , G_TYPE_STRING , " GRAY8 " ,
" width " , G_TYPE_INT , frameSize . width ,
" height " , G_TYPE_INT , frameSize . height ,
" framerate " , GST_TYPE_FRACTION , gint ( fps_num ) , gint ( fps_denom ) ,
NULL ) ) ;
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
2013-03-21 17:17:34 +08:00
}
2020-12-06 05:28:07 +08:00
else if ( ! is_color & & depth = = CV_16U )
{
ipl_depth = IPL_DEPTH_16U ;
input_pix_fmt = GST_VIDEO_FORMAT_GRAY16_LE ;
bufsize = frameSize . width * frameSize . height * 2 ;
caps . attach ( gst_caps_new_simple ( " video/x-raw " ,
" format " , G_TYPE_STRING , " GRAY16_LE " ,
" width " , G_TYPE_INT , frameSize . width ,
" height " , G_TYPE_INT , frameSize . height ,
" framerate " , GST_TYPE_FRACTION , gint ( fps_num ) , gint ( fps_denom ) ,
NULL ) ) ;
caps . attach ( gst_caps_fixate ( caps . detach ( ) ) ) ;
}
else
{
CV_WARN ( " unsupported depth= " < < depth < < " , and is_color= " < < is_color < < " combination " ) ;
pipeline . release ( ) ;
return false ;
}
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
gst_app_src_set_caps ( GST_APP_SRC ( source . get ( ) ) , caps ) ;
gst_app_src_set_stream_type ( GST_APP_SRC ( source . get ( ) ) , GST_APP_STREAM_TYPE_STREAM ) ;
gst_app_src_set_size ( GST_APP_SRC ( source . get ( ) ) , - 1 ) ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
g_object_set ( G_OBJECT ( source . get ( ) ) , " format " , GST_FORMAT_TIME , NULL ) ;
g_object_set ( G_OBJECT ( source . get ( ) ) , " block " , 1 , NULL ) ;
g_object_set ( G_OBJECT ( source . get ( ) ) , " is-live " , 0 , NULL ) ;
2014-09-16 21:05:36 +08:00
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( ! manualpipeline )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
g_object_set ( G_OBJECT ( file . get ( ) ) , " buffer-size " , bufsize , NULL ) ;
gst_bin_add_many ( GST_BIN ( pipeline . get ( ) ) , source . get ( ) , encodebin . get ( ) , file . get ( ) , NULL ) ;
if ( ! gst_element_link_many ( source . get ( ) , encodebin . get ( ) , file . get ( ) , NULL ) )
{
CV_WARN ( " cannot link elements " ) ;
pipeline . release ( ) ;
2018-04-16 20:59:25 +08:00
return false ;
2013-03-21 17:17:34 +08:00
}
}
2019-06-20 18:51:57 +08:00
GST_DEBUG_BIN_TO_DOT_FILE ( GST_BIN ( pipeline . get ( ) ) , GST_DEBUG_GRAPH_SHOW_ALL , " write-pipeline " ) ;
2018-06-05 02:38:24 +08:00
2019-06-20 18:51:57 +08:00
stateret = gst_element_set_state ( GST_ELEMENT ( pipeline . get ( ) ) , GST_STATE_PLAYING ) ;
if ( stateret = = GST_STATE_CHANGE_FAILURE )
{
2014-09-10 14:38:43 +08:00
handleMessage ( pipeline ) ;
2018-04-16 20:59:25 +08:00
CV_WARN ( " GStreamer: cannot put pipeline to play \n " ) ;
2019-06-20 18:51:57 +08:00
pipeline . release ( ) ;
2018-04-16 20:59:25 +08:00
return false ;
2013-03-21 17:17:34 +08:00
}
framerate = fps ;
num_frames = 0 ;
2014-09-10 14:38:43 +08:00
handleMessage ( pipeline ) ;
2021-03-01 23:51:04 +08:00
if ( pipeline )
{
VideoAccelerationType actual_va_type = VIDEO_ACCELERATION_NONE ;
GstIterator * iter = gst_bin_iterate_recurse ( GST_BIN ( pipeline . get ( ) ) ) ;
gst_iterator_foreach ( iter , find_hw_element , ( gpointer ) & actual_va_type ) ;
gst_iterator_free ( iter ) ;
if ( va_type ! = VIDEO_ACCELERATION_NONE & & va_type ! = VIDEO_ACCELERATION_ANY )
{
if ( va_type ! = actual_va_type )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/GStreamer: Can't select requested VideoWriter acceleration through VIDEOWRITER_PROP_HW_ACCELERATION: "
< < va_type < < " (actual is " < < actual_va_type < < " ). Bailout " ) ;
close ( ) ;
return false ;
}
}
else
{
va_type = actual_va_type ;
}
}
2013-03-21 17:17:34 +08:00
return true ;
}
/*!
* \ brief CvVideoWriter_GStreamer : : writeFrame
* \ param image
* \ return
* Pushes the given frame on the pipeline .
* The timestamp for the buffer is generated from the framerate set in open
* and ensures a smooth video
*/
bool CvVideoWriter_GStreamer : : writeFrame ( const IplImage * image )
{
GstClockTime duration , timestamp ;
GstFlowReturn ret ;
int size ;
handleMessage ( pipeline ) ;
2016-03-19 01:02:19 +08:00
if ( input_pix_fmt = = GST_VIDEO_FORMAT_ENCODED ) {
if ( image - > nChannels ! = 1 | | image - > depth ! = IPL_DEPTH_8U | | image - > height ! = 1 ) {
2018-04-16 20:59:25 +08:00
CV_WARN ( " cvWriteFrame() needs images with depth = IPL_DEPTH_8U, nChannels = 1 and height = 1. " ) ;
return false ;
2016-03-19 01:02:19 +08:00
}
}
2016-04-14 19:11:26 +08:00
else
if ( input_pix_fmt = = GST_VIDEO_FORMAT_BGR ) {
2013-03-21 17:17:34 +08:00
if ( image - > nChannels ! = 3 | | image - > depth ! = IPL_DEPTH_8U ) {
2018-04-16 20:59:25 +08:00
CV_WARN ( " cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3. " ) ;
return false ;
2013-03-21 17:17:34 +08:00
}
}
else if ( input_pix_fmt = = GST_VIDEO_FORMAT_GRAY8 ) {
if ( image - > nChannels ! = 1 | | image - > depth ! = IPL_DEPTH_8U ) {
2018-04-16 20:59:25 +08:00
CV_WARN ( " cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1. " ) ;
return false ;
2013-03-21 17:17:34 +08:00
}
}
2020-12-06 05:28:07 +08:00
else if ( input_pix_fmt = = GST_VIDEO_FORMAT_GRAY16_LE ) {
if ( image - > nChannels ! = 1 | | image - > depth ! = IPL_DEPTH_16U ) {
CV_WARN ( " cvWriteFrame() needs images with depth = IPL_DEPTH_16U and nChannels = 1. " ) ;
return false ;
}
}
2013-03-21 17:17:34 +08:00
else {
2018-04-16 20:59:25 +08:00
CV_WARN ( " cvWriteFrame() needs BGR or grayscale images \n " ) ;
2014-09-10 14:38:43 +08:00
return false ;
2013-03-21 17:17:34 +08:00
}
size = image - > imageSize ;
duration = ( ( double ) 1 / framerate ) * GST_SECOND ;
timestamp = num_frames * duration ;
//gst_app_src_push_buffer takes ownership of the buffer, so we need to supply it a copy
2019-06-20 18:51:57 +08:00
GstBuffer * buffer = gst_buffer_new_allocate ( NULL , size , NULL ) ;
2013-03-21 17:17:34 +08:00
GstMapInfo info ;
gst_buffer_map ( buffer , & info , ( GstMapFlags ) GST_MAP_READ ) ;
memcpy ( info . data , ( guint8 * ) image - > imageData , size ) ;
gst_buffer_unmap ( buffer , & info ) ;
GST_BUFFER_DURATION ( buffer ) = duration ;
GST_BUFFER_PTS ( buffer ) = timestamp ;
GST_BUFFER_DTS ( buffer ) = timestamp ;
//set the current number in the frame
2019-06-20 18:51:57 +08:00
GST_BUFFER_OFFSET ( buffer ) = num_frames ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
ret = gst_app_src_push_buffer ( GST_APP_SRC ( source . get ( ) ) , buffer ) ;
if ( ret ! = GST_FLOW_OK )
{
2014-09-10 14:38:43 +08:00
CV_WARN ( " Error pushing buffer to GStreamer pipeline " ) ;
return false ;
2013-03-21 17:17:34 +08:00
}
2014-09-16 21:05:36 +08:00
//GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");
2014-09-10 14:38:43 +08:00
2013-03-21 17:17:34 +08:00
+ + num_frames ;
return true ;
}
2021-03-01 23:51:04 +08:00
double CvVideoWriter_GStreamer : : getProperty ( int propId ) const
{
if ( propId = = VIDEOWRITER_PROP_HW_ACCELERATION )
{
return static_cast < double > ( va_type ) ;
}
else if ( propId = = VIDEOWRITER_PROP_HW_DEVICE )
{
return static_cast < double > ( hw_device ) ;
}
return 0 ;
}
2020-04-28 16:38:39 +08:00
Ptr < IVideoWriter > create_GStreamer_writer ( const std : : string & filename , int fourcc , double fps ,
const cv : : Size & frameSize , const VideoWriterParameters & params )
2013-03-21 17:17:34 +08:00
{
CvVideoWriter_GStreamer * wrt = new CvVideoWriter_GStreamer ;
2019-06-20 18:51:57 +08:00
try
{
2021-03-01 23:51:04 +08:00
if ( wrt - > open ( filename , fourcc , fps , frameSize , params ) )
2019-06-20 18:51:57 +08:00
return makePtr < LegacyWriter > ( wrt ) ;
delete wrt ;
}
catch ( . . . )
{
delete wrt ;
throw ;
}
2013-03-21 17:17:34 +08:00
return 0 ;
}
// utility functions
2019-06-20 18:51:57 +08:00
void toFraction ( const double decimal , int & numerator_i , int & denominator_i )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
double err = 1.0 ;
int denominator = 1 ;
double numerator = 0 ;
for ( int check_denominator = 1 ; ; check_denominator + + )
{
double check_numerator = ( double ) check_denominator * decimal ;
double dummy ;
double check_err = modf ( check_numerator , & dummy ) ;
if ( check_err < err )
{
err = check_err ;
denominator = check_denominator ;
numerator = check_numerator ;
if ( err < FLT_EPSILON )
break ;
}
if ( check_denominator = = 100 ) // limit
2013-03-21 17:17:34 +08:00
break ;
}
2019-06-20 18:51:57 +08:00
numerator_i = cvRound ( numerator ) ;
denominator_i = denominator ;
//printf("%g: %d/%d (err=%g)\n", decimal, numerator_i, denominator_i, err);
2013-03-21 17:17:34 +08:00
}
/*!
* \ brief handleMessage
* Handles gstreamer bus messages . Mainly for debugging purposes and ensuring clean shutdown on error
*/
void handleMessage ( GstElement * pipeline )
{
2019-06-20 18:51:57 +08:00
GSafePtr < GstBus > bus ;
2013-03-21 17:17:34 +08:00
GstStreamStatusType tp ;
GstElement * elem = NULL ;
2019-06-20 18:51:57 +08:00
bus . attach ( gst_element_get_bus ( pipeline ) ) ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
while ( gst_bus_have_pending ( bus ) )
{
GSafePtr < GstMessage > msg ;
msg . attach ( gst_bus_pop ( bus ) ) ;
if ( ! msg | | ! GST_IS_MESSAGE ( msg . get ( ) ) )
2019-03-07 00:14:59 +08:00
continue ;
2013-03-21 17:17:34 +08:00
2019-06-20 18:51:57 +08:00
if ( gst_is_missing_plugin_message ( msg ) )
2013-03-21 17:17:34 +08:00
{
2019-06-20 18:51:57 +08:00
CV_WARN ( " your GStreamer installation is missing a required plugin " ) ;
2013-03-21 17:17:34 +08:00
}
else
{
switch ( GST_MESSAGE_TYPE ( msg ) ) {
case GST_MESSAGE_STATE_CHANGED :
GstState oldstate , newstate , pendstate ;
gst_message_parse_state_changed ( msg , & oldstate , & newstate , & pendstate ) ;
break ;
case GST_MESSAGE_ERROR :
2019-06-20 18:51:57 +08:00
{
GSafePtr < GError > err ;
GSafePtr < gchar > debug ;
gst_message_parse_error ( msg , err . getRef ( ) , debug . getRef ( ) ) ;
GSafePtr < gchar > name ; name . attach ( gst_element_get_name ( GST_MESSAGE_SRC ( msg ) ) ) ;
CV_WARN ( " Embedded video playback halted; module " < < name . get ( ) < <
2021-10-08 10:07:04 +08:00
" reported: " < < ( err ? err - > message : " <unknown reason> " ) ) ;
2019-06-20 18:51:57 +08:00
CV_LOG_DEBUG ( NULL , " GStreamer debug: " < < debug . get ( ) ) ;
2013-03-21 17:17:34 +08:00
gst_element_set_state ( GST_ELEMENT ( pipeline ) , GST_STATE_NULL ) ;
break ;
2019-06-20 18:51:57 +08:00
}
2013-03-21 17:17:34 +08:00
case GST_MESSAGE_EOS :
break ;
case GST_MESSAGE_STREAM_STATUS :
gst_message_parse_stream_status ( msg , & tp , & elem ) ;
break ;
default :
break ;
}
}
}
}
2019-01-14 18:33:38 +08:00
2019-06-20 18:51:57 +08:00
} // namespace cv
2019-01-14 18:33:38 +08:00
//==================================================================================================
# if defined(BUILD_PLUGIN)
2021-01-14 06:53:56 +08:00
# define CAPTURE_ABI_VERSION 1
2021-03-01 23:51:04 +08:00
# define CAPTURE_API_VERSION 1
2021-01-14 06:53:56 +08:00
# include "plugin_capture_api.hpp"
# define WRITER_ABI_VERSION 1
# define WRITER_API_VERSION 1
# include "plugin_writer_api.hpp"
2019-01-14 18:33:38 +08:00
2019-03-03 06:36:18 +08:00
namespace cv {
2019-01-14 18:33:38 +08:00
2019-03-03 06:36:18 +08:00
static
2021-03-01 23:51:04 +08:00
CvResult CV_API_CALL cv_capture_open_with_params (
const char * filename , int camera_index ,
int * params , unsigned n_params ,
CV_OUT CvPluginCapture * handle
)
2019-01-14 18:33:38 +08:00
{
2019-03-03 06:36:18 +08:00
if ( ! handle )
return CV_ERROR_FAIL ;
* handle = NULL ;
if ( ! filename )
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
GStreamerCapture * cap = 0 ;
try
{
2021-03-01 23:51:04 +08:00
cv : : VideoCaptureParameters parameters ( params , n_params ) ;
2019-01-14 18:33:38 +08:00
cap = new GStreamerCapture ( ) ;
bool res ;
if ( filename )
2021-03-01 23:51:04 +08:00
res = cap - > open ( std : : string ( filename ) , parameters ) ;
2019-01-14 18:33:38 +08:00
else
2021-03-01 23:51:04 +08:00
res = cap - > open ( camera_index , parameters ) ;
2019-01-14 18:33:38 +08:00
if ( res )
{
2019-03-03 06:36:18 +08:00
* handle = ( CvPluginCapture ) cap ;
return CV_ERROR_OK ;
2019-01-14 18:33:38 +08:00
}
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
}
2019-01-14 18:33:38 +08:00
catch ( . . . )
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-01-14 18:33:38 +08:00
}
if ( cap )
delete cap ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2021-03-01 23:51:04 +08:00
static
CvResult CV_API_CALL cv_capture_open ( const char * filename , int camera_index , CV_OUT CvPluginCapture * handle )
{
return cv_capture_open_with_params ( filename , camera_index , NULL , 0 , handle ) ;
}
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_capture_release ( CvPluginCapture handle )
2019-01-14 18:33:38 +08:00
{
if ( ! handle )
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
GStreamerCapture * instance = ( GStreamerCapture * ) handle ;
delete instance ;
return CV_ERROR_OK ;
}
static
CvResult CV_API_CALL cv_capture_get_prop ( CvPluginCapture handle , int prop , CV_OUT double * val )
{
if ( ! handle )
return CV_ERROR_FAIL ;
if ( ! val )
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
try
{
2019-03-03 06:36:18 +08:00
GStreamerCapture * instance = ( GStreamerCapture * ) handle ;
* val = instance - > getProperty ( prop ) ;
return CV_ERROR_OK ;
2019-01-14 18:33:38 +08:00
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
return CV_ERROR_FAIL ;
}
2019-03-03 06:36:18 +08:00
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
}
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_capture_set_prop ( CvPluginCapture handle , int prop , double val )
2019-01-14 18:33:38 +08:00
{
if ( ! handle )
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
try
{
2019-03-03 06:36:18 +08:00
GStreamerCapture * instance = ( GStreamerCapture * ) handle ;
return instance - > setProperty ( prop , val ) ? CV_ERROR_OK : CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
return CV_ERROR_FAIL ;
}
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
}
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_capture_grab ( CvPluginCapture handle )
2019-01-14 18:33:38 +08:00
{
if ( ! handle )
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
try
{
2019-03-03 06:36:18 +08:00
GStreamerCapture * instance = ( GStreamerCapture * ) handle ;
return instance - > grabFrame ( ) ? CV_ERROR_OK : CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
return CV_ERROR_FAIL ;
}
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
}
2019-03-03 06:36:18 +08:00
static
2021-01-14 06:53:56 +08:00
CvResult CV_API_CALL cv_capture_retrieve ( CvPluginCapture handle , int stream_idx , cv_videoio_capture_retrieve_cb_t callback , void * userdata )
2019-01-14 18:33:38 +08:00
{
if ( ! handle )
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
try
{
2019-03-03 06:36:18 +08:00
GStreamerCapture * instance = ( GStreamerCapture * ) handle ;
2019-01-14 18:33:38 +08:00
Mat img ;
// TODO: avoid unnecessary copying - implement lower level GStreamerCapture::retrieve
2019-03-03 06:36:18 +08:00
if ( instance - > retrieveFrame ( stream_idx , img ) )
2021-01-14 06:53:56 +08:00
return callback ( stream_idx , img . data , img . step , img . cols , img . rows , img . type ( ) , userdata ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
return CV_ERROR_FAIL ;
}
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
}
2019-03-03 06:36:18 +08:00
static
2020-12-06 05:28:07 +08:00
CvResult CV_API_CALL cv_writer_open_with_params (
const char * filename , int fourcc , double fps , int width , int height ,
int * params , unsigned n_params ,
CV_OUT CvPluginWriter * handle )
2019-01-14 18:33:38 +08:00
{
CvVideoWriter_GStreamer * wrt = 0 ;
try
{
CvSize sz = { width , height } ;
2021-03-01 23:51:04 +08:00
VideoWriterParameters parameters ( params , n_params ) ;
2020-12-06 05:28:07 +08:00
wrt = new CvVideoWriter_GStreamer ( ) ;
2021-03-01 23:51:04 +08:00
if ( wrt & & wrt - > open ( filename , fourcc , fps , sz , parameters ) )
2019-01-14 18:33:38 +08:00
{
2019-03-03 06:36:18 +08:00
* handle = ( CvPluginWriter ) wrt ;
return CV_ERROR_OK ;
2019-01-14 18:33:38 +08:00
}
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
}
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-01-14 18:33:38 +08:00
}
if ( wrt )
delete wrt ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2020-12-06 05:28:07 +08:00
static
CvResult CV_API_CALL cv_writer_open ( const char * filename , int fourcc , double fps , int width , int height , int isColor ,
CV_OUT CvPluginWriter * handle )
{
int params [ 2 ] = { VIDEOWRITER_PROP_IS_COLOR , isColor } ;
return cv_writer_open_with_params ( filename , fourcc , fps , width , height , params , 1 , handle ) ;
}
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_writer_release ( CvPluginWriter handle )
2019-01-14 18:33:38 +08:00
{
2019-03-03 06:36:18 +08:00
if ( ! handle )
return CV_ERROR_FAIL ;
CvVideoWriter_GStreamer * instance = ( CvVideoWriter_GStreamer * ) handle ;
delete instance ;
return CV_ERROR_OK ;
2019-01-14 18:33:38 +08:00
}
2019-03-03 06:36:18 +08:00
static
2021-03-01 23:51:04 +08:00
CvResult CV_API_CALL cv_writer_get_prop ( CvPluginWriter handle , int prop , CV_OUT double * val )
2019-01-14 18:33:38 +08:00
{
2021-03-01 23:51:04 +08:00
if ( ! handle )
return CV_ERROR_FAIL ;
if ( ! val )
return CV_ERROR_FAIL ;
try
{
CvVideoWriter_GStreamer * instance = ( CvVideoWriter_GStreamer * ) handle ;
* val = instance - > getProperty ( prop ) ;
return CV_ERROR_OK ;
}
catch ( . . . )
{
return CV_ERROR_FAIL ;
}
2019-03-03 06:36:18 +08:00
}
2021-03-01 23:51:04 +08:00
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_writer_set_prop ( CvPluginWriter /*handle*/ , int /*prop*/ , double /*val*/ )
{
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2019-03-03 06:36:18 +08:00
static
CvResult CV_API_CALL cv_writer_write ( CvPluginWriter handle , const unsigned char * data , int step , int width , int height , int cn )
2019-01-14 18:33:38 +08:00
{
if ( ! handle )
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
try
{
2019-03-03 06:36:18 +08:00
CvVideoWriter_GStreamer * instance = ( CvVideoWriter_GStreamer * ) handle ;
2019-01-14 18:33:38 +08:00
CvSize sz = { width , height } ;
IplImage img ;
2020-12-06 05:28:07 +08:00
cvInitImageHeader ( & img , sz , instance - > getIplDepth ( ) , cn ) ;
2019-01-14 18:33:38 +08:00
cvSetData ( & img , const_cast < unsigned char * > ( data ) , step ) ;
2019-03-03 06:36:18 +08:00
return instance - > writeFrame ( & img ) ? CV_ERROR_OK : CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
2021-02-25 01:45:19 +08:00
catch ( const std : : exception & e )
{
CV_LOG_WARNING ( NULL , " GStreamer: Exception is raised: " < < e . what ( ) ) ;
return CV_ERROR_FAIL ;
}
catch ( . . . )
2019-01-14 18:33:38 +08:00
{
2021-02-25 01:45:19 +08:00
CV_LOG_WARNING ( NULL , " GStreamer: Unknown C++ exception is raised " ) ;
2019-03-03 06:36:18 +08:00
return CV_ERROR_FAIL ;
2019-01-14 18:33:38 +08:00
}
}
2021-01-14 06:53:56 +08:00
static const OpenCV_VideoIO_Capture_Plugin_API capture_api =
2019-01-14 18:33:38 +08:00
{
2019-03-03 06:36:18 +08:00
{
2021-01-14 06:53:56 +08:00
sizeof ( OpenCV_VideoIO_Capture_Plugin_API ) , CAPTURE_ABI_VERSION , CAPTURE_API_VERSION ,
2019-03-03 06:36:18 +08:00
CV_VERSION_MAJOR , CV_VERSION_MINOR , CV_VERSION_REVISION , CV_VERSION_STATUS ,
2021-01-14 06:53:56 +08:00
" GStreamer OpenCV Video I/O Capture plugin "
2019-03-03 06:36:18 +08:00
} ,
2020-12-04 11:18:16 +08:00
{
/* 1*/ CAP_GSTREAMER ,
/* 2*/ cv_capture_open ,
/* 3*/ cv_capture_release ,
/* 4*/ cv_capture_get_prop ,
/* 5*/ cv_capture_set_prop ,
/* 6*/ cv_capture_grab ,
/* 7*/ cv_capture_retrieve ,
2021-03-01 23:51:04 +08:00
} ,
{
/* 8*/ cv_capture_open_with_params ,
2021-01-14 06:53:56 +08:00
}
} ;
static const OpenCV_VideoIO_Writer_Plugin_API writer_api =
{
{
sizeof ( OpenCV_VideoIO_Writer_Plugin_API ) , WRITER_ABI_VERSION , WRITER_API_VERSION ,
CV_VERSION_MAJOR , CV_VERSION_MINOR , CV_VERSION_REVISION , CV_VERSION_STATUS ,
" GStreamer OpenCV Video I/O Writer plugin "
2020-12-04 11:18:16 +08:00
} ,
{
2021-01-14 06:53:56 +08:00
/* 1*/ CAP_GSTREAMER ,
/* 2*/ cv_writer_open ,
/* 3*/ cv_writer_release ,
/* 4*/ cv_writer_get_prop ,
/* 5*/ cv_writer_set_prop ,
/* 6*/ cv_writer_write
} ,
{
/* 7*/ cv_writer_open_with_params
2020-12-04 11:18:16 +08:00
}
2019-03-03 06:36:18 +08:00
} ;
} // namespace
2021-01-14 06:53:56 +08:00
const OpenCV_VideoIO_Capture_Plugin_API * opencv_videoio_capture_plugin_init_v1 ( int requested_abi_version , int requested_api_version , void * /*reserved=NULL*/ ) CV_NOEXCEPT
{
if ( requested_abi_version = = CAPTURE_ABI_VERSION & & requested_api_version < = CAPTURE_API_VERSION )
return & cv : : capture_api ;
return NULL ;
}
const OpenCV_VideoIO_Writer_Plugin_API * opencv_videoio_writer_plugin_init_v1 ( int requested_abi_version , int requested_api_version , void * /*reserved=NULL*/ ) CV_NOEXCEPT
2019-03-03 06:36:18 +08:00
{
2021-01-14 06:53:56 +08:00
if ( requested_abi_version = = WRITER_ABI_VERSION & & requested_api_version < = WRITER_API_VERSION )
return & cv : : writer_api ;
2020-12-04 11:18:16 +08:00
return NULL ;
2019-01-14 18:33:38 +08:00
}
# endif // BUILD_PLUGIN