Hey, I am trying to create an app that will transfer filesrc from one pc to another using udpsrc, because I cant save files there I had to save it to RAM, the video I get will be played after its fully received and should be able to go forward and jump to frames, pause/play.
This is my pipeline for sending filesrc:
gst-launch-1.0 filesrc location="C:/2024_03_08_09_36_12_627_[327]/video.avi" ! decodebin ! videoconvert ! x264enc ! rtph264pay config-interval=1 pt=96 ! udpsink host=228.1.1.1 port=9000
My Receiver:
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
#include <iostream>
#include <vector>
#include <QLabel>
#include "receiver.h"
QLabel* receiver::m_label = nullptr;
std::vector<GstSample*> receiver::m_bufferVector;
// TODO data is saved raw, takes up a lot of memory
GstFlowReturn receiver::on_new_sample(GstAppSink *appsink, gpointer user_data) {
// Cast user_data back to reciever instance
receiver* self = static_cast<receiver*>(user_data);
// Retrieve the sample
GstSample *sample = gst_app_sink_pull_sample(appsink);
if (!sample) {
return GST_FLOW_ERROR;
}
// Save the sample into the buffer vector of the receiver instance
self->m_bufferVector.push_back(sample);
m_label->setText(QString::fromStdString("Frames: " + std::to_string(receiver::m_bufferVector.size())));
return GST_FLOW_OK;
}
void receiver::receive(QLabel *label) {
gst_init(nullptr, nullptr);
m_label = label;
// Include an H.264 encoder before the appsink to compress frames
std::string pipeline_str = "udpsrc address=228.1.1.1 port=9000 caps=\"application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264\" ! rtph264depay ! queue ! avdec_h264 ! videoconvert ! x264enc tune=zerolatency ! appsink name=sink";
GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), nullptr);
if (!pipeline) {
std::cerr << "Failed to create pipeline" << std::endl;
return;
}
// Get the appsink element
GstElement *sink = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
// Setup appsink
gst_app_sink_set_emit_signals((GstAppSink*)sink, true);
gst_app_sink_set_drop((GstAppSink*)sink, true);
gst_app_sink_set_max_buffers((GstAppSink*)sink, 1);
// Connect the "new-sample" signal to the callback function
g_signal_connect(sink, "new-sample", G_CALLBACK(receiver::on_new_sample), this);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
GMainLoop *main_loop = g_main_loop_new(nullptr, FALSE);
g_main_loop_run(main_loop);
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
g_main_loop_unref(main_loop);
}
My Player:
#include "player.h"
#include <gst/gst.h>
void player::play(const std::vector<GstSample*>& m_bufferVector) {
gst_init(nullptr, nullptr);
// Define a pipeline that uses appsrc as the source element for H.264 compressed frames
GstElement* pipeline = gst_parse_launch("appsrc name=source ! h264parse ! avdec_h264 ! videoconvert ! autovideosink", nullptr);
GstElement* appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "source");
// Configure appsrc's caps according to the video data you will push into it
g_object_set(G_OBJECT(appsrc), "caps",
gst_caps_new_simple("video/x-h264",
"stream-format", G_TYPE_STRING, "byte-stream",
"alignment", G_TYPE_STRING, "au",
nullptr),
nullptr);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
// Frame duration in nanoseconds for 30 FPS
const GstClockTime frame_duration = GST_SECOND / 30;
GstClockTime timestamp = 0; // Start timestamp
// Push each buffer to the appsrc element
for (GstSample* sample : m_bufferVector) {
GstBuffer* buffer = gst_sample_get_buffer(sample);
GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT);
if (buffer != nullptr) {
// Set buffer timestamp
GST_BUFFER_PTS(buffer) = timestamp;
GST_BUFFER_DURATION(buffer) = frame_duration;
timestamp += frame_duration; // Increment timestamp for the next frame
GstFlowReturn ret;
g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
if (ret != GST_FLOW_OK) {
// Log the error or handle it accordingly
break; // Exit the loop or handle the error as needed
}
}
}
// Wait until pipeline is finished
GstStateChangeReturn state_ret = gst_element_get_state(pipeline, nullptr, nullptr, GST_CLOCK_TIME_NONE);
// Ensure the state change was successful
if (state_ret == GST_STATE_CHANGE_FAILURE) {
// Handle state change failure, such as by logging or cleanup
}
// Clean up
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
}
The problem I have come to is my player plays only first frame.