755 lines
23 KiB
C++
755 lines
23 KiB
C++
/*M///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
//
|
|
// By downloading, copying, installing or using the software you agree to this license.
|
|
// If you do not agree to this license, do not download, install,
|
|
// copy or use the software.
|
|
//
|
|
//
|
|
// Intel License Agreement
|
|
// For Open Source Computer Vision Library
|
|
//
|
|
// Copyright (C) 2008, 2011, Nils Hasler, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
// are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistribution's of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// * The name of Intel Corporation may not be used to endorse or promote products
|
|
// derived from this software without specific prior written permission.
|
|
//
|
|
// This software is provided by the copyright holders and contributors "as is" and
|
|
// any express or implied warranties, including, but not limited to, the implied
|
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
|
// indirect, incidental, special, exemplary, or consequential damages
|
|
// (including, but not limited to, procurement of substitute goods or services;
|
|
// loss of use, data, or profits; or business interruption) however caused
|
|
// and on any theory of liability, whether in contract, strict liability,
|
|
// or tort (including negligence or otherwise) arising in any way out of
|
|
// the use of this software, even if advised of the possibility of such damage.
|
|
//
|
|
//M*/
|
|
|
|
// Author: Nils Hasler <hasler@mpi-inf.mpg.de>
|
|
//
|
|
// Max-Planck-Institut Informatik
|
|
//
|
|
// this implementation was inspired by gnash's gstreamer interface
|
|
|
|
//
|
|
// use GStreamer to read a video
|
|
//
|
|
|
|
#include "precomp.hpp"
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <map>
|
|
#include <gst/gst.h>
|
|
#include <gst/video/video.h>
|
|
#include <gst/app/gstappsink.h>
|
|
#include <gst/app/gstappsrc.h>
|
|
#include <gst/riff/riff-media.h>
|
|
|
|
#ifdef NDEBUG
|
|
#define CV_WARN(message)
|
|
#else
|
|
#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
|
|
#endif
|
|
|
|
static bool isInited = false;
|
|
class CvCapture_GStreamer : public CvCapture
|
|
{
|
|
public:
|
|
CvCapture_GStreamer() { init(); }
|
|
virtual ~CvCapture_GStreamer() { close(); }
|
|
|
|
virtual bool open( int type, const char* filename );
|
|
virtual void close();
|
|
|
|
virtual double getProperty(int);
|
|
virtual bool setProperty(int, double);
|
|
virtual bool grabFrame();
|
|
virtual IplImage* retrieveFrame(int);
|
|
|
|
protected:
|
|
void init();
|
|
bool reopen();
|
|
void handleMessage();
|
|
void restartPipeline();
|
|
void setFilter(const char*, int, int, int);
|
|
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;
|
|
};
|
|
|
|
void CvCapture_GStreamer::init()
|
|
{
|
|
pipeline=0;
|
|
frame=0;
|
|
buffer=0;
|
|
frame=0;
|
|
|
|
}
|
|
|
|
void CvCapture_GStreamer::handleMessage()
|
|
{
|
|
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;
|
|
}
|
|
|
|
gst_message_unref(msg);
|
|
}
|
|
|
|
gst_object_unref(GST_OBJECT(bus));
|
|
}
|
|
|
|
//
|
|
// start the pipeline, grab a buffer, and pause again
|
|
//
|
|
bool CvCapture_GStreamer::grabFrame()
|
|
{
|
|
|
|
if(!pipeline)
|
|
return false;
|
|
|
|
if(gst_app_sink_is_eos(GST_APP_SINK(sink)))
|
|
return false;
|
|
|
|
if(buffer)
|
|
gst_buffer_unref(buffer);
|
|
handleMessage();
|
|
|
|
buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink));
|
|
if(!buffer)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// decode buffer
|
|
//
|
|
IplImage * CvCapture_GStreamer::retrieveFrame(int)
|
|
{
|
|
if(!buffer)
|
|
return false;
|
|
|
|
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);
|
|
|
|
if(!gst_structure_get_int(structure, "width", &width) ||
|
|
!gst_structure_get_int(structure, "height", &height))
|
|
return false;
|
|
|
|
frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 3);
|
|
gst_caps_unref(buff_caps);
|
|
}
|
|
|
|
// no need to memcpy, just use gstreamer's buffer :-)
|
|
frame->imageData = (char *)GST_BUFFER_DATA(buffer);
|
|
//memcpy (frame->imageData, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE (buffer));
|
|
//gst_buffer_unref(buffer);
|
|
//buffer = 0;
|
|
return frame;
|
|
}
|
|
|
|
void CvCapture_GStreamer::restartPipeline()
|
|
{
|
|
CV_FUNCNAME("icvRestartPipeline");
|
|
|
|
__BEGIN__;
|
|
|
|
printf("restarting pipeline, going to ready\n");
|
|
|
|
if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_READY) ==
|
|
GST_STATE_CHANGE_FAILURE) {
|
|
CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n");
|
|
return;
|
|
}
|
|
|
|
printf("ready, relinking\n");
|
|
|
|
gst_element_unlink(uridecodebin, color);
|
|
printf("filtering with %s\n", gst_caps_to_string(caps));
|
|
gst_element_link_filtered(uridecodebin, color, caps);
|
|
|
|
printf("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");
|
|
return;
|
|
}
|
|
|
|
printf("state now paused\n");
|
|
|
|
__END__;
|
|
}
|
|
|
|
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));
|
|
if(type == G_TYPE_INT)
|
|
gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, NULL);
|
|
else
|
|
gst_caps_set_simple(caps, "video/x-raw-rgb", property, type, v1, v2, NULL);
|
|
}
|
|
|
|
restartPipeline();
|
|
}
|
|
|
|
void CvCapture_GStreamer::removeFilter(const char *filter)
|
|
{
|
|
if(!caps)
|
|
return;
|
|
|
|
GstStructure *s = gst_caps_get_structure(caps, 0);
|
|
gst_structure_remove_field(s, filter);
|
|
|
|
restartPipeline();
|
|
}
|
|
|
|
|
|
//
|
|
// connect uridecodebin dynamically created source pads to colourconverter
|
|
//
|
|
void CvCapture_GStreamer::newPad(GstElement *uridecodebin,
|
|
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);
|
|
|
|
gst_pad_link (pad, sinkpad);
|
|
|
|
gst_object_unref (sinkpad);
|
|
}
|
|
|
|
bool CvCapture_GStreamer::open( int type, const char* filename )
|
|
{
|
|
close();
|
|
CV_FUNCNAME("cvCaptureFromCAM_GStreamer");
|
|
|
|
__BEGIN__;
|
|
|
|
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;
|
|
}
|
|
|
|
if(!gst_uri_is_valid(filename)) {
|
|
uri = realpath(filename, NULL);
|
|
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 {
|
|
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();
|
|
return false;
|
|
}
|
|
stream = true;
|
|
manualpipeline = true;
|
|
}
|
|
} else {
|
|
stream = true;
|
|
uri = g_strdup(filename);
|
|
}
|
|
|
|
if(!uridecodebin) {
|
|
uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
|
|
g_object_set(G_OBJECT(uridecodebin),"uri",uri, NULL);
|
|
if(!uridecodebin) {
|
|
CV_WARN("GStreamer: Failed to create uridecodebin\n");
|
|
close();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(manualpipeline) {
|
|
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;
|
|
}
|
|
|
|
pipeline = uridecodebin;
|
|
} else {
|
|
pipeline = gst_pipeline_new (NULL);
|
|
|
|
color = gst_element_factory_make("ffmpegcolorspace", 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(!gst_element_link(color, sink)) {
|
|
CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n");
|
|
gst_object_unref(pipeline);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1);
|
|
gst_app_sink_set_drop (GST_APP_SINK(sink), stream);
|
|
|
|
{
|
|
GstCaps* caps;
|
|
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);
|
|
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();
|
|
|
|
__END__;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//
|
|
// gstreamer image sequence writer
|
|
//
|
|
//
|
|
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();
|
|
std::map<int, char*> encs;
|
|
GstElement* source;
|
|
GstElement* file;
|
|
GstElement* enc;
|
|
GstElement* mux;
|
|
GstElement* color;
|
|
GstBuffer* buffer;
|
|
GstElement* pipeline;
|
|
int input_pix_fmt;
|
|
};
|
|
|
|
void CvVideoWriter_GStreamer::init()
|
|
{
|
|
encs[CV_FOURCC('H','F','Y','U')]=(char*)"ffenc_huffyuv";
|
|
encs[CV_FOURCC('D','R','A','C')]=(char*)"diracenc";
|
|
encs[CV_FOURCC('X','V','I','D')]=(char*)"xvidenc";
|
|
encs[CV_FOURCC('X','2','6','4')]=(char*)"x264enc";
|
|
encs[CV_FOURCC('M','P','1','V')]=(char*)"mpeg2enc";
|
|
//encs[CV_FOURCC('M','P','2','V')]=(char*)"mpeg2enc";
|
|
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));
|
|
}
|
|
}
|
|
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<int,char*>::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;
|
|
}
|
|
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 false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
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)) {
|
|
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)) {
|
|
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_FOURCC:
|
|
break;
|
|
case CV_CAP_PROP_FRAME_COUNT:
|
|
format = GST_FORMAT_DEFAULT;
|
|
if(!gst_element_query_duration(pipeline, &format, &value)) {
|
|
CV_WARN("GStreamer: unable to query position of stream");
|
|
return false;
|
|
}
|
|
return value;
|
|
case CV_CAP_PROP_FORMAT:
|
|
case CV_CAP_PROP_MODE:
|
|
case CV_CAP_PROP_BRIGHTNESS:
|
|
case CV_CAP_PROP_CONTRAST:
|
|
case CV_CAP_PROP_SATURATION:
|
|
case CV_CAP_PROP_HUE:
|
|
case CV_CAP_PROP_GAIN:
|
|
case CV_CAP_PROP_CONVERT_RGB:
|
|
break;
|
|
case CV_CAP_GSTREAMER_QUEUE_LENGTH:
|
|
if(!sink) {
|
|
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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CvCapture_GStreamer::setProperty( int propId, double value )
|
|
{
|
|
GstFormat format;
|
|
GstSeekFlags flags;
|
|
|
|
if(!pipeline) {
|
|
CV_WARN("GStreamer: no pipeline");
|
|
return false;
|
|
}
|
|
|
|
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))) {
|
|
CV_WARN("GStreamer: unable to seek");
|
|
}
|
|
break;
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
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)) {
|
|
CV_WARN("GStreamer: unable to seek");
|
|
}
|
|
break;
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
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))) {
|
|
CV_WARN("GStreamer: unable to seek");
|
|
}
|
|
break;
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
if(value > 0)
|
|
setFilter("width", G_TYPE_INT, (int) value, 0);
|
|
else
|
|
removeFilter("width");
|
|
break;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
if(value > 0)
|
|
setFilter("height", G_TYPE_INT, (int) value, 0);
|
|
else
|
|
removeFilter("height");
|
|
break;
|
|
case CV_CAP_PROP_FPS:
|
|
if(value > 0) {
|
|
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);
|
|
} else
|
|
removeFilter("framerate");
|
|
break;
|
|
case CV_CAP_PROP_FOURCC:
|
|
case CV_CAP_PROP_FRAME_COUNT:
|
|
case CV_CAP_PROP_FORMAT:
|
|
case CV_CAP_PROP_MODE:
|
|
case CV_CAP_PROP_BRIGHTNESS:
|
|
case CV_CAP_PROP_CONTRAST:
|
|
case CV_CAP_PROP_SATURATION:
|
|
case CV_CAP_PROP_HUE:
|
|
case CV_CAP_PROP_GAIN:
|
|
case CV_CAP_PROP_CONVERT_RGB:
|
|
break;
|
|
case CV_CAP_GSTREAMER_QUEUE_LENGTH:
|
|
if(!sink)
|
|
break;
|
|
gst_app_sink_set_max_buffers(GST_APP_SINK(sink), (guint) value);
|
|
break;
|
|
default:
|
|
CV_WARN("GStreamer: unhandled property");
|
|
}
|
|
return false;
|
|
}
|
|
CvCapture* cvCreateCapture_GStreamer(int type, const char* filename )
|
|
{
|
|
CvCapture_GStreamer* capture = new CvCapture_GStreamer;
|
|
|
|
if( capture->open( type, filename ))
|
|
return capture;
|
|
|
|
delete capture;
|
|
return false;
|
|
}
|