From e0a3b62e6bde08b983f19d5e02f4009a1ba09271 Mon Sep 17 00:00:00 2001 From: Derek Dai Date: Thu, 13 Apr 2017 10:55:50 +0800 Subject: [PATCH] miracle-dispd: put back gstreamer in this version, the pipeline can be stopped properly, but the gst & glib allocated memory can't be release cleanly (which won't accumulate) Change-Id: I242b106158db647070e7852b84f6f90b6bbd96f4 --- src/disp/dispd.c | 4 + src/disp/meson.build | 4 +- src/disp/wfd-out-session.c | 514 +++++++++++++++++++------------------ 3 files changed, 265 insertions(+), 257 deletions(-) diff --git a/src/disp/dispd.c b/src/disp/dispd.c index fc0e679..98a086e 100644 --- a/src/disp/dispd.c +++ b/src/disp/dispd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include "ctl.h" @@ -451,6 +452,8 @@ int main(int argc, char **argv) setlocale(LC_ALL, ""); setlocale(LC_TIME, "en_US.UTF-8"); + gst_init(&argc, &argv); + if(getenv("LOG_LEVEL")) { log_max_sev = log_parse_arg(getenv("LOG_LEVEL")); } @@ -518,6 +521,7 @@ disable_watchdog: unref_event: sd_event_unref(event); end: + gst_deinit(); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/disp/meson.build b/src/disp/meson.build index 920196d..b161451 100644 --- a/src/disp/meson.build +++ b/src/disp/meson.build @@ -1,5 +1,7 @@ inc = include_directories('../..', '../ctl',) -deps = [libsystemd, libmiracle_shared_dep] +gst1 = dependency('gstreamer-1.0') +gst1_base = dependency('gstreamer-base-1.0') +deps = [libsystemd, libmiracle_shared_dep, gst1, gst1_base] if readline.found() deps += [readline] endif diff --git a/src/disp/wfd-out-session.c b/src/disp/wfd-out-session.c index 33aab6c..c4858f4 100644 --- a/src/disp/wfd-out-session.c +++ b/src/disp/wfd-out-session.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "wfd-session.h" #include "shl_log.h" #include "rtsp.h" @@ -59,8 +61,8 @@ struct wfd_out_session uint32_t mask; char *audio_dev; - /*GstElement *pipeline;*/ - /*GstBus *bus;*/ + GstElement *pipeline; + GstBus *bus; }; static const struct rtsp_dispatch_entry out_session_rtsp_disp_tbl[]; @@ -320,17 +322,17 @@ void wfd_out_session_destroy(struct wfd_session *s) os->authority = NULL; } - /*if(os->bus) {*/ - /*gst_bus_remove_watch(os->bus);*/ - /*g_object_unref(os->bus);*/ - /*os->bus = NULL;*/ - /*}*/ + if(os->bus) { + gst_bus_remove_watch(os->bus); + g_object_unref(os->bus); + os->bus = NULL; + } - /*if(os->pipeline) {*/ - /*gst_element_set_state(os->pipeline, GST_STATE_NULL);*/ - /*g_object_unref(os->pipeline);*/ - /*os->pipeline = NULL;*/ - /*}*/ + if(os->pipeline) { + gst_element_set_state(os->pipeline, GST_STATE_NULL); + g_object_unref(os->pipeline); + os->pipeline = NULL; + } } int wfd_out_session_initiate_request(struct wfd_session *s) @@ -544,43 +546,43 @@ static int wfd_out_session_request_options(struct wfd_session *s, return 0; } -//static gboolean wfd_out_session_handle_gst_message(GstBus *bus, -// GstMessage *m, -// gpointer userdata) -//{ -// struct wfd_session *s = userdata; -// struct wfd_out_session *os = userdata; -// GstState old_state, new_state; -// -// switch(GST_MESSAGE_TYPE(m)) { -// case GST_MESSAGE_STATE_CHANGED: -// if(os->pipeline != (void *) GST_MESSAGE_SRC(m)) { -// break; -// } -// -// gst_message_parse_state_changed(m, &old_state, &new_state, NULL); -// if(GST_STATE_PLAYING == new_state) { -// log_info("stream is playing"); -// wfd_session_set_state(s, WFD_SESSION_STATE_PLAYING); -// } -// else if(GST_STATE_PLAYING == old_state && -// GST_STATE_PAUSED == new_state) { -// log_info("stream is paused"); -// wfd_session_set_state(s, WFD_SESSION_STATE_PAUSED); -// } -// break; -// case GST_MESSAGE_EOS: -// case GST_MESSAGE_ERROR: -// log_warning("%s encounter unexpected error or EOS", -// GST_MESSAGE_SRC_NAME(m)); -// wfd_session_teardown(s); -// break; -// default: -// break; -// } -// -// return TRUE; -//} +static gboolean wfd_out_session_handle_gst_message(GstBus *bus, + GstMessage *m, + gpointer userdata) +{ + struct wfd_session *s = userdata; + struct wfd_out_session *os = userdata; + GstState old_state, new_state; + + switch(GST_MESSAGE_TYPE(m)) { + case GST_MESSAGE_STATE_CHANGED: + if(os->pipeline != (void *) GST_MESSAGE_SRC(m)) { + break; + } + + gst_message_parse_state_changed(m, &old_state, &new_state, NULL); + if(GST_STATE_PLAYING == new_state) { + log_info("stream is playing"); + wfd_session_set_state(s, WFD_SESSION_STATE_PLAYING); + } + else if(GST_STATE_PLAYING == old_state && + GST_STATE_PAUSED == new_state) { + log_info("stream is paused"); + wfd_session_set_state(s, WFD_SESSION_STATE_PAUSED); + } + break; + case GST_MESSAGE_EOS: + case GST_MESSAGE_ERROR: + log_warning("%s encounter unexpected error or EOS", + GST_MESSAGE_SRC_NAME(m)); + wfd_session_teardown(s); + break; + default: + break; + } + + return TRUE; +} inline static char * uint16_to_str(uint16_t i, char *buf, size_t len) { @@ -596,188 +598,188 @@ inline static char * quote_str(const char *s, char *d, size_t len) return d; } -//static int wfd_out_session_create_pipeline(struct wfd_session *s) -//{ -// char rrtp_port[16], rrtcp_port[16], lrtcp_port[16]; -// char audio_dev[256]; -// char vsrc_param1[16] = "", vsrc_param2[16] = ""; -// char vsrc_param3[16] = "", vsrc_param4[16] = ""; -// struct wfd_out_session *os = wfd_out_session(s); -// GstElement *pipeline; -// GstElement *vsrc; -// GstBus *bus; -// GError *error = NULL; -// const char **tmp; -// int r; -// const char *pipeline_desc[128] = { -// "ximagesrc", -// "name=vsrc", -// "use-damage=false", -// "show-pointer=false", -// vsrc_param1, -// vsrc_param2, -// vsrc_param3, -// vsrc_param4, -// "!", "video/x-raw,", -// "framerate=30/1", -// //"!", "vaapipostproc", -// // "scale-method=2", /* high quality scaling mode */ -// // "format=3", /* yv12" */ -// //"!", "vaapih264enc", -// // "rate-control=1", -// // "num-slices=1", /* in WFD spec, one slice per frame */ -// // "max-bframes=0", /* in H264 CHP, no bframe supporting */ -// // "cabac=true", /* in H264 CHP, CABAC entropy codeing is supported, but need more processing to decode */ -// // "dct8x8=true", /* in H264 CHP, DTC is supported */ -// // "cpb-length=50", /* shortent buffer in order to decrease latency */ -// // "keyframe-period=30", -// // /* "bitrate=62500", */ /* the max bitrate of H264 level 4.2, crashing my dongle, let codec decide */ -// "!", "videoscale", -// "method=0", -// "!", "video/x-raw,", -// "width=1920,", -// "height=1080", -// "!", "videoconvert", -// "dither=0", -// "!", "video/x-raw,", -// "format=YV12" -// "!", "x264enc", -// "pass=4", /* constant quantizer */ -// "b-adapt=false", /* no bframe suppport in CHP */ -// "key-int-max=30", /* send IDR pictures per second */ -// "speed-preset=4", /* faster */ -// "tune=4", /* zero latency */ -// "!", "h264parse", -// "!", "video/x-h264,", -// "alignment=nal,", -// "stream-format=byte-stream" -// "!", "queue", -// "max-size-buffers=0", -// "max-size-bytes=0", -// "!", "mpegtsmux", -// "name=muxer", -// "!", "rtpmp2tpay", -// "!", ".send_rtp_sink_0", "rtpbin", -// "name=session", -// "do-retransmission=true", -// "do-sync-event=true", -// "do-lost=true", -// "ntp-time-source=3", -// "buffer-mode=0", -// "latency=20", -// "max-misorder-time=30", -// "!", "application/x-rtp", -// "!", "udpsink", -// "sync=false", -// "async=false", -// "host=", wfd_out_session_get_sink(s)->peer->remote_address, -// "port=", uint16_to_str(s->stream.rtp_port, rrtp_port,sizeof(rrtp_port)), -// "udpsrc", -// "address=", wfd_out_session_get_sink(s)->peer->local_address, -// "port=", uint16_to_str(LOCAL_RTCP_PORT, lrtcp_port,sizeof(lrtcp_port)), -// "reuse=true", -// "!", "session.recv_rtcp_sink_0", -// NULL -// }; -// -// if(s->stream.rtcp_port) { -// for(tmp = pipeline_desc; *tmp; ++tmp); -// *tmp ++ = "session.send_rtcp_src_0"; -// *tmp ++ = "!"; -// *tmp ++ = "udpsink"; -// *tmp ++ = "host="; -// *tmp ++ = wfd_out_session_get_sink(s)->peer->remote_address; -// *tmp ++ = "port="; -// *tmp ++ = uint16_to_str(s->stream.rtcp_port, rrtcp_port,sizeof(rrtcp_port)); -// *tmp ++ = "sync=false"; -// *tmp ++ = "async=false"; -// *tmp ++ = NULL; -// } -// -// if(*os->audio_dev) { -// for(tmp = pipeline_desc; *tmp; ++tmp); -// *tmp ++ = "pulsesrc"; -// *tmp ++ = "do-timestamp=true"; -// *tmp ++ = "client-name=miraclecast"; -// *tmp ++ = "device="; -// *tmp ++ = quote_str(os->audio_dev, audio_dev, sizeof(audio_dev)); -// *tmp ++ = "!"; -// *tmp ++ = "voaacenc"; -// *tmp ++ = "mark-granule=true"; -// *tmp ++ = "hard-resync=true"; -// *tmp ++ = "tolerance=40"; -// *tmp ++ = "!"; -// *tmp ++ = "audio/mpeg,"; -// *tmp ++ = "rate=48000,"; -// *tmp ++ = "channels=2,"; -// *tmp ++ = "stream-format=adts,"; -// *tmp ++ = "base-profile=lc"; -// *tmp ++ = "!"; -// *tmp ++ = "queue"; -// *tmp ++ = "max-size-buffers=0"; -// *tmp ++ = "max-size-bytes=0"; -// *tmp ++ = "max-size-time=0"; -// *tmp ++ = "!"; -// *tmp ++ = "muxer."; -// *tmp ++ = NULL; -// } -// -// /* bad pratice, but since we are in the same process, -// I think this is the only way to do it */ -// if(WFD_DISPLAY_TYPE_X == os->display_type) { -// r = setenv("XAUTHORITY", os->authority, 1); -// if(0 > r) { -// return r; -// } -// -// r = setenv("DISPLAY", os->display_name, 1); -// if(0 > r) { -// return r; -// } -// -// if(!os->display_param_name) { -// snprintf(vsrc_param1, sizeof(vsrc_param1), "startx=%hu", os->x); -// snprintf(vsrc_param2, sizeof(vsrc_param2), "starty=%hu", os->y); -// snprintf(vsrc_param3, sizeof(vsrc_param3), "endx=%d", os->x + os->width - 1); -// snprintf(vsrc_param4, sizeof(vsrc_param4), "endy=%d", os->y + os->height - 1); -// } -// else if(!strcmp("xid", os->display_param_name) || -// !strcmp("xname", os->display_param_name)) { -// snprintf(vsrc_param1, sizeof(vsrc_param1), -// "%s=\"%s\"", -// os->display_param_name, -// os->display_param_value); -// } -// } -// -// pipeline = gst_parse_launchv(pipeline_desc, &error); -// if(!pipeline) { -// if(error) { -// log_error("failed to create pipeline: %s", error->message); -// g_error_free(error); -// } -// return -1; -// } -// -// vsrc = gst_bin_get_by_name(GST_BIN(pipeline), "vsrc"); -// gst_base_src_set_live(GST_BASE_SRC(vsrc), true); -// g_object_unref(vsrc); -// vsrc = NULL; -// -// r = gst_element_set_state(pipeline, GST_STATE_PAUSED); -// if(GST_STATE_CHANGE_FAILURE == r) { -// g_object_unref(pipeline); -// return -1; -// } -// -// bus = gst_element_get_bus(pipeline); -// gst_bus_add_watch(bus, wfd_out_session_handle_gst_message, s); -// -// os->pipeline = pipeline; -// os->bus = bus; -// -// return 0; -//} +static int wfd_out_session_create_pipeline(struct wfd_session *s) +{ + char rrtp_port[16], rrtcp_port[16], lrtcp_port[16]; + char audio_dev[256]; + char vsrc_param1[16] = "", vsrc_param2[16] = ""; + char vsrc_param3[16] = "", vsrc_param4[16] = ""; + struct wfd_out_session *os = wfd_out_session(s); + GstElement *pipeline; + GstElement *vsrc; + GstBus *bus; + GError *error = NULL; + const char **tmp; + int r; + const char *pipeline_desc[128] = { + "ximagesrc", + "name=vsrc", + "use-damage=false", + "show-pointer=false", + vsrc_param1, + vsrc_param2, + vsrc_param3, + vsrc_param4, + "!", "video/x-raw,", + "framerate=30/1", + //"!", "vaapipostproc", + // "scale-method=2", /* high quality scaling mode */ + // "format=3", /* yv12" */ + //"!", "vaapih264enc", + // "rate-control=1", + // "num-slices=1", /* in WFD spec, one slice per frame */ + // "max-bframes=0", /* in H264 CHP, no bframe supporting */ + // "cabac=true", /* in H264 CHP, CABAC entropy codeing is supported, but need more processing to decode */ + // "dct8x8=true", /* in H264 CHP, DTC is supported */ + // "cpb-length=50", /* shortent buffer in order to decrease latency */ + // "keyframe-period=30", + // /* "bitrate=62500", */ /* the max bitrate of H264 level 4.2, crashing my dongle, let codec decide */ + "!", "videoscale", + "method=0", + "!", "video/x-raw,", + "width=1920,", + "height=1080", + "!", "videoconvert", + "dither=0", + "!", "video/x-raw,", + "format=YV12" + "!", "x264enc", + "pass=4", /* constant quantizer */ + "b-adapt=false", /* no bframe suppport in CHP */ + "key-int-max=30", /* send IDR pictures per second */ + "speed-preset=4", /* faster */ + "tune=4", /* zero latency */ + "!", "h264parse", + "!", "video/x-h264,", + "alignment=nal,", + "stream-format=byte-stream" + "!", "queue", + "max-size-buffers=0", + "max-size-bytes=0", + "!", "mpegtsmux", + "name=muxer", + "!", "rtpmp2tpay", + "!", ".send_rtp_sink_0", "rtpbin", + "name=session", + "do-retransmission=true", + "do-sync-event=true", + "do-lost=true", + "ntp-time-source=3", + "buffer-mode=0", + "latency=20", + "max-misorder-time=30", + "!", "application/x-rtp", + "!", "udpsink", + "sync=false", + "async=false", + "host=", wfd_out_session_get_sink(s)->peer->remote_address, + "port=", uint16_to_str(s->stream.rtp_port, rrtp_port,sizeof(rrtp_port)), + "udpsrc", + "address=", wfd_out_session_get_sink(s)->peer->local_address, + "port=", uint16_to_str(LOCAL_RTCP_PORT, lrtcp_port,sizeof(lrtcp_port)), + "reuse=true", + "!", "session.recv_rtcp_sink_0", + NULL + }; + + if(s->stream.rtcp_port) { + for(tmp = pipeline_desc; *tmp; ++tmp); + *tmp ++ = "session.send_rtcp_src_0"; + *tmp ++ = "!"; + *tmp ++ = "udpsink"; + *tmp ++ = "host="; + *tmp ++ = wfd_out_session_get_sink(s)->peer->remote_address; + *tmp ++ = "port="; + *tmp ++ = uint16_to_str(s->stream.rtcp_port, rrtcp_port,sizeof(rrtcp_port)); + *tmp ++ = "sync=false"; + *tmp ++ = "async=false"; + *tmp ++ = NULL; + } + + if(*os->audio_dev) { + for(tmp = pipeline_desc; *tmp; ++tmp); + *tmp ++ = "pulsesrc"; + *tmp ++ = "do-timestamp=true"; + *tmp ++ = "client-name=miraclecast"; + *tmp ++ = "device="; + *tmp ++ = quote_str(os->audio_dev, audio_dev, sizeof(audio_dev)); + *tmp ++ = "!"; + *tmp ++ = "voaacenc"; + *tmp ++ = "mark-granule=true"; + *tmp ++ = "hard-resync=true"; + *tmp ++ = "tolerance=40"; + *tmp ++ = "!"; + *tmp ++ = "audio/mpeg,"; + *tmp ++ = "rate=48000,"; + *tmp ++ = "channels=2,"; + *tmp ++ = "stream-format=adts,"; + *tmp ++ = "base-profile=lc"; + *tmp ++ = "!"; + *tmp ++ = "queue"; + *tmp ++ = "max-size-buffers=0"; + *tmp ++ = "max-size-bytes=0"; + *tmp ++ = "max-size-time=0"; + *tmp ++ = "!"; + *tmp ++ = "muxer."; + *tmp ++ = NULL; + } + + /* bad pratice, but since we are in the same process, + I think this is the only way to do it */ + if(WFD_DISPLAY_TYPE_X == os->display_type) { + r = setenv("XAUTHORITY", os->authority, 1); + if(0 > r) { + return r; + } + + r = setenv("DISPLAY", os->display_name, 1); + if(0 > r) { + return r; + } + + if(!os->display_param_name) { + snprintf(vsrc_param1, sizeof(vsrc_param1), "startx=%hu", os->x); + snprintf(vsrc_param2, sizeof(vsrc_param2), "starty=%hu", os->y); + snprintf(vsrc_param3, sizeof(vsrc_param3), "endx=%d", os->x + os->width - 1); + snprintf(vsrc_param4, sizeof(vsrc_param4), "endy=%d", os->y + os->height - 1); + } + else if(!strcmp("xid", os->display_param_name) || + !strcmp("xname", os->display_param_name)) { + snprintf(vsrc_param1, sizeof(vsrc_param1), + "%s=\"%s\"", + os->display_param_name, + os->display_param_value); + } + } + + pipeline = gst_parse_launchv(pipeline_desc, &error); + if(!pipeline) { + if(error) { + log_error("failed to create pipeline: %s", error->message); + g_error_free(error); + } + return -1; + } + + vsrc = gst_bin_get_by_name(GST_BIN(pipeline), "vsrc"); + gst_base_src_set_live(GST_BASE_SRC(vsrc), true); + g_object_unref(vsrc); + vsrc = NULL; + + r = gst_element_set_state(pipeline, GST_STATE_PAUSED); + if(GST_STATE_CHANGE_FAILURE == r) { + g_object_unref(pipeline); + return -1; + } + + bus = gst_element_get_bus(pipeline); + gst_bus_add_watch(bus, wfd_out_session_handle_gst_message, s); + + os->pipeline = pipeline; + os->bus = bus; + + return 0; +} static int wfd_out_session_handle_pause_request(struct wfd_session *s, struct rtsp_message *req, @@ -786,10 +788,10 @@ static int wfd_out_session_handle_pause_request(struct wfd_session *s, _rtsp_message_unref_ struct rtsp_message *m = NULL; int r; -// r = gst_element_set_state(wfd_out_session(s)->pipeline, GST_STATE_READY); -// if(GST_STATE_CHANGE_FAILURE == r) { -// return -1; -// } + r = gst_element_set_state(wfd_out_session(s)->pipeline, GST_STATE_READY); + if(GST_STATE_CHANGE_FAILURE == r) { + return -1; + } r = rtsp_message_new_reply_for(req, &m, @@ -813,18 +815,18 @@ static int wfd_out_session_handle_teardown_request(struct wfd_session *s, int r; wfd_session_set_state(s, WFD_SESSION_STATE_TEARING_DOWN); - /*gst_element_set_state(wfd_out_session(s)->pipeline, GST_STATE_NULL);*/ + gst_element_set_state(wfd_out_session(s)->pipeline, GST_STATE_NULL); - /*r = rtsp_message_new_reply_for(req,*/ - /*&m,*/ - /*RTSP_CODE_OK,*/ - /*NULL);*/ - /*if(0 > r) {*/ - /*return r;*/ - /*}*/ + r = rtsp_message_new_reply_for(req, + &m, + RTSP_CODE_OK, + NULL); + if(0 > r) { + return r; + } - /**out_rep = m;*/ - /*m = NULL;*/ + *out_rep = m; + m = NULL; return 0; } @@ -834,17 +836,17 @@ static int wfd_out_session_post_handle_play(sd_event_source *source, void *userdata) { struct wfd_session *s = userdata; - //GstStateChangeReturn r; + GstStateChangeReturn r; sd_event_source_unref(source); wfd_out_session(s)->gst_launch_source = NULL; - /*r = gst_element_set_state(wfd_out_session(s)->pipeline,*/ - /*GST_STATE_PLAYING);*/ - /*if(GST_STATE_CHANGE_FAILURE == r) {*/ - /*wfd_session_teardown(s);*/ - /*return -1;*/ - /*}*/ + r = gst_element_set_state(wfd_out_session(s)->pipeline, + GST_STATE_PLAYING); + if(GST_STATE_CHANGE_FAILURE == r) { + wfd_session_teardown(s); + return -1; + } wfd_session_set_state(s, WFD_SESSION_STATE_PLAYING); @@ -972,10 +974,10 @@ static int wfd_out_session_handle_setup_request(struct wfd_session *s, return r; } - /*r = wfd_out_session_create_pipeline(s);*/ - /*if(0 > r) {*/ - /*return r;*/ - /*}*/ + r = wfd_out_session_create_pipeline(s); + if(0 > r) { + return r; + } *out_rep = m; m = NULL;