From 047775303b9428e2ff1d85f3941fd5ed3f32bcf8 Mon Sep 17 00:00:00 2001 From: Derek Dai Date: Fri, 21 Oct 2016 10:17:11 +0800 Subject: [PATCH] integrate miracle-sender with miracast-srcctl --- src/ctl/ctl-src.c | 27 +--- src/ctl/ctl-src.h | 4 - src/ctl/ctl.h | 1 + src/ctl/srcctl.c | 198 +++++++++++------------- src/stream/CMakeLists.txt | 13 +- src/stream/sender.c | 309 ++++++++++++++++++++++++++++++++------ 6 files changed, 370 insertions(+), 182 deletions(-) diff --git a/src/ctl/ctl-src.c b/src/ctl/ctl-src.c index ad00014..68087da 100644 --- a/src/ctl/ctl-src.c +++ b/src/ctl/ctl-src.c @@ -94,7 +94,7 @@ static void src_handle_setup(struct ctl_src *s, goto error; } - char buf[128]; + char buf[256]; snprintf(buf, sizeof(buf), "RTP/AVP/UDP;unicast;client_port=%d", s->sink.rtp_ports.port0); r = rtsp_message_append(rep, "", "Transport", buf); @@ -181,6 +181,8 @@ static void src_handle_play(struct ctl_src *s, goto error; } + ctl_fn_src_playing(s); + return; error: @@ -518,27 +520,6 @@ error: return r; } -static int src_set_format(struct ctl_src *s, - unsigned int cea_res, - unsigned int vesa_res, - unsigned int hh_res) -{ - int hres, vres; - -// if ((vfd_get_cea_resolution(cea_res, &hres, &vres) == 0) || -// (vfd_get_vesa_resolution(vesa_res, &hres, &vres) == 0) || -// (vfd_get_hh_resolution(hh_res, &hres, &vres) == 0)) { -// if (hres && vres) { -// s->hres = hres; -// s->vres = vres; -// ctl_fn_sink_resolution_set(s); -// return 0; -// } -// } -// - return -EINVAL; -} - static void src_handle(struct ctl_src *s, struct rtsp_message *m) { @@ -634,7 +615,7 @@ static void src_connected(struct ctl_src *s) sd_event_source_set_enabled(s->fd_source, SD_EVENT_OFF); len = sizeof(addr); - int fd = accept(s->fd, (struct sockaddr *) &addr, &len); + int fd = accept4(s->fd, (struct sockaddr *) &addr, &len, SOCK_CLOEXEC); r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len); if (r < 0) { diff --git a/src/ctl/ctl-src.h b/src/ctl/ctl-src.h index 971f616..0239220 100644 --- a/src/ctl/ctl-src.h +++ b/src/ctl/ctl-src.h @@ -1,6 +1,4 @@ /* - * vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab - * * MiracleCast - Wifi-Display/Miracast Implementation * * MiracleCast is free software; you can redistribute it and/or modify it @@ -82,8 +80,6 @@ struct ctl_src { char *local; char *session; char url[256]; -// char *uibc_config; -// char *uibc_setting; struct sockaddr_storage addr; size_t addr_size; int fd; diff --git a/src/ctl/ctl.h b/src/ctl/ctl.h index dc9e7a0..a1abefd 100644 --- a/src/ctl/ctl.h +++ b/src/ctl/ctl.h @@ -253,6 +253,7 @@ void ctl_fn_peer_disconnected(struct ctl_peer *p); void ctl_fn_link_new(struct ctl_link *l); void ctl_fn_link_free(struct ctl_link *l); +void ctl_fn_src_playing(struct ctl_src *s); void ctl_fn_src_connected(struct ctl_src *s); void ctl_fn_src_disconnected(struct ctl_src *s); diff --git a/src/ctl/srcctl.c b/src/ctl/srcctl.c index 0d73365..0fde264 100644 --- a/src/ctl/srcctl.c +++ b/src/ctl/srcctl.c @@ -33,8 +33,11 @@ #include #include #include +#include +#include +#include #include "ctl.h" -//#include "ctl-sink.h" +#include "ctl-src.h" #include "wfd.h" #include "shl_macro.h" #include "shl_util.h" @@ -47,12 +50,14 @@ static sd_event_source *scan_timeout; static sd_event_source *src_timeout; static unsigned int src_timeout_time; static bool src_connected; -//static pid_t sink_pid; -// +static pid_t src_pid; + //static char *selected_ link; static struct ctl_link *running_link; static struct ctl_peer *running_peer; static struct ctl_peer *pending_peer; + +void launch_sender(struct ctl_src *s); // //char *gst_scale_res; int gst_audio_en = 1; @@ -452,109 +457,81 @@ static const struct cli_cmd cli_cmds[] = { { "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" }, { }, }; -// -//static void spawn_gst(struct ctl_sink *s) -//{ -// pid_t pid; -// int fd_journal; -// sigset_t mask; -// -// if (sink_pid > 0) -// return; -// -// pid = fork(); -// if (pid < 0) { -// return cli_vERRNO(); -// } else if (!pid) { -// /* child */ -// -// sigemptyset(&mask); -// sigprocmask(SIG_SETMASK, &mask, NULL); -// -// /* redirect stdout/stderr to journal */ -// fd_journal = sd_journal_stream_fd("miracle-sinkctl-gst", -// LOG_DEBUG, -// false); -// if (fd_journal >= 0) { -// /* dup journal-fd to stdout and stderr */ -// dup2(fd_journal, 1); -// dup2(fd_journal, 2); -// } else { -// /* no journal? redirect stdout to parent's stderr */ -// dup2(2, 1); -// } -// -// launch_player(s); -// _exit(1); -// } else { -// sink_pid = pid; -// } -//} -// -//void launch_player(struct ctl_sink *s) { -// char *argv[64]; -// char resolution[64]; -// char port[64]; -// char uibc_portStr[64]; -// int i = 0; -// char* player; -// if (uibc) { -// player = "uibc-viewer"; -// } else { -// player = "miracle-gst"; -// } -// -// argv[i++] = player; -// if (uibc) { -// argv[i++] = s->target; -// sprintf(uibc_portStr, "%d", uibc_port); -// argv[i++] = uibc_portStr; -// } -// if (cli_max_sev >= 7) -// argv[i++] = "-d 3"; -// if (gst_audio_en) -// argv[i++] = "-a"; -// if (gst_scale_res) { -// argv[i++] = "-s"; -// argv[i++] = gst_scale_res; -// } -// argv[i++] = "-p"; -// sprintf(port, "%d", rstp_port); -// argv[i++] = port; -// + +static void spawn_gst(struct ctl_src *s) +{ + pid_t pid; + int fd_journal; + sigset_t mask; + + if (src_pid > 0) + return; + + pid = fork(); + if (pid < 0) { + return cli_vERRNO(); + } else if (!pid) { + /* child */ + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + + /* redirect stdout/stderr to journal */ + fd_journal = sd_journal_stream_fd("miracle-srcctl-gst", + LOG_DEBUG, + false); + if (fd_journal >= 0) { + /* dup journal-fd to stdout and stderr */ + dup2(fd_journal, 1); + dup2(fd_journal, 2); + } else { + /* no journal? redirect stdout to parent's stderr */ + dup2(2, 1); + } + + launch_sender(s); + _exit(1); + } else { + src_pid = pid; + } +} + +void launch_sender(struct ctl_src *s) { + char * argv[64]; + char resolution[64]; + char port[64]; + char uibc_portStr[64]; + int i = 0; + + argv[i++] = "miracle-sender"; + //if (gst_audio_en) { + // argv[i++] = "--acodec"; + // argv[i++] = "aac"; + //} + argv[i++] = "--host"; + argv[i++] = inet_ntoa(((struct sockaddr_in *) &s->addr)->sin_addr); + argv[i++] = "-p"; + sprintf(port, "%d", rstp_port); + argv[i++] = port; + // if (s->hres && s->vres) { // sprintf(resolution, "%dx%d", s->hres, s->vres); // argv[i++] = "-r"; // argv[i++] = resolution; // } -// -// argv[i] = NULL; -// -// i = 0; -// int size = 0; -// while (argv[i]) { -// size += strlen(argv[i++] + 1); -// } -// -// char* player_command = malloc(size); -// i = 0; -// strcpy(player_command, argv[i++]); -// while (argv[i]) { -// strcat(player_command, " "); -// strcat(player_command, argv[i++]); -// } -// log_debug("player command: %s", player_command); -// //free(player_command); -// if (execvpe(argv[0], argv, environ) < 0) { -// cli_debug("stream player failed (%d): %m", errno); -// int i = 0; -// cli_debug("printing environment: "); -// while (environ[i]) { -// cli_debug("%s", environ[i++]); -// } -// } -//} -// + + argv[i] = NULL; + + if (execvpe(argv[0], argv, environ) < 0) { + cli_debug("stream sender failed (%d): %m", errno); + int i = 0; + cli_debug("printing environment: "); + while (environ[i]) { + cli_debug("%s", environ[i++]); + } + } +} + //void launch_uibc_daemon(int port) { // char *argv[64]; // char portStr[64]; @@ -571,11 +548,11 @@ static const struct cli_cmd cli_cmds[] = { // //static void kill_gst(void) //{ -// if (sink_pid <= 0) +// if (src_pid <= 0) // return; // -// kill(sink_pid, SIGTERM); -// sink_pid = 0; +// kill(src_pid, SIGTERM); +// src_pid = 0; //} void ctl_fn_src_connected(struct ctl_src *s) @@ -595,12 +572,13 @@ void ctl_fn_src_disconnected(struct ctl_src *s) } } -//void ctl_fn_sink_resolution_set(struct ctl_sink *s) -//{ -// cli_printf("SINK set resolution %dx%d\n", s->hres, s->vres); -// if (sink_connected) -// spawn_gst(s); -//} +void ctl_fn_src_playing(struct ctl_src *s) +{ + cli_printf("SRC got play request\n"); + // TODO src_connected must be true, why if() failed? + //if (src_connected) + spawn_gst(s); +} void ctl_fn_peer_new(struct ctl_peer *p) { diff --git a/src/stream/CMakeLists.txt b/src/stream/CMakeLists.txt index a40e416..3f2fdd8 100644 --- a/src/stream/CMakeLists.txt +++ b/src/stream/CMakeLists.txt @@ -1,10 +1,19 @@ +pkg_check_modules(GTK REQUIRED gtk+-3.0) pkg_check_modules(GST REQUIRED gstreamer-1.0) pkg_check_modules(GST_PBUTILS REQUIRED gstreamer-pbutils-1.0) -include_directories(${GST_INCLUDE_DIRS} ${GST_PBUTILS_INCLUDE_DIRS}) +include_directories( + ${GTK_INCLUDE_DIRS} + ${GST_INCLUDE_DIRS} + ${GST_PBUTILS_INCLUDE_DIRS} +) add_executable(miracle-sender sender) -target_link_libraries(miracle-sender ${GST_LIBRARIES} ${GST_PBUTILS_LIBRARIES}) +target_link_libraries( + miracle-sender + ${GTK_LIBRARIES} + ${GST_LIBRARIES} + ${GST_PBUTILS_LIBRARIES}) install(TARGETS miracle-sender DESTINATION bin) diff --git a/src/stream/sender.c b/src/stream/sender.c index 26e075f..ac29af5 100644 --- a/src/stream/sender.c +++ b/src/stream/sender.c @@ -1,86 +1,309 @@ /* - * ===================================================================================== + * MiracleCast - Wifi-Display/Miracast Implementation * - * Filename: sender.c + * MiracleCast is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. * - * Description: + * MiracleCast is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * Version: 1.0 - * Created: 2016年10月17日 18時11分16秒 - * Revision: none - * Compiler: gcc - * - * Author: YOUR NAME (), - * Organization: - * - * ===================================================================================== + * You should have received a copy of the GNU Lesser General Public License + * along with MiracleCast; If not, see . */ #include -#include +#include +#include #include +#include +#include +#include +#include #include #include #include -int main(int argc, char *args[]) +struct CmdChannel +{ + gint in; + gint out; + + guint in_source; + + gboolean controllee; +}; + +static gboolean on_req_in(gint fd, GIOCondition cond, struct CmdChannel *self) +{ + +} + +static struct CmdChannel * cmd_channel_new() +{ + int fds[2]; + GError *error = NULL; + struct CmdChannel *self = g_slice_new(struct CmdChannel); + if(!self) { + goto end; + } + + if(!g_unix_open_pipe(fds, 0, &error)) { + g_warning("%s", error->message); + goto free_self; + } + + self->in = fds[0]; + self->out = fds[1]; + + self->in_source = g_unix_fd_add(self->in, + G_IO_IN | G_IO_ERR | G_IO_HUP, + (GUnixFDSourceFunc) on_req_in, + self); + + goto end; + +free_self: + g_slice_free(struct CmdChannel, self); + self = NULL; +end: + return self; +} + +static void cmd_channel_free(struct CmdChannel *self) +{ + g_source_remove(self->in_source); + g_close(self->in, NULL); + g_close(self->out, NULL); + g_slice_free(struct CmdChannel, self); +} + +struct Sender { GMainLoop *loop; + GstElement *pipeline; + + struct CmdChannel *channel; +}; + +static gchar *arg_host = NULL; + +static guint arg_port = 1991; + +static guint arg_width = 0; + +static guint arg_height = 0; + +static gint arg_screen = -1; + +static gchar *arg_acodec = NULL; + +static struct Sender *sender_new() +{ + struct Sender *self = g_slice_new(struct Sender); + if(!self) { + goto end; + } + +end: + return self; +} + +static void sender_free(struct Sender *self) +{ + if(self->pipeline) { + gst_element_set_state(self->pipeline, GST_STATE_NULL); + g_object_unref(G_OBJECT(self->pipeline)); + } + + if(self->loop) { + g_main_loop_unref(self->loop); + } + + g_slice_free(struct Sender, self); +} + +static int sender_prepare(struct Sender *self) +{ GError *error = NULL; + GString *desc = g_string_new("ximagesrc "); + GstElement *encoder; + GstElement *vconv; + gint screen_no; - gst_init(&argc, &args); - gst_pb_utils_init(); - - GString *desc = g_string_new(""); + if(arg_screen == -1) { + GdkScreen *screen = gdk_screen_get_default(); + screen_no = gdk_screen_get_number(screen); + } + else { + screen_no = arg_screen; + } g_string_append_printf(desc, - "ximagesrc " - "! videoscale " - "! video/x-raw, width=%d, height=%d, framerate=%d/1 " - "! videoconvert name=vconverter " + "screen-num=%d ! video/x-raw, framerate=30/1 ", + screen_no); + + if(arg_width || arg_height) { + g_string_append(desc, "! videoscale ! video/x-raw, "); + } + if(arg_width) { + g_string_append_printf(desc, "width=%d ", arg_width); + } + if(arg_height) { + g_string_append_printf(desc, "height=%d ", arg_height); + } + + g_string_append_printf(desc, + "! videoconvert name=vconv " "! video/x-raw, format=NV12 " "! encodebin name=encoder " "! rtpmp2tpay " - "! udpsink host=%s port=%d ", - 1280, 720, 30, - "127.0.0.1", - 1991); + "! udpsink host=\"%s\" port=%d ", + arg_host, + arg_port); - pipeline = gst_parse_launch(desc->str, &error); + if(arg_acodec) { + g_string_append_printf(desc, "pulsesrc device=\"%s\" ! encoder.", + "alsa_output.pci-0000_00_1b.0.analog-stereo"); + } + + g_info("final pipeline: %s", desc->str); + + self->pipeline = gst_parse_launch(desc->str, &error); if(GST_PARSE_ERROR_LINK != error->code) { g_error("%s", error->message); goto error; } GstCaps *caps = gst_caps_from_string("video/mpegts, systemstream=true, packetsize=188"); - GstEncodingContainerProfile *container = gst_encoding_container_profile_new("mpeg-ts-profile", + GstEncodingContainerProfile *cprofile = gst_encoding_container_profile_new("mpeg-ts-profile", NULL, caps, NULL); gst_caps_unref(caps); caps = gst_caps_from_string("video/x-h264, format=YV12, width=1280, height=720"); - GstEncodingVideoProfile *vencoder = gst_encoding_video_profile_new(caps, NULL, NULL, 0); - gst_caps_unref(caps); + GstEncodingVideoProfile *vprofile = gst_encoding_video_profile_new(caps, NULL, NULL, 0); + gst_encoding_container_profile_add_profile(cprofile, GST_ENCODING_PROFILE(vprofile)); + //gst_caps_unref(caps); + /*g_object_unref(vprofile);*/ - gst_encoding_container_profile_add_profile(container, GST_ENCODING_PROFILE(vencoder)); - /*g_object_unref(vencoder);*/ + if(arg_acodec) { + if(!strcmp("aac", arg_acodec)) { + caps = gst_caps_from_string("audio/aac"); + } + GstEncodingAudioProfile *aprofile = gst_encoding_audio_profile_new(caps, NULL, NULL, 0); + //gst_caps_unref(caps); - GstElement *encoder = gst_bin_get_by_name(GST_BIN(pipeline), "encoder"); - g_object_set(G_OBJECT(encoder), "profile", container, NULL); - /*g_object_unref(container);*/ - - GstElement *vconverter = gst_bin_get_by_name(GST_BIN(pipeline), "vconverter"); - if(!gst_element_link(vconverter, encoder)) { - printf("failed to link vconverter to encoder\n"); + gst_encoding_container_profile_add_profile(cprofile, GST_ENCODING_PROFILE(aprofile)); + /*g_object_unref(aprofile);*/ } - gst_element_set_state(pipeline, GST_STATE_PLAYING); + encoder = gst_bin_get_by_name(GST_BIN(self->pipeline), "encoder"); + g_object_set(G_OBJECT(encoder), "profile", cprofile, NULL); + /*g_object_unref(cprofile);*/ - loop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(loop); + vconv = gst_bin_get_by_name(GST_BIN(self->pipeline), "vconv"); + if(!gst_element_link(vconv, encoder)) { + printf("failed to link vconv to encoder\n"); + } + + self->loop = g_main_loop_new(NULL, FALSE); + + goto end; error: - g_object_unref(pipeline); + if(self->pipeline) { + g_object_unref(self->pipeline); + self->pipeline = NULL; + } end: + g_string_free(desc, TRUE); + + return 0; +} + +static void sender_start(struct Sender *self) +{ + gst_element_set_state(self->pipeline, GST_STATE_PLAYING); +} + +static void sender_pause(struct Sender *self) +{ + gst_element_set_state(self->pipeline, GST_STATE_PAUSED); +} + +static void sender_stop(struct Sender *self) +{ + gst_element_set_state(self->pipeline, GST_STATE_NULL); +} + +static void sender_run(struct Sender *self) +{ + g_main_loop_run(self->loop); +} + +static void arg_enable_audio(); + +static GOptionEntry entries[] = { + { "host", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &arg_host, "the hostname of sink", "" }, + { "port", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_port, "the port which sink is waiting for RTP string", "" }, + { "width", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_width, "", "" }, + { "height", 'h', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_height, "", "" }, + { "screen-num", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT, &arg_screen, "screen number to cast to", "" }, + { "acodec", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &arg_acodec, "codec to encode audio", "" }, +}; + +static void arg_parse(int *argc, char ***args) +{ + GOptionContext *opt_context; + GError *error = NULL; + + opt_context = g_option_context_new(""); + if(!opt_context) { + g_error("%s", strerror(errno)); + } + + g_option_context_add_main_entries(opt_context, entries, NULL); + if(!g_option_context_parse(opt_context, argc, args, &error)) { + g_fprintf(stderr, "%s\n", error->message); + exit(1); + } + + if(0 >= arg_port || arg_port > 65535) { + g_fprintf(stderr, "Invalid port number: %i\n", arg_port); + exit(1); + } + + g_option_context_free(opt_context); +} + +int main(int argc, char *args[]) +{ + struct Sender *sender; + + arg_parse(&argc, &args); + + gdk_init(&argc, &args); + gst_init(&argc, &args); + gst_pb_utils_init(); + + sender = sender_new(); + if(!sender) { + g_error("%s", strerror(errno)); + } + + if(sender_prepare(sender) < 0) { + g_error("%s", strerror(errno)); + } + + sender_start(sender); + + sender_run(sender); + + sender_free(sender); + return 0; }