From 053e3d73adc3de04632b6c23b2e0e1668a05ca7e Mon Sep 17 00:00:00 2001 From: Rex Dieter Date: Fri, 25 Jul 2014 07:09:56 -0500 Subject: [PATCH] backport support for GStreamer 1 (#1123078) --- ...streamer-videocapture-and-videowrite.patch | 1844 +++++++++++++++++ 0552-eliminated-warnings.patch | 39 + 0587-Fix-build-with-gstreamer-0.10.28.patch | 100 + 0865-gstreamer-cleaning-up-resources.patch | 72 + ...rbitraty-number-of-sources-and-sinks.patch | 165 ++ opencv.spec | 28 +- 6 files changed, 2247 insertions(+), 1 deletion(-) create mode 100644 0550-bomb-commit-of-gstreamer-videocapture-and-videowrite.patch create mode 100644 0552-eliminated-warnings.patch create mode 100644 0587-Fix-build-with-gstreamer-0.10.28.patch create mode 100644 0865-gstreamer-cleaning-up-resources.patch create mode 100644 0871-allow-for-arbitraty-number-of-sources-and-sinks.patch diff --git a/0550-bomb-commit-of-gstreamer-videocapture-and-videowrite.patch b/0550-bomb-commit-of-gstreamer-videocapture-and-videowrite.patch new file mode 100644 index 0000000..cda41e4 --- /dev/null +++ b/0550-bomb-commit-of-gstreamer-videocapture-and-videowrite.patch @@ -0,0 +1,1844 @@ +diff -up opencv-2.4.9/CMakeLists.txt.10 opencv-2.4.9/CMakeLists.txt +--- opencv-2.4.9/CMakeLists.txt.10 2014-04-11 05:15:26.000000000 -0500 ++++ opencv-2.4.9/CMakeLists.txt 2014-07-24 16:34:29.528440217 -0500 +@@ -136,6 +136,7 @@ OCV_OPTION(WITH_EIGEN "Include + OCV_OPTION(WITH_VFW "Include Video for Windows support" ON IF WIN32 ) + OCV_OPTION(WITH_FFMPEG "Include FFMPEG support" ON IF (NOT ANDROID AND NOT IOS)) + OCV_OPTION(WITH_GSTREAMER "Include Gstreamer support" ON IF (UNIX AND NOT APPLE AND NOT ANDROID) ) ++OCV_OPTION(WITH_GSTREAMER_1_X "Include Gstreamer 1.x support" OFF) + OCV_OPTION(WITH_GTK "Include GTK support" ON IF (UNIX AND NOT APPLE AND NOT ANDROID) ) + OCV_OPTION(WITH_IMAGEIO "ImageIO support for OS X" OFF IF APPLE ) + OCV_OPTION(WITH_IPP "Include Intel IPP support" OFF IF (MSVC OR X86 OR X86_64) ) +@@ -861,10 +862,12 @@ endif(DEFINED WITH_FFMPEG) + if(DEFINED WITH_GSTREAMER) + status(" GStreamer:" HAVE_GSTREAMER THEN "" ELSE NO) + if(HAVE_GSTREAMER) +- status(" base:" "YES (ver ${ALIASOF_gstreamer-base-0.10_VERSION})") +- status(" app:" "YES (ver ${ALIASOF_gstreamer-app-0.10_VERSION})") +- status(" video:" "YES (ver ${ALIASOF_gstreamer-video-0.10_VERSION})") +- endif() ++ status(" base:" "YES (ver ${GSTREAMER_BASE_VERSION})") ++ status(" video:" "YES (ver ${GSTREAMER_VIDEO_VERSION})") ++ status(" app:" "YES (ver ${GSTREAMER_APP_VERSION})") ++ status(" riff:" "YES (ver ${GSTREAMER_RIFF_VERSION})") ++ status(" pbutils:" "YES (ver ${GSTREAMER_PBUTILS_VERSION})") ++ endif(HAVE_GSTREAMER) + endif(DEFINED WITH_GSTREAMER) + + if(DEFINED WITH_OPENNI) +diff -up opencv-2.4.9/cmake/OpenCVFindLibsVideo.cmake.10 opencv-2.4.9/cmake/OpenCVFindLibsVideo.cmake +--- opencv-2.4.9/cmake/OpenCVFindLibsVideo.cmake.10 2014-04-11 05:15:26.000000000 -0500 ++++ opencv-2.4.9/cmake/OpenCVFindLibsVideo.cmake 2014-07-24 16:30:53.272087384 -0500 +@@ -12,15 +12,44 @@ endif(WITH_VFW) + + # --- GStreamer --- + ocv_clear_vars(HAVE_GSTREAMER) +-if(WITH_GSTREAMER) +- CHECK_MODULE(gstreamer-base-0.10 HAVE_GSTREAMER) +- if(HAVE_GSTREAMER) +- CHECK_MODULE(gstreamer-app-0.10 HAVE_GSTREAMER) ++# try to find gstreamer 0.10 first ++if(WITH_GSTREAMER AND NOT WITH_GSTREAMER_1_X) ++ CHECK_MODULE(gstreamer-base-0.10 HAVE_GSTREAMER_BASE) ++ CHECK_MODULE(gstreamer-video-0.10 HAVE_GSTREAMER_VIDEO) ++ CHECK_MODULE(gstreamer-app-0.10 HAVE_GSTREAMER_APP) ++ CHECK_MODULE(gstreamer-riff-0.10 HAVE_GSTREAMER_RIFF) ++ CHECK_MODULE(gstreamer-pbutils-0.10 HAVE_GSTREAMER_PBUTILS) ++ ++ if(HAVE_GSTREAMER_BASE AND HAVE_GSTREAMER_VIDEO AND HAVE_GSTREAMER_APP AND HAVE_GSTREAMER_RIFF AND HAVE_GSTREAMER_PBUTILS) ++ set(HAVE_GSTREAMER TRUE) ++ set(GSTREAMER_BASE_VERSION ${ALIASOF_gstreamer-base-0.10_VERSION}) ++ set(GSTREAMER_VIDEO_VERSION ${ALIASOF_gstreamer-video-0.10_VERSION}) ++ set(GSTREAMER_APP_VERSION ${ALIASOF_gstreamer-app-0.10_VERSION}) ++ set(GSTREAMER_RIFF_VERSION ${ALIASOF_gstreamer-riff-0.10_VERSION}) ++ set(GSTREAMER_PBUTILS_VERSION ${ALIASOF_gstreamer-pbutils-0.10_VERSION}) + endif() +- if(HAVE_GSTREAMER) +- CHECK_MODULE(gstreamer-video-0.10 HAVE_GSTREAMER) ++ ++endif(WITH_GSTREAMER AND NOT WITH_GSTREAMER_1_X) ++ ++# if gstreamer 0.10 was not found, or we specified we wanted 1.x, try to find it ++if(WITH_GSTREAMER_1_X OR NOT HAVE_GSTREAMER) ++ #check for 1.x ++ CHECK_MODULE(gstreamer-base-1.0 HAVE_GSTREAMER_BASE) ++ CHECK_MODULE(gstreamer-video-1.0 HAVE_GSTREAMER_VIDEO) ++ CHECK_MODULE(gstreamer-app-1.0 HAVE_GSTREAMER_APP) ++ CHECK_MODULE(gstreamer-riff-1.0 HAVE_GSTREAMER_RIFF) ++ CHECK_MODULE(gstreamer-pbutils-1.0 HAVE_GSTREAMER_PBUTILS) ++ ++ if(HAVE_GSTREAMER_BASE AND HAVE_GSTREAMER_VIDEO AND HAVE_GSTREAMER_APP AND HAVE_GSTREAMER_RIFF AND HAVE_GSTREAMER_PBUTILS) ++ set(HAVE_GSTREAMER TRUE) ++ set(GSTREAMER_BASE_VERSION ${ALIASOF_gstreamer-base-1.0_VERSION}) ++ set(GSTREAMER_VIDEO_VERSION ${ALIASOF_gstreamer-video-1.0_VERSION}) ++ set(GSTREAMER_APP_VERSION ${ALIASOF_gstreamer-app-1.0_VERSION}) ++ set(GSTREAMER_RIFF_VERSION ${ALIASOF_gstreamer-riff-1.0_VERSION}) ++ set(GSTREAMER_PBUTILS_VERSION ${ALIASOF_gstreamer-pbutils-1.0_VERSION}) + endif() +-endif(WITH_GSTREAMER) ++ ++endif(WITH_GSTREAMER_1_X OR NOT HAVE_GSTREAMER) + + # --- unicap --- + ocv_clear_vars(HAVE_UNICAP) +diff -up opencv-2.4.9/modules/highgui/src/cap_gstreamer.cpp.10 opencv-2.4.9/modules/highgui/src/cap_gstreamer.cpp +--- opencv-2.4.9/modules/highgui/src/cap_gstreamer.cpp.10 2014-04-11 05:15:26.000000000 -0500 ++++ opencv-2.4.9/modules/highgui/src/cap_gstreamer.cpp 2014-07-24 16:30:53.273087371 -0500 +@@ -39,25 +39,27 @@ + // + //M*/ + +-// Author: Nils Hasler +-// +-// Max-Planck-Institut Informatik +-// +-// this implementation was inspired by gnash's gstreamer interface +- +-// +-// use GStreamer to read a video +-// +- ++/*! ++ * \file cap_gstreamer.cpp ++ * \author Nils Hasler ++ * Max-Planck-Institut Informatik ++ * \author Dirk Van Haerenborgh ++ * ++ * \brief Use GStreamer to read/write video ++ */ + #include "precomp.hpp" + #include + #include +-#include + #include ++#include + #include + #include + #include + #include ++#include ++#include ++//#include ++ + + #ifdef NDEBUG + #define CV_WARN(message) +@@ -65,9 +67,23 @@ + #define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) + #endif + ++#if GST_VERSION_MAJOR > 0 ++#define COLOR_ELEM "videoconvert" ++#else ++#define COLOR_ELEM "ffmpegcolorspace" ++#endif ++ ++ ++void toFraction(double decimal, double &numerator, double &denominator); ++void handleMessage(GstElement * pipeline); ++ ++ + static cv::Mutex gst_initializer_mutex; + +-class gst_initializer ++/*! ++ * \brief The gst_initializer class ++ * Initializes gstreamer once in the whole process ++ */class gst_initializer + { + public: + static void init() +@@ -80,9 +96,16 @@ private: + gst_initializer() + { + gst_init(NULL, NULL); ++// gst_debug_set_active(TRUE); ++// gst_debug_set_colored(TRUE); ++// gst_debug_set_default_threshold(GST_LEVEL_INFO); + } + }; + ++/*! ++ * \brief The CvCapture_GStreamer class ++ * Use GStreamer to capture video ++ */ + class CvCapture_GStreamer : public CvCapture + { + public: +@@ -100,259 +123,486 @@ public: + protected: + void init(); + bool reopen(); +- void handleMessage(); ++ bool isPipelinePlaying(); ++ void startPipeline(); ++ void stopPipeline(); + void restartPipeline(); +- void setFilter(const char*, int, int, int); ++ void setFilter(const char* prop, int type, int v1, int v2 = NULL); + void removeFilter(const char *filter); +- void static newPad(GstElement *myelement, +- GstPad *pad, +- gpointer data); +- GstElement *pipeline; +- GstElement *uridecodebin; +- GstElement *color; +- GstElement *sink; +- +- GstBuffer *buffer; +- GstCaps *caps; +- IplImage *frame; ++ static void newPad(GstElement *myelement, ++ GstPad *pad, ++ gpointer data); ++ GstElement* pipeline; ++ GstElement* uridecodebin; ++ GstElement* color; ++ GstElement* sink; ++#if GST_VERSION_MAJOR > 0 ++ GstSample* sample; ++ GstMapInfo* info; ++#endif ++ GstBuffer* buffer; ++ GstCaps* caps; ++ GstCaps* buffer_caps; ++ IplImage* frame; + }; + ++/*! ++ * \brief CvCapture_GStreamer::init ++ * inits the class ++ */ + void CvCapture_GStreamer::init() + { +- pipeline=0; +- frame=0; +- buffer=0; +- frame=0; +- ++ pipeline = NULL; ++ frame = NULL; ++ buffer = NULL; ++ buffer_caps = NULL; ++#if GST_VERSION_MAJOR > 0 ++ sample = NULL; ++ info = new GstMapInfo; ++#endif + } + +-void CvCapture_GStreamer::handleMessage() ++/*! ++ * \brief CvCapture_GStreamer::close ++ * Closes the pipeline and destroys all instances ++ */ ++void CvCapture_GStreamer::close() + { +- GstBus* bus = gst_element_get_bus(pipeline); +- +- while(gst_bus_have_pending(bus)) { +- GstMessage* msg = gst_bus_pop(bus); +- +-// printf("Got %s message\n", GST_MESSAGE_TYPE_NAME(msg)); +- +- switch (GST_MESSAGE_TYPE (msg)) { +- case GST_MESSAGE_STATE_CHANGED: +- GstState oldstate, newstate, pendstate; +- gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate); +-// printf("state changed from %d to %d (%d)\n", oldstate, newstate, pendstate); +- break; +- case GST_MESSAGE_ERROR: { +- GError *err; +- gchar *debug; +- gst_message_parse_error(msg, &err, &debug); +- +- fprintf(stderr, "GStreamer Plugin: Embedded video playback halted; module %s reported: %s\n", +- gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message); +- +- g_error_free(err); +- g_free(debug); +- +- gst_element_set_state(pipeline, GST_STATE_NULL); +- +- break; +- } +- case GST_MESSAGE_EOS: +-// CV_WARN("NetStream has reached the end of the stream."); +- +- break; +- default: +-// CV_WARN("unhandled message\n"); +- break; +- } ++ if (isPipelinePlaying()) ++ this->stopPipeline(); + +- gst_message_unref(msg); ++ if(pipeline) { ++ gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); ++ gst_object_unref(GST_OBJECT(pipeline)); ++ } ++ if(uridecodebin){ ++ gst_object_unref(GST_OBJECT(uridecodebin)); ++ } ++ if(color){ ++ gst_object_unref(GST_OBJECT(color)); ++ } ++ if(sink){ ++ gst_object_unref(GST_OBJECT(sink)); ++ } ++ if(buffer) ++ gst_buffer_unref(buffer); ++ if(frame) { ++ frame->imageData = 0; ++ cvReleaseImage(&frame); ++ } ++ if(caps){ ++ gst_caps_unref(caps); + } ++ if(buffer_caps){ ++ gst_caps_unref(buffer_caps); ++ } ++#if GST_VERSION_MAJOR > 0 ++ if(sample){ ++ gst_sample_unref(sample); ++ } ++#endif + +- gst_object_unref(GST_OBJECT(bus)); + } + +-// +-// start the pipeline, grab a buffer, and pause again +-// ++/*! ++ * \brief CvCapture_GStreamer::grabFrame ++ * \return ++ * Grabs a sample from the pipeline, awaiting consumation by retreiveFrame. ++ * The pipeline is started if it was not running yet ++ */ + bool CvCapture_GStreamer::grabFrame() + { +- + if(!pipeline) + return false; + ++ // start the pipeline if it was not in playing state yet ++ if(!this->isPipelinePlaying()) ++ this->startPipeline(); ++ ++ // bail out if EOS + if(gst_app_sink_is_eos(GST_APP_SINK(sink))) + return false; + ++#if GST_VERSION_MAJOR == 0 + if(buffer) + gst_buffer_unref(buffer); +- handleMessage(); + + buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink)); ++#else ++ if(sample) ++ gst_sample_unref(sample); ++ ++ sample = gst_app_sink_pull_sample(GST_APP_SINK(sink)); ++ ++ if(!sample) ++ return false; ++ ++ buffer = gst_sample_get_buffer(sample); ++#endif ++ + if(!buffer) + return false; + + return true; + } + +-// +-// decode buffer +-// ++/*! ++ * \brief CvCapture_GStreamer::retrieveFrame ++ * \return IplImage pointer. [Transfer Full] ++ * Retreive the previously grabbed buffer, and wrap it in an IPLImage structure ++ */ + IplImage * CvCapture_GStreamer::retrieveFrame(int) + { + if(!buffer) + return 0; + +- if(!frame) { ++ //construct a frame header if we did not have any yet ++ if(!frame) ++ { + gint height, width; +- GstCaps *buff_caps = gst_buffer_get_caps(buffer); +- assert(gst_caps_get_size(buff_caps) == 1); +- GstStructure* structure = gst_caps_get_structure(buff_caps, 0); + ++ //reuse the caps ptr ++ if (buffer_caps) ++ gst_caps_unref(buffer_caps); ++ ++#if GST_VERSION_MAJOR == 0 ++ buffer_caps = gst_buffer_get_caps(buffer); ++#else ++ buffer_caps = gst_sample_get_caps(sample); ++#endif ++ // bail out in no caps ++ assert(gst_caps_get_size(buffer_caps) == 1); ++ GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); ++ ++ // bail out if width or height are 0 + if(!gst_structure_get_int(structure, "width", &width) || +- !gst_structure_get_int(structure, "height", &height)) ++ !gst_structure_get_int(structure, "height", &height)) ++ { ++ return 0; ++ } ++ ++ ++ int depth = 3; ++#if GST_VERSION_MAJOR > 0 ++ depth = 0; ++ const gchar* name = gst_structure_get_name(structure); ++ const gchar* format = gst_structure_get_string(structure, "format"); ++ ++ if (!name || !format) + return 0; + +- frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 3); +- gst_caps_unref(buff_caps); ++ // we support 3 types of data: ++ // video/x-raw, format=BGR -> 8bit, 3 channels ++ // video/x-raw, format=GRAY8 -> 8bit, 1 channel ++ // video/x-bayer -> 8bit, 1 channel ++ // bayer data is never decoded, the user is responsible for that ++ // everything is 8 bit, so we just test the caps for bit depth ++ ++ if (strcasecmp(name, "video/x-raw") == 0) ++ { ++ if (strcasecmp(format, "BGR") == 0) { ++ depth = 3; ++ } ++ else if(strcasecmp(format, "GRAY8") == 0){ ++ depth = 1; ++ } ++ } ++ else if (strcasecmp(name, "video/x-bayer") == 0) ++ { ++ depth = 1; ++ } ++#endif ++ if (depth > 0) { ++ frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, depth); ++ }else{ ++ return 0; ++ } + } + +- // no need to memcpy, just use gstreamer's buffer :-) ++ // gstreamer expects us to handle the memory at this point ++ // so we can just wrap the raw buffer and be done with it ++#if GST_VERSION_MAJOR == 0 + frame->imageData = (char *)GST_BUFFER_DATA(buffer); +- //memcpy (frame->imageData, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE (buffer)); +- //gst_buffer_unref(buffer); +- //buffer = 0; ++#else ++ // the data ptr in GstMapInfo is only valid throughout the mapifo objects life. ++ // TODO: check if reusing the mapinfo object is ok. ++ ++ gboolean success = gst_buffer_map(buffer,info, (GstMapFlags)GST_MAP_READ); ++ if (!success){ ++ //something weird went wrong here. abort. abort. ++ //fprintf(stderr,"GStreamer: unable to map buffer"); ++ return 0; ++ } ++ frame->imageData = (char*)info->data; ++ gst_buffer_unmap(buffer,info); ++#endif ++ + return frame; + } + +-void CvCapture_GStreamer::restartPipeline() ++ ++/*! ++ * \brief CvCapture_GStreamer::isPipelinePlaying ++ * \return if the pipeline is currently playing. ++ */ ++bool CvCapture_GStreamer::isPipelinePlaying() ++{ ++ GstState current, pending; ++ GstClockTime timeout = 5*GST_SECOND; ++ if(!GST_IS_ELEMENT(pipeline)){ ++ return false; ++ } ++ ++ GstStateChangeReturn ret = gst_element_get_state(GST_ELEMENT(pipeline),¤t, &pending, timeout); ++ if (!ret){ ++ //fprintf(stderr, "GStreamer: unable to query pipeline state\n"); ++ return false; ++ } ++ ++ return current == GST_STATE_PLAYING; ++} ++ ++/*! ++ * \brief CvCapture_GStreamer::startPipeline ++ * Start the pipeline by setting it to the playing state ++ */ ++void CvCapture_GStreamer::startPipeline() + { +- CV_FUNCNAME("icvRestartPipeline"); ++ CV_FUNCNAME("icvStartPipeline"); + + __BEGIN__; + +- printf("restarting pipeline, going to ready\n"); +- +- if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY) == +- GST_STATE_CHANGE_FAILURE) { ++ //fprintf(stderr, "relinked, pausing\n"); ++ if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) == ++ GST_STATE_CHANGE_FAILURE) { + CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); ++ gst_object_unref(pipeline); + return; + } + +- printf("ready, relinking\n"); ++ //printf("state now playing\n"); ++ handleMessage(pipeline); ++ __END__; ++} ++ + +- gst_element_unlink(uridecodebin, color); +- printf("filtering with %s\n", gst_caps_to_string(caps)); +- gst_element_link_filtered(uridecodebin, color, caps); ++/*! ++ * \brief CvCapture_GStreamer::stopPipeline ++ * Stop the pipeline by setting it to NULL ++ */ ++void CvCapture_GStreamer::stopPipeline() ++{ ++ CV_FUNCNAME("icvStopPipeline"); + +- printf("relinked, pausing\n"); ++ __BEGIN__; + +- if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) == +- GST_STATE_CHANGE_FAILURE) { +- CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); ++ //fprintf(stderr, "restarting pipeline, going to ready\n"); ++ if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL) == ++ GST_STATE_CHANGE_FAILURE) { ++ CV_ERROR(CV_StsError, "GStreamer: unable to stop pipeline\n"); ++ gst_object_unref(pipeline); + return; + } ++ __END__; ++} + +- printf("state now paused\n"); ++/*! ++ * \brief CvCapture_GStreamer::restartPipeline ++ * Restart the pipeline ++ */ ++void CvCapture_GStreamer::restartPipeline() ++{ ++ handleMessage(pipeline); + +- __END__; ++ this->stopPipeline(); ++ this->startPipeline(); + } + +-void CvCapture_GStreamer::setFilter(const char *property, int type, int v1, int v2) +-{ + +- if(!caps) { +- if(type == G_TYPE_INT) +- caps = gst_caps_new_simple("video/x-raw-rgb", property, type, v1, NULL); +- else +- caps = gst_caps_new_simple("video/x-raw-rgb", property, type, v1, v2, NULL); +- } else { +- //printf("caps before setting %s\n", gst_caps_to_string(caps)); ++/*! ++ * \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 ++ */ ++void CvCapture_GStreamer::setFilter(const char *prop, int type, int v1, int v2) ++{ ++ //printf("GStreamer: setFilter \n"); ++ if(!caps || !( GST_IS_CAPS (caps) )) ++ { + if(type == G_TYPE_INT) +- gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, NULL); ++ { ++#if GST_VERSION_MAJOR == 0 ++ caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, NULL); ++#else ++ caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, NULL); ++#endif ++ } + else +- gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, v2, NULL); ++ { ++#if GST_VERSION_MAJOR == 0 ++ caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, v2, NULL); ++#else ++ caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, v2, NULL); ++#endif ++ } ++ } ++ else ++ { ++#if GST_VERSION_MAJOR > 0 ++ if (! gst_caps_is_writable(caps)) ++ caps = gst_caps_make_writable (caps); ++#endif ++ if(type == G_TYPE_INT){ ++ gst_caps_set_simple(caps, prop, type, v1, NULL); ++ }else{ ++ gst_caps_set_simple(caps, prop, type, v1, v2, NULL); ++ } + } + +- restartPipeline(); ++#if GST_VERSION_MAJOR > 0 ++ caps = gst_caps_fixate(caps); ++#endif ++ ++ gst_app_sink_set_caps(GST_APP_SINK(sink), caps); ++ //printf("filtering with %s\n", gst_caps_to_string(caps)); + } + ++ ++/*! ++ * \brief CvCapture_GStreamer::removeFilter ++ * \param filter filter to remove ++ * remove the specified filter from the appsink template caps ++ */ + void CvCapture_GStreamer::removeFilter(const char *filter) + { + if(!caps) + return; + ++#if GST_VERSION_MAJOR > 0 ++ if (! gst_caps_is_writable(caps)) ++ caps = gst_caps_make_writable (caps); ++#endif ++ + GstStructure *s = gst_caps_get_structure(caps, 0); + gst_structure_remove_field(s, filter); + +- restartPipeline(); ++ gst_app_sink_set_caps(GST_APP_SINK(sink), caps); + } + +- +-// +-// connect uridecodebin dynamically created source pads to colourconverter +-// +-void CvCapture_GStreamer::newPad(GstElement * /*uridecodebin*/, +- GstPad *pad, +- gpointer data) ++/*! ++ * \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 ++ */ ++void CvCapture_GStreamer::newPad(GstElement * /*elem*/, ++ GstPad *pad, ++ gpointer data) + { + GstPad *sinkpad; + GstElement *color = (GstElement *) data; + +- + sinkpad = gst_element_get_static_pad (color, "sink"); +- +-// printf("linking dynamic pad to colourconverter %p %p\n", uridecodebin, pad); ++ if (!sinkpad){ ++ //fprintf(stderr, "Gstreamer: no pad named sink\n"); ++ return; ++ } + + gst_pad_link (pad, sinkpad); +- + gst_object_unref (sinkpad); + } + ++/*! ++ * \brief CvCapture_GStreamer::open Open the given file with gstreamer ++ * \param type CvCapture type. One of CV_CAP_GSTREAMER_* ++ * \param filename Filename to open in case of CV_CAP_GSTREAMER_FILE ++ * \return boolean. Specifies if opening was succesful. ++ * ++ * In case of CV_CAP_GSTREAMER_V4L(2), a pipelin is constructed as follows: ++ * 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' ++ * ++ * When dealing with a file, CvCapture_GStreamer will not drop frames if the grabbing interval ++ * larger than the framerate period. (Unlike the uri or manual pipeline description, which assume ++ * a live source) ++ * ++ * 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. ++ * ++ * TODO: the 'type' parameter is imo unneeded. for v4l2, filename 'v4l2:///dev/video0' can be used. ++ * I expect this to be the same for CV_CAP_GSTREAMER_1394. Is anyone actually still using v4l (v1)? ++ * ++ */ + bool CvCapture_GStreamer::open( int type, const char* filename ) + { +- close(); + CV_FUNCNAME("cvCaptureFromCAM_GStreamer"); + + __BEGIN__; + + gst_initializer::init(); + +-// if(!isInited) { +-// printf("gst_init\n"); +-// gst_init (NULL, NULL); +- +-// gst_debug_set_active(TRUE); +-// gst_debug_set_colored(TRUE); +-// gst_debug_set_default_threshold(GST_LEVEL_WARNING); +- +-// isInited = true; +-// } + bool stream = false; + bool manualpipeline = false; + char *uri = NULL; + uridecodebin = NULL; +- if(type != CV_CAP_GSTREAMER_FILE) { +- close(); +- return false; ++ GstElementFactory * testfac; ++ ++ if (type == CV_CAP_GSTREAMER_V4L){ ++ testfac = gst_element_factory_find("v4lsrc"); ++ if (!testfac){ ++ return false; ++ } ++ g_object_unref(G_OBJECT(testfac)); ++ filename = "v4lsrc ! "COLOR_ELEM" ! appsink"; + } ++ if (type == CV_CAP_GSTREAMER_V4L2){ ++ testfac = gst_element_factory_find("v4l2src"); ++ if (!testfac){ ++ return false; ++ } ++ g_object_unref(G_OBJECT(testfac)); ++ filename = "v4l2src ! "COLOR_ELEM" ! appsink"; ++ } ++ + +- if(!gst_uri_is_valid(filename)) { ++ // 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. ++ if(!gst_uri_is_valid(filename)) ++ { + uri = realpath(filename, NULL); +- stream=false; +- if(uri) { ++ stream = false; ++ if(uri) ++ { + uri = g_filename_to_uri(uri, NULL, NULL); + if(!uri) { + CV_WARN("GStreamer: Error opening file\n"); + close(); + return false; + } +- } else { ++ } ++ else ++ { + GError *err = NULL; +- //uridecodebin = gst_parse_bin_from_description(filename, FALSE, &err); + uridecodebin = gst_parse_launch(filename, &err); + if(!uridecodebin) { +- CV_WARN("GStreamer: Error opening bin\n"); +- close(); ++ //fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); ++ //close(); + return false; + } + stream = true; +@@ -363,32 +613,75 @@ bool CvCapture_GStreamer::open( int type + uri = g_strdup(filename); + } + +- if(!uridecodebin) { +- uridecodebin = gst_element_factory_make ("uridecodebin", NULL); +- g_object_set(G_OBJECT(uridecodebin),"uri",uri, NULL); ++ bool element_from_uri = false; ++ if(!uridecodebin) ++ { ++ // 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. ++ gchar * protocol = gst_uri_get_protocol(uri); ++ if (!strcasecmp(protocol , "v4l2")) ++ { ++#if GST_VERSION_MAJOR == 0 ++ uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src"); ++#else ++ uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src", NULL); ++#endif ++ element_from_uri = true; ++ }else{ ++ uridecodebin = gst_element_factory_make ("uridecodebin", NULL); ++ g_object_set(G_OBJECT(uridecodebin),"uri",uri, NULL); ++ } ++ g_free(protocol); ++ + if(!uridecodebin) { +- CV_WARN("GStreamer: Failed to create uridecodebin\n"); ++ //fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); + close(); + return false; + } + } + +- if(manualpipeline) { ++ if(manualpipeline) ++ { ++#if GST_VERSION_MAJOR == 0 + GstIterator *it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); + if(gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) { +- CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); +- return false; ++ CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); ++ return false; ++ } ++#else ++ sink = gst_bin_get_by_name(GST_BIN(uridecodebin), "opencvsink"); ++ if (!sink){ ++ sink = gst_bin_get_by_name(GST_BIN(uridecodebin), "appsink0"); + } + +- pipeline = uridecodebin; +- } else { +- pipeline = gst_pipeline_new (NULL); +- +- color = gst_element_factory_make("ffmpegcolorspace", NULL); ++ if (!sink){ ++ CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); ++ return false; ++ } ++#endif ++ pipeline = uridecodebin; ++ } ++ else ++ { ++ pipeline = gst_pipeline_new (NULL); ++ // videoconvert (in 0.10: ffmpegcolorspace) automatically selects the correct colorspace ++ // conversion based on caps. ++ color = gst_element_factory_make(COLOR_ELEM, NULL); + sink = gst_element_factory_make("appsink", NULL); + + gst_bin_add_many(GST_BIN(pipeline), uridecodebin, color, sink, NULL); +- g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color); ++ ++ if(element_from_uri) { ++ if(!gst_element_link(uridecodebin, color)) { ++ CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); ++ gst_object_unref(pipeline); ++ return false; ++ } ++ }else{ ++ g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color); ++ } + + if(!gst_element_link(color, sink)) { + CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); +@@ -397,266 +690,131 @@ bool CvCapture_GStreamer::open( int type + } + } + ++ //TODO: is 1 single buffer really high enough? + gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1); + gst_app_sink_set_drop (GST_APP_SINK(sink), stream); ++ //do not emit signals: all calls will be synchronous and blocking ++ gst_app_sink_set_emit_signals (GST_APP_SINK(sink), FALSE); ++ ++#if GST_VERSION_MAJOR == 0 + caps = gst_caps_new_simple("video/x-raw-rgb", + "red_mask", G_TYPE_INT, 0x0000FF, + "green_mask", G_TYPE_INT, 0x00FF00, + "blue_mask", G_TYPE_INT, 0xFF0000, + NULL); ++#else ++ // support 1 and 3 channel 8 bit data, as well as bayer (also 1 channel, 8bit) ++ caps = gst_caps_from_string("video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}"); ++#endif + gst_app_sink_set_caps(GST_APP_SINK(sink), caps); + gst_caps_unref(caps); + +- if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY) == +- GST_STATE_CHANGE_FAILURE) { +- CV_WARN("GStreamer: unable to set pipeline to ready\n"); +- gst_object_unref(pipeline); +- return false; +- } +- +- if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) == +- GST_STATE_CHANGE_FAILURE) { +- gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); +- CV_WARN("GStreamer: unable to set pipeline to playing\n"); +- gst_object_unref(pipeline); +- return false; +- } +- +- +- +- handleMessage(); +- ++ //we do not start recording here just yet. ++ // the user probably wants to set capture properties first, so start recording whenever the first frame is requested + __END__; + + return true; + } + +-// +-// +-// gstreamer image sequence writer +-// +-// +-class CvVideoWriter_GStreamer : public CvVideoWriter ++/*! ++ * \brief CvCapture_GStreamer::getProperty retreive the requested property from the pipeline ++ * \param propId requested property ++ * \return property value ++ * ++ * There are two ways the properties can be retreived. For seek-based properties we can query the pipeline. ++ * For frame-based properties, we use the caps of the lasst receivef sample. This means that some properties ++ * are not available until a first frame was received ++ */ ++double CvCapture_GStreamer::getProperty( int propId ) + { +-public: +- CvVideoWriter_GStreamer() { init(); } +- virtual ~CvVideoWriter_GStreamer() { close(); } ++ GstFormat format; ++ gint64 value; ++ gboolean status; + +- virtual bool open( const char* filename, int fourcc, +- double fps, CvSize frameSize, bool isColor ); +- virtual void close(); +- virtual bool writeFrame( const IplImage* image ); +-protected: +- void init(); +- std::map encs; +- GstElement* source; +- GstElement* file; +- GstElement* enc; +- GstElement* mux; +- GstElement* color; +- GstBuffer* buffer; +- GstElement* pipeline; +- int input_pix_fmt; +-}; ++#if GST_VERSION_MAJOR == 0 ++#define FORMAT &format ++#else ++#define FORMAT format ++#endif + +-void CvVideoWriter_GStreamer::init() +-{ +- encs[CV_FOURCC('D','R','A','C')]=(char*)"diracenc"; +- encs[CV_FOURCC('H','F','Y','U')]=(char*)"ffenc_huffyuv"; +- encs[CV_FOURCC('J','P','E','G')]=(char*)"jpegenc"; +- encs[CV_FOURCC('M','J','P','G')]=(char*)"jpegenc"; +- encs[CV_FOURCC('M','P','1','V')]=(char*)"mpeg2enc"; +- encs[CV_FOURCC('M','P','2','V')]=(char*)"mpeg2enc"; +- encs[CV_FOURCC('T','H','E','O')]=(char*)"theoraenc"; +- encs[CV_FOURCC('V','P','8','0')]=(char*)"vp8enc"; +- encs[CV_FOURCC('H','2','6','4')]=(char*)"x264enc"; +- encs[CV_FOURCC('X','2','6','4')]=(char*)"x264enc"; +- encs[CV_FOURCC('X','V','I','D')]=(char*)"xvidenc"; +- encs[CV_FOURCC('F','F','Y','U')]=(char*)"y4menc"; +- //encs[CV_FOURCC('H','F','Y','U')]=(char*)"y4menc"; +- pipeline=0; +- buffer=0; +-} +-void CvVideoWriter_GStreamer::close() +-{ +- if (pipeline) { +- gst_app_src_end_of_stream(GST_APP_SRC(source)); +- gst_element_set_state (pipeline, GST_STATE_NULL); +- gst_object_unref (GST_OBJECT (pipeline)); ++ if(!pipeline) { ++ CV_WARN("GStreamer: no pipeline"); ++ return false; + } +-} +-bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, +- double fps, CvSize frameSize, bool is_color ) +-{ +- CV_FUNCNAME("CvVideoWriter_GStreamer::open"); +- +- __BEGIN__; +- //actually doesn't support fourcc parameter and encode an avi with jpegenc +- //we need to find a common api between backend to support fourcc for avi +- //but also to choose in a common way codec and container format (ogg,dirac,matroska) +- // check arguments + +- assert (filename); +- assert (fps > 0); +- assert (frameSize.width > 0 && frameSize.height > 0); +- std::map::iterator encit; +- encit=encs.find(fourcc); +- if (encit==encs.end()) +- CV_ERROR( CV_StsUnsupportedFormat,"Gstreamer Opencv backend doesn't support this codec acutally."); +-// if(!isInited) { +-// gst_init (NULL, NULL); +-// isInited = true; +-// } +- gst_initializer::init(); +- close(); +- source=gst_element_factory_make("appsrc",NULL); +- file=gst_element_factory_make("filesink", NULL); +- enc=gst_element_factory_make(encit->second, NULL); +- mux=gst_element_factory_make("avimux", NULL); +- color = gst_element_factory_make("ffmpegcolorspace", NULL); +- if (!enc) +- CV_ERROR( CV_StsUnsupportedFormat, "Your version of Gstreamer doesn't support this codec acutally or needed plugin missing."); +- g_object_set(G_OBJECT(file), "location", filename, NULL); +- pipeline = gst_pipeline_new (NULL); +- GstCaps* caps; +- if (is_color) { +- input_pix_fmt=1; +- caps= gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR, +- frameSize.width, +- frameSize.height, +- (int) (fps * 1000), +- 1000, +- 1, +- 1); +- } +- else { +- input_pix_fmt=0; +- caps= gst_caps_new_simple("video/x-raw-gray", +- "width", G_TYPE_INT, frameSize.width, +- "height", G_TYPE_INT, frameSize.height, +- "framerate", GST_TYPE_FRACTION, int(fps),1, +- "bpp",G_TYPE_INT,8, +- "depth",G_TYPE_INT,8, +- NULL); +- } +- gst_app_src_set_caps(GST_APP_SRC(source), caps); +- if (fourcc==CV_FOURCC_DEFAULT) { +- gst_bin_add_many(GST_BIN(pipeline), source, color,mux, file, NULL); +- if(!gst_element_link_many(source,color,enc,mux,file,NULL)) { +- CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n"); +- } +- } +- else { +- gst_bin_add_many(GST_BIN(pipeline), source, color,enc,mux, file, NULL); +- if(!gst_element_link_many(source,color,enc,mux,file,NULL)) { +- CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n"); +- } +- } +- +- +- if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING) == +- GST_STATE_CHANGE_FAILURE) { +- CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n"); +- } +- __END__; +- return true; +-} +-bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) +-{ +- +- CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame"); +- +- __BEGIN__; +- if (input_pix_fmt == 1) { +- if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) { +- CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); +- } +- } +- else if (input_pix_fmt == 0) { +- if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { +- CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); +- } +- } +- else { +- assert(false); +- } +- int size; +- size = image->imageSize; +- buffer = gst_buffer_new_and_alloc (size); +- //gst_buffer_set_data (buffer,(guint8*)image->imageData, size); +- memcpy (GST_BUFFER_DATA(buffer),image->imageData, size); +- gst_app_src_push_buffer(GST_APP_SRC(source),buffer); +- //gst_buffer_unref(buffer); +- //buffer = 0; +- __END__; +- return true; +-} +-CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, double fps, +- CvSize frameSize, int isColor ) +-{ +- CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer; +- if( wrt->open(filename, fourcc, fps,frameSize, isColor)) +- return wrt; +- +- delete wrt; +- return 0; +-} +- +-void CvCapture_GStreamer::close() +-{ +- if(pipeline) { +- gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); +- gst_object_unref(GST_OBJECT(pipeline)); +- } +- if(buffer) +- gst_buffer_unref(buffer); +- if(frame) { +- frame->imageData = 0; +- cvReleaseImage(&frame); +- } +-} +- +-double CvCapture_GStreamer::getProperty( int propId ) +-{ +- GstFormat format; +- //GstQuery q; +- gint64 value; +- +- if(!pipeline) { +- CV_WARN("GStreamer: no pipeline"); +- return false; +- } +- +- switch(propId) { +- case CV_CAP_PROP_POS_MSEC: +- format = GST_FORMAT_TIME; +- if(!gst_element_query_position(sink, &format, &value)) { +- CV_WARN("GStreamer: unable to query position of stream"); +- return false; ++ switch(propId) { ++ case CV_CAP_PROP_POS_MSEC: ++ format = GST_FORMAT_TIME; ++ status = gst_element_query_position(sink, FORMAT, &value); ++ if(!status) { ++ CV_WARN("GStreamer: unable to query position of stream"); ++ return false; + } + return value * 1e-6; // nano seconds to milli seconds + case CV_CAP_PROP_POS_FRAMES: + format = GST_FORMAT_DEFAULT; +- if(!gst_element_query_position(sink, &format, &value)) { ++ status = gst_element_query_position(sink, FORMAT, &value); ++ if(!status) { + CV_WARN("GStreamer: unable to query position of stream"); + return false; + } + return value; + case CV_CAP_PROP_POS_AVI_RATIO: + format = GST_FORMAT_PERCENT; +- if(!gst_element_query_position(pipeline, &format, &value)) { ++ status = gst_element_query_position(sink, FORMAT, &value); ++ if(!status) { + CV_WARN("GStreamer: unable to query position of stream"); + return false; + } + return ((double) value) / GST_FORMAT_PERCENT_MAX; +- case CV_CAP_PROP_FRAME_WIDTH: +- case CV_CAP_PROP_FRAME_HEIGHT: +- case CV_CAP_PROP_FPS: ++ case CV_CAP_PROP_FRAME_WIDTH: { ++ if (!buffer_caps){ ++ CV_WARN("GStreamer: unable to query width of frame; no frame grabbed yet"); ++ return 0; ++ } ++ GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); ++ gint width = 0; ++ if(!gst_structure_get_int(structure, "width", &width)){ ++ CV_WARN("GStreamer: unable to query width of frame"); ++ return 0; ++ } ++ return width; ++ break; ++ } ++ case CV_CAP_PROP_FRAME_HEIGHT: { ++ if (!buffer_caps){ ++ CV_WARN("GStreamer: unable to query height of frame; no frame grabbed yet"); ++ return 0; ++ } ++ GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); ++ gint height = 0; ++ if(!gst_structure_get_int(structure, "height", &height)){ ++ CV_WARN("GStreamer: unable to query height of frame"); ++ return 0; ++ } ++ return height; ++ break; ++ } ++ case CV_CAP_PROP_FPS: { ++ if (!buffer_caps){ ++ CV_WARN("GStreamer: unable to query framerate of stream; no frame grabbed yet"); ++ return 0; ++ } ++ GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); ++ gint num = 0, denom=1; ++ if(!gst_structure_get_fraction(structure, "framerate", &num, &denom)){ ++ CV_WARN("GStreamer: unable to query framerate of stream"); ++ return 0; ++ } ++ return (double)num/(double)denom; ++ break; ++ } + case CV_CAP_PROP_FOURCC: + break; + case CV_CAP_PROP_FRAME_COUNT: + format = GST_FORMAT_DEFAULT; +- if(!gst_element_query_duration(pipeline, &format, &value)) { ++ status = gst_element_query_position(sink, FORMAT, &value); ++ if(!status) { + CV_WARN("GStreamer: unable to query position of stream"); + return false; + } +@@ -672,20 +830,31 @@ double CvCapture_GStreamer::getProperty( + break; + case CV_CAP_GSTREAMER_QUEUE_LENGTH: + if(!sink) { +- CV_WARN("GStreamer: there is no sink yet"); +- return false; ++ CV_WARN("GStreamer: there is no sink yet"); ++ return false; + } + return gst_app_sink_get_max_buffers(GST_APP_SINK(sink)); + default: + CV_WARN("GStreamer: unhandled property"); + break; + } ++ ++#undef FORMAT ++ + return false; + } + ++/*! ++ * \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 ++ */ + bool CvCapture_GStreamer::setProperty( int propId, double value ) + { +- GstFormat format; ++ GstFormat format; + GstSeekFlags flags; + + if(!pipeline) { +@@ -693,12 +862,17 @@ bool CvCapture_GStreamer::setProperty( i + return false; + } + ++ bool wasPlaying = this->isPipelinePlaying(); ++ if (wasPlaying) ++ this->stopPipeline(); ++ ++ + switch(propId) { + case CV_CAP_PROP_POS_MSEC: + format = GST_FORMAT_TIME; + flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, +- flags, (gint64) (value * GST_MSECOND))) { ++ flags, (gint64) (value * GST_MSECOND))) { + CV_WARN("GStreamer: unable to seek"); + } + break; +@@ -706,7 +880,7 @@ bool CvCapture_GStreamer::setProperty( i + format = GST_FORMAT_DEFAULT; + flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, +- flags, (gint64) value)) { ++ flags, (gint64) value)) { + CV_WARN("GStreamer: unable to seek"); + } + break; +@@ -714,7 +888,7 @@ bool CvCapture_GStreamer::setProperty( i + format = GST_FORMAT_PERCENT; + flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); + if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, +- flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) { ++ flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) { + CV_WARN("GStreamer: unable to seek"); + } + break; +@@ -732,15 +906,9 @@ bool CvCapture_GStreamer::setProperty( i + break; + case CV_CAP_PROP_FPS: + if(value > 0) { +- int num, denom; +- num = (int) value; +- if(value != num) { // FIXME this supports only fractions x/1 and x/2 +- num = (int) (value * 2); +- denom = 2; +- } else +- denom = 1; +- +- setFilter("framerate", GST_TYPE_FRACTION, num, denom); ++ double num=0, denom = 1; ++ toFraction(value, num, denom); ++ setFilter("framerate", GST_TYPE_FRACTION, value, denom); + } else + removeFilter("framerate"); + break; +@@ -763,8 +931,19 @@ bool CvCapture_GStreamer::setProperty( i + default: + CV_WARN("GStreamer: unhandled property"); + } ++ ++ if (wasPlaying) ++ this->startPipeline(); ++ + return false; + } ++ ++/*! ++ * \brief cvCreateCapture_GStreamer ++ * \param type ++ * \param filename ++ * \return ++ */ + CvCapture* cvCreateCapture_GStreamer(int type, const char* filename ) + { + CvCapture_GStreamer* capture = new CvCapture_GStreamer; +@@ -775,3 +954,498 @@ CvCapture* cvCreateCapture_GStreamer(int + delete capture; + return 0; + } ++ ++ ++/*! ++ * \brief The CvVideoWriter_GStreamer class ++ * Use Gstreamer to write video ++ */ ++class CvVideoWriter_GStreamer : public CvVideoWriter ++{ ++public: ++ CvVideoWriter_GStreamer() { init(); } ++ virtual ~CvVideoWriter_GStreamer() { close(); } ++ ++ virtual bool open( const char* filename, int fourcc, ++ double fps, CvSize frameSize, bool isColor ); ++ virtual void close(); ++ virtual bool writeFrame( const IplImage* image ); ++protected: ++ void init(); ++ const char* filenameToMimetype(const char* filename); ++ GstElement* pipeline; ++ GstElement* source; ++ GstElement* encodebin; ++ GstElement* file; ++ ++ GstBuffer* buffer; ++ int input_pix_fmt; ++ int num_frames; ++ double framerate; ++}; ++ ++/*! ++ * \brief CvVideoWriter_GStreamer::init ++ * initialise all variables ++ */ ++void CvVideoWriter_GStreamer::init() ++{ ++ pipeline = NULL; ++ source = NULL; ++ encodebin = NULL; ++ file = NULL; ++ buffer = NULL; ++ ++ num_frames = 0; ++ framerate = 0; ++} ++ ++/*! ++ * \brief CvVideoWriter_GStreamer::close ++ * ends the pipeline by sending EOS and destroys the pipeline and all ++ * elements afterwards ++ */ ++void CvVideoWriter_GStreamer::close() ++{ ++ if (pipeline) { ++ GstFlowReturn ret; ++ ret = gst_app_src_end_of_stream(GST_APP_SRC(source)); ++ ++ //wait for EOS to trickle down the pipeline. This will let all elements finish properly ++ GstBus* bus = gst_element_get_bus(pipeline); ++ GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); ++ if(msg != NULL){ ++ gst_message_unref(msg); ++ g_object_unref(G_OBJECT(bus)); ++ } ++ ++ gst_element_set_state (pipeline, GST_STATE_NULL); ++ handleMessage(pipeline); ++ ++ gst_object_unref (GST_OBJECT (pipeline)); ++ } ++} ++ ++ ++/*! ++ * \brief CvVideoWriter_GStreamer::filenameToMimetype ++ * \param filename ++ * \return mimetype ++ * Resturns a container mime type for a given filename by looking at it's extension ++ */ ++const char* CvVideoWriter_GStreamer::filenameToMimetype(const char *filename) ++{ ++ //get extension ++ const char *ext = strrchr(filename, '.'); ++ if(!ext || ext == filename) return NULL; ++ ext += 1; //exclude the dot ++ ++ // 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 ++ if (strncasecmp(ext,"avi", 3) == 0) ++ return (const char*)"video/x-msvideo"; ++ ++ if (strncasecmp(ext,"mkv", 3) == 0 || strncasecmp(ext,"mk3d",4) == 0 || strncasecmp(ext,"webm",4) == 0 ) ++ return (const char*)"video/x-matroska"; ++ ++ if (strncasecmp(ext,"wmv", 3) == 0) ++ return (const char*)"video/x-ms-asf"; ++ ++ if (strncasecmp(ext,"mov", 3) == 0) ++ return (const char*)"video/x-quicktime"; ++ ++ if (strncasecmp(ext,"ogg", 3) == 0 || strncasecmp(ext,"ogv", 3) == 0) ++ return (const char*)"application/ogg"; ++ ++ if (strncasecmp(ext,"rm", 3) == 0) ++ return (const char*)"vnd.rn-realmedia"; ++ ++ if (strncasecmp(ext,"swf", 3) == 0) ++ return (const char*)"application/x-shockwave-flash"; ++ ++ if (strncasecmp(ext,"mp4", 3) == 0) ++ return (const char*)"video/x-quicktime, variant=(string)iso"; ++ ++ //default to avi ++ return (const char*)"video/x-msvideo"; ++} ++ ++ ++/*! ++ * \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 ++ * \param is_color color or grayscale ++ * \return success ++ * ++ * We support 2 modes of operation. Either the user enters a filename and a fourcc ++ * code, or enters a manual pipeline description like in CvVideoCapture_Gstreamer. ++ * 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 ++ * ++ */ ++bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, ++ double fps, CvSize frameSize, bool is_color ) ++{ ++ CV_FUNCNAME("CvVideoWriter_GStreamer::open"); ++ ++ // check arguments ++ assert (filename); ++ assert (fps > 0); ++ assert (frameSize.width > 0 && frameSize.height > 0); ++ ++ // init gstreamer ++ gst_initializer::init(); ++ ++ // init vars ++ bool manualpipeline = true; ++ int bufsize = 0; ++ GError *err = NULL; ++ const char* mime = NULL; ++ GstStateChangeReturn stateret; ++ ++ GstCaps* caps = NULL; ++ GstCaps* videocaps = NULL; ++ GstCaps* containercaps = NULL; ++ GstEncodingContainerProfile* containerprofile = NULL; ++ GstEncodingVideoProfile* videoprofile = NULL; ++ ++#if GST_VERSION_MAJOR == 0 ++ GstIterator *it = NULL; ++#endif ++ ++ // we first try to construct a pipeline from the given string. ++ // if that fails, we assume it is an ordinary filename ++ ++ __BEGIN__; ++ ++ encodebin = gst_parse_launch(filename, &err); ++ if(!encodebin) { ++ manualpipeline = false; ++ } ++ ++ if(manualpipeline) ++ { ++#if GST_VERSION_MAJOR == 0 ++ it = gst_bin_iterate_sources(GST_BIN(encodebin)); ++ if(gst_iterator_next(it, (gpointer *)&source) != GST_ITERATOR_OK) { ++ CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); ++ return false; ++ } ++#else ++ source = gst_bin_get_by_name(GST_BIN(encodebin), "opencvsrc"); ++ if (!source){ ++ source = gst_bin_get_by_name(GST_BIN(encodebin), "appsrc0"); ++ } ++ ++ if (!source){ ++ CV_ERROR(CV_StsError, "GStreamer: cannot find appsrc in manual pipeline\n"); ++ return false; ++ } ++#endif ++ pipeline = encodebin; ++ } ++ else ++ { ++ pipeline = gst_pipeline_new (NULL); ++ ++ // we just got a filename and a fourcc code. ++ // first, try to guess the container from the filename ++ //encodebin = gst_element_factory_make("encodebin", NULL); ++ ++ //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'); ++ ++ //create encoder caps from fourcc ++ videocaps = gst_riff_create_video_caps(fourcc, NULL, NULL, NULL, NULL, NULL); ++ if (!videocaps){ ++ CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this codec."); ++ } ++ ++ //create container caps from file extension ++ mime = filenameToMimetype(filename); ++ if (!mime) { ++ CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this file type."); ++ } ++ containercaps = gst_caps_from_string(mime); ++ ++ //create encodebin profile ++ containerprofile = gst_encoding_container_profile_new("container", "container", containercaps, NULL); ++ videoprofile = gst_encoding_video_profile_new(videocaps, NULL, NULL, 1); ++ gst_encoding_container_profile_add_profile(containerprofile, (GstEncodingProfile *) videoprofile); ++ ++ //create pipeline elements ++ encodebin = gst_element_factory_make("encodebin", NULL); ++ g_object_set(G_OBJECT(encodebin), "profile", containerprofile, NULL); ++ source = gst_element_factory_make("appsrc", NULL); ++ file = gst_element_factory_make("filesink", NULL); ++ g_object_set(G_OBJECT(file), "location", filename, NULL); ++ } ++ ++ if (is_color) ++ { ++ input_pix_fmt = GST_VIDEO_FORMAT_BGR; ++ bufsize = frameSize.width * frameSize.height * 3; ++ ++#if GST_VERSION_MAJOR == 0 ++ caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR, ++ frameSize.width, ++ frameSize.height, ++ int(fps), 1, ++ 1, 1); ++#else ++ caps = 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, int(fps), 1, ++ NULL); ++ caps = gst_caps_fixate(caps); ++ ++#endif ++ ++ } ++ else ++ { ++ input_pix_fmt = GST_VIDEO_FORMAT_GRAY8; ++ bufsize = frameSize.width * frameSize.height; ++ ++#if GST_VERSION_MAJOR == 0 ++ caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_GRAY8, ++ frameSize.width, ++ frameSize.height, ++ int(fps), 1, ++ 1, 1); ++#else ++ caps = 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, int(fps), 1, ++ NULL); ++ caps = gst_caps_fixate(caps); ++#endif ++ } ++ ++ gst_app_src_set_caps(GST_APP_SRC(source), caps); ++ gst_app_src_set_stream_type(GST_APP_SRC(source), GST_APP_STREAM_TYPE_STREAM); ++ gst_app_src_set_size (GST_APP_SRC(source), -1); ++ ++ g_object_set(G_OBJECT(source), "format", GST_FORMAT_TIME, NULL); ++ g_object_set(G_OBJECT(source), "block", TRUE, NULL); ++ g_object_set(G_OBJECT(source), "is-live", FALSE, NULL); ++ g_object_set(G_OBJECT(source), "emit-signals", TRUE, NULL); ++ ++ if(!manualpipeline) ++ { ++ g_object_set(G_OBJECT(file), "buffer-size", bufsize, NULL); ++ gst_bin_add_many(GST_BIN(pipeline), source, encodebin, file, NULL); ++ if(!gst_element_link_many(source, encodebin, file, NULL)) { ++ CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n"); ++ } ++ } ++ ++ stateret = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); ++ if(stateret == GST_STATE_CHANGE_FAILURE) { ++ CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n"); ++ } ++ handleMessage(pipeline); ++ ++ framerate = fps; ++ num_frames = 0; ++ ++ __END__; ++ ++ 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 ) ++{ ++ ++ CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame"); ++ ++ GstClockTime duration, timestamp; ++ GstFlowReturn ret; ++ int size; ++ ++ __BEGIN__; ++ handleMessage(pipeline); ++ ++ if (input_pix_fmt == GST_VIDEO_FORMAT_BGR) { ++ if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) { ++ CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); ++ } ++ } ++ else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY8) { ++ if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { ++ CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); ++ } ++ } ++ else { ++ assert(false); ++ } ++ ++ 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 ++#if GST_VERSION_MAJOR == 0 ++ buffer = gst_buffer_new_and_alloc (size); ++ memcpy(GST_BUFFER_DATA (buffer), (guint8*)image->imageData, size); ++ GST_BUFFER_DURATION(buffer) = duration; ++ GST_BUFFER_TIMESTAMP(buffer) = timestamp; ++#else ++ buffer = gst_buffer_new_allocate (NULL, size, NULL); ++ 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; ++#endif ++ //set the current number in the frame ++ GST_BUFFER_OFFSET(buffer) = num_frames; ++ ++ ret = gst_app_src_push_buffer(GST_APP_SRC(source), buffer); ++ if (ret != GST_FLOW_OK) { ++ /* something wrong, stop pushing */ ++ assert(false); ++ } ++ //gst_debug_bin_to_dot_file (GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); ++ ++num_frames; ++ ++ __END__; ++ return true; ++} ++ ++/*! ++ * \brief cvCreateVideoWriter_GStreamer ++ * \param filename ++ * \param fourcc ++ * \param fps ++ * \param frameSize ++ * \param isColor ++ * \return ++ * Constructor ++ */ ++CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, double fps, ++ CvSize frameSize, int isColor ) ++{ ++ CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer; ++ if( wrt->open(filename, fourcc, fps,frameSize, isColor)) ++ return wrt; ++ ++ delete wrt; ++ return 0; ++} ++ ++// utility functions ++ ++/*! ++ * \brief toFraction ++ * \param decimal ++ * \param numerator ++ * \param denominator ++ * Split a floating point value into numerator and denominator ++ */ ++void toFraction(double decimal, double &numerator, double &denominator) ++{ ++ double dummy; ++ double whole; ++ decimal = modf (decimal, &whole); ++ for (denominator = 1; denominator<=100; denominator++){ ++ if (modf(denominator * decimal, &dummy) < 0.001f) ++ break; ++ } ++ numerator = denominator * decimal; ++} ++ ++ ++/*! ++ * \brief handleMessage ++ * Handles gstreamer bus messages. Mainly for debugging purposes and ensuring clean shutdown on error ++ */ ++void handleMessage(GstElement * pipeline) ++{ ++ CV_FUNCNAME("handlemessage"); ++ ++ GError *err = NULL; ++ gchar *debug = NULL; ++ GstBus* bus = NULL; ++ GstStreamStatusType tp; ++ GstElement * elem = NULL; ++ GstMessage* msg = NULL; ++ ++ __BEGIN__; ++ bus = gst_element_get_bus(pipeline); ++ ++ while(gst_bus_have_pending(bus)) { ++ msg = gst_bus_pop(bus); ++ ++ //printf("Got %s message\n", GST_MESSAGE_TYPE_NAME(msg)); ++ ++ if(gst_is_missing_plugin_message(msg)) ++ { ++ CV_ERROR(CV_StsError, "GStreamer: your gstreamer installation is missing a required plugin\n"); ++ } ++ else ++ { ++ switch (GST_MESSAGE_TYPE (msg)) { ++ case GST_MESSAGE_STATE_CHANGED: ++ GstState oldstate, newstate, pendstate; ++ gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate); ++ //fprintf(stderr, "state changed from %s to %s (pending: %s)\n", gst_element_state_get_name(oldstate), ++ // gst_element_state_get_name(newstate), gst_element_state_get_name(pendstate)); ++ break; ++ case GST_MESSAGE_ERROR: ++ gst_message_parse_error(msg, &err, &debug); ++ ++ //fprintf(stderr, "GStreamer Plugin: Embedded video playback halted; module %s reported: %s\n", ++ // gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message); ++ ++ g_error_free(err); ++ g_free(debug); ++ ++ gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); ++ break; ++ case GST_MESSAGE_EOS: ++ //fprintf(stderr, "reached the end of the stream."); ++ break; ++ case GST_MESSAGE_STREAM_STATUS: ++ ++ gst_message_parse_stream_status(msg,&tp,&elem); ++ //fprintf(stderr, "stream status: elem %s, %i\n", GST_ELEMENT_NAME(elem), tp); ++ break; ++ default: ++ //fprintf(stderr, "unhandled message\n"); ++ break; ++ } ++ } ++ gst_message_unref(msg); ++ } ++ ++ gst_object_unref(GST_OBJECT(bus)); ++ ++ __END__ ++} diff --git a/0552-eliminated-warnings.patch b/0552-eliminated-warnings.patch new file mode 100644 index 0000000..21097df --- /dev/null +++ b/0552-eliminated-warnings.patch @@ -0,0 +1,39 @@ +From 921675c4e233d8f9e78786550d549e9e05ffeb2a Mon Sep 17 00:00:00 2001 +From: Dirk Van Haerenborgh +Date: Thu, 21 Mar 2013 16:11:53 +0100 +Subject: [PATCH 0552/3152] eliminated warnings + +--- + modules/highgui/src/cap_gstreamer.cpp | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp +index b1a2ca0..9ef0a3f 100644 +--- a/modules/highgui/src/cap_gstreamer.cpp ++++ b/modules/highgui/src/cap_gstreamer.cpp +@@ -83,7 +83,8 @@ static cv::Mutex gst_initializer_mutex; + /*! + * \brief The gst_initializer class + * Initializes gstreamer once in the whole process +- */class gst_initializer ++ */ ++class gst_initializer + { + public: + static void init() +@@ -1007,9 +1008,9 @@ void CvVideoWriter_GStreamer::init() + */ + void CvVideoWriter_GStreamer::close() + { +- if (pipeline) { +- GstFlowReturn ret; +- ret = gst_app_src_end_of_stream(GST_APP_SRC(source)); ++ if (pipeline) ++ { ++ gst_app_src_end_of_stream(GST_APP_SRC(source)); + + //wait for EOS to trickle down the pipeline. This will let all elements finish properly + GstBus* bus = gst_element_get_bus(pipeline); +-- +1.9.3 + diff --git a/0587-Fix-build-with-gstreamer-0.10.28.patch b/0587-Fix-build-with-gstreamer-0.10.28.patch new file mode 100644 index 0000000..eb8d627 --- /dev/null +++ b/0587-Fix-build-with-gstreamer-0.10.28.patch @@ -0,0 +1,100 @@ +From 6377922716f37b00e4f8f5eab87a8fbcb16422e5 Mon Sep 17 00:00:00 2001 +From: Andrey Kamaev +Date: Tue, 26 Mar 2013 12:27:39 +0400 +Subject: [PATCH 0587/3152] Fix build with gstreamer 0.10.28 + +--- + modules/highgui/src/cap_gstreamer.cpp | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp +index 9ef0a3f..b8c7f4f 100644 +--- a/modules/highgui/src/cap_gstreamer.cpp ++++ b/modules/highgui/src/cap_gstreamer.cpp +@@ -56,9 +56,15 @@ + #include + #include + #include +-#include + #include ++ ++#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) ++ ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,32) ++#include + //#include ++#endif + + + #ifdef NDEBUG +@@ -1114,9 +1120,12 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + + GstCaps* caps = NULL; + GstCaps* videocaps = NULL; ++ ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,32) + GstCaps* containercaps = NULL; + GstEncodingContainerProfile* containerprofile = NULL; + GstEncodingVideoProfile* videoprofile = NULL; ++#endif + + #if GST_VERSION_MAJOR == 0 + GstIterator *it = NULL; +@@ -1178,16 +1187,21 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + if (!mime) { + CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this file type."); + } ++ ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,32) + containercaps = gst_caps_from_string(mime); + + //create encodebin profile + containerprofile = gst_encoding_container_profile_new("container", "container", containercaps, NULL); + videoprofile = gst_encoding_video_profile_new(videocaps, NULL, NULL, 1); + gst_encoding_container_profile_add_profile(containerprofile, (GstEncodingProfile *) videoprofile); ++#endif + + //create pipeline elements + encodebin = gst_element_factory_make("encodebin", NULL); ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,32) + g_object_set(G_OBJECT(encodebin), "profile", containerprofile, NULL); ++#endif + source = gst_element_factory_make("appsrc", NULL); + file = gst_element_factory_make("filesink", NULL); + g_object_set(G_OBJECT(file), "location", filename, NULL); +@@ -1218,6 +1232,7 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + } + else + { ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,29) + input_pix_fmt = GST_VIDEO_FORMAT_GRAY8; + bufsize = frameSize.width * frameSize.height; + +@@ -1236,6 +1251,9 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + NULL); + caps = gst_caps_fixate(caps); + #endif ++#else ++ CV_Assert(!"Gstreamer 0.10.29 or newer is required for grayscale input"); ++#endif + } + + gst_app_src_set_caps(GST_APP_SRC(source), caps); +@@ -1296,11 +1314,13 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) + CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); + } + } ++#if FULL_GST_VERSION >= VERSION_NUM(0,10,29) + else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY8) { + if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { + CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); + } + } ++#endif + else { + assert(false); + } +-- +1.9.3 + diff --git a/0865-gstreamer-cleaning-up-resources.patch b/0865-gstreamer-cleaning-up-resources.patch new file mode 100644 index 0000000..8ef07ec --- /dev/null +++ b/0865-gstreamer-cleaning-up-resources.patch @@ -0,0 +1,72 @@ +From 6d66d11046bb526d508e9543ecc37cfee91f4435 Mon Sep 17 00:00:00 2001 +From: Dirk Van Haerenborgh +Date: Wed, 12 Jun 2013 16:58:16 +0200 +Subject: [PATCH 0865/3152] gstreamer: cleaning up resources + +--- + modules/highgui/src/cap_gstreamer.cpp | 42 +++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp +index b8f4eb8..a347a74 100644 +--- a/modules/highgui/src/cap_gstreamer.cpp ++++ b/modules/highgui/src/cap_gstreamer.cpp +@@ -1030,6 +1030,19 @@ void CvVideoWriter_GStreamer::close() + handleMessage(pipeline); + + gst_object_unref (GST_OBJECT (pipeline)); ++ ++ if (source) ++ gst_object_unref (GST_OBJECT (source)); ++ ++ if (encodebin) ++ gst_object_unref (GST_OBJECT (encodebin)); ++ ++ if (file) ++ gst_object_unref (GST_OBJECT (file)); ++ ++ if (buffer) ++ gst_object_unref (GST_OBJECT (buffer)); ++ + } + } + +@@ -1155,6 +1168,35 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + source = gst_bin_get_by_name(GST_BIN(encodebin), "appsrc0"); + } + ++// GstIterator *it = gst_bin_iterate_sources (GST_BIN(encodebin)); ++ ++ ++// gboolean done = FALSE; ++// GstElement *item = NULL; ++ ++// while (!done) { ++// switch (gst_iterator_next (it, &item)) { ++// case GST_ITERATOR_OK: ++// source = item; ++// gst_object_unref (item); ++// done = TRUE; ++// break; ++// case GST_ITERATOR_RESYNC: ++// gst_iterator_resync (it); ++// break; ++// case GST_ITERATOR_ERROR: ++// done = TRUE; ++// break; ++// case GST_ITERATOR_DONE: ++// done = TRUE; ++// break; ++// } ++// } ++// gst_iterator_free (it); ++ ++ ++ ++ + if (!source){ + CV_ERROR(CV_StsError, "GStreamer: cannot find appsrc in manual pipeline\n"); + return false; +-- +1.9.3 + diff --git a/0871-allow-for-arbitraty-number-of-sources-and-sinks.patch b/0871-allow-for-arbitraty-number-of-sources-and-sinks.patch new file mode 100644 index 0000000..a264a11 --- /dev/null +++ b/0871-allow-for-arbitraty-number-of-sources-and-sinks.patch @@ -0,0 +1,165 @@ +From 30f7f9717f1f0a8c11ba88d4f04b0c7cf26bba70 Mon Sep 17 00:00:00 2001 +From: Dirk Van Haerenborgh +Date: Thu, 13 Jun 2013 11:16:33 +0200 +Subject: [PATCH 0871/3152] allow for arbitraty number of sources and sinks + +--- + modules/highgui/src/cap_gstreamer.cpp | 110 ++++++++++++++++++++-------------- + 1 file changed, 65 insertions(+), 45 deletions(-) + +diff --git a/modules/highgui/src/cap_gstreamer.cpp b/modules/highgui/src/cap_gstreamer.cpp +index a347a74..4d4dc71 100644 +--- a/modules/highgui/src/cap_gstreamer.cpp ++++ b/modules/highgui/src/cap_gstreamer.cpp +@@ -651,17 +651,47 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) + + if(manualpipeline) + { ++ GstIterator *it = NULL; + #if GST_VERSION_MAJOR == 0 +- GstIterator *it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); ++ it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); + if(gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) { + CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); + return false; + } + #else +- sink = gst_bin_get_by_name(GST_BIN(uridecodebin), "opencvsink"); +- if (!sink){ +- sink = gst_bin_get_by_name(GST_BIN(uridecodebin), "appsink0"); ++ it = gst_bin_iterate_sinks (GST_BIN(uridecodebin)); ++ ++ gboolean done = FALSE; ++ GstElement *element = NULL; ++ gchar* name = NULL; ++ GValue value = G_VALUE_INIT; ++ ++ while (!done) { ++ switch (gst_iterator_next (it, &value)) { ++ case GST_ITERATOR_OK: ++ element = GST_ELEMENT (g_value_get_object (&value)); ++ name = gst_element_get_name(element); ++ if (name){ ++ if(strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) { ++ sink = GST_ELEMENT ( gst_object_ref (element) ); ++ done = TRUE; ++ } ++ g_free(name); ++ } ++ g_value_unset (&value); ++ ++ break; ++ case GST_ITERATOR_RESYNC: ++ gst_iterator_resync (it); ++ break; ++ case GST_ITERATOR_ERROR: ++ case GST_ITERATOR_DONE: ++ done = TRUE; ++ break; ++ } + } ++ gst_iterator_free (it); ++ + + if (!sink){ + CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); +@@ -1034,15 +1064,8 @@ void CvVideoWriter_GStreamer::close() + if (source) + gst_object_unref (GST_OBJECT (source)); + +- if (encodebin) +- gst_object_unref (GST_OBJECT (encodebin)); +- + if (file) + gst_object_unref (GST_OBJECT (file)); +- +- if (buffer) +- gst_object_unref (GST_OBJECT (buffer)); +- + } + } + +@@ -1140,9 +1163,7 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + GstEncodingVideoProfile* videoprofile = NULL; + #endif + +-#if GST_VERSION_MAJOR == 0 + GstIterator *it = NULL; +-#endif + + // we first try to construct a pipeline from the given string. + // if that fails, we assume it is an ordinary filename +@@ -1163,39 +1184,38 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, + return false; + } + #else +- source = gst_bin_get_by_name(GST_BIN(encodebin), "opencvsrc"); +- if (!source){ +- source = gst_bin_get_by_name(GST_BIN(encodebin), "appsrc0"); ++ it = gst_bin_iterate_sources (GST_BIN(encodebin)); ++ ++ gboolean done = FALSE; ++ GstElement *element = NULL; ++ gchar* name = NULL; ++ GValue value = G_VALUE_INIT; ++ ++ while (!done) { ++ switch (gst_iterator_next (it, &value)) { ++ case GST_ITERATOR_OK: ++ element = GST_ELEMENT (g_value_get_object (&value)); ++ name = gst_element_get_name(element); ++ if (name){ ++ if(strstr(name, "opencvsrc") != NULL || strstr(name, "appsrc") != NULL) { ++ source = GST_ELEMENT ( gst_object_ref (element) ); ++ done = TRUE; ++ } ++ g_free(name); ++ } ++ g_value_unset (&value); ++ ++ break; ++ case GST_ITERATOR_RESYNC: ++ gst_iterator_resync (it); ++ break; ++ case GST_ITERATOR_ERROR: ++ case GST_ITERATOR_DONE: ++ done = TRUE; ++ break; ++ } + } +- +-// GstIterator *it = gst_bin_iterate_sources (GST_BIN(encodebin)); +- +- +-// gboolean done = FALSE; +-// GstElement *item = NULL; +- +-// while (!done) { +-// switch (gst_iterator_next (it, &item)) { +-// case GST_ITERATOR_OK: +-// source = item; +-// gst_object_unref (item); +-// done = TRUE; +-// break; +-// case GST_ITERATOR_RESYNC: +-// gst_iterator_resync (it); +-// break; +-// case GST_ITERATOR_ERROR: +-// done = TRUE; +-// break; +-// case GST_ITERATOR_DONE: +-// done = TRUE; +-// break; +-// } +-// } +-// gst_iterator_free (it); +- +- +- ++ gst_iterator_free (it); + + if (!source){ + CV_ERROR(CV_StsError, "GStreamer: cannot find appsrc in manual pipeline\n"); +-- +1.9.3 + diff --git a/opencv.spec b/opencv.spec index 3e8a1c3..0bec970 100644 --- a/opencv.spec +++ b/opencv.spec @@ -2,7 +2,7 @@ Name: opencv Version: 2.4.9 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Collection of algorithms for computer vision Group: Development/Libraries # This is normal three clause BSD. @@ -24,6 +24,17 @@ Patch3: opencv-2.4.9-ts_static.patch # https://bugzilla.redhat.com/1031312 Patch4: opencv-2.4.7-cmake_paths.patch +# relevant gst1-related patches from upstream master branch +%if 0%{?fedora} > 20 +%global gst1 1 +%endif +# 0550 needed slight rebasing -- rex +Patch10: 0550-bomb-commit-of-gstreamer-videocapture-and-videowrite.patch +Patch11: 0552-eliminated-warnings.patch +Patch12: 0587-Fix-build-with-gstreamer-0.10.28.patch +Patch13: 0865-gstreamer-cleaning-up-resources.patch +Patch14: 0871-allow-for-arbitraty-number-of-sources-and-sinks.patch + BuildRequires: libtool BuildRequires: cmake >= 2.6.3 BuildRequires: chrpath @@ -63,7 +74,11 @@ BuildRequires: python2-devel BuildRequires: numpy, swig >= 1.3.24 BuildRequires: python-sphinx %{?_with_ffmpeg:BuildRequires: ffmpeg-devel >= 0.4.9} +%if 0%{?gst1} +%{!?_without_gstreamer:BuildRequires: gstreamer1-devel gstreamer1-plugins-base-devel} +%else %{!?_without_gstreamer:BuildRequires: gstreamer-devel gstreamer-plugins-base-devel} +%endif %{?_with_xine:BuildRequires: xine-lib-devel} BuildRequires: opencl-headers @@ -120,6 +135,14 @@ This package contains Python bindings for the OpenCV library. %patch3 -p1 -b .ts_static %patch4 -p1 -b .cmake_paths +%if 0%{?gst1} +%patch10 -p1 -b .10 +%patch11 -p1 -b .11 +%patch12 -p1 -b .12 +%patch13 -p1 -b .13 +%patch14 -p1 -b .14 +%endif + # fix dos end of lines sed -i 's|\r||g' samples/c/adaptiveskindetector.cpp @@ -250,6 +273,9 @@ popd %{python2_sitearch}/cv2.so %changelog +* Fri Jul 25 2014 Rex Dieter 2.4.9-2 +- backport support for GStreamer 1 (#1123078) + * Thu Jul 03 2014 Nicolas Chauvet - 2.4.9-1 - Update to 2.4.9