2 #include <farsight/farsight.h>
3 #include <farsight/farsight-transport.h>
4 #include <dbus/dbus-glib.h>
5 #include <glib/gprintf.h>
7 #include "tox-session.h"
8 #include "tox-marshal.h"
10 G_DEFINE_TYPE(ToxSession
, tox_session
, G_TYPE_OBJECT
)
12 typedef struct _ToxSessionPrivate
{
13 FarsightSession
*session
;
14 /* for now, one stream is enough */
15 FarsightStream
*stream
;
17 int have_source
, have_sink
;
19 gboolean dispose_has_run
;
22 gboolean
tox_session_destroy(ToxSession
*obj
, GError
**error
);
23 gboolean
tox_session_set_default_audio_sink(ToxSession
*obj
, GError
**error
);
24 gboolean
tox_session_set_ogg_vorbis_audio_source(ToxSession
*obj
, char *filename
, GError
**error
);
25 gboolean
tox_session_add_remote_candidate(ToxSession
*obj
, GPtrArray
*candidate
, GError
**error
);
26 gboolean
tox_session_set_remote_codecs(ToxSession
*obj
, GPtrArray
*codecs
, GError
**error
);
27 gboolean
tox_session_get_local_codecs(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
);
28 gboolean
tox_session_get_codec_intersection(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
);
34 static void tox_session_set_property(GObject
*obj
, guint property_id
, const GValue
*value
, GParamSpec
*pspec
);
39 NATIVE_CANDIDATES_PREPARED
,
41 NEW_ACTIVE_CANDIDATE_PAIR
,
45 static guint signals
[LAST_SIGNAL
];
47 #include "tox-session-glue.h"
49 static GstElement
*prepare_source(const char *filename
);
50 static void new_pad(GstElement
*, GstPad
*, gpointer
);
51 static GstElement
*prepare_sink(void);
52 static void stream_done(ToxSession
*);
54 static GValueArray
* candidate_list_to_dbus_array(const GList
*candidates
);
56 static void tox_session_native_candidates_prepared(FarsightStream
*stream
, gpointer user_data
);
57 static void tox_session_new_native_candidate(FarsightStream
*stream
, gchar
*candidate_id
, ToxSession
*self
);
58 static void tox_session_state_changed(FarsightStream
*stream
, gint state
, gint direction
, ToxSession
*self
);
59 static void tox_session_new_active_candidate_pair(FarsightStream
*stream
, gchar
*native_candidate_id
, gchar
*remote_candidate_id
, ToxSession
*self
);
62 tox_session_init(ToxSession
*obj
)
64 ToxSessionPrivate
*priv
;
65 priv
= g_new0(ToxSessionPrivate
, 1);
68 priv
->session
= farsight_session_factory_make("rtp");
69 g_assert(priv
->session
);
71 /* we need to know the direction to create a stream,
72 so we do that when that property is set.
77 static GObjectClass
*parent_class
= NULL
;
80 tox_session_dispose(GObject
*obj
)
82 ToxSession
*self
= (ToxSession
*)obj
;
84 if (self
->priv
->dispose_has_run
) {
87 g_debug("in tox_session_dispose\n");
88 self
->priv
->dispose_has_run
= TRUE
;
90 if (self
->priv
->stream
)
91 g_object_unref(self
->priv
->stream
);
92 if (self
->priv
->session
)
93 g_object_unref(self
->priv
->session
);
95 self
->priv
->stream
= NULL
;
96 self
->priv
->session
= NULL
;
98 G_OBJECT_CLASS(parent_class
)->dispose(obj
);
102 tox_session_finalize(GObject
*obj
)
104 ToxSession
*self
= (ToxSession
*)obj
;
106 g_debug("in tox_session_finalize\n");
109 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
113 tox_session_class_init(ToxSessionClass
*klass
)
115 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
116 GParamSpec
*direction_param_spec
;
118 gobject_class
->dispose
= tox_session_dispose
;
119 gobject_class
->finalize
= tox_session_finalize
;
121 gobject_class
->set_property
= tox_session_set_property
;
123 direction_param_spec
= g_param_spec_uint("direction",
125 "1 means send-only, 2 means receive-only, 3 means both",
129 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
);
130 g_object_class_install_property(gobject_class
,
132 direction_param_spec
);
134 signals
[NEW_NATIVE_CANDIDATE
] =
135 g_signal_new("new-native-candidate",
136 G_OBJECT_CLASS_TYPE(klass
),
137 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
140 tox_marshal_VOID__BOXED
,
145 signals
[NATIVE_CANDIDATES_PREPARED
] =
146 g_signal_new("native-candidates-prepared",
147 G_OBJECT_CLASS_TYPE(klass
),
148 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
151 tox_marshal_VOID__BOXED
,
156 signals
[STATE_CHANGED
] =
157 g_signal_new("state-changed",
158 G_OBJECT_CLASS_TYPE(klass
),
159 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
162 tox_marshal_VOID__UCHAR_UCHAR
,
168 signals
[NEW_ACTIVE_CANDIDATE_PAIR
] =
169 g_signal_new("new-active-candidate-pair",
170 G_OBJECT_CLASS_TYPE(klass
),
171 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
174 tox_marshal_VOID__STRING_STRING
,
180 dbus_g_object_type_install_info(TOX_TYPE_SESSION
, &dbus_glib_tox_session_object_info
);
182 parent_class
= g_type_class_peek_parent (klass
);
186 tox_session_set_property(GObject
*obj
, guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
188 ToxSession
*self
= (ToxSession
*)obj
;
191 switch(property_id
) {
193 if (self
->priv
->stream
)
194 g_object_unref(self
->priv
->stream
);
195 /* Now we know the direction, so we create a stream. */
196 dir
= g_value_get_uint(value
);
197 self
->priv
->stream
= farsight_session_create_stream(self
->priv
->session
,
198 FARSIGHT_MEDIA_TYPE_AUDIO
,
200 g_assert(self
->priv
->stream
);
202 g_object_set(G_OBJECT(self
->priv
->stream
), "transmitter", "libjingle", NULL
);
204 /* XXX: should we set null source/sink here? */
207 /* send-only, we need no sink */
208 self
->priv
->have_sink
= 1;
211 /* receive-only, we need no source */
212 self
->priv
->have_source
= 1;
216 /* start preparing native candidates */
217 g_debug("About to prepare native candidates...\n");
218 g_signal_connect(self
->priv
->stream
, "new-native-candidate",
219 (GCallback
)tox_session_new_native_candidate
, (gpointer
)self
);
220 g_signal_connect(self
->priv
->stream
, "native-candidates-prepared",
221 (GCallback
)tox_session_native_candidates_prepared
, (gpointer
)self
);
222 /* but we can't actually do it until we have a pipeline.
223 so, we'll call stream_done when we do. */
225 /* Other signals we want to forward */
226 g_signal_connect(self
->priv
->stream
, "state-changed",
227 (GCallback
)tox_session_state_changed
, (gpointer
)self
);
228 g_signal_connect(self
->priv
->stream
, "new-active-candidate-pair",
229 (GCallback
)tox_session_new_active_candidate_pair
, (gpointer
)self
);
233 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, property_id
, pspec
);
238 tox_session_destroy(ToxSession
*obj
, GError
**error
)
245 tox_session_set_default_audio_sink(ToxSession
*obj
, GError
**error
)
247 GstElement
*sink
= prepare_sink();
248 farsight_stream_set_sink(obj
->priv
->stream
, sink
);
249 obj
->priv
->have_sink
= 1;
251 if (obj
->priv
->have_sink
&& obj
->priv
->have_source
)
258 tox_session_set_ogg_vorbis_audio_source(ToxSession
*obj
, char *filename
, GError
**error
)
260 GstElement
*source
= prepare_source(filename
);
261 farsight_stream_set_source(obj
->priv
->stream
, source
);
262 obj
->priv
->have_source
= 1;
264 if (obj
->priv
->have_sink
&& obj
->priv
->have_source
)
271 prepare_source(const char *filename
)
273 GstElement
*bin
, *filesrc
, *demux
, *decode
;
275 bin
= gst_bin_new("mysource");
276 filesrc
= gst_element_factory_make("filesrc", "file-source");
277 g_object_set(G_OBJECT(filesrc
), "location", filename
, NULL
);
279 demux
= gst_element_factory_make("oggdemux", "ogg-parser");
280 decode
= gst_element_factory_make("vorbisdec", "vorbis-decoder");
282 gst_element_link(filesrc
, demux
);
283 g_signal_connect(demux
, "pad-added", G_CALLBACK(new_pad
), decode
);
285 gst_bin_add_many(GST_BIN(bin
), filesrc
, demux
, decode
, NULL
);
291 new_pad(GstElement
*demux
, GstPad
*pad
, gpointer data
)
293 GstElement
*decode
= (GstElement
*)data
;
296 decoder_pad
= gst_element_get_pad(decode
, "sink");
297 gst_pad_link(pad
, decoder_pad
);
299 gst_object_unref(decoder_pad
);
305 GstElement
*bin
, *converter
, *audiosink
;
307 bin
= gst_bin_new("mysink");
308 converter
= gst_element_factory_make("audioconvert", "converter");
309 audiosink
= gst_element_factory_make("autoaudiosink", "audiosink");
310 gst_element_link(converter
, audiosink
);
312 gst_bin_add_many(GST_BIN(bin
), converter
, audiosink
, NULL
);
318 stream_done(ToxSession
*self
)
320 farsight_stream_prepare_transports(self
->priv
->stream
);
324 tox_session_add_remote_candidate(ToxSession
*self
, GPtrArray
*candidate
, GError
**error
)
328 GList
*candidate_list
;
330 candidate_list
= NULL
;
332 /* Here we convert the array of structs into a GList of
333 FarsightTransportInfo. The argument list is described in
335 for (i
= 0; i
< candidate
->len
; i
++) {
336 GValueArray
*component
;
337 FarsightTransportInfo
*info
;
340 component
= g_ptr_array_index(candidate
, i
);
341 info
= g_new0(FarsightTransportInfo
, 1);
345 g_value_array_get_nth(component
, 0));
348 g_value_array_get_nth(component
, 1));
351 g_value_array_get_nth(component
, 2));
354 g_value_array_get_nth(component
, 3));
356 s
= g_value_dup_string(g_value_array_get_nth(component
, 4));
357 if (strcmp(s
, "tcp") == 0)
358 info
->proto
= FARSIGHT_NETWORK_PROTOCOL_TCP
;
359 else if (strcmp(s
, "udp") == 0)
360 info
->proto
= FARSIGHT_NETWORK_PROTOCOL_UDP
;
362 g_set_error(error
, DBUS_GERROR
,
363 DBUS_GERROR_REMOTE_EXCEPTION
,
364 "Unexpected protocol '%s'",
372 /* this should be "RTP" */
373 info
->proto_subtype
=
375 g_value_array_get_nth(component
, 5));
376 /* this should be "AVP" */
377 info
->proto_profile
=
379 g_value_array_get_nth(component
, 6));
383 g_value_array_get_nth(component
, 7))
386 n
= g_value_get_uint(g_value_array_get_nth(component
, 8));
389 info
->type
= FARSIGHT_CANDIDATE_TYPE_LOCAL
;
392 info
->type
= FARSIGHT_CANDIDATE_TYPE_DERIVED
;
395 info
->type
= FARSIGHT_CANDIDATE_TYPE_RELAY
;
398 g_set_error(error
, DBUS_GERROR
,
399 DBUS_GERROR_REMOTE_EXCEPTION
,
400 "Unexpected type %u",
406 info
->username
= g_value_dup_string(
407 g_value_array_get_nth(component
, 9));
408 info
->password
= g_value_dup_string(
409 g_value_array_get_nth(component
, 10));
411 candidate_list
= g_list_append(candidate_list
, info
);
414 farsight_stream_add_remote_candidate(self
->priv
->stream
, candidate_list
);
418 farsight_transport_list_destroy(candidate_list
);
423 tox_session_set_remote_codecs(ToxSession
*obj
, GPtrArray
*codecs
, GError
**error
)
431 for (i
= 0; i
< codecs
->len
; i
++) {
432 GValueArray
*codec_in
;
433 FarsightCodec
*codec_out
;
435 codec_in
= g_ptr_array_index(codecs
, i
);
436 codec_out
= g_new0(FarsightCodec
, 1);
438 codec_out
->id
= g_value_get_int(g_value_array_get_nth(codec_in
, 0));
439 codec_out
->encoding_name
= g_value_dup_string(g_value_array_get_nth(codec_in
, 1));
440 /* maybe check range of media_type... */
441 codec_out
->media_type
= g_value_get_uchar(g_value_array_get_nth(codec_in
, 2));
442 codec_out
->clock_rate
= g_value_get_uint(g_value_array_get_nth(codec_in
, 3));
443 codec_out
->channels
= g_value_get_uint(g_value_array_get_nth(codec_in
, 4));
445 codec_list
= g_list_append(codec_list
, codec_out
);
448 farsight_stream_set_remote_codecs(obj
->priv
->stream
, codec_list
);
450 /* should the elements be freed, or just the list itself? */
451 /*for (j = codec_list; j; j = g_list_next(j)) {
454 g_list_free(codec_list
);
460 tox_session_get_local_codecs(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
)
462 FarsightStream
*stream
;
463 const GList
*codec_list
;
465 GType hash_string_string
;
467 stream
= obj
->priv
->stream
;
468 codec_list
= farsight_stream_get_local_codecs(stream
);
470 hash_string_string
= dbus_g_type_get_map("GHashTable", G_TYPE_STRING
, G_TYPE_STRING
);
472 *codecs
= g_ptr_array_sized_new(g_list_length(codec_list
));
473 for (; codec_list
; codec_list
= g_list_next(codec_list
)) {
474 GValueArray
*codec_struct
;
475 const FarsightCodec
*codec
;
477 GHashTable
*parameters
;
480 memset(&value
, 0, sizeof value
);
482 codec
= (const FarsightCodec
*)codec_list
->data
;
483 codec_struct
= g_value_array_new(2);
485 g_value_init(&value
, G_TYPE_INT
);
486 g_value_set_int(&value
, codec
->id
);
487 g_value_array_append(codec_struct
, &value
);
488 g_value_unset(&value
);
490 g_value_init(&value
, G_TYPE_STRING
);
491 g_value_set_string(&value
, codec
->encoding_name
);
492 g_value_array_append(codec_struct
, &value
);
493 g_value_unset(&value
);
495 /* XXX: why is this the same as id? */
496 g_value_init(&value
, G_TYPE_UCHAR
);
497 g_value_set_uchar(&value
, codec
->media_type
);
498 g_value_array_append(codec_struct
, &value
);
499 g_value_unset(&value
);
501 g_value_init(&value
, G_TYPE_UINT
);
502 g_value_set_uint(&value
, codec
->clock_rate
);
503 g_value_array_append(codec_struct
, &value
);
504 g_value_unset(&value
);
506 g_value_init(&value
, G_TYPE_UINT
);
507 g_value_set_uint(&value
, codec
->channels
);
508 g_value_array_append(codec_struct
, &value
);
509 g_value_unset(&value
);
511 /* optional parameters - this should be a hash table, I think */
512 /* parameters = g_hash_table_new(g_str_hash, g_str_equal); */
513 parameters
= dbus_g_type_specialized_construct(hash_string_string
);
514 for (p
= codec
->optional_params
; p
; p
= g_list_next(p
)) {
515 FarsightCodecParameter
*param
= p
->data
;
517 g_hash_table_insert(parameters
, param
->name
, param
->value
);
519 g_value_init(&value
, hash_string_string
);
520 g_value_set_boxed(&value
, parameters
);
521 g_value_array_append(codec_struct
, &value
);
522 g_value_unset(&value
);
523 g_hash_table_unref(parameters
);
525 g_assert(codec_struct
->n_values
== 6);
527 g_ptr_array_add(*codecs
, codec_struct
);
529 g_debug("Local codec: %s\n", codec
->encoding_name
);
536 tox_session_get_codec_intersection(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
)
538 FarsightStream
*stream
;
541 stream
= obj
->priv
->stream
;
542 codec_list
= farsight_stream_get_codec_intersection(stream
);
544 *codecs
= g_ptr_array_sized_new(g_list_length(codec_list
));
545 for (; codec_list
; codec_list
= g_list_next(codec_list
)) {
546 GValueArray
*codec_struct
;
547 FarsightCodec
*codec
;
550 codec
= (FarsightCodec
*)codec_list
->data
;
551 codec_struct
= g_value_array_new(6);
553 value
= g_new(GValue
, 1);
554 g_value_init(value
, G_TYPE_INT
);
555 g_value_set_int(value
, codec
->id
);
556 g_value_array_append(codec_struct
, value
);
558 value
= g_new(GValue
, 1);
559 g_value_init(value
, G_TYPE_STRING
);
560 g_value_set_string(value
, codec
->encoding_name
);
561 g_value_array_append(codec_struct
, value
);
563 value
= g_new(GValue
, 1);
564 g_value_init(value
, G_TYPE_UCHAR
);
565 g_value_set_uchar(value
, codec
->media_type
);
566 g_value_array_append(codec_struct
, value
);
568 value
= g_new(GValue
, 1);
569 g_value_init(value
, G_TYPE_UINT
);
570 g_value_set_uint(value
, codec
->clock_rate
);
571 g_value_array_append(codec_struct
, value
);
573 value
= g_new(GValue
, 1);
574 g_value_init(value
, G_TYPE_UINT
);
575 g_value_set_uint(value
, codec
->channels
);
576 g_value_array_append(codec_struct
, value
);
578 /* XXX: do something about optional parameters */
579 value
= g_new(GValue
, 1);
580 g_value_init(value
, G_TYPE_HASH_TABLE
);
581 g_value_set_boxed(value
, g_hash_table_new(g_str_hash
, g_str_equal
));
582 g_value_array_append(codec_struct
, value
);
584 g_assert(codec_struct
->n_values
== 6);
586 g_ptr_array_add(*codecs
, codec_struct
);
593 tox_session_native_candidates_prepared(FarsightStream
*stream
, gpointer user_data
)
595 ToxSession
*self
= (ToxSession
*)user_data
;
596 const GList
*candidates
;
599 candidates
= farsight_stream_get_native_candidate_list(stream
);
601 array
= candidate_list_to_dbus_array(candidates
);
603 g_debug("Sending signal NativeCandidatesPrepared!\n");
604 g_signal_emit(self
, signals
[NATIVE_CANDIDATES_PREPARED
], 0, array
);
608 tox_session_new_native_candidate(FarsightStream
*stream
, gchar
*candidate_id
, ToxSession
*self
)
611 farsight_stream_get_native_candidate (stream
, candidate_id
);
612 FarsightTransportInfo
*trans
= candidate
->data
;
615 g_debug ("tox_session_new_native_candidate: New native candidate"
616 " with %d components, the first being: "
621 "proto_subtype: %s, "
622 "proto_profile: %s, "
625 "username: %s password: %s>",
626 g_list_length(candidate
),
627 trans
->candidate_id
, trans
->component
,
628 trans
->ip
, trans
->port
, trans
->proto
, trans
->proto_subtype
,
629 trans
->proto_profile
, trans
->preference
,
630 trans
->type
, trans
->username
, trans
->password
);
632 array
= candidate_list_to_dbus_array(candidate
);
633 g_debug("Sending signal NewNativeCandidate!\n");
634 g_signal_emit(self
, signals
[NEW_NATIVE_CANDIDATE
], 0, array
);
638 candidate_list_to_dbus_array(const GList
*candidates
)
642 array
= g_value_array_new(1);
644 for (; candidates
; candidates
= g_list_next(candidates
)) {
645 GValueArray
*candidate
;
646 FarsightTransportInfo
*info
;
649 info
= (FarsightTransportInfo
*)candidates
->data
;
650 candidate
= g_value_array_new(11);
652 memset(&value
, 0, sizeof value
);
654 g_value_init(&value
, G_TYPE_STRING
);
655 g_value_set_string(&value
, info
->candidate_id
);
656 g_value_array_append(candidate
, &value
);
657 g_value_unset(&value
);
659 g_value_init(&value
, G_TYPE_UINT
);
660 g_value_set_uint(&value
, info
->component
);
661 g_value_array_append(candidate
, &value
);
662 g_value_unset(&value
);
664 g_value_init(&value
, G_TYPE_STRING
);
665 g_value_set_string(&value
, info
->ip
);
666 g_value_array_append(candidate
, &value
);
667 g_value_unset(&value
);
669 g_value_init(&value
, G_TYPE_UINT
);
670 g_value_set_uint(&value
, info
->port
);
671 g_value_array_append(candidate
, &value
);
672 g_value_unset(&value
);
674 g_value_init(&value
, G_TYPE_STRING
);
675 switch(info
->proto
) {
676 case FARSIGHT_NETWORK_PROTOCOL_UDP
:
677 g_value_set_static_string(&value
, "udp");
679 case FARSIGHT_NETWORK_PROTOCOL_TCP
:
680 g_value_set_static_string(&value
, "tcp");
683 g_error("Unknown protocol value %u\n", info
->proto
);
685 g_value_array_append(candidate
, &value
);
686 g_value_unset(&value
);
688 g_value_init(&value
, G_TYPE_STRING
);
689 g_value_set_string(&value
, info
->proto_subtype
);
690 g_value_array_append(candidate
, &value
);
691 g_value_unset(&value
);
693 g_value_init(&value
, G_TYPE_STRING
);
694 g_value_set_string(&value
, info
->proto_profile
);
695 g_value_array_append(candidate
, &value
);
696 g_value_unset(&value
);
698 g_value_init(&value
, G_TYPE_UINT
);
699 g_value_set_uint(&value
, (guint
)(info
->preference
* 100));
700 g_value_array_append(candidate
, &value
);
701 g_value_unset(&value
);
703 g_value_init(&value
, G_TYPE_UINT
);
705 case FARSIGHT_CANDIDATE_TYPE_LOCAL
:
706 g_value_set_uint(&value
, 0);
708 case FARSIGHT_CANDIDATE_TYPE_DERIVED
:
709 g_value_set_uint(&value
, 1);
711 case FARSIGHT_CANDIDATE_TYPE_RELAY
:
712 g_value_set_uint(&value
, 2);
715 g_error("Unknown candidate type %u\n", info
->proto
);
717 g_value_array_append(candidate
, &value
);
718 g_value_unset(&value
);
720 g_value_init(&value
, G_TYPE_STRING
);
721 g_value_set_string(&value
, info
->username
);
722 g_value_array_append(candidate
, &value
);
723 g_value_unset(&value
);
725 g_value_init(&value
, G_TYPE_STRING
);
726 g_value_set_string(&value
, info
->password
);
727 g_value_array_append(candidate
, &value
);
728 g_value_unset(&value
);
730 g_assert(candidate
->n_values
== 11);
732 g_value_init(&value
, G_TYPE_VALUE_ARRAY
);
733 /* apparently GValueArray is a "boxed" type */
734 g_value_set_boxed(&value
, candidate
);
735 g_value_array_append(array
, &value
);
736 g_value_unset(&value
);
743 tox_session_state_changed(FarsightStream
*stream
, gint state
, gint direction
, ToxSession
*self
)
745 g_signal_emit(self
, signals
[STATE_CHANGED
], 0, state
, direction
);
749 tox_session_new_active_candidate_pair(FarsightStream
*stream
, gchar
*native_candidate_id
, gchar
*remote_candidate_id
, ToxSession
*self
)
751 g_signal_emit(self
, signals
[NEW_ACTIVE_CANDIDATE_PAIR
], 0, native_candidate_id
, remote_candidate_id
);