diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index fea148f91f..6cd68ceef8 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -74,7 +74,7 @@ #endif #if GST_VERSION_MAJOR > 0 -#define COLOR_ELEM "videoconvert" +#define COLOR_ELEM "autovideoconvert" #else #define COLOR_ELEM "ffmpegcolorspace" #endif @@ -151,6 +151,7 @@ protected: GstCaps* caps; GstCaps* buffer_caps; IplImage* frame; + gint64 duration; }; /*! @@ -171,6 +172,7 @@ void CvCapture_GStreamer::init() caps = NULL; buffer_caps = NULL; frame = NULL; + duration = -1; } /*! @@ -187,42 +189,8 @@ void CvCapture_GStreamer::close() gst_object_unref(GST_OBJECT(pipeline)); pipeline = NULL; } - if(uridecodebin){ - gst_object_unref(GST_OBJECT(uridecodebin)); - uridecodebin = NULL; - } - if(color){ - gst_object_unref(GST_OBJECT(color)); - color = NULL; - } - if(sink){ - gst_object_unref(GST_OBJECT(sink)); - sink = NULL; - } - if(buffer) { - gst_buffer_unref(buffer); - buffer = NULL; - } - if(frame) { - frame->imageData = 0; - cvReleaseImage(&frame); - frame = NULL; - } - if(caps){ - gst_caps_unref(caps); - caps = NULL; - } - if(buffer_caps){ - gst_caps_unref(buffer_caps); - buffer_caps = NULL; - } -#if GST_VERSION_MAJOR > 0 - if(sample){ - gst_sample_unref(sample); - sample = NULL; - } -#endif + duration = -1; } /*! @@ -394,10 +362,20 @@ void CvCapture_GStreamer::startPipeline() __BEGIN__; //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"); + GstStateChangeReturn status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + if (status == GST_STATE_CHANGE_ASYNC) + { + // wait for status update + GstState st1; + GstState st2; + status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); + } + if (status == GST_STATE_CHANGE_FAILURE) + { + handleMessage(pipeline); gst_object_unref(pipeline); + pipeline = NULL; + CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); return; } @@ -422,6 +400,7 @@ void CvCapture_GStreamer::stopPipeline() GST_STATE_CHANGE_FAILURE) { CV_ERROR(CV_StsError, "GStreamer: unable to stop pipeline\n"); gst_object_unref(pipeline); + pipeline = NULL; return; } __END__; @@ -581,6 +560,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) char *uri = NULL; uridecodebin = NULL; GstElementFactory * testfac; + GstStateChangeReturn status; if (type == CV_CAP_GSTREAMER_V4L){ testfac = gst_element_factory_find("v4lsrc"); @@ -651,7 +631,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) #endif element_from_uri = true; }else{ - uridecodebin = gst_element_factory_make ("uridecodebin", NULL); + uridecodebin = gst_element_factory_make("uridecodebin", NULL); g_object_set(G_OBJECT(uridecodebin),"uri",uri, NULL); } g_free(protocol); @@ -716,9 +696,9 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) } else { - pipeline = gst_pipeline_new (NULL); - // videoconvert (in 0.10: ffmpegcolorspace) automatically selects the correct colorspace - // conversion based on caps. + pipeline = gst_pipeline_new(NULL); + // videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert) + //automatically selects the correct colorspace conversion based on caps. color = gst_element_factory_make(COLOR_ELEM, NULL); sink = gst_element_factory_make("appsink", NULL); @@ -728,6 +708,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if(!gst_element_link(uridecodebin, color)) { CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); gst_object_unref(pipeline); + pipeline = NULL; return false; } }else{ @@ -737,6 +718,7 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) if(!gst_element_link(color, sink)) { CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); gst_object_unref(pipeline); + pipeline = NULL; return false; } } @@ -761,8 +743,45 @@ bool CvCapture_GStreamer::open( int type, const char* filename ) gst_app_sink_set_caps(GST_APP_SINK(sink), caps); gst_caps_unref(caps); - //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 + // For video files only: set pipeline to PAUSED state to get its duration + if (stream) + { + status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED); + if (status == GST_STATE_CHANGE_ASYNC) + { + // wait for status update + GstState st1; + GstState st2; + status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); + } + if (status == GST_STATE_CHANGE_FAILURE) + { + handleMessage(pipeline); + gst_object_unref(pipeline); + pipeline = NULL; + CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); + return false; + } + + GstFormat format; + + format = GST_FORMAT_DEFAULT; +#if GST_VERSION_MAJOR == 0 + if(!gst_element_query_duration(sink, &format, &duration)) +#else + if(!gst_element_query_duration(sink, format, &duration)) +#endif + { + CV_WARN("GStreamer: unable to query duration of stream"); + duration = -1; + return true; + } + } + else + { + duration = -1; + } + __END__; return true; @@ -864,13 +883,7 @@ double CvCapture_GStreamer::getProperty( int propId ) case CV_CAP_PROP_FOURCC: break; case CV_CAP_PROP_FRAME_COUNT: - format = GST_FORMAT_DEFAULT; - status = gst_element_query_position(sink, FORMAT, &value); - if(!status) { - CV_WARN("GStreamer: unable to query position of stream"); - return false; - } - return value; + return duration; case CV_CAP_PROP_FORMAT: case CV_CAP_PROP_MODE: case CV_CAP_PROP_BRIGHTNESS: @@ -1059,28 +1072,51 @@ void CvVideoWriter_GStreamer::init() */ void CvVideoWriter_GStreamer::close() { + GstStateChangeReturn status; if (pipeline) { - gst_app_src_end_of_stream(GST_APP_SRC(source)); + handleMessage(pipeline); + + if (gst_app_src_end_of_stream(GST_APP_SRC(source)) != GST_FLOW_OK) + { + CV_WARN("Cannot send EOS to GStreamer pipeline\n"); + return; + } //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){ + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) + { + CV_WARN("Error during VideoWriter finalization\n"); + return; + } + + if(msg != NULL) + { gst_message_unref(msg); g_object_unref(G_OBJECT(bus)); } - gst_element_set_state (pipeline, GST_STATE_NULL); - handleMessage(pipeline); + status = gst_element_set_state (pipeline, GST_STATE_NULL); + if (status == GST_STATE_CHANGE_ASYNC) + { + // wait for status update + GstState st1; + GstState st2; + status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); + } + if (status == GST_STATE_CHANGE_FAILURE) + { + handleMessage (pipeline); + gst_object_unref (GST_OBJECT (pipeline)); + pipeline = NULL; + CV_WARN("Unable to stop gstreamer pipeline\n"); + return; + } gst_object_unref (GST_OBJECT (pipeline)); - - if (source) - gst_object_unref (GST_OBJECT (source)); - - if (file) - gst_object_unref (GST_OBJECT (file)); + pipeline = NULL; } } @@ -1353,13 +1389,15 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, stateret = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); if(stateret == GST_STATE_CHANGE_FAILURE) { + handleMessage(pipeline); CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n"); } - handleMessage(pipeline); framerate = fps; num_frames = 0; + handleMessage(pipeline); + __END__; return true; @@ -1376,7 +1414,6 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, */ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) { - CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame"); GstClockTime duration, timestamp; @@ -1384,6 +1421,7 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) int size; __BEGIN__; + handleMessage(pipeline); if (input_pix_fmt == GST_VIDEO_FORMAT_BGR) { @@ -1399,7 +1437,8 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) } #endif else { - assert(false); + CV_WARN("Invalid video format!\n"); + return false; } size = image->imageSize; @@ -1427,13 +1466,15 @@ bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) ret = gst_app_src_push_buffer(GST_APP_SRC(source), buffer); if (ret != GST_FLOW_OK) { - /* something wrong, stop pushing */ - assert(false); + CV_WARN("Error pushing buffer to GStreamer pipeline"); + return false; } //gst_debug_bin_to_dot_file (GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); + ++num_frames; __END__; + return true; } @@ -1518,7 +1559,6 @@ void handleMessage(GstElement * pipeline) 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); @@ -1531,12 +1571,11 @@ void handleMessage(GstElement * pipeline) //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"); + //fprintf(stderr, "unhandled message %s\n",GST_MESSAGE_TYPE_NAME(msg)); break; } }