2

I've written C++ wrapper for each Gstreamer types. They're simple and intuitive, so I don't think their implementation needs to be posted here (though I could post them (maybe at github) if need arises).

The problem I'm facing is that I start playing a video (and simulteneously saving it to a file using gst tee element)....and while it is playing, I pause (from different thread) which is working great. However, when I want to resume it, it doesn't work:

void pause()
{
    _pipeline.state(GST_STATE_PAUSED)
}

void resume()
{
    _pipeline.state(GST_STATE_PLAYING);
}

And here is the play() function where I create the pipeline and set it state to GST_STATE_PLAYING.

int play(std::string const & source_path, std::string const & save_as_file)
{
    gst::element source(source_path.substr(0,4) == "http" ? "souphttpsrc" : "filesrc", "media-source");
    gst::element demuxer("decodebin", "decoder");
    gst::element vconv("videoconvert",  "vconverter");
    gst::element vsink("autovideosink", "video-output");
    gst::element aconv("audioconvert",  "aconverter");
    gst::element asink("autoaudiosink", "audio-output");
    gst::element filesink("filesink", "file-sink");
    gst::element fq("queue", "file-queqe");
    gst::element tee("tee", "media-tee");
    gst::element aq("queue", "audio-queue");
    gst::element vq("queue", "video-queue");

    source.set("location", source_path.c_str());

    gst::bus bus = _pipeline.bus();
    guint bus_watch_id = _session.add_watch(bus);


    _pipeline.add(source, demuxer, vconv, vsink, aconv, asink, filesink, tee,fq, aq, vq);

    gst::element::link(source, tee); 

    gst::element::link(vq, vconv, vsink);
    gst::element::link(aq, aconv, asink);

    gst::pad tee_src_pad = tee.request_pad("src_%u");
    gst::pad demuxer_sink_pad = demuxer.static_pad("sink");

    gst::pad::link(tee_src_pad, demuxer_sink_pad);

    filesink.set("location",  save_as_file.c_str());

    gst::element::link(fq, filesink);

    gst::pad tee_src_pad2 = tee.request_pad("src_%u");
    gst::pad fq_pad = fq.static_pad ("sink");
    gst::pad::link(tee_src_pad2, fq_pad);

    gst::element::dynamic_link(demuxer, aq);
    gst::element::dynamic_link(demuxer, vq);

    g_print ("Now playing: %s\n", source_path.c_str());
    _pipeline.state(GST_STATE_PLAYING);

    //code
    _session.run()

    //cleanup
}

I'd appreciate if anybody could help me figuring out the solution to this problem.

I'm playing the video on Qt widget using its handle and passing it to gstreamer video overlay.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Does the actual state of the pipeline change back to playing? If it does, you probably need to re-expose the overlay. – Benjamin Trent Dec 18 '14 at 14:54
  • Can you test your pause/resume code on a simpler pipeline, like a simple videotestsrc with no tee? My experience with GStreamer is that the more complicated your pipelines get, the less likely it is that simple commands like this will work. There's just too many ways of interpreting what "pause" should do across all the elements. e.g. how should an mp4 muxer handle timestamps on a paused stream? Create a long-running frozen frames, or pick up right where the user left off? Then what if you have mp4 muxer and matroska muxer implementing different behaviors? That kind of thing. – mpr Dec 19 '14 at 14:45
  • @mpr: Wow... If I remove the tee element (along with the entire branch), then pause/resume works just great. I also noticed when I use tee, then pause does pause the play, but it doesn't change the state (the state remains at *_PLAYING) – Nawaz Dec 19 '14 at 16:09
  • @BenjaminTrent: No it doesn't change any state. Even when I do pause, it doens't change the state to *_PAUSED. The state stays at *_PLAYING. But pause/resume works great (and the state changes too) if I remove tee element. – Nawaz Dec 19 '14 at 16:11
  • @mpr: Any idea what things I should try? – Nawaz Dec 19 '14 at 16:25
  • 1
    Do you really think it may be a Qt issue? If not, can you please remove the Qt tag? What worries me is that I do not see a single `Q` in your snippet. – László Papp Dec 23 '14 at 20:22

2 Answers2

5

Do you have queue elements on each branch that comes off the tee? One for the file and one for the decode? You could also try messing around with the "sync" property on the filesink. Maybe set it to true.


Edited by Nawaz.

Since this answer first gave me few directions and almost a very-near-to-the-solution direction, it deserves the bounty I've set for this question. But before that, here is the solution (explanation is in my answer - Nawaz).

gst::element filesink("filesink", "file-sink");
filesink.set("async", gboolean(FALSE));

Hope that helps other as well.

Community
  • 1
  • 1
mpr
  • 3,250
  • 26
  • 44
  • The branch becomes like this: `source -> tee` (at tee become two branches). then `tee-> demuxer -> audio-queue/video-queue-> audio-sink/video-sink`. and other branch, `tee-> file-queue->file-sink`. – Nawaz Dec 19 '14 at 16:55
  • BTW, I don't see `sync` property on the filesink: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-plugins/html/gstreamer-plugins-filesink.html – Nawaz Dec 19 '14 at 17:00
  • Add a queue between tee and demuxer. – mpr Dec 19 '14 at 17:07
  • Okay. Let me try that. BTW could you explain why you think that is needed? – Nawaz Dec 19 '14 at 17:16
  • Honestly I don't know, but the docs say to structure your tees that way. http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-plugins/html/gstreamer-plugins-tee.html – mpr Dec 19 '14 at 17:20
  • I didn't quite understand it. Maybe I should take time to explore this part as well. Right now I'm just trying to add a queue between tee and demuxer (I don't know how I should do that though). – Nawaz Dec 19 '14 at 17:24
  • The same way you did the queue between the tee and the filesink. By the way it seems a little odd that you're directly referencing the pads. There are methods for linking the elements automatically. – mpr Dec 19 '14 at 17:31
  • I did inserted a queue between tee and demuxer. But the problem is same. BTW, how should I link tee with queues automatically? I don't know that. – Nawaz Dec 19 '14 at 17:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67346/discussion-between-mpr-and-nawaz). – mpr Dec 19 '14 at 17:48
5

I found the answer myself. I tried to print the status of all the elements as:

void print_status_of_all()
{
    auto it  = gst_bin_iterate_elements(GST_BIN(_pipeline.raw()));
    GValue value = G_VALUE_INIT;
    for(GstIteratorResult r = gst_iterator_next(it, &value); r != GST_ITERATOR_DONE; r = gst_iterator_next(it, &value))
    {
         if ( r == GST_ITERATOR_OK )
         {
             GstElement *e = static_cast<GstElement*>(g_value_peek_pointer(&value));
             GstState  current, pending;
             auto ret = gst_element_get_state(e, &current, &pending, 100000);
             g_print("%s(%s), status = %s, pending = %s\n", G_VALUE_TYPE_NAME(&value), gst_element_get_name(e), gst_element_state_get_name(current), gst_element_state_get_name(pending));
         }
    }
}

And then it helped me figured out that the status of all the elements were changing from PLAYING to PAUSED and PAUSED to PLAYING, without any pending state, except filesink element whose state remains at PLAYING and a pending PAUSED state (which is because it attempts to change it asynchronously) — that eventually led me to the async property of the GstBaseSink which is the base class of filesink. I just set it to FALSE as:

gst::element filesink("filesink", "file-sink");
filesink.set("async", gboolean(FALSE));

That's it. Now pause and resume works great — status of all the elements change to PLAYING to PAUSED and PAUSED to PLAYING, without any pending state!

Nawaz
  • 353,942
  • 115
  • 666
  • 851