Issue getting query position and duration

Hey, I am creating videoplayer that also tracks frame count, the problem I came to is when I change my videorate using

        g_object_set(videorate, "rate", newPlayRate, nullptr);

My frames are updated in this way:
If before update it was 100/500, now its 100/1000, I understand frames are doubled, but why wouldnt current frame position double as well. I wanted to fix this just using 100*(1/newPlayRate), but the problem comes when we seek, because after the seek frames would show correctly as 200/1000, with my new change of adding playrate its now 400/1000.

Seek code:

void HttpVideoPlayer::seekToFrame(gint64 seekTime) {
    qDebug() << "Seeking to frame at time:" << seekTime;
    if (seekTime < 0) seekTime = 0;
    if (pipeline) {
        gst_element_seek_simple(pipeline, GST_FORMAT_TIME,
                                static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_ACCURATE), seekTime);
    }
}

MaxFrame and Current Frame code:

gint64 HttpVideoPlayer::getCurrentFrame() {
    if (pipeline) {
        gint64 current;
        gst_element_query_position(pipeline, GST_FORMAT_TIME, &current);
        return current * fps / GST_SECOND;
    }
    return 0;
}

gint64 HttpVideoPlayer::getMaxFrame() {
    if (pipeline) {
        gint64 duration = 0;
        gst_element_query_duration(pipeline, GST_FORMAT_TIME, &duration);
        return duration * fps / GST_SECOND;
    }
    return 0;
}

Usually the way you would change the rate in a playback application is by passing it to the seek using gst_element_seek() - is there are a reason you’re not doing that and using a videorate element instead?

Hey!

Thank you for your response, I am getting my stream from http REST API server. For some reason using gst_element_seek() didnt actually do anything but said that it changed the videorate and than still played 30FPS

void HttpVideoPlayer::setupGStreamer(QString videoUri, WId xwinid) {
    qDebug() << "Setting up GStreamer with URI:" << videoUri;
    cleanupGStreamer();
    gst_init(nullptr, nullptr);

    pipeline = gst_pipeline_new("http-player");
    source = gst_element_factory_make("souphttpsrc", "http-source");
    GstElement *decodebin = gst_element_factory_make("decodebin", "decodebin");
    convert = gst_element_factory_make("videoconvert", "converter");
    crop = gst_element_factory_make("videocrop", "videocrop");
    scale = gst_element_factory_make("videoscale", "videoscale");
    sink = gst_element_factory_make("glimagesink", "video-output");
    GstElement *capsfilter = gst_element_factory_make("capsfilter", "capsfilter");
    videorate = gst_element_factory_make("videorate", "videorate");

    if (!pipeline || !source || !decodebin || !convert || !crop || !scale || !sink || !capsfilter || !videorate) {
        g_error("Failed to create elements");
    }

    GstCaps *caps = gst_caps_from_string("video/x-raw");
    if (!caps) {
        g_error("Failed to create caps");
    }

    g_object_set(capsfilter, "caps", caps, nullptr);
    gst_caps_unref(caps);

    g_object_set(videorate, "max-rate", 30, nullptr);

    gst_bin_add_many(GST_BIN(pipeline), source, decodebin, videorate, convert, capsfilter, crop, scale, sink, nullptr);
    g_signal_connect(decodebin, "pad-added", G_CALLBACK(on_pad_added), convert);

    if (!gst_element_link_many(convert, capsfilter, videorate, crop, scale, sink, nullptr)) {
        g_error("Failed to link elements");
    }

    qDebug() << "Setting source location to:" << videoUri;
    g_object_set(source, "location", videoUri.toStdString().c_str(), nullptr);

    if (!gst_element_link(source, decodebin)) {
        g_error("Failed to link source to decodebin");
    }

    qDebug() << "Setting video overlay window handle.";
    gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(sink), xwinid);

    GstPad *pad = gst_element_get_static_pad(convert, "sink");
    if (pad) {
        caps = gst_pad_get_current_caps(pad);
        if (caps) {
            const GstStructure *str = gst_caps_get_structure(caps, 0);
            if (gst_structure_get_fraction(str, "framerate", &fps_numerator, &fps_denominator)) {
                fps = static_cast<double>(fps_numerator) / fps_denominator;
            }
            gst_caps_unref(caps);
        }
        gst_object_unref(pad);
    }
    qDebug() << "GStreamer setup complete.";
}

Just to be sure, the framerate in the caps will always stay the same, no matter what rate you pass to the seek.

Only the sink element will act on the rate from the seek (which will be proxied back to the elements as part of a Segment event), and will interpret timestamps according to the rate then.

1 Like

Framerate should always be 30 anyway if i understand correctly, videorate double the frames on 0.5x so on 30 FPS its 0.5x cuz frames are doubled.

So I don’t seek on the pipeline I seek the sink to change framerate?

Okay that helped a lot, I need to send event to sink not pipeline, this fixed the problem:

GstEvent *seek_event = gst_event_new_instant_rate_change(newPlayRate,GST_SEGMENT_FLAG_NONE);
gst_element_send_event(sink, seek_event);