/* GStreamer
 * Copyright (C) 2016 Igalia S.L
 *   @author Philippe Normand <philn@igalia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include <gst/gst.h>

/*
 * An bundling RTP server
 *  creates two sessions and streams audio on one, video on the other, with RTCP
 *  on both sessions. The destination is 127.0.0.1.
 *
 *  The RTP streams are bundled to a single outgoing connection. Same for the RTCP streams.
 *
 *  .-------.    .-------.    .-------.      .------------.         .------.
 *  |audiots|    |alawenc|    |pcmapay|      | rtpbin     |         |funnel|
 *  |      src->sink    src->sink    src->send_rtp_0 send_rtp_0--->sink_0  |    .-------.
 *  '-------'    '-------'    '-------'      |            |         |      |    |udpsink|
 *                                           |            |         |     src->sink     |
 *  .-------.               .---------.      |            |         |      |    '-------'
 *  |videots|               | vrawpay |      |            |         |      |
 *  |      src------------>sink      src->send_rtp_1 send_rtp_1--->sink_1  |
 *  '-------'               '---------'      |            |         '------'
 *                                           |            |
 *                               .------.    |            |
 *                               |udpsrc|    |            |         .------.
 *                               |     src->recv_rtcp_0   |         |funnel|
 *                               '------'    |       send_rtcp_0-->sink_0  |   .-------.
 *                                           |            |         |      |   |udpsink|
 *                               .------.    |            |         |    src->sink     |
 *                               |udpsrc|    |            |         |      |   '-------'
 *                               |     src->recv_rtcp_1   |         |      |
 *                               '------'    |       send_rtcp_1-->sink_1  |
 *                                           '------------'         '------'
 *
 */

