Path: blob/master/modules/videoio/src/cap_gstreamer.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2008, 2011, Nils Hasler, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041/*!42* \file cap_gstreamer.cpp43* \author Nils Hasler <[email protected]>44* Max-Planck-Institut Informatik45* \author Dirk Van Haerenborgh <[email protected]>46*47* \brief Use GStreamer to read/write video48*/49#include "precomp.hpp"50#include <iostream>51using namespace std;52#ifndef _MSC_VER53#include <unistd.h>54#endif55#include <string.h>56#include <gst/gst.h>57#include <gst/gstbuffer.h>58#include <gst/video/video.h>59#include <gst/app/gstappsink.h>60#include <gst/app/gstappsrc.h>61#include <gst/riff/riff-media.h>62#include <gst/pbutils/missing-plugins.h>6364#define VERSION_NUM(major, minor, micro) (major * 1000000 + minor * 1000 + micro)65#define FULL_GST_VERSION VERSION_NUM(GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO)6667#if FULL_GST_VERSION >= VERSION_NUM(0,10,32)68#include <gst/pbutils/encoding-profile.h>69//#include <gst/base/gsttypefindhelper.h>70#endif717273#ifdef NDEBUG74#define CV_WARN(message)75#else76#define CV_WARN(message) fprintf(stderr, "OpenCV | GStreamer warning: %s (%s:%d)\n", message, __FILE__, __LINE__)77#endif7879#if GST_VERSION_MAJOR == 080#define COLOR_ELEM "ffmpegcolorspace"81#define COLOR_ELEM_NAME "ffmpegcsp"82#else83#define COLOR_ELEM "videoconvert"84#define COLOR_ELEM_NAME COLOR_ELEM85#endif8687#if defined(_WIN32) || defined(_WIN64)88#if defined(__MINGW32__)89inline char *realpath(const char *path, char *resolved_path)90{91return _fullpath(resolved_path,path,PATH_MAX);92}93#endif94#define snprintf _snprintf95#define vsnprintf _vsnprintf96#define strcasecmp _stricmp97#define strncasecmp _strnicmp98#include <sys/stat.h>99#endif100101void toFraction(double decimal, double &numerator, double &denominator);102void handleMessage(GstElement * pipeline);103104using namespace cv;105106static cv::Mutex gst_initializer_mutex;107108/*!109* \brief The gst_initializer class110* Initializes gstreamer once in the whole process111*/112class gst_initializer113{114public:115static void init()116{117gst_initializer_mutex.lock();118static gst_initializer init;119gst_initializer_mutex.unlock();120}121private:122gst_initializer()123{124gst_init(NULL, NULL);125guint major, minor, micro, nano;126gst_version(&major, &minor, µ, &nano);127if (GST_VERSION_MAJOR != major)128{129CV_WARN("incompatible gstreamer version");130}131// gst_debug_set_active(1);132// gst_debug_set_colored(1);133// gst_debug_set_default_threshold(GST_LEVEL_INFO);134}135};136137inline static string get_gst_propname(int propId)138{139switch (propId)140{141case CV_CAP_PROP_BRIGHTNESS: return "brightness";142case CV_CAP_PROP_CONTRAST: return "contrast";143case CV_CAP_PROP_SATURATION: return "saturation";144case CV_CAP_PROP_HUE: return "hue";145default: return string();146}147}148149inline static bool is_gst_element_exists(const std::string & name)150{151GstElementFactory * testfac = gst_element_factory_find(name.c_str());152if (!testfac)153return false;154g_object_unref(G_OBJECT(testfac));155return true;156}157158//==================================================================================================159160class GStreamerCapture CV_FINAL : public IVideoCapture161{162private:163GstElement* pipeline;164GstElement* v4l2src;165GstElement* sink;166#if GST_VERSION_MAJOR > 0167GstSample* sample;168#else169void * sample; // unused170GstBuffer* buffer;171#endif172GstCaps* caps;173gint64 duration;174gint width;175gint height;176gint channels;177double fps;178bool isPosFramesSupported;179bool isPosFramesEmulated;180gint64 emulatedFrameNumber;181bool isOutputByteBuffer;182183public:184GStreamerCapture();185~GStreamerCapture();186virtual bool grabFrame() CV_OVERRIDE;187virtual bool retrieveFrame(int /*unused*/, OutputArray dst) CV_OVERRIDE;188virtual double getProperty(int propId) const CV_OVERRIDE;189virtual bool setProperty(int propId, double value) CV_OVERRIDE;190virtual bool isOpened() const CV_OVERRIDE;191virtual int getCaptureDomain() CV_OVERRIDE { return cv::CAP_GSTREAMER; }192bool open(int id);193bool open(const String &filename_);194static void newPad(GstElement * /*elem*/, GstPad *pad, gpointer data);195196protected:197bool determineFrameDims(Size & sz);198bool isPipelinePlaying();199void startPipeline();200void stopPipeline();201void restartPipeline();202void setFilter(const char *prop, int type, int v1, int v2);203void removeFilter(const char *filter);204};205206/*!207* \brief CvCapture_GStreamer::init208* inits the class209*/210GStreamerCapture::GStreamerCapture() :211pipeline(NULL), v4l2src(NULL), sink(NULL), sample(NULL),212#if GST_VERSION_MAJOR == 0213buffer(NULL),214#endif215caps(NULL),216duration(-1), width(-1), height(-1), channels(0), fps(-1),217isPosFramesSupported(false),218isPosFramesEmulated(false),219emulatedFrameNumber(-1),220isOutputByteBuffer(false)221{222}223224/*!225* \brief CvCapture_GStreamer::close226* Closes the pipeline and destroys all instances227*/228GStreamerCapture::~GStreamerCapture()229{230if (isPipelinePlaying())231stopPipeline();232if (pipeline && GST_IS_ELEMENT(pipeline))233{234gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);235gst_object_unref(GST_OBJECT(pipeline));236}237}238239/*!240* \brief CvCapture_GStreamer::grabFrame241* \return242* Grabs a sample from the pipeline, awaiting consumation by retreiveFrame.243* The pipeline is started if it was not running yet244*/245bool GStreamerCapture::grabFrame()246{247if(!pipeline)248return false;249250// start the pipeline if it was not in playing state yet251if(!this->isPipelinePlaying())252this->startPipeline();253254// bail out if EOS255if(gst_app_sink_is_eos(GST_APP_SINK(sink)))256return false;257258#if GST_VERSION_MAJOR == 0259if(buffer)260gst_buffer_unref(buffer);261buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink));262if(!buffer)263return false;264#else265if(sample)266gst_sample_unref(sample);267sample = gst_app_sink_pull_sample(GST_APP_SINK(sink));268if(!sample)269return false;270#endif271272if (isPosFramesEmulated)273emulatedFrameNumber++;274275return true;276}277278/*!279* \brief CvCapture_GStreamer::retrieveFrame280* \return IplImage pointer. [Transfer Full]281* Retrieve the previously grabbed buffer, and wrap it in an IPLImage structure282*/283bool GStreamerCapture::retrieveFrame(int, OutputArray dst)284{285#if GST_VERSION_MAJOR == 0286if (!buffer)287return false;288#else289if(!sample)290return false;291#endif292Size sz;293if (!determineFrameDims(sz))294return false;295296// gstreamer expects us to handle the memory at this point297// so we can just wrap the raw buffer and be done with it298#if GST_VERSION_MAJOR == 0299Mat src(sz, CV_8UC1, (uchar*)GST_BUFFER_DATA(buffer));300src.copyTo(dst);301#else302GstBuffer * buf = gst_sample_get_buffer(sample);303if (!buf)304return false;305GstMapInfo info;306if (!gst_buffer_map(buf, &info, GST_MAP_READ))307{308//something weird went wrong here. abort. abort.309CV_WARN("Failed to map GStreamerbuffer to system memory");310return false;311}312313{314Mat src;315if (isOutputByteBuffer)316src = Mat(Size(info.size, 1), CV_8UC1, info.data);317else318src = Mat(sz, CV_MAKETYPE(CV_8U, channels), info.data);319CV_Assert(src.isContinuous());320src.copyTo(dst);321}322gst_buffer_unmap(buf, &info);323#endif324325return true;326}327328bool GStreamerCapture::determineFrameDims(Size &sz)329{330#if GST_VERSION_MAJOR == 0331GstCaps * frame_caps = gst_buffer_get_caps(buffer);332#else333GstCaps * frame_caps = gst_sample_get_caps(sample);334#endif335// bail out in no caps336if (!GST_CAPS_IS_SIMPLE(frame_caps))337return false;338339GstStructure* structure = gst_caps_get_structure(frame_caps, 0);340341// bail out if width or height are 0342if (!gst_structure_get_int(structure, "width", &width)343|| !gst_structure_get_int(structure, "height", &height))344return false;345346sz = Size(width, height);347348#if GST_VERSION_MAJOR > 0349const gchar* name = gst_structure_get_name(structure);350351if (!name)352return false;353354// we support 11 types of data:355// video/x-raw, format=BGR -> 8bit, 3 channels356// video/x-raw, format=GRAY8 -> 8bit, 1 channel357// video/x-raw, format=UYVY -> 8bit, 2 channel358// video/x-raw, format=YUY2 -> 8bit, 2 channel359// video/x-raw, format=YVYU -> 8bit, 2 channel360// video/x-raw, format=NV12 -> 8bit, 1 channel (height is 1.5x larger than true height)361// video/x-raw, format=NV21 -> 8bit, 1 channel (height is 1.5x larger than true height)362// video/x-raw, format=YV12 -> 8bit, 1 channel (height is 1.5x larger than true height)363// video/x-raw, format=I420 -> 8bit, 1 channel (height is 1.5x larger than true height)364// video/x-bayer -> 8bit, 1 channel365// image/jpeg -> 8bit, mjpeg: buffer_size x 1 x 1366// bayer data is never decoded, the user is responsible for that367// everything is 8 bit, so we just test the caps for bit depth368if (strcasecmp(name, "video/x-raw") == 0)369{370const gchar* format = gst_structure_get_string(structure, "format");371if (!format)372return false;373if (strcasecmp(format, "BGR") == 0)374{375channels = 3;376}377else if( (strcasecmp(format, "UYVY") == 0) || (strcasecmp(format, "YUY2") == 0) || (strcasecmp(format, "YVYU") == 0) )378{379channels = 2;380}381else if( (strcasecmp(format, "NV12") == 0) || (strcasecmp(format, "NV21") == 0) || (strcasecmp(format, "YV12") == 0) || (strcasecmp(format, "I420") == 0) )382{383channels = 1;384sz.height = sz.height * 3 / 2;385}386else if(strcasecmp(format, "GRAY8") == 0)387{388channels = 1;389}390}391else if (strcasecmp(name, "video/x-bayer") == 0)392{393channels = 1;394}395else if(strcasecmp(name, "image/jpeg") == 0)396{397// the correct size will be set once the first frame arrives398channels = 1;399isOutputByteBuffer = true;400}401#else402// we support only video/x-raw, format=BGR -> 8bit, 3 channels403channels = 3;404#endif405return true;406}407408/*!409* \brief CvCapture_GStreamer::isPipelinePlaying410* \return if the pipeline is currently playing.411*/412bool GStreamerCapture::isPipelinePlaying()413{414GstState current, pending;415GstClockTime timeout = 5*GST_SECOND;416GstStateChangeReturn ret = gst_element_get_state(pipeline, ¤t, &pending, timeout);417if (!ret)418{419CV_WARN("GStreamer: unable to query pipeline state");420return false;421}422return current == GST_STATE_PLAYING;423}424425/*!426* \brief CvCapture_GStreamer::startPipeline427* Start the pipeline by setting it to the playing state428*/429void GStreamerCapture::startPipeline()430{431//fprintf(stderr, "relinked, pausing\n");432GstStateChangeReturn status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);433if (status == GST_STATE_CHANGE_ASYNC)434{435// wait for status update436status = gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);437}438if (status == GST_STATE_CHANGE_FAILURE)439{440handleMessage(pipeline);441gst_object_unref(pipeline);442pipeline = NULL;443CV_WARN("GStreamer: unable to start pipeline");444return;445}446447if (isPosFramesEmulated)448emulatedFrameNumber = 0;449450//printf("state now playing\n");451handleMessage(pipeline);452}453454/*!455* \brief CvCapture_GStreamer::stopPipeline456* Stop the pipeline by setting it to NULL457*/458void GStreamerCapture::stopPipeline()459{460//fprintf(stderr, "restarting pipeline, going to ready\n");461if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)462{463CV_WARN("GStreamer: unable to stop pipeline");464gst_object_unref(pipeline);465pipeline = NULL;466}467}468469/*!470* \brief CvCapture_GStreamer::restartPipeline471* Restart the pipeline472*/473void GStreamerCapture::restartPipeline()474{475handleMessage(pipeline);476477this->stopPipeline();478this->startPipeline();479}480481/*!482* \brief CvCapture_GStreamer::setFilter483* \param prop the property name484* \param type glib property type485* \param v1 the value486* \param v2 second value of property type requires it, else NULL487* Filter the output formats by setting appsink caps properties488*/489void GStreamerCapture::setFilter(const char *prop, int type, int v1, int v2)490{491//printf("GStreamer: setFilter \n");492if(!caps || !( GST_IS_CAPS (caps) ))493{494if(type == G_TYPE_INT)495{496#if GST_VERSION_MAJOR == 0497caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, NULL);498#else499caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, NULL);500#endif501}502else503{504#if GST_VERSION_MAJOR == 0505caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, v2, NULL);506#else507caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, v2, NULL);508#endif509}510}511else512{513#if GST_VERSION_MAJOR > 0514if (! gst_caps_is_writable(caps))515caps = gst_caps_make_writable (caps);516#endif517if(type == G_TYPE_INT){518gst_caps_set_simple(caps, prop, type, v1, NULL);519}else{520gst_caps_set_simple(caps, prop, type, v1, v2, NULL);521}522}523524#if GST_VERSION_MAJOR > 0525caps = gst_caps_fixate(caps);526#endif527528gst_app_sink_set_caps(GST_APP_SINK(sink), caps);529//printf("filtering with %s\n", gst_caps_to_string(caps));530}531532/*!533* \brief CvCapture_GStreamer::removeFilter534* \param filter filter to remove535* remove the specified filter from the appsink template caps536*/537void GStreamerCapture::removeFilter(const char *filter)538{539if(!caps)540return;541542#if GST_VERSION_MAJOR > 0543if (! gst_caps_is_writable(caps))544caps = gst_caps_make_writable (caps);545#endif546547GstStructure *s = gst_caps_get_structure(caps, 0);548gst_structure_remove_field(s, filter);549550gst_app_sink_set_caps(GST_APP_SINK(sink), caps);551}552553/*!554* \brief CvCapture_GStreamer::newPad link dynamic padd555* \param pad556* \param data557* decodebin creates pads based on stream information, which is not known upfront558* on receiving the pad-added signal, we connect it to the colorspace conversion element559*/560void GStreamerCapture::newPad(GstElement *, GstPad *pad, gpointer data)561{562GstPad *sinkpad;563GstElement *color = (GstElement *) data;564565sinkpad = gst_element_get_static_pad (color, "sink");566if (!sinkpad){567//fprintf(stderr, "Gstreamer: no pad named sink\n");568return;569}570571gst_pad_link (pad, sinkpad);572gst_object_unref (sinkpad);573}574575bool GStreamerCapture::isOpened() const576{577return pipeline != NULL;578}579580/*!581* \brief CvCapture_GStreamer::open Open the given file with gstreamer582* \param type CvCapture type. One of CV_CAP_GSTREAMER_*583* \param filename Filename to open in case of CV_CAP_GSTREAMER_FILE584* \return boolean. Specifies if opening was successful.585*586* In case of CV_CAP_GSTREAMER_V4L(2), a pipelin is constructed as follows:587* v4l2src ! autoconvert ! appsink588*589*590* The 'filename' parameter is not limited to filesystem paths, and may be one of the following:591*592* - a normal filesystem path:593* e.g. video.avi or /path/to/video.avi or C:\\video.avi594* - an uri:595* e.g. file:///path/to/video.avi or rtsp:///path/to/stream.asf596* - a gstreamer pipeline description:597* e.g. videotestsrc ! videoconvert ! appsink598* the appsink name should be either 'appsink0' (the default) or 'opencvsink'599*600* When dealing with a file, CvCapture_GStreamer will not drop frames if the grabbing interval601* larger than the framerate period. (Unlike the uri or manual pipeline description, which assume602* a live source)603*604* The pipeline will only be started whenever the first frame is grabbed. Setting pipeline properties605* is really slow if we need to restart the pipeline over and over again.606*607* TODO: the 'type' parameter is imo unneeded. for v4l2, filename 'v4l2:///dev/video0' can be used.608* I expect this to be the same for CV_CAP_GSTREAMER_1394. Is anyone actually still using v4l (v1)?609*610*/611bool GStreamerCapture::open(int id)612{613gst_initializer::init();614615if (!is_gst_element_exists("v4l2src"))616return false;617std::ostringstream desc;618desc << "v4l2src device=/dev/video" << id619<< " ! " << COLOR_ELEM620<< " ! appsink";621return open(desc.str());622}623624bool GStreamerCapture::open(const String &filename_)625{626gst_initializer::init();627628const gchar * filename = filename_.c_str();629630bool file = false;631//bool stream = false;632bool manualpipeline = false;633char *uri = NULL;634GstElement* uridecodebin = NULL;635GstElement* color = NULL;636GstStateChangeReturn status;637638// test if we have a valid uri. If so, open it with an uridecodebin639// else, we might have a file or a manual pipeline.640// if gstreamer cannot parse the manual pipeline, we assume we were given and641// ordinary file path.642if (!gst_uri_is_valid(filename))643{644#ifdef _MSC_VER645uri = new char[2048];646DWORD pathSize = GetFullPathName(filename, 2048, uri, NULL);647struct stat buf;648if (pathSize == 0 || stat(uri, &buf) != 0)649{650delete[] uri;651uri = NULL;652}653#else654uri = realpath(filename, NULL);655#endif656//stream = false;657if(uri)658{659uri = g_filename_to_uri(uri, NULL, NULL);660if(uri)661{662file = true;663}664else665{666CV_WARN("GStreamer: Error opening file\n");667CV_WARN(filename);668CV_WARN(uri);669return false;670}671}672else673{674GError *err = NULL;675uridecodebin = gst_parse_launch(filename, &err);676if(!uridecodebin)677{678fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message);679return false;680}681//stream = true;682manualpipeline = true;683}684}685else686{687//stream = true;688uri = g_strdup(filename);689}690691bool element_from_uri = false;692if(!uridecodebin)693{694// At this writing, the v4l2 element (and maybe others too) does not support caps renegotiation.695// This means that we cannot use an uridecodebin when dealing with v4l2, since setting696// capture properties will not work.697// The solution (probably only until gstreamer 1.2) is to make an element from uri when dealing with v4l2.698gchar * protocol = gst_uri_get_protocol(uri);699if (!strcasecmp(protocol , "v4l2"))700{701#if GST_VERSION_MAJOR == 0702uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src");703#else704uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src", NULL);705#endif706element_from_uri = true;707}708else709{710uridecodebin = gst_element_factory_make("uridecodebin", NULL);711g_object_set(G_OBJECT(uridecodebin), "uri", uri, NULL);712}713g_free(protocol);714715if(!uridecodebin)716{717CV_WARN("Can not parse GStreamer URI bin");718return false;719}720}721722if (manualpipeline)723{724GstIterator *it = gst_bin_iterate_elements(GST_BIN(uridecodebin));725726GstElement *element = NULL;727gboolean done = false;728gchar* name = NULL;729#if GST_VERSION_MAJOR > 0730GValue value = G_VALUE_INIT;731#endif732733while (!done)734{735#if GST_VERSION_MAJOR > 0736switch (gst_iterator_next (it, &value))737{738case GST_ITERATOR_OK:739element = GST_ELEMENT (g_value_get_object (&value));740#else741switch (gst_iterator_next (it, (gpointer *)&element))742{743case GST_ITERATOR_OK:744#endif745name = gst_element_get_name(element);746if (name)747{748if (strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL)749{750sink = GST_ELEMENT ( gst_object_ref (element) );751}752else if (strstr(name, COLOR_ELEM_NAME) != NULL)753{754color = GST_ELEMENT ( gst_object_ref (element) );755}756else if (strstr(name, "v4l") != NULL)757{758v4l2src = GST_ELEMENT ( gst_object_ref (element) );759}760g_free(name);761762done = sink && color && v4l2src;763}764#if GST_VERSION_MAJOR > 0765g_value_unset (&value);766#endif767768break;769case GST_ITERATOR_RESYNC:770gst_iterator_resync (it);771break;772case GST_ITERATOR_ERROR:773case GST_ITERATOR_DONE:774done = TRUE;775break;776}777}778gst_iterator_free (it);779780if (!sink)781{782CV_WARN("GStreamer: cannot find appsink in manual pipeline\n");783return false;784}785786pipeline = uridecodebin;787}788else789{790pipeline = gst_pipeline_new(NULL);791// videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert)792//automatically selects the correct colorspace conversion based on caps.793color = gst_element_factory_make(COLOR_ELEM, NULL);794sink = gst_element_factory_make("appsink", NULL);795796gst_bin_add_many(GST_BIN(pipeline), uridecodebin, color, sink, NULL);797798if(element_from_uri)799{800if(!gst_element_link(uridecodebin, color))801{802CV_WARN("cannot link color -> sink");803gst_object_unref(pipeline);804pipeline = NULL;805return false;806}807}808else809{810g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color);811}812813if(!gst_element_link(color, sink))814{815CV_WARN("GStreamer: cannot link color -> sink\n");816gst_object_unref(pipeline);817pipeline = NULL;818return false;819}820}821822//TODO: is 1 single buffer really high enough?823gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1);824// gst_app_sink_set_drop (GST_APP_SINK(sink), stream);825//do not emit signals: all calls will be synchronous and blocking826gst_app_sink_set_emit_signals (GST_APP_SINK(sink), FALSE);827// gst_base_sink_set_sync(GST_BASE_SINK(sink), FALSE);828829#if GST_VERSION_MAJOR == 0830caps = gst_caps_new_simple("video/x-raw-rgb",831"bpp", G_TYPE_INT, 24,832"red_mask", G_TYPE_INT, 0x0000FF,833"green_mask", G_TYPE_INT, 0x00FF00,834"blue_mask", G_TYPE_INT, 0xFF0000,835NULL);836#else837838caps = gst_caps_from_string("video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}; image/jpeg");839840if(manualpipeline){841GstPad* sink_pad = gst_element_get_static_pad(sink, "sink");842GstCaps* peer_caps = gst_pad_peer_query_caps(sink_pad,NULL);843if (!gst_caps_can_intersect(caps, peer_caps)) {844gst_caps_unref(caps);845caps = gst_caps_from_string("video/x-raw, format=(string){UYVY,YUY2,YVYU,NV12,NV21,YV12,I420}");846}847gst_object_unref(sink_pad);848gst_caps_unref(peer_caps);849}850851#endif852gst_app_sink_set_caps(GST_APP_SINK(sink), caps);853gst_caps_unref(caps);854855{856GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-init");857858status = gst_element_set_state(GST_ELEMENT(pipeline),859file ? GST_STATE_PAUSED : GST_STATE_PLAYING);860if (status == GST_STATE_CHANGE_ASYNC)861{862// wait for status update863status = gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);864}865if (status == GST_STATE_CHANGE_FAILURE)866{867GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-error");868handleMessage(pipeline);869gst_object_unref(pipeline);870pipeline = NULL;871CV_WARN("GStreamer: unable to start pipeline\n");872return false;873}874875GstFormat format;876877format = GST_FORMAT_DEFAULT;878#if GST_VERSION_MAJOR == 0879if(!gst_element_query_duration(sink, &format, &duration))880#else881if(!gst_element_query_duration(sink, format, &duration))882#endif883{884handleMessage(pipeline);885CV_WARN("GStreamer: unable to query duration of stream");886duration = -1;887}888889handleMessage(pipeline);890891GstPad* pad = gst_element_get_static_pad(sink, "sink");892#if GST_VERSION_MAJOR == 0893GstCaps* buffer_caps = gst_pad_get_caps(pad);894#else895GstCaps* buffer_caps = gst_pad_get_current_caps(pad);896#endif897const GstStructure *structure = gst_caps_get_structure (buffer_caps, 0);898899if (!gst_structure_get_int (structure, "width", &width))900{901CV_WARN("Cannot query video width\n");902}903904if (!gst_structure_get_int (structure, "height", &height))905{906CV_WARN("Cannot query video height\n");907}908909gint num = 0, denom=1;910if(!gst_structure_get_fraction(structure, "framerate", &num, &denom))911{912CV_WARN("Cannot query video fps\n");913}914915fps = (double)num/(double)denom;916917{918GstFormat format_;919gint64 value_ = -1;920gboolean status_;921922format_ = GST_FORMAT_DEFAULT;923#if GST_VERSION_MAJOR == 0924#define FORMAT &format_925#else926#define FORMAT format_927#endif928status_ = gst_element_query_position(sink, FORMAT, &value_);929#undef FORMAT930if (!status_ || value_ != 0 || duration < 0)931{932CV_WARN(cv::format("Cannot query video position: status=%d value=%lld duration=%lld\n",933(int)status_, (long long int)value_, (long long int)duration).c_str());934isPosFramesSupported = false;935isPosFramesEmulated = true;936emulatedFrameNumber = 0;937}938else939isPosFramesSupported = true;940}941942GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");943}944945return true;946}947948/*!949* \brief CvCapture_GStreamer::getProperty retrieve the requested property from the pipeline950* \param propId requested property951* \return property value952*953* There are two ways the properties can be retrieved. For seek-based properties we can query the pipeline.954* For frame-based properties, we use the caps of the lasst receivef sample. This means that some properties955* are not available until a first frame was received956*/957double GStreamerCapture::getProperty(int propId) const958{959GstFormat format;960gint64 value;961gboolean status;962963#if GST_VERSION_MAJOR == 0964#define FORMAT &format965#else966#define FORMAT format967#endif968969if(!pipeline) {970CV_WARN("GStreamer: no pipeline");971return 0;972}973974switch(propId) {975case CV_CAP_PROP_POS_MSEC:976format = GST_FORMAT_TIME;977status = gst_element_query_position(sink, FORMAT, &value);978if(!status) {979handleMessage(pipeline);980CV_WARN("GStreamer: unable to query position of stream");981return 0;982}983return value * 1e-6; // nano seconds to milli seconds984case CV_CAP_PROP_POS_FRAMES:985if (!isPosFramesSupported)986{987if (isPosFramesEmulated)988return emulatedFrameNumber;989return 0; // TODO getProperty() "unsupported" value should be changed990}991format = GST_FORMAT_DEFAULT;992status = gst_element_query_position(sink, FORMAT, &value);993if(!status) {994handleMessage(pipeline);995CV_WARN("GStreamer: unable to query position of stream");996return 0;997}998return value;999case CV_CAP_PROP_POS_AVI_RATIO:1000format = GST_FORMAT_PERCENT;1001status = gst_element_query_position(sink, FORMAT, &value);1002if(!status) {1003handleMessage(pipeline);1004CV_WARN("GStreamer: unable to query position of stream");1005return 0;1006}1007return ((double) value) / GST_FORMAT_PERCENT_MAX;1008case CV_CAP_PROP_FRAME_WIDTH:1009return width;1010case CV_CAP_PROP_FRAME_HEIGHT:1011return height;1012case CV_CAP_PROP_FPS:1013return fps;1014case CV_CAP_PROP_FRAME_COUNT:1015return duration;1016case CV_CAP_PROP_BRIGHTNESS:1017case CV_CAP_PROP_CONTRAST:1018case CV_CAP_PROP_SATURATION:1019case CV_CAP_PROP_HUE:1020if (v4l2src)1021{1022string propName = get_gst_propname(propId);1023if (!propName.empty())1024{1025gint32 val = 0;1026g_object_get(G_OBJECT(v4l2src), propName.c_str(), &val, NULL);1027return static_cast<double>(val);1028}1029}1030break;1031case CV_CAP_GSTREAMER_QUEUE_LENGTH:1032if(!sink)1033{1034CV_WARN("there is no sink yet");1035return 0;1036}1037return gst_app_sink_get_max_buffers(GST_APP_SINK(sink));1038default:1039CV_WARN("GStreamer: unhandled property");1040break;1041}10421043#undef FORMAT10441045return 0;1046}10471048/*!1049* \brief CvCapture_GStreamer::setProperty1050* \param propId1051* \param value1052* \return success1053* Sets the desired property id with val. If the pipeline is running,1054* it is briefly stopped and started again after the property was set1055*/1056bool GStreamerCapture::setProperty(int propId, double value)1057{1058const GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);10591060if(!pipeline)1061{1062CV_WARN("no pipeline");1063return false;1064}10651066bool wasPlaying = this->isPipelinePlaying();1067if (wasPlaying)1068this->stopPipeline();10691070switch(propId)1071{1072case CV_CAP_PROP_POS_MSEC:1073if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_TIME,1074flags, (gint64) (value * GST_MSECOND))) {1075handleMessage(pipeline);1076CV_WARN("GStreamer: unable to seek");1077}1078else1079{1080if (isPosFramesEmulated)1081{1082if (value == 0)1083{1084emulatedFrameNumber = 0;1085return true;1086}1087else1088{1089isPosFramesEmulated = false; // reset frame counter emulation1090}1091}1092}1093break;1094case CV_CAP_PROP_POS_FRAMES:1095{1096if (!isPosFramesSupported)1097{1098if (isPosFramesEmulated)1099{1100if (value == 0)1101{1102restartPipeline();1103emulatedFrameNumber = 0;1104return true;1105}1106}1107return false;1108CV_WARN("unable to seek");1109}1110if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_DEFAULT,1111flags, (gint64) value)) {1112handleMessage(pipeline);1113CV_WARN("GStreamer: unable to seek");1114break;1115}1116// wait for status update1117gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);1118return true;1119}1120case CV_CAP_PROP_POS_AVI_RATIO:1121if(!gst_element_seek_simple(GST_ELEMENT(pipeline), GST_FORMAT_PERCENT,1122flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) {1123handleMessage(pipeline);1124CV_WARN("GStreamer: unable to seek");1125}1126else1127{1128if (isPosFramesEmulated)1129{1130if (value == 0)1131{1132emulatedFrameNumber = 0;1133return true;1134}1135else1136{1137isPosFramesEmulated = false; // reset frame counter emulation1138}1139}1140}1141break;1142case CV_CAP_PROP_FRAME_WIDTH:1143if(value > 0)1144setFilter("width", G_TYPE_INT, (int) value, 0);1145else1146removeFilter("width");1147break;1148case CV_CAP_PROP_FRAME_HEIGHT:1149if(value > 0)1150setFilter("height", G_TYPE_INT, (int) value, 0);1151else1152removeFilter("height");1153break;1154case CV_CAP_PROP_FPS:1155if(value > 0) {1156double num=0, denom = 1;1157toFraction(value, num, denom);1158setFilter("framerate", GST_TYPE_FRACTION, value, denom);1159} else1160removeFilter("framerate");1161break;1162case CV_CAP_PROP_BRIGHTNESS:1163case CV_CAP_PROP_CONTRAST:1164case CV_CAP_PROP_SATURATION:1165case CV_CAP_PROP_HUE:1166if (v4l2src)1167{1168string propName = get_gst_propname(propId);1169if (!propName.empty())1170{1171gint32 val = cv::saturate_cast<gint32>(value);1172g_object_set(G_OBJECT(v4l2src), propName.c_str(), &val, NULL);1173return true;1174}1175}1176return false;1177case CV_CAP_PROP_GAIN:1178case CV_CAP_PROP_CONVERT_RGB:1179break;1180case CV_CAP_GSTREAMER_QUEUE_LENGTH:1181{1182if(!sink)1183{1184CV_WARN("there is no sink yet");1185return false;1186}1187gst_app_sink_set_max_buffers(GST_APP_SINK(sink), (guint) value);1188return true;1189}1190default:1191CV_WARN("GStreamer: unhandled property");1192}11931194if (wasPlaying)1195this->startPipeline();11961197return false;1198}119912001201Ptr<IVideoCapture> cv::createGStreamerCapture(const String& filename)1202{1203Ptr<GStreamerCapture> cap = makePtr<GStreamerCapture>();1204if (cap && cap->open(filename))1205return cap;1206return Ptr<IVideoCapture>();1207}12081209Ptr<IVideoCapture> cv::createGStreamerCapture(int index)1210{1211Ptr<GStreamerCapture> cap = makePtr<GStreamerCapture>();1212if (cap && cap->open(index))1213return cap;1214return Ptr<IVideoCapture>();1215}12161217//==================================================================================================12181219/*!1220* \brief The CvVideoWriter_GStreamer class1221* Use Gstreamer to write video1222*/1223class CvVideoWriter_GStreamer : public CvVideoWriter1224{1225public:1226CvVideoWriter_GStreamer()1227: pipeline(0), source(0), encodebin(0), file(0), buffer(0), input_pix_fmt(0),1228num_frames(0), framerate(0)1229{1230}1231virtual ~CvVideoWriter_GStreamer() CV_OVERRIDE { close(); }12321233int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_GSTREAMER; }12341235virtual bool open( const char* filename, int fourcc,1236double fps, CvSize frameSize, bool isColor );1237virtual void close();1238virtual bool writeFrame( const IplImage* image ) CV_OVERRIDE;1239protected:1240const char* filenameToMimetype(const char* filename);1241GstElement* pipeline;1242GstElement* source;1243GstElement* encodebin;1244GstElement* file;12451246GstBuffer* buffer;1247int input_pix_fmt;1248int num_frames;1249double framerate;1250};12511252/*!1253* \brief CvVideoWriter_GStreamer::close1254* ends the pipeline by sending EOS and destroys the pipeline and all1255* elements afterwards1256*/1257void CvVideoWriter_GStreamer::close()1258{1259GstStateChangeReturn status;1260if (pipeline)1261{1262handleMessage(pipeline);12631264if (gst_app_src_end_of_stream(GST_APP_SRC(source)) != GST_FLOW_OK)1265{1266CV_WARN("Cannot send EOS to GStreamer pipeline\n");1267return;1268}12691270//wait for EOS to trickle down the pipeline. This will let all elements finish properly1271GstBus* bus = gst_element_get_bus(pipeline);1272GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));1273if (!msg || GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR)1274{1275CV_WARN("Error during VideoWriter finalization\n");1276if(msg != NULL)1277{1278gst_message_unref(msg);1279g_object_unref(G_OBJECT(bus));1280}1281return;1282}12831284gst_message_unref(msg);1285g_object_unref(G_OBJECT(bus));12861287status = gst_element_set_state (pipeline, GST_STATE_NULL);1288if (status == GST_STATE_CHANGE_ASYNC)1289{1290// wait for status update1291GstState st1;1292GstState st2;1293status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE);1294}1295if (status == GST_STATE_CHANGE_FAILURE)1296{1297handleMessage (pipeline);1298gst_object_unref (GST_OBJECT (pipeline));1299pipeline = NULL;1300CV_WARN("Unable to stop gstreamer pipeline\n");1301return;1302}13031304gst_object_unref (GST_OBJECT (pipeline));1305pipeline = NULL;1306}1307}130813091310/*!1311* \brief CvVideoWriter_GStreamer::filenameToMimetype1312* \param filename1313* \return mimetype1314* Resturns a container mime type for a given filename by looking at it's extension1315*/1316const char* CvVideoWriter_GStreamer::filenameToMimetype(const char *filename)1317{1318//get extension1319const char *ext = strrchr(filename, '.');1320if(!ext || ext == filename) return NULL;1321ext += 1; //exclude the dot13221323// return a container mime based on the given extension.1324// gstreamer's function returns too much possibilities, which is not useful to us13251326//return the appropriate mime1327if (strncasecmp(ext,"avi", 3) == 0)1328return (const char*)"video/x-msvideo";13291330if (strncasecmp(ext,"mkv", 3) == 0 || strncasecmp(ext,"mk3d",4) == 0 || strncasecmp(ext,"webm",4) == 0 )1331return (const char*)"video/x-matroska";13321333if (strncasecmp(ext,"wmv", 3) == 0)1334return (const char*)"video/x-ms-asf";13351336if (strncasecmp(ext,"mov", 3) == 0)1337return (const char*)"video/x-quicktime";13381339if (strncasecmp(ext,"ogg", 3) == 0 || strncasecmp(ext,"ogv", 3) == 0)1340return (const char*)"application/ogg";13411342if (strncasecmp(ext,"rm", 3) == 0)1343return (const char*)"vnd.rn-realmedia";13441345if (strncasecmp(ext,"swf", 3) == 0)1346return (const char*)"application/x-shockwave-flash";13471348if (strncasecmp(ext,"mp4", 3) == 0)1349return (const char*)"video/x-quicktime, variant=(string)iso";13501351//default to avi1352return (const char*)"video/x-msvideo";1353}13541355/*!1356* \brief CvVideoWriter_GStreamer::open1357* \param filename filename to output to1358* \param fourcc desired codec fourcc1359* \param fps desired framerate1360* \param frameSize the size of the expected frames1361* \param is_color color or grayscale1362* \return success1363*1364* We support 2 modes of operation. Either the user enters a filename and a fourcc1365* code, or enters a manual pipeline description like in CvVideoCapture_Gstreamer.1366* In the latter case, we just push frames on the appsink with appropriate caps.1367* In the former case, we try to deduce the correct container from the filename,1368* and the correct encoder from the fourcc profile.1369*1370* If the file extension did was not recognize, an avi container is used1371*1372*/1373bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc,1374double fps, CvSize frameSize, bool is_color )1375{1376// check arguments1377assert (filename);1378assert (fps > 0);1379assert (frameSize.width > 0 && frameSize.height > 0);13801381// init gstreamer1382gst_initializer::init();13831384// init vars1385bool manualpipeline = true;1386int bufsize = 0;1387GError *err = NULL;1388const char* mime = NULL;1389GstStateChangeReturn stateret;13901391GstCaps* caps = NULL;1392GstCaps* videocaps = NULL;13931394#if FULL_GST_VERSION >= VERSION_NUM(0,10,32)1395GstCaps* containercaps = NULL;1396GstEncodingContainerProfile* containerprofile = NULL;1397GstEncodingVideoProfile* videoprofile = NULL;1398#endif13991400GstIterator* it = NULL;1401gboolean done = FALSE;1402GstElement *element = NULL;1403gchar* name = NULL;14041405#if GST_VERSION_MAJOR == 01406GstElement* splitter = NULL;1407GstElement* combiner = NULL;1408#endif14091410// we first try to construct a pipeline from the given string.1411// if that fails, we assume it is an ordinary filename14121413encodebin = gst_parse_launch(filename, &err);1414manualpipeline = (encodebin != NULL);14151416if(manualpipeline)1417{1418#if GST_VERSION_MAJOR == 01419it = gst_bin_iterate_sources(GST_BIN(encodebin));1420if(gst_iterator_next(it, (gpointer *)&source) != GST_ITERATOR_OK) {1421CV_WARN("GStreamer: cannot find appsink in manual pipeline\n");1422return false;1423}1424#else1425it = gst_bin_iterate_sources (GST_BIN(encodebin));1426GValue value = G_VALUE_INIT;14271428while (!done) {1429switch (gst_iterator_next (it, &value)) {1430case GST_ITERATOR_OK:1431element = GST_ELEMENT (g_value_get_object (&value));1432name = gst_element_get_name(element);1433if (name){1434if(strstr(name, "opencvsrc") != NULL || strstr(name, "appsrc") != NULL) {1435source = GST_ELEMENT ( gst_object_ref (element) );1436done = TRUE;1437}1438g_free(name);1439}1440g_value_unset (&value);14411442break;1443case GST_ITERATOR_RESYNC:1444gst_iterator_resync (it);1445break;1446case GST_ITERATOR_ERROR:1447case GST_ITERATOR_DONE:1448done = TRUE;1449break;1450}1451}1452gst_iterator_free (it);14531454if (!source){1455CV_WARN("GStreamer: cannot find appsrc in manual pipeline\n");1456return false;1457}1458#endif1459pipeline = encodebin;1460}1461else1462{1463pipeline = gst_pipeline_new (NULL);14641465// we just got a filename and a fourcc code.1466// first, try to guess the container from the filename1467//encodebin = gst_element_factory_make("encodebin", NULL);14681469//proxy old non existing fourcc ids. These were used in previous opencv versions,1470//but do not even exist in gstreamer any more1471if (fourcc == CV_FOURCC('M','P','1','V')) fourcc = CV_FOURCC('M', 'P', 'G' ,'1');1472if (fourcc == CV_FOURCC('M','P','2','V')) fourcc = CV_FOURCC('M', 'P', 'G' ,'2');1473if (fourcc == CV_FOURCC('D','R','A','C')) fourcc = CV_FOURCC('d', 'r', 'a' ,'c');147414751476//create encoder caps from fourcc14771478videocaps = gst_riff_create_video_caps(fourcc, NULL, NULL, NULL, NULL, NULL);1479if (!videocaps){1480CV_WARN("Gstreamer Opencv backend does not support this codec.");1481return false;1482}14831484//create container caps from file extension1485mime = filenameToMimetype(filename);1486if (!mime) {1487CV_WARN("Gstreamer Opencv backend does not support this file type.");1488return false;1489}14901491#if FULL_GST_VERSION >= VERSION_NUM(0,10,32)1492containercaps = gst_caps_from_string(mime);14931494//create encodebin profile1495containerprofile = gst_encoding_container_profile_new("container", "container", containercaps, NULL);1496videoprofile = gst_encoding_video_profile_new(videocaps, NULL, NULL, 1);1497gst_encoding_container_profile_add_profile(containerprofile, (GstEncodingProfile *) videoprofile);1498#endif14991500//create pipeline elements1501encodebin = gst_element_factory_make("encodebin", NULL);15021503#if FULL_GST_VERSION >= VERSION_NUM(0,10,32)1504g_object_set(G_OBJECT(encodebin), "profile", containerprofile, NULL);1505#endif1506source = gst_element_factory_make("appsrc", NULL);1507file = gst_element_factory_make("filesink", NULL);1508g_object_set(G_OBJECT(file), "location", filename, NULL);1509}15101511if (fourcc == CV_FOURCC('M','J','P','G') && frameSize.height == 1)1512{1513#if GST_VERSION_MAJOR > 01514input_pix_fmt = GST_VIDEO_FORMAT_ENCODED;1515caps = gst_caps_new_simple("image/jpeg",1516"framerate", GST_TYPE_FRACTION, int(fps), 1,1517NULL);1518caps = gst_caps_fixate(caps);1519#else1520CV_WARN("Gstreamer 0.10 Opencv backend does not support writing encoded MJPEG data.");1521return false;1522#endif1523}1524else if(is_color)1525{1526input_pix_fmt = GST_VIDEO_FORMAT_BGR;1527bufsize = frameSize.width * frameSize.height * 3;15281529#if GST_VERSION_MAJOR == 01530caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR,1531frameSize.width,1532frameSize.height,1533int(fps), 1,15341, 1);1535#else1536caps = gst_caps_new_simple("video/x-raw",1537"format", G_TYPE_STRING, "BGR",1538"width", G_TYPE_INT, frameSize.width,1539"height", G_TYPE_INT, frameSize.height,1540"framerate", GST_TYPE_FRACTION, int(fps), 1,1541NULL);1542caps = gst_caps_fixate(caps);15431544#endif15451546}1547else1548{1549#if FULL_GST_VERSION >= VERSION_NUM(0,10,29)1550input_pix_fmt = GST_VIDEO_FORMAT_GRAY8;1551bufsize = frameSize.width * frameSize.height;15521553#if GST_VERSION_MAJOR == 01554caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_GRAY8,1555frameSize.width,1556frameSize.height,1557int(fps), 1,15581, 1);1559#else1560caps = gst_caps_new_simple("video/x-raw",1561"format", G_TYPE_STRING, "GRAY8",1562"width", G_TYPE_INT, frameSize.width,1563"height", G_TYPE_INT, frameSize.height,1564"framerate", GST_TYPE_FRACTION, int(fps), 1,1565NULL);1566caps = gst_caps_fixate(caps);1567#endif1568#else1569CV_Error(Error::StsError,1570"Gstreamer 0.10.29 or newer is required for grayscale input");1571#endif1572}15731574gst_app_src_set_caps(GST_APP_SRC(source), caps);1575gst_app_src_set_stream_type(GST_APP_SRC(source), GST_APP_STREAM_TYPE_STREAM);1576gst_app_src_set_size (GST_APP_SRC(source), -1);15771578g_object_set(G_OBJECT(source), "format", GST_FORMAT_TIME, NULL);1579g_object_set(G_OBJECT(source), "block", 1, NULL);1580g_object_set(G_OBJECT(source), "is-live", 0, NULL);158115821583if(!manualpipeline)1584{1585g_object_set(G_OBJECT(file), "buffer-size", bufsize, NULL);1586gst_bin_add_many(GST_BIN(pipeline), source, encodebin, file, NULL);1587if(!gst_element_link_many(source, encodebin, file, NULL)) {1588CV_WARN("GStreamer: cannot link elements\n");1589return false;1590}1591}15921593#if GST_VERSION_MAJOR == 01594// HACK: remove streamsplitter and streamcombiner from1595// encodebin pipeline to prevent early EOF event handling1596// We always fetch BGR or gray-scale frames, so combiner->spliter1597// endge in graph is useless.1598it = gst_bin_iterate_recurse (GST_BIN(encodebin));1599while (!done) {1600switch (gst_iterator_next (it, (void**)&element)) {1601case GST_ITERATOR_OK:1602name = gst_element_get_name(element);1603if (strstr(name, "streamsplitter"))1604splitter = element;1605else if (strstr(name, "streamcombiner"))1606combiner = element;1607break;1608case GST_ITERATOR_RESYNC:1609gst_iterator_resync (it);1610break;1611case GST_ITERATOR_ERROR:1612done = true;1613break;1614case GST_ITERATOR_DONE:1615done = true;1616break;1617}1618}16191620gst_iterator_free (it);16211622if (splitter && combiner)1623{1624gst_element_unlink(splitter, combiner);16251626GstPad* src = gst_element_get_pad(combiner, "src");1627GstPad* sink = gst_element_get_pad(combiner, "encodingsink");16281629GstPad* srcPeer = gst_pad_get_peer(src);1630GstPad* sinkPeer = gst_pad_get_peer(sink);16311632gst_pad_unlink(sinkPeer, sink);1633gst_pad_unlink(src, srcPeer);16341635gst_pad_link(sinkPeer, srcPeer);16361637src = gst_element_get_pad(splitter, "encodingsrc");1638sink = gst_element_get_pad(splitter, "sink");16391640srcPeer = gst_pad_get_peer(src);1641sinkPeer = gst_pad_get_peer(sink);16421643gst_pad_unlink(sinkPeer, sink);1644gst_pad_unlink(src, srcPeer);16451646gst_pad_link(sinkPeer, srcPeer);1647}1648#endif16491650GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "write-pipeline");16511652stateret = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);1653if(stateret == GST_STATE_CHANGE_FAILURE) {1654handleMessage(pipeline);1655CV_WARN("GStreamer: cannot put pipeline to play\n");1656return false;1657}16581659framerate = fps;1660num_frames = 0;16611662handleMessage(pipeline);16631664return true;1665}166616671668/*!1669* \brief CvVideoWriter_GStreamer::writeFrame1670* \param image1671* \return1672* Pushes the given frame on the pipeline.1673* The timestamp for the buffer is generated from the framerate set in open1674* and ensures a smooth video1675*/1676bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image )1677{1678GstClockTime duration, timestamp;1679GstFlowReturn ret;1680int size;16811682handleMessage(pipeline);16831684#if GST_VERSION_MAJOR > 01685if (input_pix_fmt == GST_VIDEO_FORMAT_ENCODED) {1686if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U || image->height != 1) {1687CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U, nChannels = 1 and height = 1.");1688return false;1689}1690}1691else1692#endif1693if(input_pix_fmt == GST_VIDEO_FORMAT_BGR) {1694if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) {1695CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3.");1696return false;1697}1698}1699#if FULL_GST_VERSION >= VERSION_NUM(0,10,29)1700else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY8) {1701if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) {1702CV_WARN("cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1.");1703return false;1704}1705}1706#endif1707else {1708CV_WARN("cvWriteFrame() needs BGR or grayscale images\n");1709return false;1710}17111712size = image->imageSize;1713duration = ((double)1/framerate) * GST_SECOND;1714timestamp = num_frames * duration;17151716//gst_app_src_push_buffer takes ownership of the buffer, so we need to supply it a copy1717#if GST_VERSION_MAJOR == 01718buffer = gst_buffer_try_new_and_alloc (size);1719if (!buffer)1720{1721CV_WARN("Cannot create GStreamer buffer");1722}17231724memcpy(GST_BUFFER_DATA (buffer), (guint8*)image->imageData, size);1725GST_BUFFER_DURATION(buffer) = duration;1726GST_BUFFER_TIMESTAMP(buffer) = timestamp;1727#else1728buffer = gst_buffer_new_allocate (NULL, size, NULL);1729GstMapInfo info;1730gst_buffer_map(buffer, &info, (GstMapFlags)GST_MAP_READ);1731memcpy(info.data, (guint8*)image->imageData, size);1732gst_buffer_unmap(buffer, &info);1733GST_BUFFER_DURATION(buffer) = duration;1734GST_BUFFER_PTS(buffer) = timestamp;1735GST_BUFFER_DTS(buffer) = timestamp;1736#endif1737//set the current number in the frame1738GST_BUFFER_OFFSET(buffer) = num_frames;17391740ret = gst_app_src_push_buffer(GST_APP_SRC(source), buffer);1741if (ret != GST_FLOW_OK) {1742CV_WARN("Error pushing buffer to GStreamer pipeline");1743return false;1744}17451746//GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");17471748++num_frames;17491750return true;1751}17521753/*!1754* \brief cvCreateVideoWriter_GStreamer1755* \param filename1756* \param fourcc1757* \param fps1758* \param frameSize1759* \param isColor1760* \return1761* Constructor1762*/1763CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, double fps,1764CvSize frameSize, int isColor )1765{1766CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer;1767if( wrt->open(filename, fourcc, fps,frameSize, isColor))1768return wrt;17691770delete wrt;1771return 0;1772}17731774// utility functions17751776/*!1777* \brief toFraction1778* \param decimal1779* \param numerator1780* \param denominator1781* Split a floating point value into numerator and denominator1782*/1783void toFraction(double decimal, double &numerator, double &denominator)1784{1785double dummy;1786double whole;1787decimal = modf (decimal, &whole);1788for (denominator = 1; denominator<=100; denominator++){1789if (modf(denominator * decimal, &dummy) < 0.001f)1790break;1791}1792numerator = denominator * decimal;1793}179417951796/*!1797* \brief handleMessage1798* Handles gstreamer bus messages. Mainly for debugging purposes and ensuring clean shutdown on error1799*/1800void handleMessage(GstElement * pipeline)1801{1802GError *err = NULL;1803gchar *debug = NULL;1804GstBus* bus = NULL;1805GstStreamStatusType tp;1806GstElement * elem = NULL;1807GstMessage* msg = NULL;18081809bus = gst_element_get_bus(pipeline);18101811while(gst_bus_have_pending(bus)) {1812msg = gst_bus_pop(bus);18131814//printf("\t\tGot %s message\n", GST_MESSAGE_TYPE_NAME(msg));18151816if(gst_is_missing_plugin_message(msg))1817{1818CV_WARN("your gstreamer installation is missing a required plugin\n");1819}1820else1821{1822switch (GST_MESSAGE_TYPE (msg)) {1823case GST_MESSAGE_STATE_CHANGED:1824GstState oldstate, newstate, pendstate;1825gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate);1826//fprintf(stderr, "\t\t%s: state changed from %s to %s (pending: %s)\n",1827// gst_element_get_name(GST_MESSAGE_SRC (msg)),1828// gst_element_state_get_name(oldstate),1829// gst_element_state_get_name(newstate), gst_element_state_get_name(pendstate));1830break;1831case GST_MESSAGE_ERROR:1832gst_message_parse_error(msg, &err, &debug);1833//fprintf(stderr, "\t\tGStreamer Plugin: Embedded video playback halted; module %s reported: %s\n",1834// gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message);18351836g_error_free(err);1837g_free(debug);18381839gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);1840break;1841case GST_MESSAGE_EOS:1842//fprintf(stderr, "\t\treached the end of the stream.");1843break;1844case GST_MESSAGE_STREAM_STATUS:1845gst_message_parse_stream_status(msg,&tp,&elem);1846//fprintf(stderr, "\t\tstream status: elem %s, %i\n", GST_ELEMENT_NAME(elem), tp);1847break;1848default:1849//fprintf(stderr, "\t\tunhandled message %s\n",GST_MESSAGE_TYPE_NAME(msg));1850break;1851}1852}1853gst_message_unref(msg);1854}18551856gst_object_unref(GST_OBJECT(bus));1857}185818591860