diff --git a/demo/wfdctl.vala b/demo/wfdctl.vala index 5dd0bd8..bfba80e 100644 --- a/demo/wfdctl.vala +++ b/demo/wfdctl.vala @@ -516,7 +516,8 @@ private class WfdCtl : GLib.Application } }); Error error = null; - uint id = Timeout.add_seconds(10, () => { + var timeout_src = new TimeoutSource(10); + timeout_src.set_callback(() => { error = new WfdCtlError.TIMEOUT("failed to establish session"); Idle.add(establish_session.callback); return false; @@ -524,7 +525,7 @@ private class WfdCtl : GLib.Application yield; - Source.remove(id); + timeout_src.destroy(); if(null != error) { throw error; } diff --git a/res/gstencoder.vala b/res/gstencoder.vala new file mode 100644 index 0000000..1b11ffa --- /dev/null +++ b/res/gstencoder.vala @@ -0,0 +1,169 @@ +public enum GstEncoderConfig +{ + DISPLAY_SYSTEM, /* string */ + DISPLAY_NAME, /* string */ + MONITOR, /* uint32 */ + TOP, /* uint32 */ + LEFT, /* uint32 */ + WIDTH, /* uint32 */ + HEIGHT, /* uint32 */ + WINDOW_ID, /* uint32 */ + FRAMERATE, /* uint32 */ + SCALE_WIDTH, /* uint32 */ + SCALE_HEIGHT, /* uint32 */ + RTP_PORT1, /* uint32 */ + RTP_PORT2, /* uint32 */ + RTCP_PORT, /* uint32 */ + H264_PROFILE, + H264_LEVEL, + DEBUG_LEVEL, +} + +[DBus (name = "org.freedesktop.miracle.encoder.error")] +public errordomain GstEncoderError +{ + UNEXPECTED_EOS, + ENCODER_ERROR, + INVALID_STATE, +} + +[DBus(name = "org.freedesktop.miracle.encoder")] +public interface GstEncoder : GLib.Object +{ + public enum State + { + NULL, + CONFIGURED, + READY, + STARTED, + PAUSED, + } + + public const string OBJECT_PATH = "/org/freedesktop/miracle/encoder"; + + public abstract State state { get; protected set; default = State.NULL; } + + public abstract void configure(HashTable configs) throws GstEncoderError; + public abstract void start() throws GstEncoderError; + public abstract void pause() throws GstEncoderError; + public abstract void stop() throws GstEncoderError; +} + +internal class EncoderImpl : GstEncoder, GLib.Object +{ + private DBusConnection conn; + private HashTable configs; + private Gst.Element pipeline; + private Gst.State pipeline_state = Gst.State.NULL; + + public GstEncoder.State state { get; private set; } + + public void configure(HashTable configs) throws GstEncoderError + { + if(GstEncoder.State.NULL != state) { + throw new GstEncoderError.INVALID_STATE("already configured"); + } + + try { + pipeline = Gst.parse_launch("""videotestsrc ! autovideosink"""); + } + catch(Error e) { + throw new GstEncoderError.ENCODER_ERROR("%s", e.message); + } + + pipeline.set_state(Gst.State.READY); + var bus = pipeline.get_bus(); + bus.add_signal_watch(); + bus.message.connect(on_pipeline_message); + + this.configs = configs; + state = GstEncoder.State.CONFIGURED; + } + + public void start() throws GstEncoderError + { + check_configs(); + + pipeline.set_state(Gst.State.PLAYING); + } + + public void pause() throws GstEncoderError + { + check_configs(); + + pipeline.set_state(Gst.State.PAUSED); + } + + public void stop() throws GstEncoderError + { + if(null == pipeline) { + return; + } + + pipeline.set_state(Gst.State.NULL); + pipeline = null; + } + + public async void prepare() throws Error + { + conn = yield Bus.get(BusType.SESSION); + conn.register_object(GstEncoder.OBJECT_PATH, this as GstEncoder); + + /* we are ready, tell parent how to communicate with us */ + stderr.printf("\nunique-name: %s\n", conn.unique_name); + stderr.flush(); + } + + private void check_configs() throws GstEncoderError + { + if(null == configs || null == pipeline) { + throw new GstEncoderError.INVALID_STATE("not configure yet"); + } + } + + private void on_pipeline_message(Gst.Message m) + { + if(m.src != pipeline) { + return; + } + + switch(m.type) { + case Gst.MessageType.EOS: + break; + case Gst.MessageType.STATE_CHANGED: + Gst.State oldstate; + m.parse_state_changed(out oldstate, out pipeline_state, null); + debug("state chagned from %s to %s", + oldstate.to_string(), + pipeline_state.to_string()); + break; + default: + debug("unhandled message: %s", m.type.to_string()); + break; + } + } +} + +private MainLoop loop; + +int main(string[] argv) +{ + Gst.init(ref argv); + + var encoder = new EncoderImpl(); + encoder.prepare.begin((o, r) => { + try { + encoder.prepare.end(r); + } + catch(Error e) { + error("%s", e.message); + } + }); + + loop = new MainLoop(); + loop.run(); + + Gst.deinit(); + + return 0; +} diff --git a/res/meson.build b/res/meson.build index 8d2b0df..324b833 100644 --- a/res/meson.build +++ b/res/meson.build @@ -42,3 +42,11 @@ install_data( install_data('miracle-gst', 'gstplayer', 'uibc-viewer', install_dir: get_option('bindir'), install_mode: 'rwxr-xr-x') + +add_languages('vala') +gio2 = dependency('gio-2.0') +gst1 = dependency('gstreamer-1.0') +gst1_base = dependency('gstreamer-base-1.0') +executable('gstencoder', 'gstencoder.vala', + dependencies: [gst1, gst1_base, gio2], + install: true) diff --git a/src/disp/disp.h b/src/disp/disp.h index 9326b64..deaf5b5 100644 --- a/src/disp/disp.h +++ b/src/disp/disp.h @@ -143,8 +143,6 @@ struct ctl_wfd struct shl_htable sessions; size_t n_sessions; unsigned int id_pool; - - sd_event_source *signal_sources[4]; }; struct ctl_wfd * ctl_wfd_get(); @@ -160,6 +158,7 @@ int ctl_wfd_remove_session_by_id(struct ctl_wfd *wfd, struct wfd_session **out); void ctl_wfd_shutdown(struct ctl_wfd *wfd); unsigned int ctl_wfd_alloc_session_id(struct ctl_wfd *wfd); +void ctl_wfd_shutdown(struct ctl_wfd *wfd); static inline struct sd_event * ctl_wfd_get_loop() { return ctl_wfd_get()->loop; diff --git a/src/disp/dispd.c b/src/disp/dispd.c index 98a086e..ae0232b 100644 --- a/src/disp/dispd.c +++ b/src/disp/dispd.c @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include #include #include #include "ctl.h" @@ -77,8 +78,6 @@ error: static void ctl_wfd_free(struct ctl_wfd *wfd) { - int i; - if(!wfd) { return; } @@ -88,13 +87,6 @@ static void ctl_wfd_free(struct ctl_wfd *wfd) shl_htable_clear_str(&wfd->sinks, NULL, NULL); shl_htable_clear_uint(&wfd->sessions, NULL, NULL); - for(i = 0; i < SHL_ARRAY_LENGTH(wfd->signal_sources); ++ i) { - if(wfd->signal_sources[i]) { - sd_event_source_set_enabled(wfd->signal_sources[i], SD_EVENT_OFF); - sd_event_source_unref(wfd->signal_sources[i]); - } - } - if(wfd->loop) { sd_event_unref(wfd->loop); } @@ -108,9 +100,6 @@ static int ctl_wfd_handle_shutdown(sd_event_source *s, { struct ctl_wfd *wfd = userdata; - sd_event_source_set_enabled(s, false); - sd_event_source_unref(s); - sd_event_exit(wfd->loop, 0); return 0; @@ -283,12 +272,25 @@ static int ctl_wfd_fetch_info(sd_event_source *s, void *userdata) } static int ctl_wfd_handle_signal(sd_event_source *s, - const struct signalfd_siginfo *si, + const struct signalfd_siginfo *ssi, void *userdata) { + int r; + siginfo_t siginfo; struct ctl_wfd *wfd = userdata; - sd_event_source_set_enabled(s, false); + if(ssi->ssi_signo == SIGCHLD) { + r = waitid(P_PID, ssi->ssi_pid, &siginfo, WNOHANG | WEXITED); + if(0 > r) { + log_warning("failed to reaping child %d", ssi->ssi_pid); + } + else { + log_info("child %d exit: %d", + ssi->ssi_pid, + siginfo.si_code); + } + return 0; + } return sd_event_exit(wfd->loop, 0); } @@ -296,12 +298,12 @@ static int ctl_wfd_handle_signal(sd_event_source *s, static int ctl_wfd_init(struct ctl_wfd *wfd, sd_bus *bus) { int i, r; - const int signals[SHL_ARRAY_LENGTH(wfd->signal_sources)] = { - SIGINT, SIGHUP, SIGQUIT, SIGTERM + const int signals[] = { + SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGCHLD, }; struct ctl_wifi *wifi; - for(i = 0; i < SHL_ARRAY_LENGTH(wfd->signal_sources); i ++) { + for(i = 0; i < SHL_ARRAY_LENGTH(signals); i ++) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, signals[i]); @@ -311,7 +313,7 @@ static int ctl_wfd_init(struct ctl_wfd *wfd, sd_bus *bus) } r = sd_event_add_signal(wfd->loop, - &wfd->signal_sources[i], + NULL, signals[i], ctl_wfd_handle_signal, wfd); @@ -452,8 +454,6 @@ 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")); } @@ -481,20 +481,20 @@ int main(int argc, char **argv) goto unref_bus; } - r = ctl_wfd_new(&wfd, event, bus); + r = wfd_dbus_new(&wfd_dbus, event, bus); if(0 > r) { goto bus_detach_event; } - r = wfd_dbus_new(&wfd_dbus, event, bus); + r = ctl_wfd_new(&wfd, event, bus); if(0 > r) { - goto free_ctl_wfd; + goto free_wfd_dbus; } r = wfd_dbus_expose(wfd_dbus); if(0 > r) { log_warning("unable to publish WFD service: %s", strerror(errno)); - goto free_wfd_dbus; + goto free_ctl_wfd; } r = sd_notify(false, "READY=1\n" @@ -508,10 +508,10 @@ int main(int argc, char **argv) sd_notify(false, "STATUS=Exiting.."); -free_wfd_dbus: - wfd_dbus_free(wfd_dbus); free_ctl_wfd: ctl_wfd_free(wfd); +free_wfd_dbus: + wfd_dbus_free(wfd_dbus); bus_detach_event: sd_bus_detach_event(bus); unref_bus: @@ -521,7 +521,6 @@ disable_watchdog: unref_event: sd_event_unref(event); end: - gst_deinit(); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/disp/wfd-out-session.c b/src/disp/wfd-out-session.c index c4858f4..59f21c7 100644 --- a/src/disp/wfd-out-session.c +++ b/src/disp/wfd-out-session.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include "wfd-session.h" #include "shl_log.h" #include "rtsp.h" @@ -48,6 +46,8 @@ struct wfd_out_session sd_event_source *gst_launch_source; sd_event_source *gst_term_source; + sd_event_source *encoder_source; + enum wfd_display_type display_type; char *authority; char *display_name; @@ -61,10 +61,12 @@ struct wfd_out_session uint32_t mask; char *audio_dev; - GstElement *pipeline; - GstBus *bus; + /*GstElement *pipeline;*/ + /*GstBus *bus;*/ }; +static int force_proc_exit(pid_t pid); + static const struct rtsp_dispatch_entry out_session_rtsp_disp_tbl[]; int wfd_out_session_new(struct wfd_session **out, @@ -296,12 +298,22 @@ int wfd_out_session_teardown(struct wfd_session *s) void wfd_out_session_destroy(struct wfd_session *s) { + pid_t pid; struct wfd_out_session *os = wfd_out_session(s); if(0 <= os->fd) { close(os->fd); os->fd = -1; } + if(os->encoder_source) { + sd_event_source_get_child_pid(os->encoder_source, &pid); + kill(pid, SIGTERM); + + sd_event_source_set_userdata(os->encoder_source, NULL); + sd_event_source_unref(os->encoder_source); + os->encoder_source = NULL; + } + /*if(os->gst_launch_source) {*/ /*sd_event_source_unref(os->gst_launch_source);*/ /*os->gst_launch_source = NULL;*/ @@ -322,17 +334,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) @@ -546,43 +558,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) { @@ -598,189 +610,276 @@ 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) +static int force_proc_exit(pid_t pid) { - 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; + siginfo_t siginfo; + int r = kill(pid, SIGKILL); + if(0 > r) { + log_warning("failed to kill encoder (pid %d): %s", + pid, + strerror(errno)); + return r; } - 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; + r = waitid(P_PID, pid, &siginfo, 0); + if(0 > r) { + log_warning("failed to wait for encoder (pid %d) exit: %s", + pid, + strerror(errno)); } - /* 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; - } + return r; +} - r = setenv("DISPLAY", os->display_name, 1); - if(0 > r) { - return r; - } +static int wfd_out_session_handle_encoder_exit(sd_event_source *source, + const siginfo_t *siginfo, + void *userdata) +{ + struct wfd_out_session *os = userdata; + pid_t pid; - 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); - } + sd_event_source_get_child_pid(source, &pid); + log_info("encoder %d exited", pid); + + sd_event_source_set_enabled(source, false); + sd_event_source_unref(source); + + if(os) { + os->encoder_source = NULL; + wfd_session_teardown(wfd_session(os)); } - 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) +{ + int r; + pid_t pid; + sigset_t mask; + struct wfd_out_session *os = wfd_out_session(s); + + if(os->encoder_source) { + return -EALREADY; + } + + pid = fork(); + if(0 > pid) { + return pid; + } + else if(0 < pid) { + log_info("forked child %d", pid); + r = sd_event_add_child(ctl_wfd_get_loop(), + &os->encoder_source, + pid, + WEXITED, + wfd_out_session_handle_encoder_exit, + s); + if(0 > r) { + force_proc_exit(pid); + return r; + } + + return 0; + } + + log_info("exec gstencoder"); + + setuid(1000); + setgid(1000); + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + + r = execvpe("gstencoder", + (char *[]){ "gstencoder", NULL }, + (char *[]){ "DISPLAY=:0", "XAUTHORITY=/run/user/1000/gdm/Xauthority", "G_MESSAGES_DEBUG=all", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus", NULL }); + _exit(1); +} + +//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, struct rtsp_message **out_rep) @@ -788,10 +887,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, @@ -811,22 +910,36 @@ static int wfd_out_session_handle_teardown_request(struct wfd_session *s, struct rtsp_message *req, struct rtsp_message **out_rep) { - _rtsp_message_unref_ struct rtsp_message *m = NULL; + pid_t pid; + struct wfd_out_session *os = wfd_out_session(s); + /*_rtsp_message_unref_ struct rtsp_message *m = NULL;*/ 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(!os->encoder_source) { + return 0; + } + + r = sd_event_source_get_child_pid(os->encoder_source, &pid); if(0 > r) { return r; } - *out_rep = m; - m = NULL; + log_info("terminating encoder %d", pid); + r = kill(pid, SIGTERM); + + /*r = rtsp_message_new_reply_for(req,*/ + /*&m,*/ + /*RTSP_CODE_OK,*/ + /*NULL);*/ + /*if(0 > r) {*/ + /*return r;*/ + /*}*/ + + /**out_rep = m;*/ + /*m = NULL;*/ return 0; } @@ -836,17 +949,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); diff --git a/src/disp/wfd-session.c b/src/disp/wfd-session.c index de4c9b2..eb7c6e2 100644 --- a/src/disp/wfd-session.c +++ b/src/disp/wfd-session.c @@ -49,7 +49,6 @@ static const char * rtsp_message_id_to_string(enum rtsp_message_id id); static int wfd_session_handle_request(struct rtsp *bus, struct rtsp_message *m, void *userdata); -static bool wfd_session_is_hup(struct wfd_session *s); static void wfd_session_hup(struct wfd_session *s); const struct wfd_session_vtable session_vtbl[] = { @@ -657,11 +656,6 @@ end: return r; } -static bool wfd_session_is_hup(struct wfd_session *s) -{ - return NULL == s->rtsp; -} - static void wfd_session_hup(struct wfd_session *s) { if(!s || !s->rtsp) {