I am working on a project to put in GPU memory frames (1920x1080, at ~25 fps) into HDMI port, as a starting point the above code from #Honey_Patouceul definitely works. However i have a relative large latency on the pipe line. To investigate it, my first step is to get rid of opencv, hence i have the following two versions:
using namespace ngv;
using namespace cv;
using namespace std;
#define MY_IMG_W 1280
#define MY_IMG_H 720
#define MY_FPS 25
#define FEED_BY_CB 0
#define USE_GBUF_HD_COPY 1
GstClockTime g_timestamp=0;
const guint64 g_frmIntreval = gst_util_uint64_scale_int(1, GST_SECOND, MY_FPS);
const uint32_t g_rgbImgSz = MY_IMG_H * MY_IMG_W * 3;
static void cb_need_data(GstElement *appsrc, guint unused, void *data)
{
static int fn = 0;
GstBuffer *buffer = 0;
GstFlowReturn ret;
boost::posix_time::ptime t0 = POSIX_LOCAL_TIME;
std::string ts0 = "fn=" + std::to_string(fn) + ", ts0=" + getPrettyTimeStamp(t0 );
//generate a noise image
cv::Mat I(MY_IMG_H, MY_IMG_W, CV_8UC3);
cv::randu(I, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));
cv::putText(I, "fn=" + std::to_string(fn), cv::Point(10, 200), cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);
//copy <I> to <buffer>
#if USE_GBUF_HD_COPY
buffer = gst_buffer_new_allocate(NULL, g_rgbImgSz, NULL);
gst_buffer_fill(buffer, 0, (gconstpointer)I.data, (gsize)g_rgbImgSz);
#else
buffer = gst_buffer_new_wrapped( (gpointer)I.data, g_rgbImgSz);
#endif
/* increment the g_timestamp every 1/25 second */
GST_BUFFER_PTS(buffer) = g_timestamp;
GST_BUFFER_DURATION(buffer) = g_frmIntreval;
g_timestamp += g_frmIntreval;
g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
#if USE_GBUF_HD_COPY
gst_buffer_unref(buffer);
#endif
printf("%s, ts1=%s, ts1-ts0=%d, g_timestamp=%lld\n", ts0.c_str(), getPrettyTimeStamp().c_str(), timeIntervalMillisec(t0), g_timestamp);
fn++;
}
int test_hdmi_out_v1(int argc, char *argv[])
{
/* init GStreamer */
gst_init(&argc, &argv);
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
/* setup pipeline */
GstElement *pipeline = gst_parse_launch("appsrc name=mySrc ! video/x-raw, format=(string)BGR, width=(int)1280, height=(int)720 ! \
videoconvert ! video/x-raw, format=(string)I420, framerate=(fraction)25/1 ! \
nvvidconv ! video/x-raw(memory:NVMM) ! \
nvoverlaysink", NULL);
GstElement *appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "mySrc");
g_assert(appsrc!= 0);
/* setup appsrc */
g_object_set(G_OBJECT(appsrc), "format", GST_FORMAT_TIME, NULL);
g_signal_connect(appsrc, "need-data", G_CALLBACK(cb_need_data), NULL);
/* play */
gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_print("PLAY\n");
g_main_loop_run(loop);
/* clean up */
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
g_main_loop_unref(loop);
return 0;
}
int test_hdmi_out_v2(int argc, char *argv[])
{
/* init GStreamer */
gst_init(&argc, &argv);
/* setup pipeline */
GstElement *pipeline = gst_parse_launch("appsrc name=mySrc ! video/x-raw, format=(string)BGR, width=(int)1280, height=(int)720 ! \
videoconvert ! video/x-raw, format=(string)I420, framerate=(fraction)25/1 ! \
nvvidconv ! video/x-raw(memory:NVMM) ! \
nvoverlaysink", NULL);
GstElement *appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "mySrc");
g_assert(appsrc != 0);
/* setup appsrc */
g_object_set(G_OBJECT(appsrc), "format", GST_FORMAT_TIME, NULL);
//g_signal_connect(appsrc, "need-data", G_CALLBACK(cb_need_data), NULL);
/* play */
GstStateChangeReturn state_ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
g_assert(state_ret == GST_STATE_CHANGE_ASYNC);
g_print("PLAY\n");
//Push the data from buffer to gstpipeline 100 times
g_timestamp = 0;
#if USE_GBUF_HD_COPY
GstBuffer *pushbuffer = gst_buffer_new_allocate(NULL, g_rgbImgSz, NULL);
#endif
for (int i = 0; i < 1000; i++) {
cout << "---- i =" << i << "-----------" << endl;
//generate a noise image
cv::Mat I(MY_IMG_H, MY_IMG_W, CV_8UC3);
cv::randu(I, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));
cv::putText(I, "fn=" + std::to_string(i), cv::Point(10, 200), cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);
//ngv::myImgWrite(I, "./", "tmp", i, 0);
#if USE_GBUF_HD_COPY
gst_buffer_fill(pushbuffer, 0, (gconstpointer)I.data, g_rgbImgSz);
#else
GstBuffer *pushbuffer = gst_buffer_new_wrapped(I.data, g_rgbImgSz); //Wrap the data
#endif
GST_BUFFER_PTS(pushbuffer) = g_timestamp;
GST_BUFFER_DTS(pushbuffer) = g_timestamp;
GST_BUFFER_DURATION(pushbuffer) = g_frmIntreval;
g_timestamp += g_frmIntreval;
GstFlowReturn ret = gst_app_src_push_buffer((GstAppSrc*)appsrc, pushbuffer); //Push data into pipeline
g_assert(ret == GST_FLOW_OK);
cout << "i =" << i << ",g_timestamp=" << g_timestamp << ",refCnt=" << pushbuffer->mini_object.refcount << endl;
}
// clean up
#if USE_GBUF_HD_COPY
gst_buffer_unref(pushbuffer);
#endif
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
return 0;
}
the callback version test_hdmi_out_v1() works as expected, but the for-loop version test_hdmi_out_v2() pops up a run-time-error:
PLAY
---- i =0 --------------
i =0,g_timestamp=40000000,refCnt1
---- i =1 --------------
i =1,g_timestamp=80000000,refCnt1
---- i =2 --------------
**
ERROR:gstappsrc.c:1225:gst_app_src_create: assertion failed: (GST_IS_BUFFER_LIST (obj))
Aborted (core dumped)
What i missed in test_hdmi_out_v2()? Thank you very much!