From 72aad834774598a50cbaf4bedc22e8101b02cef5 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Thu, 4 Sep 2014 02:20:34 +0200 Subject: added send stream pipeline with options --- src/options.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++----------- src/options.h | 16 ++++++++- src/sydra.c | 61 +++++++++++++++++++++++++++++++---- 3 files changed, 154 insertions(+), 25 deletions(-) diff --git a/src/options.c b/src/options.c index afe9a37..260438b 100644 --- a/src/options.c +++ b/src/options.c @@ -199,12 +199,25 @@ int options_parse(options_t* opt, int argc, char* argv[]) PARSE_STRING_LIST("-L","--log", opt->log_targets_) PARSE_BOOL_PARAM("-U", "--debug", opt->debug_) PARSE_STRING_PARAM("-n","--appname", opt->appname_) - PARSE_STRING_PARAM("-R","--rec-dir", opt->rec_dir_) + PARSE_STRING_PARAM("-vs","--video-source", opt->video_src_) + PARSE_STRING_PARAM("-ve","--video-encoder", opt->video_enc_) + PARSE_STRING_PARAM("-vp","--video-payloader", opt->video_payloader_) + PARSE_STRING_PARAM("-V","--videosink", opt->videosink_) + PARSE_STRING_PARAM("-as","--audio-source", opt->audio_src_) + PARSE_STRING_PARAM("-ae","--audio-encoder-stream", opt->audio_enc_stream_) + PARSE_STRING_PARAM("-ap","--audio-payloader", opt->audio_payloader_) + PARSE_STRING_PARAM("-o","--rtp-host", opt->rtp_host_) + PARSE_INT_PARAM("-O","--rtp-port-base", opt->rtp_port_base_, 10) + PARSE_STRING_PARAM("-ar","--audio-encoder-rec", opt->audio_enc_rec_) + PARSE_STRING_PARAM("-m","--rec-mux", opt->rec_mux_) PARSE_STRING_PARAM("-r","--rec-name-format", opt->rec_name_format_) else return i; } + if(opt->rtp_port_base_ < 1 || opt->rtp_port_base_ > 65535) + return -4; + if(opt->debug_) { string_list_add(&opt->log_targets_, "stdout:5"); opt->daemonize_ = 0; @@ -239,8 +252,23 @@ void options_default(options_t* opt) string_list_init(&opt->log_targets_); opt->debug_ = 0; opt->appname_ = NULL; - opt->rec_dir_ = strdup("./recordings/"); - opt->rec_name_format_ = strdup("%Y-%m-%d_%H-%M-$S"); + + opt->video_src_ = strdup("v4l2src ! videoconvert ! videoscale ! video/x-raw,format=I420,width=864,height=480,framerate=25/1,pixel-aspect-ratio=1/1"); + opt->video_enc_ = strdup("vp8enc keyframe-max-dist=25 error-resilient=2 end-usage=1 target-bitrate=1800000 cpu-used=4 deadline=1000000 threads=2"); + opt->video_payloader_ = strdup("rtpvp8pay"); + opt->videosink_ = strdup("xvimagesink"); + + opt->audio_src_ = strdup("autoaudiosrc ! audio/x-raw,format=S16LE,channels=1,rate=48000"); + opt->audio_enc_stream_ = strdup("opusenc bitrate=64000 cbr=true packet-loss-percentage=0 inband-fec=false"); + opt->audio_payloader_ = strdup("rtpopuspay"); + + opt->rtp_host_ = strdup("127.0.0.1"); + opt->rtp_port_base_ = 5100; + + opt->audio_enc_rec_ = strdup("audioconvert ! vorbisenc bitrate=96000"); + opt->rec_mux_ = strdup("matroskamux"); + + opt->rec_name_format_ = strdup("./recordings/%Y-%m-%d_%H-%M-%S.mkv"); } void options_clear(options_t* opt) @@ -261,8 +289,26 @@ void options_clear(options_t* opt) string_list_clear(&opt->log_targets_); if(opt->appname_) free(opt->appname_); - if(opt->rec_dir_) - free(opt->rec_dir_); + if(opt->video_src_) + free(opt->video_src_); + if(opt->video_enc_) + free(opt->video_enc_); + if(opt->video_payloader_) + free(opt->video_payloader_); + if(opt->videosink_) + free(opt->videosink_); + if(opt->audio_src_) + free(opt->audio_src_); + if(opt->audio_enc_stream_) + free(opt->audio_enc_stream_); + if(opt->audio_payloader_) + free(opt->audio_payloader_); + if(opt->rtp_host_) + free(opt->rtp_host_); + if(opt->audio_enc_rec_) + free(opt->audio_enc_rec_); + if(opt->rec_mux_) + free(opt->rec_mux_); if(opt->rec_name_format_) free(opt->rec_name_format_); } @@ -281,7 +327,17 @@ void options_print_usage() printf(" add a log target, can be invoked several times\n"); printf(" [-U|--debug] don't daemonize and log to stdout with maximum log level\n"); printf(" [-n|--appname] set the application name (will be used by xvimagesink for window title\n"); - printf(" [-R|--rec-dir] path to the recording directory\n"); + printf(" [-vs|--video-source pipeline for raw video (i.e. videotestsrc)\n"); + printf(" [-ve|--video-encoder pipeline for video encoder (stream and recording)\n"); + printf(" [-vp|--video-payloader video payloader element (i.e. rtpvp8pay\n"); + printf(" [-V|--videosink video sink element (i.e. xvimagesink\n"); + printf(" [-as|--audio-source pipeline for raw audio (audiotestsrc)\n"); + printf(" [-ae|--audio-encoder-stream pipeline for audio encoder (stream only)\n"); + printf(" [-ap|--audio-payloader audio payloader element (i.e. rptopuspay)\n"); + printf(" [-o|--rtp-host remote host for RTP packets\n"); + printf(" [-O|--rtp-port-base base number for remote ports\n"); + printf(" [-ar|--audio-encoder-rec pipeline for audio encoder (recording only)\n"); + printf(" [-m|--rec-mux muxer elemenent (i.e. matroskamux)\n"); printf(" [-r|--rec-name-format] the recording file name format, see manpage of strftime for the syntax\n"); } @@ -296,16 +352,26 @@ void options_print(options_t* opt) if(!opt) return; - printf("progname: '%s'\n", opt->progname_); - printf("daemonize: %d\n", opt->daemonize_); - printf("username: '%s'\n", opt->username_); - printf("groupname: '%s'\n", opt->groupname_); - printf("chroot_dir: '%s'\n", opt->chroot_dir_); - printf("pid_file: '%s'\n", opt->pid_file_); - printf("log_targets: \n"); - string_list_print(&opt->log_targets_, " '", "'\n"); - printf("debug: %s\n", !opt->debug_ ? "false" : "true"); - printf("appname: >>%s<<\n", opt->appname_); - printf("rec_output_dir: '%s'\n", opt->rec_dir_); - printf("rec_name_format: '%s'\n", opt->rec_name_format_); + printf(" progname: '%s'\n", opt->progname_); + printf(" daemonize: %d\n", opt->daemonize_); + printf(" username: '%s'\n", opt->username_); + printf(" groupname: '%s'\n", opt->groupname_); + printf(" chroot_dir: '%s'\n", opt->chroot_dir_); + printf(" pid_file: '%s'\n", opt->pid_file_); + printf(" log_targets: \n"); + string_list_print(&opt->log_targets_, " '", "'\n"); + printf(" debug: %s\n", !opt->debug_ ? "false" : "true"); + printf(" appname: >>%s<<\n", opt->appname_); + printf(" video_src: >>%s<<\n", opt->video_src_); + printf(" video_enc: >>%s<<\n", opt->video_enc_); + printf(" video_payloader: >>%s<<\n", opt->video_payloader_); + printf(" video_sink: >>%s<<\n", opt->videosink_); + printf(" audio_src: >>%s<<\n", opt->audio_src_); + printf(" audio_enc_stream: >>%s<<\n", opt->audio_enc_stream_); + printf(" audio_payloader: >>%s<<\n", opt->audio_payloader_); + printf(" rtp_host: >>%s<<\n", opt->rtp_host_); + printf(" rtp_port_base: %d\n", opt->rtp_port_base_); + printf(" audio_enc_rec: >>%s<<\n", opt->audio_enc_rec_); + printf(" rec_mux: >>%s<<\n", opt->rec_mux_); + printf(" rec_name_format: '%s'\n", opt->rec_name_format_); } diff --git a/src/options.h b/src/options.h index a89bb25..31197b5 100644 --- a/src/options.h +++ b/src/options.h @@ -45,7 +45,21 @@ struct options_struct { int debug_; char* appname_; - char* rec_dir_; + + char* video_src_; + char* video_enc_; + char* video_payloader_; + char* videosink_; + + char* audio_src_; + char* audio_enc_stream_; + char* audio_payloader_; + + char* rtp_host_; + int rtp_port_base_; + + char* audio_enc_rec_; + char* rec_mux_; char* rec_name_format_; }; typedef struct options_struct options_t; diff --git a/src/sydra.c b/src/sydra.c index ec87af8..b4b8d9b 100644 --- a/src/sydra.c +++ b/src/sydra.c @@ -27,11 +27,13 @@ * along with sydra. If not, see . */ +#define _GNU_SOURCE #include #include #include #include #include +#include #include @@ -75,17 +77,19 @@ static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) break; } default: + log_printf(WARNING, "unkonwn message %d", GST_MESSAGE_TYPE(msg)); break; } return TRUE; } + int main_loop(options_t* opt) { log_printf(INFO, "entering main loop"); GMainLoop *loop; - GstElement *pipeline, *source; + GstElement *pipeline, *sender; GstBus *bus; loop = g_main_loop_new(NULL, FALSE); @@ -95,17 +99,62 @@ int main_loop(options_t* opt) return -1; } + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + struct tm bd_time; + localtime_r(&(now.tv_sec), &bd_time); + char recfile[1024]; //TODO: fix this hardcoded length + strftime(recfile, sizeof(recfile), opt->rec_name_format_, &bd_time); + + int rtp_port_v = opt->rtp_port_base_; + int rtcp_port_v = opt->rtp_port_base_ + 100; + int rtp_port_a = opt->rtp_port_base_ + 200; + int rtcp_port_a = opt->rtp_port_base_ + 300; + + char* sender_str; + int slen = asprintf(&sender_str, "rtpbin name=rtpbin \ + %s ! tee name=vt \ + vt. ! queue silent=true ! %s ! tee name=cvt \ + cvt. ! %s ! rtpbin.send_rtp_sink_0 \ + rtpbin.send_rtp_src_0 ! udpsink port=%d host=%s \ + rtpbin.send_rtcp_src_0 ! udpsink port=%d host=%s sync=false async=false \ + %s ! tee name=at \ + at. ! queue silent=true ! %s ! %s ! rtpbin.send_rtp_sink_1 \ + rtpbin.send_rtp_src_1 ! udpsink port=%d host=%s \ + rtpbin.send_rtcp_src_1 ! udpsink port=%d host=%s sync=false async=false \ + %s name=recmux ! filesink location=\"%s\" \ + cvt. ! queue silent=true ! recmux. \ + at. ! queue silent=true ! %s ! recmux. \ + vt. ! queue silent=true ! textoverlay text=\" local \" shaded-background=true halignment=center valignment=baseline font-desc=\"Sans 18\" ! %s", + opt->video_src_, opt->video_enc_, opt->video_payloader_, rtp_port_v, opt->rtp_host_, rtcp_port_v, opt->rtp_host_, + opt->audio_src_, opt->audio_enc_stream_, opt->audio_payloader_, rtp_port_a, opt->rtp_host_, rtcp_port_a, opt->rtp_host_, + opt->rec_mux_, recfile, opt->audio_enc_rec_, opt->videosink_); + + if(slen < 0) { + log_printf(ERROR, "memory error while constructing sender pipeline"); + gst_object_unref(GST_OBJECT(pipeline)); + gst_object_unref(GST_OBJECT(loop)); + return -1; + } + + printf("\n\n\n"); + printf("%s", sender_str); + printf("\n\n\n"); + + GError *error = NULL; - source = gst_parse_bin_from_description("videotestsrc ! xvimagesink", TRUE, &error); - if(!source || error) { - log_printf(ERROR, "Source Bin Description Parser Error: %s", error ? error->message : "unknown"); + sender = gst_parse_bin_from_description(sender_str, TRUE, &error); + if(!sender || error) { + log_printf(ERROR, "Sending Bin Description Parser Error: %s", error ? error->message : "unknown"); g_error_free(error); + free(sender_str); gst_object_unref(GST_OBJECT(pipeline)); gst_object_unref(GST_OBJECT(loop)); return -1; } + free(sender_str); - gst_bin_add_many(GST_BIN(pipeline), source, NULL); + gst_bin_add_many(GST_BIN(pipeline), sender, NULL); bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); gst_bus_add_watch(bus, bus_call, loop); gst_object_unref(bus); @@ -140,7 +189,7 @@ int main(int argc, char* argv[]) if(ret == -3) options_print_version(); if(ret == -4) - fprintf(stderr, "the interval must be bigger than 0\n"); + fprintf(stderr, "the port number is invalid\n"); if(ret != -2 && ret != -3 && ret != -4) options_print_usage(); -- cgit v1.2.3