1 /*M/////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4 // 5 // By downloading, copying, installing or using the software you agree to this license. 6 // If you do not agree to this license, do not download, install, 7 // copy or use the software. 8 // 9 // 10 // Intel License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2008, 2011, Nils Hasler, all rights reserved. 14 // Third party copyrights are property of their respective owners. 15 // 16 // Redistribution and use in source and binary forms, with or without modification, 17 // are permitted provided that the following conditions are met: 18 // 19 // * Redistribution's of source code must retain the above copyright notice, 20 // this list of conditions and the following disclaimer. 21 // 22 // * Redistribution's in binary form must reproduce the above copyright notice, 23 // this list of conditions and the following disclaimer in the documentation 24 // and/or other materials provided with the distribution. 25 // 26 // * The name of Intel Corporation may not be used to endorse or promote products 27 // derived from this software without specific prior written permission. 28 // 29 // This software is provided by the copyright holders and contributors "as is" and 30 // any express or implied warranties, including, but not limited to, the implied 31 // warranties of merchantability and fitness for a particular purpose are disclaimed. 32 // In no event shall the Intel Corporation or contributors be liable for any direct, 33 // indirect, incidental, special, exemplary, or consequential damages 34 // (including, but not limited to, procurement of substitute goods or services; 35 // loss of use, data, or profits; or business interruption) however caused 36 // and on any theory of liability, whether in contract, strict liability, 37 // or tort (including negligence or otherwise) arising in any way out of 38 // the use of this software, even if advised of the possibility of such damage. 39 // 40 //M*/ 41 42 /*! 43 * \file cap_gstreamer.cpp 44 * \author Nils Hasler <hasler (at) mpi-inf.mpg.de> 45 * Max-Planck-Institut Informatik 46 * \author Dirk Van Haerenborgh <vhdirk (at) gmail.com> 47 * 48 * \brief Use GStreamer to read/write video 49 */ 50 #include "precomp.hpp" 51 #include <unistd.h> 52 #include <string.h> 53 #include <gst/gst.h> 54 #include <gst/gstbuffer.h> 55 #include <gst/video/video.h> 56 #include <gst/app/gstappsink.h> 57 #include <gst/app/gstappsrc.h> 58 #include <gst/riff/riff-media.h> 59 #include <gst/pbutils/missing-plugins.h> 60 61 #define VERSION_NUM(major, minor, micro) (major * 1000000 + minor * 1000 + micro) 62 #define FULL_GST_VERSION VERSION_NUM(GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO) 63 64 #if FULL_GST_VERSION >= VERSION_NUM(0,10,32) 65 #include <gst/pbutils/encoding-profile.h> 66 //#include <gst/base/gsttypefindhelper.h> 67 #endif 68 69 70 #ifdef NDEBUG 71 #define CV_WARN(message) 72 #else 73 #define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) 74 #endif 75 76 #if GST_VERSION_MAJOR == 0 77 #define COLOR_ELEM "ffmpegcolorspace" 78 #elif FULL_GST_VERSION < VERSION_NUM(1,5,0) 79 #define COLOR_ELEM "videoconvert" 80 #else 81 #define COLOR_ELEM "autovideoconvert" 82 #endif 83 84 void toFraction(double decimal, double &numerator, double &denominator); 85 void handleMessage(GstElement * pipeline); 86 87 88 static cv::Mutex gst_initializer_mutex; 89 90 /*! 91 * \brief The gst_initializer class 92 * Initializes gstreamer once in the whole process 93 */ 94 class gst_initializer 95 { 96 public: 97 static void init() 98 { 99 gst_initializer_mutex.lock(); 100 static gst_initializer init; 101 gst_initializer_mutex.unlock(); 102 } 103 private: 104 gst_initializer() 105 { 106 gst_init(NULL, NULL); 107 // gst_debug_set_active(1); 108 // gst_debug_set_colored(1); 109 // gst_debug_set_default_threshold(GST_LEVEL_INFO); 110 } 111 }; 112 113 /*! 114 * \brief The CvCapture_GStreamer class 115 * Use GStreamer to capture video 116 */ 117 class CvCapture_GStreamer : public CvCapture 118 { 119 public: 120 CvCapture_GStreamer() { init(); } 121 virtual ~CvCapture_GStreamer() { close(); } 122 123 virtual bool open( int type, const char* filename ); 124 virtual void close(); 125 126 virtual double getProperty(int) const; 127 virtual bool setProperty(int, double); 128 virtual bool grabFrame(); 129 virtual IplImage* retrieveFrame(int); 130 131 protected: 132 void init(); 133 bool reopen(); 134 bool isPipelinePlaying(); 135 void startPipeline(); 136 void stopPipeline(); 137 void restartPipeline(); 138 void setFilter(const char* prop, int type, int v1, int v2 = 0); 139 void removeFilter(const char *filter); 140 static void newPad(GstElement *myelement, 141 GstPad *pad, 142 gpointer data); 143 GstElement* pipeline; 144 GstElement* uridecodebin; 145 GstElement* color; 146 GstElement* sink; 147 #if GST_VERSION_MAJOR > 0 148 GstSample* sample; 149 GstMapInfo* info; 150 #endif 151 GstBuffer* buffer; 152 GstCaps* caps; 153 IplImage* frame; 154 gint64 duration; 155 gint width; 156 gint height; 157 double fps; 158 }; 159 160 /*! 161 * \brief CvCapture_GStreamer::init 162 * inits the class 163 */ 164 void CvCapture_GStreamer::init() 165 { 166 pipeline = NULL; 167 uridecodebin = NULL; 168 color = NULL; 169 sink = NULL; 170 #if GST_VERSION_MAJOR > 0 171 sample = NULL; 172 info = new GstMapInfo; 173 #endif 174 buffer = NULL; 175 caps = NULL; 176 frame = NULL; 177 duration = -1; 178 width = -1; 179 height = -1; 180 fps = -1; 181 } 182 183 /*! 184 * \brief CvCapture_GStreamer::close 185 * Closes the pipeline and destroys all instances 186 */ 187 void CvCapture_GStreamer::close() 188 { 189 if (isPipelinePlaying()) 190 this->stopPipeline(); 191 192 if(pipeline) { 193 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); 194 gst_object_unref(GST_OBJECT(pipeline)); 195 pipeline = NULL; 196 } 197 198 duration = -1; 199 width = -1; 200 height = -1; 201 fps = -1; 202 } 203 204 /*! 205 * \brief CvCapture_GStreamer::grabFrame 206 * \return 207 * Grabs a sample from the pipeline, awaiting consumation by retreiveFrame. 208 * The pipeline is started if it was not running yet 209 */ 210 bool CvCapture_GStreamer::grabFrame() 211 { 212 if(!pipeline) 213 return false; 214 215 // start the pipeline if it was not in playing state yet 216 if(!this->isPipelinePlaying()) 217 this->startPipeline(); 218 219 // bail out if EOS 220 if(gst_app_sink_is_eos(GST_APP_SINK(sink))) 221 return false; 222 223 #if GST_VERSION_MAJOR == 0 224 if(buffer) 225 gst_buffer_unref(buffer); 226 227 buffer = gst_app_sink_pull_buffer(GST_APP_SINK(sink)); 228 #else 229 if(sample) 230 gst_sample_unref(sample); 231 232 sample = gst_app_sink_pull_sample(GST_APP_SINK(sink)); 233 234 if(!sample) 235 return false; 236 237 buffer = gst_sample_get_buffer(sample); 238 #endif 239 240 if(!buffer) 241 return false; 242 243 return true; 244 } 245 246 /*! 247 * \brief CvCapture_GStreamer::retrieveFrame 248 * \return IplImage pointer. [Transfer Full] 249 * Retreive the previously grabbed buffer, and wrap it in an IPLImage structure 250 */ 251 IplImage * CvCapture_GStreamer::retrieveFrame(int) 252 { 253 if(!buffer) 254 return 0; 255 256 //construct a frame header if we did not have any yet 257 if(!frame) 258 { 259 #if GST_VERSION_MAJOR == 0 260 GstCaps* buffer_caps = gst_buffer_get_caps(buffer); 261 #else 262 GstCaps* buffer_caps = gst_sample_get_caps(sample); 263 #endif 264 // bail out in no caps 265 assert(gst_caps_get_size(buffer_caps) == 1); 266 GstStructure* structure = gst_caps_get_structure(buffer_caps, 0); 267 268 // bail out if width or height are 0 269 if(!gst_structure_get_int(structure, "width", &width) || 270 !gst_structure_get_int(structure, "height", &height)) 271 { 272 gst_caps_unref(buffer_caps); 273 return 0; 274 } 275 276 int depth = 3; 277 #if GST_VERSION_MAJOR > 0 278 depth = 0; 279 const gchar* name = gst_structure_get_name(structure); 280 const gchar* format = gst_structure_get_string(structure, "format"); 281 282 if (!name || !format) 283 return 0; 284 285 // we support 3 types of data: 286 // video/x-raw, format=BGR -> 8bit, 3 channels 287 // video/x-raw, format=GRAY8 -> 8bit, 1 channel 288 // video/x-bayer -> 8bit, 1 channel 289 // bayer data is never decoded, the user is responsible for that 290 // everything is 8 bit, so we just test the caps for bit depth 291 292 if (strcasecmp(name, "video/x-raw") == 0) 293 { 294 if (strcasecmp(format, "BGR") == 0) { 295 depth = 3; 296 } 297 else if(strcasecmp(format, "GRAY8") == 0){ 298 depth = 1; 299 } 300 } 301 else if (strcasecmp(name, "video/x-bayer") == 0) 302 { 303 depth = 1; 304 } 305 #endif 306 if (depth > 0) { 307 frame = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, depth); 308 } else { 309 gst_caps_unref(buffer_caps); 310 return 0; 311 } 312 313 gst_caps_unref(buffer_caps); 314 } 315 316 // gstreamer expects us to handle the memory at this point 317 // so we can just wrap the raw buffer and be done with it 318 #if GST_VERSION_MAJOR == 0 319 frame->imageData = (char *)GST_BUFFER_DATA(buffer); 320 #else 321 // the data ptr in GstMapInfo is only valid throughout the mapifo objects life. 322 // TODO: check if reusing the mapinfo object is ok. 323 324 gboolean success = gst_buffer_map(buffer,info, (GstMapFlags)GST_MAP_READ); 325 if (!success){ 326 //something weird went wrong here. abort. abort. 327 //fprintf(stderr,"GStreamer: unable to map buffer"); 328 return 0; 329 } 330 frame->imageData = (char*)info->data; 331 gst_buffer_unmap(buffer,info); 332 #endif 333 334 return frame; 335 } 336 337 338 /*! 339 * \brief CvCapture_GStreamer::isPipelinePlaying 340 * \return if the pipeline is currently playing. 341 */ 342 bool CvCapture_GStreamer::isPipelinePlaying() 343 { 344 GstState current, pending; 345 GstClockTime timeout = 5*GST_SECOND; 346 if(!GST_IS_ELEMENT(pipeline)){ 347 return false; 348 } 349 350 GstStateChangeReturn ret = gst_element_get_state(GST_ELEMENT(pipeline),¤t, &pending, timeout); 351 if (!ret){ 352 //fprintf(stderr, "GStreamer: unable to query pipeline state\n"); 353 return false; 354 } 355 356 return current == GST_STATE_PLAYING; 357 } 358 359 /*! 360 * \brief CvCapture_GStreamer::startPipeline 361 * Start the pipeline by setting it to the playing state 362 */ 363 void CvCapture_GStreamer::startPipeline() 364 { 365 CV_FUNCNAME("icvStartPipeline"); 366 367 __BEGIN__; 368 369 //fprintf(stderr, "relinked, pausing\n"); 370 GstStateChangeReturn status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); 371 if (status == GST_STATE_CHANGE_ASYNC) 372 { 373 // wait for status update 374 GstState st1; 375 GstState st2; 376 status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); 377 } 378 if (status == GST_STATE_CHANGE_FAILURE) 379 { 380 handleMessage(pipeline); 381 gst_object_unref(pipeline); 382 pipeline = NULL; 383 CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); 384 return; 385 } 386 387 //printf("state now playing\n"); 388 handleMessage(pipeline); 389 __END__; 390 } 391 392 393 /*! 394 * \brief CvCapture_GStreamer::stopPipeline 395 * Stop the pipeline by setting it to NULL 396 */ 397 void CvCapture_GStreamer::stopPipeline() 398 { 399 CV_FUNCNAME("icvStopPipeline"); 400 401 __BEGIN__; 402 403 //fprintf(stderr, "restarting pipeline, going to ready\n"); 404 if(gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL) == 405 GST_STATE_CHANGE_FAILURE) { 406 CV_ERROR(CV_StsError, "GStreamer: unable to stop pipeline\n"); 407 gst_object_unref(pipeline); 408 pipeline = NULL; 409 return; 410 } 411 __END__; 412 } 413 414 /*! 415 * \brief CvCapture_GStreamer::restartPipeline 416 * Restart the pipeline 417 */ 418 void CvCapture_GStreamer::restartPipeline() 419 { 420 handleMessage(pipeline); 421 422 this->stopPipeline(); 423 this->startPipeline(); 424 } 425 426 427 /*! 428 * \brief CvCapture_GStreamer::setFilter 429 * \param prop the property name 430 * \param type glib property type 431 * \param v1 the value 432 * \param v2 second value of property type requires it, else NULL 433 * Filter the output formats by setting appsink caps properties 434 */ 435 void CvCapture_GStreamer::setFilter(const char *prop, int type, int v1, int v2) 436 { 437 //printf("GStreamer: setFilter \n"); 438 if(!caps || !( GST_IS_CAPS (caps) )) 439 { 440 if(type == G_TYPE_INT) 441 { 442 #if GST_VERSION_MAJOR == 0 443 caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, NULL); 444 #else 445 caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, NULL); 446 #endif 447 } 448 else 449 { 450 #if GST_VERSION_MAJOR == 0 451 caps = gst_caps_new_simple("video/x-raw-rgb", prop, type, v1, v2, NULL); 452 #else 453 caps = gst_caps_new_simple("video/x-raw","format",G_TYPE_STRING,"BGR", prop, type, v1, v2, NULL); 454 #endif 455 } 456 } 457 else 458 { 459 #if GST_VERSION_MAJOR > 0 460 if (! gst_caps_is_writable(caps)) 461 caps = gst_caps_make_writable (caps); 462 #endif 463 if(type == G_TYPE_INT){ 464 gst_caps_set_simple(caps, prop, type, v1, NULL); 465 }else{ 466 gst_caps_set_simple(caps, prop, type, v1, v2, NULL); 467 } 468 } 469 470 #if GST_VERSION_MAJOR > 0 471 caps = gst_caps_fixate(caps); 472 #endif 473 474 gst_app_sink_set_caps(GST_APP_SINK(sink), caps); 475 //printf("filtering with %s\n", gst_caps_to_string(caps)); 476 } 477 478 479 /*! 480 * \brief CvCapture_GStreamer::removeFilter 481 * \param filter filter to remove 482 * remove the specified filter from the appsink template caps 483 */ 484 void CvCapture_GStreamer::removeFilter(const char *filter) 485 { 486 if(!caps) 487 return; 488 489 #if GST_VERSION_MAJOR > 0 490 if (! gst_caps_is_writable(caps)) 491 caps = gst_caps_make_writable (caps); 492 #endif 493 494 GstStructure *s = gst_caps_get_structure(caps, 0); 495 gst_structure_remove_field(s, filter); 496 497 gst_app_sink_set_caps(GST_APP_SINK(sink), caps); 498 } 499 500 /*! 501 * \brief CvCapture_GStreamer::newPad link dynamic padd 502 * \param pad 503 * \param data 504 * decodebin creates pads based on stream information, which is not known upfront 505 * on receiving the pad-added signal, we connect it to the colorspace conversion element 506 */ 507 void CvCapture_GStreamer::newPad(GstElement * /*elem*/, 508 GstPad *pad, 509 gpointer data) 510 { 511 GstPad *sinkpad; 512 GstElement *color = (GstElement *) data; 513 514 sinkpad = gst_element_get_static_pad (color, "sink"); 515 if (!sinkpad){ 516 //fprintf(stderr, "Gstreamer: no pad named sink\n"); 517 return; 518 } 519 520 gst_pad_link (pad, sinkpad); 521 gst_object_unref (sinkpad); 522 } 523 524 /*! 525 * \brief CvCapture_GStreamer::open Open the given file with gstreamer 526 * \param type CvCapture type. One of CV_CAP_GSTREAMER_* 527 * \param filename Filename to open in case of CV_CAP_GSTREAMER_FILE 528 * \return boolean. Specifies if opening was successful. 529 * 530 * In case of CV_CAP_GSTREAMER_V4L(2), a pipelin is constructed as follows: 531 * v4l2src ! autoconvert ! appsink 532 * 533 * 534 * The 'filename' parameter is not limited to filesystem paths, and may be one of the following: 535 * 536 * - a normal filesystem path: 537 * e.g. video.avi or /path/to/video.avi or C:\\video.avi 538 * - an uri: 539 * e.g. file:///path/to/video.avi or rtsp:///path/to/stream.asf 540 * - a gstreamer pipeline description: 541 * e.g. videotestsrc ! videoconvert ! appsink 542 * the appsink name should be either 'appsink0' (the default) or 'opencvsink' 543 * 544 * When dealing with a file, CvCapture_GStreamer will not drop frames if the grabbing interval 545 * larger than the framerate period. (Unlike the uri or manual pipeline description, which assume 546 * a live source) 547 * 548 * The pipeline will only be started whenever the first frame is grabbed. Setting pipeline properties 549 * is really slow if we need to restart the pipeline over and over again. 550 * 551 * TODO: the 'type' parameter is imo unneeded. for v4l2, filename 'v4l2:///dev/video0' can be used. 552 * I expect this to be the same for CV_CAP_GSTREAMER_1394. Is anyone actually still using v4l (v1)? 553 * 554 */ 555 bool CvCapture_GStreamer::open( int type, const char* filename ) 556 { 557 CV_FUNCNAME("cvCaptureFromCAM_GStreamer"); 558 559 __BEGIN__; 560 561 gst_initializer::init(); 562 563 bool file = false; 564 bool stream = false; 565 bool manualpipeline = false; 566 char *uri = NULL; 567 uridecodebin = NULL; 568 GstElementFactory * testfac; 569 GstStateChangeReturn status; 570 571 if (type == CV_CAP_GSTREAMER_V4L){ 572 testfac = gst_element_factory_find("v4lsrc"); 573 if (!testfac){ 574 return false; 575 } 576 g_object_unref(G_OBJECT(testfac)); 577 filename = "v4lsrc ! " COLOR_ELEM " ! appsink"; 578 } 579 if (type == CV_CAP_GSTREAMER_V4L2){ 580 testfac = gst_element_factory_find("v4l2src"); 581 if (!testfac){ 582 return false; 583 } 584 g_object_unref(G_OBJECT(testfac)); 585 filename = "v4l2src ! " COLOR_ELEM " ! appsink"; 586 } 587 588 589 // test if we have a valid uri. If so, open it with an uridecodebin 590 // else, we might have a file or a manual pipeline. 591 // if gstreamer cannot parse the manual pipeline, we assume we were given and 592 // ordinary file path. 593 if(!gst_uri_is_valid(filename)) 594 { 595 uri = realpath(filename, NULL); 596 stream = false; 597 if(uri) 598 { 599 uri = g_filename_to_uri(uri, NULL, NULL); 600 if(uri) 601 { 602 file = true; 603 } 604 else 605 { 606 CV_WARN("GStreamer: Error opening file\n"); 607 close(); 608 return false; 609 } 610 } 611 else 612 { 613 GError *err = NULL; 614 uridecodebin = gst_parse_launch(filename, &err); 615 if(!uridecodebin) 616 { 617 fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); 618 return false; 619 } 620 stream = true; 621 manualpipeline = true; 622 } 623 } else { 624 stream = true; 625 uri = g_strdup(filename); 626 } 627 628 bool element_from_uri = false; 629 if(!uridecodebin) 630 { 631 // At this writing, the v4l2 element (and maybe others too) does not support caps renegotiation. 632 // This means that we cannot use an uridecodebin when dealing with v4l2, since setting 633 // capture properties will not work. 634 // The solution (probably only until gstreamer 1.2) is to make an element from uri when dealing with v4l2. 635 gchar * protocol = gst_uri_get_protocol(uri); 636 if (!strcasecmp(protocol , "v4l2")) 637 { 638 #if GST_VERSION_MAJOR == 0 639 uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src"); 640 #else 641 uridecodebin = gst_element_make_from_uri(GST_URI_SRC, uri, "src", NULL); 642 #endif 643 element_from_uri = true; 644 }else{ 645 uridecodebin = gst_element_factory_make("uridecodebin", NULL); 646 g_object_set(G_OBJECT(uridecodebin), "uri", uri, NULL); 647 } 648 g_free(protocol); 649 650 if(!uridecodebin) { 651 //fprintf(stderr, "GStreamer: Error opening bin: %s\n", err->message); 652 close(); 653 return false; 654 } 655 } 656 657 if(manualpipeline) 658 { 659 GstIterator *it = NULL; 660 #if GST_VERSION_MAJOR == 0 661 it = gst_bin_iterate_sinks(GST_BIN(uridecodebin)); 662 if(gst_iterator_next(it, (gpointer *)&sink) != GST_ITERATOR_OK) { 663 CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); 664 return false; 665 } 666 #else 667 it = gst_bin_iterate_sinks (GST_BIN(uridecodebin)); 668 669 gboolean done = FALSE; 670 GstElement *element = NULL; 671 gchar* name = NULL; 672 GValue value = G_VALUE_INIT; 673 674 while (!done) { 675 switch (gst_iterator_next (it, &value)) { 676 case GST_ITERATOR_OK: 677 element = GST_ELEMENT (g_value_get_object (&value)); 678 name = gst_element_get_name(element); 679 if (name){ 680 if(strstr(name, "opencvsink") != NULL || strstr(name, "appsink") != NULL) { 681 sink = GST_ELEMENT ( gst_object_ref (element) ); 682 done = TRUE; 683 } 684 g_free(name); 685 } 686 g_value_unset (&value); 687 688 break; 689 case GST_ITERATOR_RESYNC: 690 gst_iterator_resync (it); 691 break; 692 case GST_ITERATOR_ERROR: 693 case GST_ITERATOR_DONE: 694 done = TRUE; 695 break; 696 } 697 } 698 gst_iterator_free (it); 699 700 701 if (!sink){ 702 CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); 703 return false; 704 } 705 #endif 706 pipeline = uridecodebin; 707 } 708 else 709 { 710 pipeline = gst_pipeline_new(NULL); 711 // videoconvert (in 0.10: ffmpegcolorspace, in 1.x autovideoconvert) 712 //automatically selects the correct colorspace conversion based on caps. 713 color = gst_element_factory_make(COLOR_ELEM, NULL); 714 sink = gst_element_factory_make("appsink", NULL); 715 716 gst_bin_add_many(GST_BIN(pipeline), uridecodebin, color, sink, NULL); 717 718 if(element_from_uri) { 719 if(!gst_element_link(uridecodebin, color)) { 720 CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); 721 gst_object_unref(pipeline); 722 pipeline = NULL; 723 return false; 724 } 725 }else{ 726 g_signal_connect(uridecodebin, "pad-added", G_CALLBACK(newPad), color); 727 } 728 729 if(!gst_element_link(color, sink)) { 730 CV_ERROR(CV_StsError, "GStreamer: cannot link color -> sink\n"); 731 gst_object_unref(pipeline); 732 pipeline = NULL; 733 return false; 734 } 735 } 736 737 //TODO: is 1 single buffer really high enough? 738 gst_app_sink_set_max_buffers (GST_APP_SINK(sink), 1); 739 gst_app_sink_set_drop (GST_APP_SINK(sink), stream); 740 //do not emit signals: all calls will be synchronous and blocking 741 gst_app_sink_set_emit_signals (GST_APP_SINK(sink), 0); 742 743 #if GST_VERSION_MAJOR == 0 744 caps = gst_caps_new_simple("video/x-raw-rgb", 745 "bpp", G_TYPE_INT, 24, 746 "red_mask", G_TYPE_INT, 0x0000FF, 747 "green_mask", G_TYPE_INT, 0x00FF00, 748 "blue_mask", G_TYPE_INT, 0xFF0000, 749 NULL); 750 #else 751 // support 1 and 3 channel 8 bit data, as well as bayer (also 1 channel, 8bit) 752 caps = gst_caps_from_string("video/x-raw, format=(string){BGR, GRAY8}; video/x-bayer,format=(string){rggb,bggr,grbg,gbrg}"); 753 #endif 754 gst_app_sink_set_caps(GST_APP_SINK(sink), caps); 755 gst_caps_unref(caps); 756 757 // For video files only: set pipeline to PAUSED state to get its duration 758 if (file) 759 { 760 status = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED); 761 if (status == GST_STATE_CHANGE_ASYNC) 762 { 763 // wait for status update 764 GstState st1; 765 GstState st2; 766 status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); 767 } 768 if (status == GST_STATE_CHANGE_FAILURE) 769 { 770 handleMessage(pipeline); 771 gst_object_unref(pipeline); 772 pipeline = NULL; 773 CV_ERROR(CV_StsError, "GStreamer: unable to start pipeline\n"); 774 return false; 775 } 776 777 GstFormat format; 778 779 format = GST_FORMAT_DEFAULT; 780 #if GST_VERSION_MAJOR == 0 781 if(!gst_element_query_duration(sink, &format, &duration)) 782 #else 783 if(!gst_element_query_duration(sink, format, &duration)) 784 #endif 785 { 786 handleMessage(pipeline); 787 CV_WARN("GStreamer: unable to query duration of stream"); 788 duration = -1; 789 } 790 791 GstPad* pad = gst_element_get_static_pad(color, "src"); 792 #if GST_VERSION_MAJOR == 0 793 GstCaps* buffer_caps = gst_pad_get_caps(pad); 794 #else 795 GstCaps* buffer_caps = gst_pad_get_current_caps(pad); 796 #endif 797 const GstStructure *structure = gst_caps_get_structure (buffer_caps, 0); 798 799 if (!gst_structure_get_int (structure, "width", &width)) 800 { 801 CV_WARN("Cannot query video width\n"); 802 } 803 804 if (!gst_structure_get_int (structure, "height", &height)) 805 { 806 CV_WARN("Cannot query video heigth\n"); 807 } 808 809 gint num = 0, denom=1; 810 if(!gst_structure_get_fraction(structure, "framerate", &num, &denom)) 811 { 812 CV_WARN("Cannot query video fps\n"); 813 } 814 815 fps = (double)num/(double)denom; 816 817 // GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); 818 } 819 else 820 { 821 duration = -1; 822 width = -1; 823 height = -1; 824 fps = -1; 825 } 826 827 __END__; 828 829 return true; 830 } 831 832 /*! 833 * \brief CvCapture_GStreamer::getProperty retreive the requested property from the pipeline 834 * \param propId requested property 835 * \return property value 836 * 837 * There are two ways the properties can be retreived. For seek-based properties we can query the pipeline. 838 * For frame-based properties, we use the caps of the lasst receivef sample. This means that some properties 839 * are not available until a first frame was received 840 */ 841 double CvCapture_GStreamer::getProperty( int propId ) const 842 { 843 GstFormat format; 844 gint64 value; 845 gboolean status; 846 847 #if GST_VERSION_MAJOR == 0 848 #define FORMAT &format 849 #else 850 #define FORMAT format 851 #endif 852 853 if(!pipeline) { 854 CV_WARN("GStreamer: no pipeline"); 855 return false; 856 } 857 858 switch(propId) { 859 case CV_CAP_PROP_POS_MSEC: 860 format = GST_FORMAT_TIME; 861 status = gst_element_query_position(sink, FORMAT, &value); 862 if(!status) { 863 CV_WARN("GStreamer: unable to query position of stream"); 864 return false; 865 } 866 return value * 1e-6; // nano seconds to milli seconds 867 case CV_CAP_PROP_POS_FRAMES: 868 format = GST_FORMAT_DEFAULT; 869 status = gst_element_query_position(sink, FORMAT, &value); 870 if(!status) { 871 CV_WARN("GStreamer: unable to query position of stream"); 872 return false; 873 } 874 return value; 875 case CV_CAP_PROP_POS_AVI_RATIO: 876 format = GST_FORMAT_PERCENT; 877 status = gst_element_query_position(sink, FORMAT, &value); 878 if(!status) { 879 CV_WARN("GStreamer: unable to query position of stream"); 880 return false; 881 } 882 return ((double) value) / GST_FORMAT_PERCENT_MAX; 883 case CV_CAP_PROP_FRAME_WIDTH: 884 return width; 885 case CV_CAP_PROP_FRAME_HEIGHT: 886 return height; 887 case CV_CAP_PROP_FPS: 888 return fps; 889 case CV_CAP_PROP_FOURCC: 890 break; 891 case CV_CAP_PROP_FRAME_COUNT: 892 return duration; 893 case CV_CAP_PROP_FORMAT: 894 case CV_CAP_PROP_MODE: 895 case CV_CAP_PROP_BRIGHTNESS: 896 case CV_CAP_PROP_CONTRAST: 897 case CV_CAP_PROP_SATURATION: 898 case CV_CAP_PROP_HUE: 899 case CV_CAP_PROP_GAIN: 900 case CV_CAP_PROP_CONVERT_RGB: 901 break; 902 case CV_CAP_GSTREAMER_QUEUE_LENGTH: 903 if(!sink) { 904 CV_WARN("GStreamer: there is no sink yet"); 905 return false; 906 } 907 return gst_app_sink_get_max_buffers(GST_APP_SINK(sink)); 908 default: 909 CV_WARN("GStreamer: unhandled property"); 910 break; 911 } 912 913 #undef FORMAT 914 915 return false; 916 } 917 918 /*! 919 * \brief CvCapture_GStreamer::setProperty 920 * \param propId 921 * \param value 922 * \return success 923 * Sets the desired property id with val. If the pipeline is running, 924 * it is briefly stopped and started again after the property was set 925 */ 926 bool CvCapture_GStreamer::setProperty( int propId, double value ) 927 { 928 GstFormat format; 929 GstSeekFlags flags; 930 931 if(!pipeline) { 932 CV_WARN("GStreamer: no pipeline"); 933 return false; 934 } 935 936 bool wasPlaying = this->isPipelinePlaying(); 937 if (wasPlaying) 938 this->stopPipeline(); 939 940 941 switch(propId) { 942 case CV_CAP_PROP_POS_MSEC: 943 format = GST_FORMAT_TIME; 944 flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); 945 if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, 946 flags, (gint64) (value * GST_MSECOND))) { 947 CV_WARN("GStreamer: unable to seek"); 948 } 949 break; 950 case CV_CAP_PROP_POS_FRAMES: 951 format = GST_FORMAT_DEFAULT; 952 flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); 953 if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, 954 flags, (gint64) value)) { 955 CV_WARN("GStreamer: unable to seek"); 956 } 957 break; 958 case CV_CAP_PROP_POS_AVI_RATIO: 959 format = GST_FORMAT_PERCENT; 960 flags = (GstSeekFlags) (GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_ACCURATE); 961 if(!gst_element_seek_simple(GST_ELEMENT(pipeline), format, 962 flags, (gint64) (value * GST_FORMAT_PERCENT_MAX))) { 963 CV_WARN("GStreamer: unable to seek"); 964 } 965 break; 966 case CV_CAP_PROP_FRAME_WIDTH: 967 if(value > 0) 968 setFilter("width", G_TYPE_INT, (int) value, 0); 969 else 970 removeFilter("width"); 971 break; 972 case CV_CAP_PROP_FRAME_HEIGHT: 973 if(value > 0) 974 setFilter("height", G_TYPE_INT, (int) value, 0); 975 else 976 removeFilter("height"); 977 break; 978 case CV_CAP_PROP_FPS: 979 if(value > 0) { 980 double num=0, denom = 1; 981 toFraction(value, num, denom); 982 setFilter("framerate", GST_TYPE_FRACTION, value, denom); 983 } else 984 removeFilter("framerate"); 985 break; 986 case CV_CAP_PROP_FOURCC: 987 case CV_CAP_PROP_FRAME_COUNT: 988 case CV_CAP_PROP_FORMAT: 989 case CV_CAP_PROP_MODE: 990 case CV_CAP_PROP_BRIGHTNESS: 991 case CV_CAP_PROP_CONTRAST: 992 case CV_CAP_PROP_SATURATION: 993 case CV_CAP_PROP_HUE: 994 case CV_CAP_PROP_GAIN: 995 case CV_CAP_PROP_CONVERT_RGB: 996 break; 997 case CV_CAP_GSTREAMER_QUEUE_LENGTH: 998 if(!sink) 999 break; 1000 gst_app_sink_set_max_buffers(GST_APP_SINK(sink), (guint) value); 1001 break; 1002 default: 1003 CV_WARN("GStreamer: unhandled property"); 1004 } 1005 1006 if (wasPlaying) 1007 this->startPipeline(); 1008 1009 return false; 1010 } 1011 1012 /*! 1013 * \brief cvCreateCapture_GStreamer 1014 * \param type 1015 * \param filename 1016 * \return 1017 */ 1018 CvCapture* cvCreateCapture_GStreamer(int type, const char* filename ) 1019 { 1020 CvCapture_GStreamer* capture = new CvCapture_GStreamer; 1021 1022 if( capture->open( type, filename )) 1023 return capture; 1024 1025 delete capture; 1026 return 0; 1027 } 1028 1029 1030 /*! 1031 * \brief The CvVideoWriter_GStreamer class 1032 * Use Gstreamer to write video 1033 */ 1034 class CvVideoWriter_GStreamer : public CvVideoWriter 1035 { 1036 public: 1037 CvVideoWriter_GStreamer() { init(); } 1038 virtual ~CvVideoWriter_GStreamer() { close(); } 1039 1040 virtual bool open( const char* filename, int fourcc, 1041 double fps, CvSize frameSize, bool isColor ); 1042 virtual void close(); 1043 virtual bool writeFrame( const IplImage* image ); 1044 protected: 1045 void init(); 1046 const char* filenameToMimetype(const char* filename); 1047 GstElement* pipeline; 1048 GstElement* source; 1049 GstElement* encodebin; 1050 GstElement* file; 1051 1052 GstBuffer* buffer; 1053 int input_pix_fmt; 1054 int num_frames; 1055 double framerate; 1056 }; 1057 1058 /*! 1059 * \brief CvVideoWriter_GStreamer::init 1060 * initialise all variables 1061 */ 1062 void CvVideoWriter_GStreamer::init() 1063 { 1064 pipeline = NULL; 1065 source = NULL; 1066 encodebin = NULL; 1067 file = NULL; 1068 buffer = NULL; 1069 1070 num_frames = 0; 1071 framerate = 0; 1072 } 1073 1074 /*! 1075 * \brief CvVideoWriter_GStreamer::close 1076 * ends the pipeline by sending EOS and destroys the pipeline and all 1077 * elements afterwards 1078 */ 1079 void CvVideoWriter_GStreamer::close() 1080 { 1081 GstStateChangeReturn status; 1082 if (pipeline) 1083 { 1084 handleMessage(pipeline); 1085 1086 if (gst_app_src_end_of_stream(GST_APP_SRC(source)) != GST_FLOW_OK) 1087 { 1088 CV_WARN("Cannot send EOS to GStreamer pipeline\n"); 1089 return; 1090 } 1091 1092 //wait for EOS to trickle down the pipeline. This will let all elements finish properly 1093 GstBus* bus = gst_element_get_bus(pipeline); 1094 GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); 1095 if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) 1096 { 1097 CV_WARN("Error during VideoWriter finalization\n"); 1098 return; 1099 } 1100 1101 if(msg != NULL) 1102 { 1103 gst_message_unref(msg); 1104 g_object_unref(G_OBJECT(bus)); 1105 } 1106 1107 status = gst_element_set_state (pipeline, GST_STATE_NULL); 1108 if (status == GST_STATE_CHANGE_ASYNC) 1109 { 1110 // wait for status update 1111 GstState st1; 1112 GstState st2; 1113 status = gst_element_get_state(pipeline, &st1, &st2, GST_CLOCK_TIME_NONE); 1114 } 1115 if (status == GST_STATE_CHANGE_FAILURE) 1116 { 1117 handleMessage (pipeline); 1118 gst_object_unref (GST_OBJECT (pipeline)); 1119 pipeline = NULL; 1120 CV_WARN("Unable to stop gstreamer pipeline\n"); 1121 return; 1122 } 1123 1124 gst_object_unref (GST_OBJECT (pipeline)); 1125 pipeline = NULL; 1126 } 1127 } 1128 1129 1130 /*! 1131 * \brief CvVideoWriter_GStreamer::filenameToMimetype 1132 * \param filename 1133 * \return mimetype 1134 * Resturns a container mime type for a given filename by looking at it's extension 1135 */ 1136 const char* CvVideoWriter_GStreamer::filenameToMimetype(const char *filename) 1137 { 1138 //get extension 1139 const char *ext = strrchr(filename, '.'); 1140 if(!ext || ext == filename) return NULL; 1141 ext += 1; //exclude the dot 1142 1143 // return a container mime based on the given extension. 1144 // gstreamer's function returns too much possibilities, which is not useful to us 1145 1146 //return the appropriate mime 1147 if (strncasecmp(ext,"avi", 3) == 0) 1148 return (const char*)"video/x-msvideo"; 1149 1150 if (strncasecmp(ext,"mkv", 3) == 0 || strncasecmp(ext,"mk3d",4) == 0 || strncasecmp(ext,"webm",4) == 0 ) 1151 return (const char*)"video/x-matroska"; 1152 1153 if (strncasecmp(ext,"wmv", 3) == 0) 1154 return (const char*)"video/x-ms-asf"; 1155 1156 if (strncasecmp(ext,"mov", 3) == 0) 1157 return (const char*)"video/x-quicktime"; 1158 1159 if (strncasecmp(ext,"ogg", 3) == 0 || strncasecmp(ext,"ogv", 3) == 0) 1160 return (const char*)"application/ogg"; 1161 1162 if (strncasecmp(ext,"rm", 3) == 0) 1163 return (const char*)"vnd.rn-realmedia"; 1164 1165 if (strncasecmp(ext,"swf", 3) == 0) 1166 return (const char*)"application/x-shockwave-flash"; 1167 1168 if (strncasecmp(ext,"mp4", 3) == 0) 1169 return (const char*)"video/x-quicktime, variant=(string)iso"; 1170 1171 //default to avi 1172 return (const char*)"video/x-msvideo"; 1173 } 1174 1175 /*! 1176 * \brief CvVideoWriter_GStreamer::open 1177 * \param filename filename to output to 1178 * \param fourcc desired codec fourcc 1179 * \param fps desired framerate 1180 * \param frameSize the size of the expected frames 1181 * \param is_color color or grayscale 1182 * \return success 1183 * 1184 * We support 2 modes of operation. Either the user enters a filename and a fourcc 1185 * code, or enters a manual pipeline description like in CvVideoCapture_Gstreamer. 1186 * In the latter case, we just push frames on the appsink with appropriate caps. 1187 * In the former case, we try to deduce the correct container from the filename, 1188 * and the correct encoder from the fourcc profile. 1189 * 1190 * If the file extension did was not recognize, an avi container is used 1191 * 1192 */ 1193 bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc, 1194 double fps, CvSize frameSize, bool is_color ) 1195 { 1196 CV_FUNCNAME("CvVideoWriter_GStreamer::open"); 1197 1198 // check arguments 1199 assert (filename); 1200 assert (fps > 0); 1201 assert (frameSize.width > 0 && frameSize.height > 0); 1202 1203 // init gstreamer 1204 gst_initializer::init(); 1205 1206 // init vars 1207 bool manualpipeline = true; 1208 int bufsize = 0; 1209 GError *err = NULL; 1210 const char* mime = NULL; 1211 GstStateChangeReturn stateret; 1212 1213 GstCaps* caps = NULL; 1214 GstCaps* videocaps = NULL; 1215 1216 #if FULL_GST_VERSION >= VERSION_NUM(0,10,32) 1217 GstCaps* containercaps = NULL; 1218 GstEncodingContainerProfile* containerprofile = NULL; 1219 GstEncodingVideoProfile* videoprofile = NULL; 1220 #endif 1221 1222 GstIterator* it = NULL; 1223 gboolean done = FALSE; 1224 GstElement *element = NULL; 1225 gchar* name = NULL; 1226 1227 #if GST_VERSION_MAJOR == 0 1228 GstElement* splitter = NULL; 1229 GstElement* combiner = NULL; 1230 #endif 1231 1232 // we first try to construct a pipeline from the given string. 1233 // if that fails, we assume it is an ordinary filename 1234 1235 __BEGIN__; 1236 1237 encodebin = gst_parse_launch(filename, &err); 1238 manualpipeline = (encodebin != NULL); 1239 1240 if(manualpipeline) 1241 { 1242 #if GST_VERSION_MAJOR == 0 1243 it = gst_bin_iterate_sources(GST_BIN(encodebin)); 1244 if(gst_iterator_next(it, (gpointer *)&source) != GST_ITERATOR_OK) { 1245 CV_ERROR(CV_StsError, "GStreamer: cannot find appsink in manual pipeline\n"); 1246 return false; 1247 } 1248 #else 1249 it = gst_bin_iterate_sources (GST_BIN(encodebin)); 1250 GValue value = G_VALUE_INIT; 1251 1252 while (!done) { 1253 switch (gst_iterator_next (it, &value)) { 1254 case GST_ITERATOR_OK: 1255 element = GST_ELEMENT (g_value_get_object (&value)); 1256 name = gst_element_get_name(element); 1257 if (name){ 1258 if(strstr(name, "opencvsrc") != NULL || strstr(name, "appsrc") != NULL) { 1259 source = GST_ELEMENT ( gst_object_ref (element) ); 1260 done = TRUE; 1261 } 1262 g_free(name); 1263 } 1264 g_value_unset (&value); 1265 1266 break; 1267 case GST_ITERATOR_RESYNC: 1268 gst_iterator_resync (it); 1269 break; 1270 case GST_ITERATOR_ERROR: 1271 case GST_ITERATOR_DONE: 1272 done = TRUE; 1273 break; 1274 } 1275 } 1276 gst_iterator_free (it); 1277 1278 if (!source){ 1279 CV_ERROR(CV_StsError, "GStreamer: cannot find appsrc in manual pipeline\n"); 1280 return false; 1281 } 1282 #endif 1283 pipeline = encodebin; 1284 } 1285 else 1286 { 1287 pipeline = gst_pipeline_new (NULL); 1288 1289 // we just got a filename and a fourcc code. 1290 // first, try to guess the container from the filename 1291 //encodebin = gst_element_factory_make("encodebin", NULL); 1292 1293 //proxy old non existing fourcc ids. These were used in previous opencv versions, 1294 //but do not even exist in gstreamer any more 1295 if (fourcc == CV_FOURCC('M','P','1','V')) fourcc = CV_FOURCC('M', 'P', 'G' ,'1'); 1296 if (fourcc == CV_FOURCC('M','P','2','V')) fourcc = CV_FOURCC('M', 'P', 'G' ,'2'); 1297 if (fourcc == CV_FOURCC('D','R','A','C')) fourcc = CV_FOURCC('d', 'r', 'a' ,'c'); 1298 1299 1300 //create encoder caps from fourcc 1301 1302 videocaps = gst_riff_create_video_caps(fourcc, NULL, NULL, NULL, NULL, NULL); 1303 if (!videocaps){ 1304 CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this codec."); 1305 } 1306 1307 //create container caps from file extension 1308 mime = filenameToMimetype(filename); 1309 if (!mime) { 1310 CV_ERROR( CV_StsUnsupportedFormat, "Gstreamer Opencv backend does not support this file type."); 1311 } 1312 1313 #if FULL_GST_VERSION >= VERSION_NUM(0,10,32) 1314 containercaps = gst_caps_from_string(mime); 1315 1316 //create encodebin profile 1317 containerprofile = gst_encoding_container_profile_new("container", "container", containercaps, NULL); 1318 videoprofile = gst_encoding_video_profile_new(videocaps, NULL, NULL, 1); 1319 gst_encoding_container_profile_add_profile(containerprofile, (GstEncodingProfile *) videoprofile); 1320 #endif 1321 1322 //create pipeline elements 1323 encodebin = gst_element_factory_make("encodebin", NULL); 1324 1325 #if FULL_GST_VERSION >= VERSION_NUM(0,10,32) 1326 g_object_set(G_OBJECT(encodebin), "profile", containerprofile, NULL); 1327 #endif 1328 source = gst_element_factory_make("appsrc", NULL); 1329 file = gst_element_factory_make("filesink", NULL); 1330 g_object_set(G_OBJECT(file), "location", filename, NULL); 1331 } 1332 1333 if (is_color) 1334 { 1335 input_pix_fmt = GST_VIDEO_FORMAT_BGR; 1336 bufsize = frameSize.width * frameSize.height * 3; 1337 1338 #if GST_VERSION_MAJOR == 0 1339 caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_BGR, 1340 frameSize.width, 1341 frameSize.height, 1342 int(fps), 1, 1343 1, 1); 1344 #else 1345 caps = gst_caps_new_simple("video/x-raw", 1346 "format", G_TYPE_STRING, "BGR", 1347 "width", G_TYPE_INT, frameSize.width, 1348 "height", G_TYPE_INT, frameSize.height, 1349 "framerate", GST_TYPE_FRACTION, int(fps), 1, 1350 NULL); 1351 caps = gst_caps_fixate(caps); 1352 1353 #endif 1354 1355 } 1356 else 1357 { 1358 #if FULL_GST_VERSION >= VERSION_NUM(0,10,29) 1359 input_pix_fmt = GST_VIDEO_FORMAT_GRAY8; 1360 bufsize = frameSize.width * frameSize.height; 1361 1362 #if GST_VERSION_MAJOR == 0 1363 caps = gst_video_format_new_caps(GST_VIDEO_FORMAT_GRAY8, 1364 frameSize.width, 1365 frameSize.height, 1366 int(fps), 1, 1367 1, 1); 1368 #else 1369 caps = gst_caps_new_simple("video/x-raw", 1370 "format", G_TYPE_STRING, "GRAY8", 1371 "width", G_TYPE_INT, frameSize.width, 1372 "height", G_TYPE_INT, frameSize.height, 1373 "framerate", GST_TYPE_FRACTION, int(fps), 1, 1374 NULL); 1375 caps = gst_caps_fixate(caps); 1376 #endif 1377 #else 1378 CV_Assert(!"Gstreamer 0.10.29 or newer is required for grayscale input"); 1379 #endif 1380 } 1381 1382 gst_app_src_set_caps(GST_APP_SRC(source), caps); 1383 gst_app_src_set_stream_type(GST_APP_SRC(source), GST_APP_STREAM_TYPE_STREAM); 1384 gst_app_src_set_size (GST_APP_SRC(source), -1); 1385 1386 g_object_set(G_OBJECT(source), "format", GST_FORMAT_TIME, NULL); 1387 g_object_set(G_OBJECT(source), "block", 1, NULL); 1388 g_object_set(G_OBJECT(source), "is-live", 0, NULL); 1389 1390 1391 if(!manualpipeline) 1392 { 1393 g_object_set(G_OBJECT(file), "buffer-size", bufsize, NULL); 1394 gst_bin_add_many(GST_BIN(pipeline), source, encodebin, file, NULL); 1395 if(!gst_element_link_many(source, encodebin, file, NULL)) { 1396 CV_ERROR(CV_StsError, "GStreamer: cannot link elements\n"); 1397 } 1398 } 1399 1400 #if GST_VERSION_MAJOR == 0 1401 // HACK: remove streamsplitter and streamcombiner from 1402 // encodebin pipeline to prevent early EOF event handling 1403 // We always fetch BGR or gray-scale frames, so combiner->spliter 1404 // endge in graph is useless. 1405 it = gst_bin_iterate_recurse (GST_BIN(encodebin)); 1406 while (!done) { 1407 switch (gst_iterator_next (it, (void**)&element)) { 1408 case GST_ITERATOR_OK: 1409 name = gst_element_get_name(element); 1410 if (strstr(name, "streamsplitter")) 1411 splitter = element; 1412 else if (strstr(name, "streamcombiner")) 1413 combiner = element; 1414 break; 1415 case GST_ITERATOR_RESYNC: 1416 gst_iterator_resync (it); 1417 break; 1418 case GST_ITERATOR_ERROR: 1419 done = true; 1420 break; 1421 case GST_ITERATOR_DONE: 1422 done = true; 1423 break; 1424 } 1425 } 1426 1427 gst_iterator_free (it); 1428 1429 if (splitter && combiner) 1430 { 1431 gst_element_unlink(splitter, combiner); 1432 1433 GstPad* src = gst_element_get_pad(combiner, "src"); 1434 GstPad* sink = gst_element_get_pad(combiner, "encodingsink"); 1435 1436 GstPad* srcPeer = gst_pad_get_peer(src); 1437 GstPad* sinkPeer = gst_pad_get_peer(sink); 1438 1439 gst_pad_unlink(sinkPeer, sink); 1440 gst_pad_unlink(src, srcPeer); 1441 1442 gst_pad_link(sinkPeer, srcPeer); 1443 1444 src = gst_element_get_pad(splitter, "encodingsrc"); 1445 sink = gst_element_get_pad(splitter, "sink"); 1446 1447 srcPeer = gst_pad_get_peer(src); 1448 sinkPeer = gst_pad_get_peer(sink); 1449 1450 gst_pad_unlink(sinkPeer, sink); 1451 gst_pad_unlink(src, srcPeer); 1452 1453 gst_pad_link(sinkPeer, srcPeer); 1454 } 1455 #endif 1456 1457 stateret = gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); 1458 if(stateret == GST_STATE_CHANGE_FAILURE) { 1459 handleMessage(pipeline); 1460 CV_ERROR(CV_StsError, "GStreamer: cannot put pipeline to play\n"); 1461 } 1462 1463 framerate = fps; 1464 num_frames = 0; 1465 1466 handleMessage(pipeline); 1467 1468 __END__; 1469 1470 return true; 1471 } 1472 1473 1474 /*! 1475 * \brief CvVideoWriter_GStreamer::writeFrame 1476 * \param image 1477 * \return 1478 * Pushes the given frame on the pipeline. 1479 * The timestamp for the buffer is generated from the framerate set in open 1480 * and ensures a smooth video 1481 */ 1482 bool CvVideoWriter_GStreamer::writeFrame( const IplImage * image ) 1483 { 1484 CV_FUNCNAME("CvVideoWriter_GStreamer::writerFrame"); 1485 1486 GstClockTime duration, timestamp; 1487 GstFlowReturn ret; 1488 int size; 1489 1490 __BEGIN__; 1491 1492 handleMessage(pipeline); 1493 1494 if (input_pix_fmt == GST_VIDEO_FORMAT_BGR) { 1495 if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) { 1496 CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); 1497 } 1498 } 1499 #if FULL_GST_VERSION >= VERSION_NUM(0,10,29) 1500 else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY8) { 1501 if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { 1502 CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); 1503 } 1504 } 1505 #endif 1506 else { 1507 CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs BGR or grayscale images\n"); 1508 return false; 1509 } 1510 1511 size = image->imageSize; 1512 duration = ((double)1/framerate) * GST_SECOND; 1513 timestamp = num_frames * duration; 1514 1515 //gst_app_src_push_buffer takes ownership of the buffer, so we need to supply it a copy 1516 #if GST_VERSION_MAJOR == 0 1517 buffer = gst_buffer_try_new_and_alloc (size); 1518 if (!buffer) 1519 { 1520 CV_ERROR(CV_StsBadSize, "Cannot create GStreamer buffer"); 1521 } 1522 1523 memcpy(GST_BUFFER_DATA (buffer), (guint8*)image->imageData, size); 1524 GST_BUFFER_DURATION(buffer) = duration; 1525 GST_BUFFER_TIMESTAMP(buffer) = timestamp; 1526 #else 1527 buffer = gst_buffer_new_allocate (NULL, size, NULL); 1528 GstMapInfo info; 1529 gst_buffer_map(buffer, &info, (GstMapFlags)GST_MAP_READ); 1530 memcpy(info.data, (guint8*)image->imageData, size); 1531 gst_buffer_unmap(buffer, &info); 1532 GST_BUFFER_DURATION(buffer) = duration; 1533 GST_BUFFER_PTS(buffer) = timestamp; 1534 GST_BUFFER_DTS(buffer) = timestamp; 1535 #endif 1536 //set the current number in the frame 1537 GST_BUFFER_OFFSET(buffer) = num_frames; 1538 1539 ret = gst_app_src_push_buffer(GST_APP_SRC(source), buffer); 1540 if (ret != GST_FLOW_OK) { 1541 CV_WARN("Error pushing buffer to GStreamer pipeline"); 1542 return false; 1543 } 1544 1545 //GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline"); 1546 1547 ++num_frames; 1548 1549 __END__; 1550 1551 return true; 1552 } 1553 1554 /*! 1555 * \brief cvCreateVideoWriter_GStreamer 1556 * \param filename 1557 * \param fourcc 1558 * \param fps 1559 * \param frameSize 1560 * \param isColor 1561 * \return 1562 * Constructor 1563 */ 1564 CvVideoWriter* cvCreateVideoWriter_GStreamer(const char* filename, int fourcc, double fps, 1565 CvSize frameSize, int isColor ) 1566 { 1567 CvVideoWriter_GStreamer* wrt = new CvVideoWriter_GStreamer; 1568 if( wrt->open(filename, fourcc, fps,frameSize, isColor)) 1569 return wrt; 1570 1571 delete wrt; 1572 return 0; 1573 } 1574 1575 // utility functions 1576 1577 /*! 1578 * \brief toFraction 1579 * \param decimal 1580 * \param numerator 1581 * \param denominator 1582 * Split a floating point value into numerator and denominator 1583 */ 1584 void toFraction(double decimal, double &numerator, double &denominator) 1585 { 1586 double dummy; 1587 double whole; 1588 decimal = modf (decimal, &whole); 1589 for (denominator = 1; denominator<=100; denominator++){ 1590 if (modf(denominator * decimal, &dummy) < 0.001f) 1591 break; 1592 } 1593 numerator = denominator * decimal; 1594 } 1595 1596 1597 /*! 1598 * \brief handleMessage 1599 * Handles gstreamer bus messages. Mainly for debugging purposes and ensuring clean shutdown on error 1600 */ 1601 void handleMessage(GstElement * pipeline) 1602 { 1603 CV_FUNCNAME("handlemessage"); 1604 1605 GError *err = NULL; 1606 gchar *debug = NULL; 1607 GstBus* bus = NULL; 1608 GstStreamStatusType tp; 1609 GstElement * elem = NULL; 1610 GstMessage* msg = NULL; 1611 1612 __BEGIN__; 1613 bus = gst_element_get_bus(pipeline); 1614 1615 while(gst_bus_have_pending(bus)) { 1616 msg = gst_bus_pop(bus); 1617 1618 //printf("Got %s message\n", GST_MESSAGE_TYPE_NAME(msg)); 1619 1620 if(gst_is_missing_plugin_message(msg)) 1621 { 1622 CV_ERROR(CV_StsError, "GStreamer: your gstreamer installation is missing a required plugin\n"); 1623 } 1624 else 1625 { 1626 switch (GST_MESSAGE_TYPE (msg)) { 1627 case GST_MESSAGE_STATE_CHANGED: 1628 GstState oldstate, newstate, pendstate; 1629 gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendstate); 1630 //fprintf(stderr, "state changed from %s to %s (pending: %s)\n", gst_element_state_get_name(oldstate), 1631 // gst_element_state_get_name(newstate), gst_element_state_get_name(pendstate)); 1632 break; 1633 case GST_MESSAGE_ERROR: 1634 gst_message_parse_error(msg, &err, &debug); 1635 fprintf(stderr, "GStreamer Plugin: Embedded video playback halted; module %s reported: %s\n", 1636 gst_element_get_name(GST_MESSAGE_SRC (msg)), err->message); 1637 1638 g_error_free(err); 1639 g_free(debug); 1640 1641 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL); 1642 break; 1643 case GST_MESSAGE_EOS: 1644 //fprintf(stderr, "reached the end of the stream."); 1645 break; 1646 case GST_MESSAGE_STREAM_STATUS: 1647 gst_message_parse_stream_status(msg,&tp,&elem); 1648 //fprintf(stderr, "stream status: elem %s, %i\n", GST_ELEMENT_NAME(elem), tp); 1649 break; 1650 default: 1651 //fprintf(stderr, "unhandled message %s\n",GST_MESSAGE_TYPE_NAME(msg)); 1652 break; 1653 } 1654 } 1655 gst_message_unref(msg); 1656 } 1657 1658 gst_object_unref(GST_OBJECT(bus)); 1659 1660 __END__ 1661 } 1662