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
,
43 static guint signals
[LAST_SIGNAL
];
45 #include "tox-session-glue.h"
47 static GstElement
*prepare_source(const char *filename
);
48 static void new_pad(GstElement
*, GstPad
*, gpointer
);
49 static GstElement
*prepare_sink(void);
50 static void stream_done(ToxSession
*);
52 static GValueArray
* candidate_list_to_dbus_array(const GList
*candidates
);
54 static void tox_session_native_candidates_prepared(FarsightStream
*stream
, gpointer user_data
);
55 static void tox_session_new_native_candidate(FarsightStream
*stream
, gchar
*candidate_id
, ToxSession
*self
);
58 tox_session_init(ToxSession
*obj
)
60 ToxSessionPrivate
*priv
;
61 priv
= g_new0(ToxSessionPrivate
, 1);
64 priv
->session
= farsight_session_factory_make("rtp");
65 g_assert(priv
->session
);
67 /* we need to know the direction to create a stream,
68 so we do that when that property is set.
73 static GObjectClass
*parent_class
= NULL
;
76 tox_session_dispose(GObject
*obj
)
78 ToxSession
*self
= (ToxSession
*)obj
;
80 if (self
->priv
->dispose_has_run
) {
83 g_debug("in tox_session_dispose\n");
84 self
->priv
->dispose_has_run
= TRUE
;
86 if (self
->priv
->stream
)
87 g_object_unref(self
->priv
->stream
);
88 if (self
->priv
->session
)
89 g_object_unref(self
->priv
->session
);
91 self
->priv
->stream
= NULL
;
92 self
->priv
->session
= NULL
;
94 G_OBJECT_CLASS(parent_class
)->dispose(obj
);
98 tox_session_finalize(GObject
*obj
)
100 ToxSession
*self
= (ToxSession
*)obj
;
102 g_debug("in tox_session_finalize\n");
105 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
109 tox_session_class_init(ToxSessionClass
*klass
)
111 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
112 GParamSpec
*direction_param_spec
;
114 gobject_class
->dispose
= tox_session_dispose
;
115 gobject_class
->finalize
= tox_session_finalize
;
117 gobject_class
->set_property
= tox_session_set_property
;
119 direction_param_spec
= g_param_spec_uint("direction",
121 "1 means send-only, 2 means receive-only, 3 means both",
125 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
);
126 g_object_class_install_property(gobject_class
,
128 direction_param_spec
);
130 signals
[NEW_NATIVE_CANDIDATE
] =
131 g_signal_new("new-native-candidate",
132 G_OBJECT_CLASS_TYPE(klass
),
133 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
136 tox_marshal_VOID__BOXED
,
141 signals
[NATIVE_CANDIDATES_PREPARED
] =
142 g_signal_new("native-candidates-prepared",
143 G_OBJECT_CLASS_TYPE(klass
),
144 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
147 tox_marshal_VOID__BOXED
,
152 dbus_g_object_type_install_info(TOX_TYPE_SESSION
, &dbus_glib_tox_session_object_info
);
154 parent_class
= g_type_class_peek_parent (klass
);
158 tox_session_set_property(GObject
*obj
, guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
160 ToxSession
*self
= (ToxSession
*)obj
;
163 switch(property_id
) {
165 if (self
->priv
->stream
)
166 g_object_unref(self
->priv
->stream
);
167 /* Now we know the direction, so we create a stream. */
168 dir
= g_value_get_uint(value
);
169 self
->priv
->stream
= farsight_session_create_stream(self
->priv
->session
,
170 FARSIGHT_MEDIA_TYPE_AUDIO
,
172 g_assert(self
->priv
->stream
);
174 g_object_set(G_OBJECT(self
->priv
->stream
), "transmitter", "libjingle", NULL
);
176 /* XXX: should we set null source/sink here? */
179 /* send-only, we need no sink */
180 self
->priv
->have_sink
= 1;
183 /* receive-only, we need no source */
184 self
->priv
->have_source
= 1;
188 /* start preparing native candidates */
189 g_debug("About to prepare native candidates...\n");
190 g_signal_connect(self
->priv
->stream
, "new-native-candidate",
191 (GCallback
)tox_session_new_native_candidate
, (gpointer
)self
);
192 g_signal_connect(self
->priv
->stream
, "native-candidates-prepared",
193 (GCallback
)tox_session_native_candidates_prepared
, (gpointer
)self
);
194 /* but we can't actually do it until we have a pipeline.
195 so, we'll call stream_done when we do. */
198 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, property_id
, pspec
);
203 tox_session_destroy(ToxSession
*obj
, GError
**error
)
210 tox_session_set_default_audio_sink(ToxSession
*obj
, GError
**error
)
212 GstElement
*sink
= prepare_sink();
213 farsight_stream_set_sink(obj
->priv
->stream
, sink
);
214 obj
->priv
->have_sink
= 1;
216 if (obj
->priv
->have_sink
&& obj
->priv
->have_source
)
223 tox_session_set_ogg_vorbis_audio_source(ToxSession
*obj
, char *filename
, GError
**error
)
225 GstElement
*source
= prepare_source(filename
);
226 farsight_stream_set_source(obj
->priv
->stream
, source
);
227 obj
->priv
->have_source
= 1;
229 if (obj
->priv
->have_sink
&& obj
->priv
->have_source
)
236 prepare_source(const char *filename
)
238 GstElement
*bin
, *filesrc
, *demux
, *decode
;
240 bin
= gst_bin_new("mysource");
241 filesrc
= gst_element_factory_make("filesrc", "file-source");
242 g_object_set(G_OBJECT(filesrc
), "location", filename
, NULL
);
244 demux
= gst_element_factory_make("oggdemux", "ogg-parser");
245 decode
= gst_element_factory_make("vorbisdec", "vorbis-decoder");
247 gst_element_link(filesrc
, demux
);
248 g_signal_connect(demux
, "pad-added", G_CALLBACK(new_pad
), decode
);
250 gst_bin_add_many(GST_BIN(bin
), filesrc
, demux
, decode
, NULL
);
256 new_pad(GstElement
*demux
, GstPad
*pad
, gpointer data
)
258 GstElement
*decode
= (GstElement
*)data
;
261 decoder_pad
= gst_element_get_pad(decode
, "sink");
262 gst_pad_link(pad
, decoder_pad
);
264 gst_object_unref(decoder_pad
);
270 GstElement
*bin
, *converter
, *audiosink
;
272 bin
= gst_bin_new("mysink");
273 converter
= gst_element_factory_make("audioconvert", "converter");
274 audiosink
= gst_element_factory_make("autoaudiosink", "audiosink");
275 gst_element_link(converter
, audiosink
);
277 gst_bin_add_many(GST_BIN(bin
), converter
, audiosink
, NULL
);
283 stream_done(ToxSession
*self
)
285 farsight_stream_prepare_transports(self
->priv
->stream
);
289 tox_session_add_remote_candidate(ToxSession
*self
, GPtrArray
*candidate
, GError
**error
)
293 GList
*candidate_list
;
295 candidate_list
= NULL
;
297 /* Here we convert the array of structs into a GList of
298 FarsightTransportInfo. The argument list is described in
300 for (i
= 0; i
< candidate
->len
; i
++) {
301 GValueArray
*component
;
302 FarsightTransportInfo
*info
;
305 component
= g_ptr_array_index(candidate
, i
);
306 info
= g_new0(FarsightTransportInfo
, 1);
310 g_value_array_get_nth(component
, 0));
313 g_value_array_get_nth(component
, 1));
316 g_value_array_get_nth(component
, 2));
319 g_value_array_get_nth(component
, 3));
321 s
= g_value_dup_string(g_value_array_get_nth(component
, 4));
322 if (strcmp(s
, "tcp") == 0)
323 info
->proto
= FARSIGHT_NETWORK_PROTOCOL_TCP
;
324 else if (strcmp(s
, "udp") == 0)
325 info
->proto
= FARSIGHT_NETWORK_PROTOCOL_UDP
;
327 g_set_error(error
, DBUS_GERROR
,
328 DBUS_GERROR_REMOTE_EXCEPTION
,
329 "Unexpected protocol '%s'",
337 /* this should be "RTP" */
338 info
->proto_subtype
=
340 g_value_array_get_nth(component
, 5));
341 /* this should be "AVP" */
342 info
->proto_profile
=
344 g_value_array_get_nth(component
, 6));
348 g_value_array_get_nth(component
, 7))
351 n
= g_value_get_uint(g_value_array_get_nth(component
, 8));
354 info
->type
= FARSIGHT_CANDIDATE_TYPE_LOCAL
;
357 info
->type
= FARSIGHT_CANDIDATE_TYPE_DERIVED
;
360 info
->type
= FARSIGHT_CANDIDATE_TYPE_RELAY
;
363 g_set_error(error
, DBUS_GERROR
,
364 DBUS_GERROR_REMOTE_EXCEPTION
,
365 "Unexpected type %u",
371 info
->username
= g_value_dup_string(
372 g_value_array_get_nth(component
, 9));
373 info
->password
= g_value_dup_string(
374 g_value_array_get_nth(component
, 10));
376 candidate_list
= g_list_append(candidate_list
, info
);
379 farsight_stream_add_remote_candidate(self
->priv
->stream
, candidate_list
);
383 farsight_transport_list_destroy(candidate_list
);
388 tox_session_set_remote_codecs(ToxSession
*obj
, GPtrArray
*codecs
, GError
**error
)
396 for (i
= 0; i
< codecs
->len
; i
++) {
397 GValueArray
*codec_in
;
398 FarsightCodec
*codec_out
;
400 codec_in
= g_ptr_array_index(codecs
, i
);
401 codec_out
= g_new0(FarsightCodec
, 1);
403 codec_out
->id
= g_value_get_int(g_value_array_get_nth(codec_in
, 0));
404 codec_out
->encoding_name
= g_value_dup_string(g_value_array_get_nth(codec_in
, 1));
405 /* maybe check range of media_type... */
406 codec_out
->media_type
= g_value_get_int(g_value_array_get_nth(codec_in
, 2));
407 codec_out
->clock_rate
= g_value_get_uint(g_value_array_get_nth(codec_in
, 3));
408 codec_out
->channels
= g_value_get_uint(g_value_array_get_nth(codec_in
, 4));
410 codec_list
= g_list_append(codec_list
, codec_out
);
413 farsight_stream_set_remote_candidate_list(obj
->priv
->stream
, codec_list
);
415 /* should the elements be freed, or just the list itself? */
416 /*for (j = codec_list; j; j = g_list_next(j)) {
419 g_list_free(codec_list
);
425 tox_session_get_local_codecs(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
)
427 FarsightStream
*stream
;
428 const GList
*codec_list
;
430 GType hash_string_string
;
432 stream
= obj
->priv
->stream
;
433 codec_list
= farsight_stream_get_local_codecs(stream
);
435 hash_string_string
= dbus_g_type_get_map("GHashTable", G_TYPE_STRING
, G_TYPE_STRING
);
437 *codecs
= g_ptr_array_sized_new(g_list_length(codec_list
));
438 for (; codec_list
; codec_list
= g_list_next(codec_list
)) {
439 GValueArray
*codec_struct
;
440 const FarsightCodec
*codec
;
442 GHashTable
*parameters
;
445 memset(&value
, 0, sizeof value
);
447 codec
= (const FarsightCodec
*)codec_list
->data
;
448 codec_struct
= g_value_array_new(2);
450 g_value_init(&value
, G_TYPE_INT
);
451 g_value_set_int(&value
, codec
->id
);
452 g_value_array_append(codec_struct
, &value
);
453 g_value_unset(&value
);
455 g_value_init(&value
, G_TYPE_STRING
);
456 g_value_set_string(&value
, codec
->encoding_name
);
457 g_value_array_append(codec_struct
, &value
);
458 g_value_unset(&value
);
460 /* XXX: why is this the same as id? */
461 g_value_init(&value
, G_TYPE_UCHAR
);
462 g_value_set_uchar(&value
, codec
->media_type
);
463 g_value_array_append(codec_struct
, &value
);
464 g_value_unset(&value
);
466 g_value_init(&value
, G_TYPE_UINT
);
467 g_value_set_uint(&value
, codec
->clock_rate
);
468 g_value_array_append(codec_struct
, &value
);
469 g_value_unset(&value
);
471 g_value_init(&value
, G_TYPE_UINT
);
472 g_value_set_uint(&value
, codec
->channels
);
473 g_value_array_append(codec_struct
, &value
);
474 g_value_unset(&value
);
476 /* optional parameters - this should be a hash table, I think */
477 /* parameters = g_hash_table_new(g_str_hash, g_str_equal); */
478 parameters
= dbus_g_type_specialized_construct(hash_string_string
);
479 for (p
= codec
->optional_params
; p
; p
= g_list_next(p
)) {
480 FarsightCodecParameter
*param
= p
->data
;
482 g_hash_table_insert(parameters
, param
->name
, param
->value
);
484 g_value_init(&value
, hash_string_string
);
485 g_value_set_boxed(&value
, parameters
);
486 g_value_array_append(codec_struct
, &value
);
487 g_value_unset(&value
);
488 g_hash_table_unref(parameters
);
490 g_assert(codec_struct
->n_values
== 6);
492 g_ptr_array_add(*codecs
, codec_struct
);
494 g_debug("Local codec: %s\n", codec
->encoding_name
);
501 tox_session_get_codec_intersection(ToxSession
*obj
, GPtrArray
**codecs
, GError
**error
)
503 FarsightStream
*stream
;
506 stream
= obj
->priv
->stream
;
507 codec_list
= farsight_stream_get_codec_intersection(stream
);
509 *codecs
= g_ptr_array_sized_new(g_list_length(codec_list
));
510 for (; codec_list
; codec_list
= g_list_next(codec_list
)) {
511 GValueArray
*codec_struct
;
512 FarsightCodec
*codec
;
515 codec
= (FarsightCodec
*)codec_list
->data
;
516 codec_struct
= g_value_array_new(6);
518 value
= g_new(GValue
, 1);
519 g_value_init(value
, G_TYPE_INT
);
520 g_value_set_int(value
, codec
->id
);
521 g_value_array_append(codec_struct
, value
);
523 value
= g_new(GValue
, 1);
524 g_value_init(value
, G_TYPE_STRING
);
525 g_value_set_string(value
, codec
->encoding_name
);
526 g_value_array_append(codec_struct
, value
);
528 value
= g_new(GValue
, 1);
529 g_value_init(value
, G_TYPE_UCHAR
);
530 g_value_set_uchar(value
, codec
->media_type
);
531 g_value_array_append(codec_struct
, value
);
533 value
= g_new(GValue
, 1);
534 g_value_init(value
, G_TYPE_UINT
);
535 g_value_set_uint(value
, codec
->clock_rate
);
536 g_value_array_append(codec_struct
, value
);
538 value
= g_new(GValue
, 1);
539 g_value_init(value
, G_TYPE_UINT
);
540 g_value_set_uint(value
, codec
->channels
);
541 g_value_array_append(codec_struct
, value
);
543 /* XXX: do something about optional parameters */
544 value
= g_new(GValue
, 1);
545 g_value_init(value
, G_TYPE_HASH_TABLE
);
546 g_value_set_boxed(value
, g_hash_table_new(g_str_hash
, g_str_equal
));
547 g_value_array_append(codec_struct
, value
);
549 g_assert(codec_struct
->n_values
== 6);
551 g_ptr_array_add(*codecs
, codec_struct
);
558 tox_session_native_candidates_prepared(FarsightStream
*stream
, gpointer user_data
)
560 ToxSession
*self
= (ToxSession
*)user_data
;
561 const GList
*candidates
;
564 candidates
= farsight_stream_get_native_candidate_list(stream
);
566 array
= candidate_list_to_dbus_array(candidates
);
568 g_debug("Sending signal NativeCandidatesPrepared!\n");
569 g_signal_emit(self
, signals
[NATIVE_CANDIDATES_PREPARED
], 0, array
);
573 tox_session_new_native_candidate(FarsightStream
*stream
, gchar
*candidate_id
, ToxSession
*self
)
576 farsight_stream_get_native_candidate (stream
, candidate_id
);
577 FarsightTransportInfo
*trans
= candidate
->data
;
580 g_debug ("tox_session_new_native_candidate: New native candidate"
581 " with %d components, the first being: "
586 "proto_subtype: %s, "
587 "proto_profile: %s, "
590 "username: %s password: %s>",
591 g_list_length(candidate
),
592 trans
->candidate_id
, trans
->component
,
593 trans
->ip
, trans
->port
, trans
->proto
, trans
->proto_subtype
,
594 trans
->proto_profile
, trans
->preference
,
595 trans
->type
, trans
->username
, trans
->password
);
597 array
= candidate_list_to_dbus_array(candidate
);
598 g_debug("Sending signal NewNativeCandidate!\n");
599 g_signal_emit(self
, signals
[NEW_NATIVE_CANDIDATE
], 0, array
);
603 candidate_list_to_dbus_array(const GList
*candidates
)
607 array
= g_value_array_new(1);
609 for (; candidates
; candidates
= g_list_next(candidates
)) {
610 GValueArray
*candidate
;
611 FarsightTransportInfo
*info
;
614 info
= (FarsightTransportInfo
*)candidates
->data
;
615 candidate
= g_value_array_new(11);
617 memset(&value
, 0, sizeof value
);
619 g_value_init(&value
, G_TYPE_STRING
);
620 g_value_set_string(&value
, info
->candidate_id
);
621 g_value_array_append(candidate
, &value
);
622 g_value_unset(&value
);
624 g_value_init(&value
, G_TYPE_UINT
);
625 g_value_set_uint(&value
, info
->component
);
626 g_value_array_append(candidate
, &value
);
627 g_value_unset(&value
);
629 g_value_init(&value
, G_TYPE_STRING
);
630 g_value_set_string(&value
, info
->ip
);
631 g_value_array_append(candidate
, &value
);
632 g_value_unset(&value
);
634 g_value_init(&value
, G_TYPE_UINT
);
635 g_value_set_uint(&value
, info
->port
);
636 g_value_array_append(candidate
, &value
);
637 g_value_unset(&value
);
639 g_value_init(&value
, G_TYPE_STRING
);
640 switch(info
->proto
) {
641 case FARSIGHT_NETWORK_PROTOCOL_UDP
:
642 g_value_set_static_string(&value
, "udp");
644 case FARSIGHT_NETWORK_PROTOCOL_TCP
:
645 g_value_set_static_string(&value
, "tcp");
648 g_error("Unknown protocol value %u\n", info
->proto
);
650 g_value_array_append(candidate
, &value
);
651 g_value_unset(&value
);
653 g_value_init(&value
, G_TYPE_STRING
);
654 g_value_set_string(&value
, info
->proto_subtype
);
655 g_value_array_append(candidate
, &value
);
656 g_value_unset(&value
);
658 g_value_init(&value
, G_TYPE_STRING
);
659 g_value_set_string(&value
, info
->proto_profile
);
660 g_value_array_append(candidate
, &value
);
661 g_value_unset(&value
);
663 g_value_init(&value
, G_TYPE_UINT
);
664 g_value_set_uint(&value
, (guint
)(info
->preference
* 100));
665 g_value_array_append(candidate
, &value
);
666 g_value_unset(&value
);
668 g_value_init(&value
, G_TYPE_UINT
);
670 case FARSIGHT_CANDIDATE_TYPE_LOCAL
:
671 g_value_set_uint(&value
, 0);
673 case FARSIGHT_CANDIDATE_TYPE_DERIVED
:
674 g_value_set_uint(&value
, 1);
676 case FARSIGHT_CANDIDATE_TYPE_RELAY
:
677 g_value_set_uint(&value
, 2);
680 g_error("Unknown candidate type %u\n", info
->proto
);
682 g_value_array_append(candidate
, &value
);
683 g_value_unset(&value
);
685 g_value_init(&value
, G_TYPE_STRING
);
686 g_value_set_string(&value
, info
->username
);
687 g_value_array_append(candidate
, &value
);
688 g_value_unset(&value
);
690 g_value_init(&value
, G_TYPE_STRING
);
691 g_value_set_string(&value
, info
->password
);
692 g_value_array_append(candidate
, &value
);
693 g_value_unset(&value
);
695 g_assert(candidate
->n_values
== 11);
697 g_value_init(&value
, G_TYPE_VALUE_ARRAY
);
698 /* apparently GValueArray is a "boxed" type */
699 g_value_set_boxed(&value
, candidate
);
700 g_value_array_append(array
, &value
);
701 g_value_unset(&value
);