static GstElement *
create_pipeline (void)
{
  GstElement *pipeline, *rtpbin, *audiosrc, *audio_encoder,
      *audio_rtppayloader, *sendrtp_udpsink,
      *send_rtcp_udpsink, *sendrtcp_funnel, *sendrtp_funnel;
  GstElement *videosrc, *video_rtppayloader, *time_overlay;
  gint rtp_udp_port = 5001;
  gint rtcp_udp_port = 5002;
  gint recv_audio_rtcp_port = 5003;
  gint recv_video_rtcp_port = 5004;
  GstElement *audio_rtcp_udpsrc, *video_rtcp_udpsrc;

  pipeline = gst_pipeline_new (NULL);

  rtpbin = gst_element_factory_make ("rtpbin", NULL);

  audiosrc = gst_element_factory_make ("audiotestsrc", NULL);
  g_object_set (audiosrc, "is-live", TRUE, NULL);
  audio_encoder = gst_element_factory_make ("alawenc", NULL);
  audio_rtppayloader = gst_element_factory_make ("rtppcmapay", NULL);
  g_object_set (audio_rtppayloader, "pt", 96, NULL);

  videosrc = gst_element_factory_make ("videotestsrc", NULL);
  g_object_set (videosrc, "is-live", TRUE, NULL);
  time_overlay = gst_element_factory_make ("timeoverlay", NULL);
  video_rtppayloader = gst_element_factory_make ("rtpvrawpay", NULL);
  g_object_set (video_rtppayloader, "pt", 100, NULL);

  /* muxed rtcp */
  sendrtcp_funnel = gst_element_factory_make ("funnel", "send_rtcp_funnel");
  send_rtcp_udpsink = gst_element_factory_make ("udpsink", NULL);
  g_object_set (send_rtcp_udpsink, "host", "127.0.0.1", NULL);
  g_object_set (send_rtcp_udpsink, "port", rtcp_udp_port, NULL);
  g_object_set (send_rtcp_udpsink, "sync", FALSE, NULL);
  g_object_set (send_rtcp_udpsink, "async", FALSE, NULL);

  /* outgoing bundled stream */
  sendrtp_funnel = gst_element_factory_make ("funnel", "send_rtp_funnel");
  sendrtp_udpsink = gst_element_factory_make ("udpsink", NULL);
  g_object_set (sendrtp_udpsink, "host", "127.0.0.1", NULL);
  g_object_set (sendrtp_udpsink, "port", rtp_udp_port, NULL);
  g_object_set (sendrtp_udpsink, "sync", FALSE, NULL);
  g_object_set (sendrtp_udpsink, "async", FALSE, NULL);

  gst_bin_add_many (GST_BIN (pipeline), rtpbin, audiosrc, audio_encoder,
      audio_rtppayloader, sendrtp_udpsink, send_rtcp_udpsink,
      sendrtp_funnel, sendrtcp_funnel, videosrc, video_rtppayloader, NULL);

  if (time_overlay)
    gst_bin_add (GST_BIN (pipeline), time_overlay);

  gst_element_link_many (audiosrc, audio_encoder, audio_rtppayloader, NULL);
  gst_element_link_pads (audio_rtppayloader, "src", rtpbin, "send_rtp_sink_0");

  if (time_overlay) {
    gst_element_link_many (videosrc, time_overlay, video_rtppayloader, NULL);
  } else {
    gst_element_link (videosrc, video_rtppayloader);
  }

  gst_element_link_pads (video_rtppayloader, "src", rtpbin, "send_rtp_sink_1");

  gst_element_link_pads (sendrtp_funnel, "src", sendrtp_udpsink, "sink");
  gst_element_link_pads (rtpbin, "send_rtp_src_0", sendrtp_funnel, "sink_%u");
  gst_element_link_pads (rtpbin, "send_rtp_src_1", sendrtp_funnel, "sink_%u");
  gst_element_link_pads (sendrtcp_funnel, "src", send_rtcp_udpsink, "sink");
  gst_element_link_pads (rtpbin, "send_rtcp_src_0", sendrtcp_funnel, "sink_%u");
  gst_element_link_pads (rtpbin, "send_rtcp_src_1", sendrtcp_funnel, "sink_%u");

  audio_rtcp_udpsrc = gst_element_factory_make ("udpsrc", NULL);
  g_object_set (audio_rtcp_udpsrc, "port", recv_audio_rtcp_port, NULL);
  video_rtcp_udpsrc = gst_element_factory_make ("udpsrc", NULL);
  g_object_set (video_rtcp_udpsrc, "port", recv_video_rtcp_port, NULL);
  gst_bin_add_many (GST_BIN (pipeline), audio_rtcp_udpsrc, video_rtcp_udpsrc,
      NULL);
  gst_element_link_pads (audio_rtcp_udpsrc, "src", rtpbin, "recv_rtcp_sink_0");
  gst_element_link_pads (video_rtcp_udpsrc, "src", rtpbin, "recv_rtcp_sink_1");

  return pipeline;
}

/*
 * Used to generate informative messages during pipeline startup
 */
static void
cb_state (GstBus * bus, GstMessage * message, gpointer data)
{
  GstObject *pipe = GST_OBJECT (data);
  GstState old, new, pending;
  gst_message_parse_state_changed (message, &old, &new, &pending);
  if (message->src == pipe) {
    g_print ("Pipeline %s changed state from %s to %s\n",
        GST_OBJECT_NAME (message->src),
        gst_element_state_get_name (old), gst_element_state_get_name (new));
  }
}

int
main (int argc, char **argv)
{
  GstElement *pipe;
  GstBus *bus;
  GMainLoop *loop;

  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

  pipe = create_pipeline ();
  bus = gst_element_get_bus (pipe);
  g_signal_connect (bus, "message::state-changed", G_CALLBACK (cb_state), pipe);
  gst_bus_add_signal_watch (bus);
  gst_object_unref (bus);

  g_print ("starting server pipeline\n");
  gst_element_set_state (pipe, GST_STATE_PLAYING);

  g_main_loop_run (loop);

  g_print ("stopping server pipeline\n");
  gst_element_set_state (pipe, GST_STATE_NULL);

  gst_object_unref (pipe);
  g_main_loop_unref (loop);

  return 0;
}