static void got_sources(GList *sources, gpointer user_data)
{
    OwrMediaSource *source = NULL;
    static gboolean have_video = FALSE, have_audio = FALSE;

    g_assert(sources);

    while (sources && (source = sources->data)) {
        OwrMediaType media_type;
        OwrSourceType source_type;

        g_assert(OWR_IS_MEDIA_SOURCE(source));

        g_object_get(source, "type", &source_type, "media-type", &media_type, NULL);

        if (!disable_video && !have_video && media_type == OWR_MEDIA_TYPE_VIDEO && source_type == OWR_SOURCE_TYPE_CAPTURE) {
            OwrVideoRenderer *renderer;
            OwrPayload *payload;

            have_video = TRUE;

            payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 103, 90000, TRUE, FALSE);
            g_object_set(payload, "width", 1280, "height", 720, "framerate", 30.0, NULL);
            g_object_set(payload, "rtx-payload-type", 123, NULL);

            owr_media_session_set_send_payload(send_session_video, payload);

            owr_media_session_set_send_source(send_session_video, source);

            owr_transport_agent_add_session(send_transport_agent, OWR_SESSION(send_session_video));

            g_print("Displaying self-view\n");

            renderer = owr_video_renderer_new(NULL);
            g_assert(renderer);
            g_object_set(renderer, "width", 1280, "height", 720, "max-framerate", 30.0, NULL);
            owr_media_renderer_set_source(OWR_MEDIA_RENDERER(renderer), source);
            video_renderer = OWR_MEDIA_RENDERER(renderer);
            video_source = g_object_ref(source);
        } else if (!disable_audio && !have_audio && media_type == OWR_MEDIA_TYPE_AUDIO && source_type == OWR_SOURCE_TYPE_CAPTURE) {
            OwrPayload *payload;

            have_audio = TRUE;

            payload = owr_audio_payload_new(OWR_CODEC_TYPE_OPUS, 100, 48000, 1);
            owr_media_session_set_send_payload(send_session_audio, payload);

            owr_media_session_set_send_source(send_session_audio, source);

            owr_transport_agent_add_session(send_transport_agent, OWR_SESSION(send_session_audio));
            audio_source = g_object_ref(source);
        }

        if ((disable_video || have_video) && (disable_audio || have_audio))
            break;

        sources = sources->next;
    }
}
static gboolean setup_transport_agents()
{
    g_print("Setting up transport agents\n");
    // LEFT
    left_transport_agent = owr_transport_agent_new(FALSE);
    g_assert(OWR_IS_TRANSPORT_AGENT(left_transport_agent));

    owr_transport_agent_set_local_port_range(left_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(left_transport_agent, "127.0.0.1");

    // RIGHT
    right_transport_agent = owr_transport_agent_new(TRUE);
    g_assert(OWR_IS_TRANSPORT_AGENT(right_transport_agent));

    owr_transport_agent_set_local_port_range(right_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(right_transport_agent, "127.0.0.1");

    left_session = owr_data_session_new(TRUE);
    right_session = owr_data_session_new(FALSE);

    g_object_set(left_session, "sctp-local-port", 5000, "sctp-remote-port", 5000, NULL);
    g_object_set(right_session, "sctp-local-port", 5000, "sctp-remote-port", 5000, NULL);

    g_signal_connect(left_session, "on-new-candidate", G_CALLBACK(got_candidate), right_session);
    g_signal_connect(right_session, "on-new-candidate", G_CALLBACK(got_candidate), left_session);

    owr_transport_agent_add_session(left_transport_agent, OWR_SESSION(left_session));
    owr_transport_agent_add_session(right_transport_agent, OWR_SESSION(right_session));

    if (wait_for_dtls) {
        gboolean peer_certificate_received;
        GAsyncQueue *msg_queue = g_async_queue_new();

        g_signal_connect(left_session, "notify::dtls-peer-certificate", G_CALLBACK(on_dtls_peer_certificate), msg_queue);
        g_signal_connect(right_session, "notify::dtls-peer-certificate", G_CALLBACK(on_dtls_peer_certificate), msg_queue);

        g_print("waiting for dtls handshake to complete\n");

        peer_certificate_received = !!g_async_queue_timeout_pop(msg_queue, 5000000);
        peer_certificate_received &= !!g_async_queue_timeout_pop(msg_queue, 5000000);
        g_async_queue_unref(msg_queue);

        if (!peer_certificate_received) {
            g_print("dtls handshake timed out\n");
            return FALSE;
        }

        g_print("dtls handshake to completed\n");
    }

    return TRUE;
}
Exemple #3
0
static void owr_session_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
    OwrSessionPrivate *priv = OWR_SESSION(object)->priv;

    switch (property_id) {
    case PROP_DTLS_CLIENT_MODE:
        g_value_set_boolean(value, priv->dtls_client_mode);
        break;

    case PROP_DTLS_CERTIFICATE:
        g_value_set_string(value, priv->dtls_certificate);
        break;

    case PROP_DTLS_KEY:
        g_value_set_string(value, priv->dtls_key);
        break;

    case PROP_DTLS_PEER_CERTIFICATE:
        g_value_set_string(value, priv->dtls_peer_certificate);
        break;

    case PROP_ICE_STATE:
        g_value_set_enum(value, priv->ice_state);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        break;
    }
}
Exemple #4
0
void got_candidate(GObject *session, OwrCandidate *candidate, gpointer user_data) {
	gchar *ufrag, *password;
	int type;
	int component;
	gchar *foundation;
	guint priority;
	int transport_type;
	gchar *address, *base_address;
	guint port, base_port;

	g_object_get(candidate,
		"foundation", &foundation,
		"ufrag", &ufrag,
		"password", &password,
		"type", &type,
		"component-type", &component,
		"priority", &priority,
		"transport-type", &transport_type,
		"address", &address, "port", &port,
		"base-address", &base_address, "base-port", &base_port,
		NULL);

	got_candidate_go(OWR_SESSION(session),
		ufrag, password, type, component, foundation, priority,
		transport_type, port, base_port, address, base_address);

	g_free(ufrag);
	g_free(password);
	g_free(foundation);
	g_free(address);
	g_free(base_address);
}
Exemple #5
0
static void owr_session_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
    OwrSessionPrivate *priv = OWR_SESSION(object)->priv;

    switch (property_id) {
    case PROP_DTLS_CLIENT_MODE:
        priv->dtls_client_mode = g_value_get_boolean(value);
        break;

    case PROP_DTLS_CERTIFICATE:
        if (priv->dtls_certificate)
            g_free(priv->dtls_certificate);
        priv->dtls_certificate = g_value_dup_string(value);
        if (priv->dtls_certificate && (!priv->dtls_certificate[0]
            || g_strstr_len(priv->dtls_certificate, 5, "null"))) {
            g_free(priv->dtls_certificate);
            priv->dtls_certificate = NULL;
        }
        GST_DEBUG_OBJECT(OWR_SESSION(object), "certificate generated: %s\n", priv->dtls_certificate);
        g_warn_if_fail(!priv->dtls_certificate
            || g_str_has_prefix(priv->dtls_certificate, "-----BEGIN CERTIFICATE-----"));
        break;

    case PROP_DTLS_KEY:
        if (priv->dtls_key)
            g_free(priv->dtls_key);
        priv->dtls_key = g_value_dup_string(value);
        if (priv->dtls_key && (!priv->dtls_key[0] || g_strstr_len(priv->dtls_key, 5, "null"))) {
            g_free(priv->dtls_key);
            priv->dtls_key = NULL;
        }
        g_warn_if_fail(!priv->dtls_key
            || g_str_has_prefix(priv->dtls_key, "-----BEGIN PRIVATE KEY-----")
            || g_str_has_prefix(priv->dtls_key, "-----BEGIN RSA PRIVATE KEY-----"));
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        break;
    }
}
Exemple #6
0
OwrSession* new_session() {
	Debug("new session");
	OwrMediaSession *session = owr_media_session_new(TRUE);
	g_signal_connect(session, "on-new-candidate",
		G_CALLBACK(got_candidate), NULL);
	g_signal_connect(session, "on-candidate-gathering-done",
		G_CALLBACK(candidate_gathering_done_go), NULL);
	g_signal_connect(session, "notify::dtls-certificate",
		G_CALLBACK(got_dtls_certificate), NULL);

	if (local_sources != NULL && local_sources->data != NULL) {
		Debug("setting local source");
		owr_media_session_set_send_source(session, OWR_MEDIA_SOURCE(local_sources->data));
	}
	// TODO make the specs (payload:120, encoding VP8, clock: 9000) mutable.
	OwrPayload *payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 120, 90000, TRUE, TRUE);
	owr_media_session_set_send_payload(session, payload);

	// Add session to the transport.
	owr_transport_agent_add_session(transport_agent, OWR_SESSION(session));

	return OWR_SESSION(session);
}
static void got_candidate(OwrMediaSession *session_a, OwrCandidate *candidate, OwrMediaSession *session_b)
{
    GList *local_candidates;
    gchar *addr;
    guint port, component;
    guint rtp_port, rtcp_port;

    if (!local_addr || !remote_addr)
        owr_session_add_remote_candidate(OWR_SESSION(session_b), candidate);
    else {
        local_candidates = g_object_get_data(G_OBJECT(session_a), "local-candidates");
        local_candidates = g_list_prepend(local_candidates, candidate);
        g_object_set_data(G_OBJECT(session_a), "local-candidates", local_candidates);

        g_object_set(candidate, "ufrag", local_addr, "password", stun_pass, NULL);
    }
}
Exemple #8
0
void got_dtls_certificate(GObject *media_session, GParamSpec *pspec, gpointer user_data)
{
	guint i;
	gchar *pem, *line;
	guchar *der, *tmp;
	gchar **lines;
	gint state = 0;
	guint save = 0;
	gsize der_length = 0;
	GChecksum *checksum;
	guint8 *digest;
	gsize digest_length;
	GString *fingerprint;

	g_object_get(media_session, "dtls-certificate", &pem, NULL);
	der = tmp = g_new0(guchar, (strlen(pem) / 4) * 3 + 3);
	lines = g_strsplit(pem, "\n", 0);
	for (i = 0, line = lines[i]; line; line = lines[++i]) {
		if (line[0] && !g_str_has_prefix(line, "-----"))
			tmp += g_base64_decode_step(line, strlen(line), tmp, &state, &save);
	}
	der_length = tmp - der;
	checksum = g_checksum_new(G_CHECKSUM_SHA256);
	digest_length = g_checksum_type_get_length(G_CHECKSUM_SHA256);
	digest = g_new(guint8, digest_length);
	g_checksum_update(checksum, der, der_length);
	g_checksum_get_digest(checksum, digest, &digest_length);
	fingerprint = g_string_new(NULL);
	for (i = 0; i < digest_length; i++) {
		if (i)
			g_string_append(fingerprint, ":");
		g_string_append_printf(fingerprint, "%02X", digest[i]);
	}
	gchar *fprint = g_string_free(fingerprint, FALSE);
	got_dtls_certificate_go(OWR_SESSION(media_session), fprint);

	g_free(fprint);
	g_free(digest);
	g_checksum_free(checksum);
	g_free(der);
	g_strfreev(lines);
}
Exemple #9
0
static void owr_session_finalize(GObject *object)
{
    OwrSession *session = OWR_SESSION(object);
    OwrSessionPrivate *priv = session->priv;

    _owr_session_clear_closures(session);

    if (priv->dtls_certificate)
        g_free(priv->dtls_certificate);
    if (priv->dtls_key)
        g_free(priv->dtls_key);
    if (priv->dtls_peer_certificate)
        g_free(priv->dtls_peer_certificate);

    g_slist_free_full(priv->local_candidates, (GDestroyNotify)g_object_unref);
    g_slist_free_full(priv->remote_candidates, (GDestroyNotify)g_object_unref);
    g_slist_free_full(priv->forced_remote_candidates, (GDestroyNotify)g_object_unref);

    owr_message_origin_bus_set_free(priv->message_origin_bus_set);
    priv->message_origin_bus_set = NULL;

    G_OBJECT_CLASS(owr_session_parent_class)->finalize(object);
}
int main(int argc, char **argv)
{
    GOptionContext *options;
    GError *error = NULL;

    options = g_option_context_new(NULL);
    g_option_context_add_main_entries(options, entries, NULL);
    if (!g_option_context_parse(options, &argc, &argv, &error)) {
        g_print("Failed to parse options: %s\n", error->message);
        return 1;
    }

    if (disable_audio && disable_video) {
        g_print("Audio and video disabled. Nothing to do.\n");
        return 0;
    }

    /* PREPARE FOR RECEIVING */

    OwrPayload *receive_payload;

    owr_init(NULL);

    bus = owr_bus_new();
    owr_bus_set_message_callback(bus, (OwrBusMessageCallback) bus_message_print_callback,
        message_origin_name_func, NULL);

    if (!print_messages) {
        g_object_set(bus, "message-type-mask", OWR_MESSAGE_TYPE_ERROR, NULL);
    }

    owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(owr_window_registry_get()));

    recv_transport_agent = owr_transport_agent_new(FALSE);
    g_assert(OWR_IS_TRANSPORT_AGENT(recv_transport_agent));
    owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(recv_transport_agent));

    owr_transport_agent_set_local_port_range(recv_transport_agent, 5120, 5127);
    if (!remote_addr)
        owr_transport_agent_add_local_address(recv_transport_agent, "127.0.0.1");
    else if (local_addr)
        owr_transport_agent_add_local_address(recv_transport_agent, local_addr);

    // SEND
    send_transport_agent = owr_transport_agent_new(TRUE);
    g_assert(OWR_IS_TRANSPORT_AGENT(send_transport_agent));
    owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(send_transport_agent));

    owr_transport_agent_set_local_port_range(send_transport_agent, 5120, 5129);
    if (!remote_addr)
        owr_transport_agent_add_local_address(send_transport_agent, "127.0.0.1");

    if (!disable_video) {
        recv_session_video = owr_media_session_new(FALSE);
        owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(recv_session_video));
        send_session_video = owr_media_session_new(TRUE);
        owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(send_session_video));
    }
    if (!disable_audio) {
        recv_session_audio = owr_media_session_new(FALSE);
        owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(recv_session_audio));
        send_session_audio = owr_media_session_new(TRUE);
        owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(send_session_audio));
    }

    if (!disable_video) {
        g_signal_connect(recv_session_video, "on-new-candidate", G_CALLBACK(got_candidate), send_session_video);
        g_signal_connect(send_session_video, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_video);
        if (remote_addr) {
            g_signal_connect(recv_session_video, "on-candidate-gathering-done", G_CALLBACK(gathering_done), send_session_video);
            g_signal_connect(send_session_video, "on-candidate-gathering-done", G_CALLBACK(gathering_done), recv_session_video);
	    owr_session_set_local_port(OWR_SESSION(send_session_video), OWR_COMPONENT_TYPE_RTP, 5120);
	    owr_session_set_local_port(OWR_SESSION(send_session_video), OWR_COMPONENT_TYPE_RTCP, 5121);
	    owr_session_set_local_port(OWR_SESSION(recv_session_video), OWR_COMPONENT_TYPE_RTP, 5122);
	    owr_session_set_local_port(OWR_SESSION(recv_session_video), OWR_COMPONENT_TYPE_RTCP, 5123);
        }
    }
    if (!disable_audio) {
        g_signal_connect(recv_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), send_session_audio);
        g_signal_connect(send_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_audio);
        if (remote_addr) {
            g_signal_connect(recv_session_audio, "on-candidate-gathering-done", G_CALLBACK(gathering_done), send_session_audio);
            g_signal_connect(send_session_audio, "on-candidate-gathering-done", G_CALLBACK(gathering_done), recv_session_audio);
	    owr_session_set_local_port(OWR_SESSION(send_session_audio), OWR_COMPONENT_TYPE_RTP, 5124);
	    owr_session_set_local_port(OWR_SESSION(send_session_audio), OWR_COMPONENT_TYPE_RTCP, 5125);
	    owr_session_set_local_port(OWR_SESSION(recv_session_audio), OWR_COMPONENT_TYPE_RTP, 5126);
	    owr_session_set_local_port(OWR_SESSION(recv_session_audio), OWR_COMPONENT_TYPE_RTCP, 5127);
        }
    }

    // VIDEO
    if (!disable_video) {
        g_signal_connect(recv_session_video, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

        receive_payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 103, 90000, TRUE, FALSE);
        g_object_set(receive_payload, "rtx-payload-type", 123, NULL);
        if (adaptation)
            g_object_set(receive_payload, "adaptation", TRUE, NULL);

        owr_media_session_add_receive_payload(recv_session_video, receive_payload);
    }

    // AUDIO
    if (!disable_audio) {
        g_signal_connect(recv_session_audio, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

        receive_payload = owr_audio_payload_new(OWR_CODEC_TYPE_OPUS, 100, 48000, 1);
        owr_media_session_add_receive_payload(recv_session_audio, receive_payload);
    }

    /* PREPARE FOR SENDING */

    if (!uri) {
        owr_get_capture_sources(
                (!disable_video ? OWR_MEDIA_TYPE_VIDEO : 0) | (!disable_audio ? OWR_MEDIA_TYPE_AUDIO : 0),
                got_sources, NULL);
    } else {
        uri_source_agent = owr_uri_source_agent_new(uri);
        g_signal_connect(uri_source_agent, "on-new-source", G_CALLBACK(on_new_source), NULL);
        owr_uri_source_agent_play(uri_source_agent);
    }

    g_timeout_add_seconds(10, (GSourceFunc)dump_cb, NULL);

    owr_run();

    g_free(remote_addr);
    g_free(uri);

    return 0;
}
static void got_sources(GList *sources, gpointer user_data)
{
    OwrMediaSource *source = NULL;
    static gboolean have_video = FALSE, have_audio = FALSE;

    g_assert(sources);

    while (sources && (source = sources->data)) {
        OwrMediaType media_type;
        OwrSourceType source_type;

        g_assert(OWR_IS_MEDIA_SOURCE(source));

        g_object_get(source, "type", &source_type, "media-type", &media_type, NULL);

        if (remote_addr) {
            owr_transport_agent_add_helper_server(send_transport_agent, OWR_HELPER_SERVER_TYPE_STUN,
                "stun.services.mozilla.com", 3478, NULL, NULL);
            owr_transport_agent_add_helper_server(recv_transport_agent, OWR_HELPER_SERVER_TYPE_STUN,
                "stun.services.mozilla.com", 3478, NULL, NULL);
        }

        if (!disable_video && !have_video && media_type == OWR_MEDIA_TYPE_VIDEO) {
            OwrVideoRenderer *renderer;
            OwrPayload *payload;

            have_video = TRUE;

            owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(source));

            payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 103, 90000, TRUE, FALSE);
            g_object_set(payload, "width", 640, "height", 480, "framerate", 30.0, NULL);
            g_object_set(payload, "rtx-payload-type", 123, NULL);
            if (adaptation)
                g_object_set(payload, "adaptation", TRUE, NULL);

            owr_media_session_set_send_payload(send_session_video, payload);

            owr_media_session_set_send_source(send_session_video, source);

            owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_video));
            owr_transport_agent_add_session(send_transport_agent, OWR_SESSION(send_session_video));

            g_print("Displaying self-view\n");

            renderer = owr_video_renderer_new(NULL);
            g_assert(renderer);
            owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(renderer));

            g_object_set(renderer, "width", 640, "height", 480, "max-framerate", 30.0, NULL);
            owr_media_renderer_set_source(OWR_MEDIA_RENDERER(renderer), source);
            video_renderer = OWR_MEDIA_RENDERER(renderer);
            video_source = g_object_ref(source);
        } else if (!disable_audio && !have_audio && media_type == OWR_MEDIA_TYPE_AUDIO) {
            OwrPayload *payload;

            have_audio = TRUE;

            owr_bus_add_message_origin(bus, OWR_MESSAGE_ORIGIN(source));

            payload = owr_audio_payload_new(OWR_CODEC_TYPE_OPUS, 100, 48000, 1);
            owr_media_session_set_send_payload(send_session_audio, payload);

            owr_media_session_set_send_source(send_session_audio, source);

            owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_audio));
            owr_transport_agent_add_session(send_transport_agent, OWR_SESSION(send_session_audio));
            audio_source = g_object_ref(source);
        }

        if ((disable_video || have_video) && (disable_audio || have_audio))
            break;

        sources = sources->next;
    }
}
Exemple #12
0
static gpointer owr_session_get_bus_set(OwrMessageOrigin *origin)
{
    return OWR_SESSION(origin)->priv->message_origin_bus_set;
}
static void got_candidate(OwrSession *ignored, OwrCandidate *candidate, OwrSession *session)
{
    (void) ignored;
    owr_session_add_remote_candidate(OWR_SESSION(session), candidate);
}
static void got_candidate(OwrMediaSession *session_a, OwrCandidate *candidate, OwrMediaSession *session_b)
{
    owr_session_add_remote_candidate(OWR_SESSION(session_b), candidate);
}
int main() {
    GMainContext *ctx = g_main_context_default();
    GMainLoop *loop = g_main_loop_new(ctx, FALSE);

    /* PREPARE FOR RECEIVING */

    OwrPayload *receive_payload;

    owr_init_with_main_context(ctx);

    recv_transport_agent = owr_transport_agent_new(FALSE);
    g_assert(OWR_IS_TRANSPORT_AGENT(recv_transport_agent));

    owr_transport_agent_set_local_port_range(recv_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(recv_transport_agent, "127.0.0.1");

    // SEND
    send_transport_agent = owr_transport_agent_new(TRUE);
    g_assert(OWR_IS_TRANSPORT_AGENT(send_transport_agent));

    owr_transport_agent_set_local_port_range(send_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(send_transport_agent, "127.0.0.1");

    recv_session_audio = owr_media_session_new(FALSE);
    recv_session_video = owr_media_session_new(FALSE);
    send_session_audio = owr_media_session_new(TRUE);
    send_session_video = owr_media_session_new(TRUE);

    g_signal_connect(recv_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), send_session_audio);
    g_signal_connect(recv_session_video, "on-new-candidate", G_CALLBACK(got_candidate), send_session_video);
    g_signal_connect(send_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_audio);
    g_signal_connect(send_session_video, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_video);

    // VIDEO
    g_signal_connect(recv_session_video, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

    receive_payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 103, 90000, TRUE, FALSE);
    g_object_set(receive_payload, "rtx-payload-type", 123, NULL);

    owr_media_session_add_receive_payload(recv_session_video, receive_payload);
    g_object_unref(receive_payload);

    owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_video));


    // AUDIO
    g_signal_connect(recv_session_audio, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

    receive_payload = owr_audio_payload_new(OWR_CODEC_TYPE_OPUS, 100, 48000, 1);
    owr_media_session_add_receive_payload(recv_session_audio, receive_payload);
    g_object_unref(receive_payload);

    owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_audio));


    /* PREPARE FOR SENDING */

    owr_get_capture_sources(OWR_MEDIA_TYPE_AUDIO|OWR_MEDIA_TYPE_VIDEO, got_sources, NULL);

    g_timeout_add_seconds(5, (GSourceFunc)dump_cb, NULL);

    g_main_loop_run(loop);

    return 0;
}
int main(int argc, char **argv)
{
    GOptionContext *options;
    GError *error = NULL;

    options = g_option_context_new(NULL);
    g_option_context_add_main_entries(options, entries, NULL);
    if (!g_option_context_parse(options, &argc, &argv, &error)) {
        g_print("Failed to parse options: %s\n", error->message);
        return 1;
    }

    if (disable_audio && disable_video) {
        g_print("Audio and video disabled. Nothing to do.\n");
        return 0;
    }

    /* PREPARE FOR RECEIVING */

    OwrPayload *receive_payload;

    owr_init(NULL);

    recv_transport_agent = owr_transport_agent_new(FALSE);
    g_assert(OWR_IS_TRANSPORT_AGENT(recv_transport_agent));

    owr_transport_agent_set_local_port_range(recv_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(recv_transport_agent, "127.0.0.1");

    // SEND
    send_transport_agent = owr_transport_agent_new(TRUE);
    g_assert(OWR_IS_TRANSPORT_AGENT(send_transport_agent));

    owr_transport_agent_set_local_port_range(send_transport_agent, 5000, 5999);
    owr_transport_agent_add_local_address(send_transport_agent, "127.0.0.1");

    if (!disable_video) {
        recv_session_video = owr_media_session_new(FALSE);
        send_session_video = owr_media_session_new(TRUE);
    }
    if (!disable_audio) {
        recv_session_audio = owr_media_session_new(FALSE);
        send_session_audio = owr_media_session_new(TRUE);
    }

    if (!disable_video) {
        g_signal_connect(recv_session_video, "on-new-candidate", G_CALLBACK(got_candidate), send_session_video);
        g_signal_connect(send_session_video, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_video);
    }
    if (!disable_audio) {
        g_signal_connect(recv_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), send_session_audio);
        g_signal_connect(send_session_audio, "on-new-candidate", G_CALLBACK(got_candidate), recv_session_audio);
    }

    // VIDEO
    if (!disable_video) {
        g_signal_connect(recv_session_video, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

        receive_payload = owr_video_payload_new(OWR_CODEC_TYPE_VP8, 103, 90000, TRUE, FALSE);
        g_object_set(receive_payload, "rtx-payload-type", 123, NULL);

        owr_media_session_add_receive_payload(recv_session_video, receive_payload);

        owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_video));
    }

    // AUDIO
    if (!disable_audio) {
        g_signal_connect(recv_session_audio, "on-incoming-source", G_CALLBACK(got_remote_source), NULL);

        receive_payload = owr_audio_payload_new(OWR_CODEC_TYPE_OPUS, 100, 48000, 1);
        owr_media_session_add_receive_payload(recv_session_audio, receive_payload);

        owr_transport_agent_add_session(recv_transport_agent, OWR_SESSION(recv_session_audio));
    }

    /* PREPARE FOR SENDING */

    owr_get_capture_sources((!disable_video ? OWR_MEDIA_TYPE_VIDEO : 0) | (!disable_audio ? OWR_MEDIA_TYPE_AUDIO : 0),
        got_sources, NULL);

    g_timeout_add_seconds(10, (GSourceFunc)dump_cb, NULL);

    owr_run();

    return 0;
}