--- a/hangouts_media.c Tue Apr 26 16:07:23 2016 +1200
+++ b/hangouts_media.c Thu Apr 28 00:05:27 2016 +1200
@@ -30,4 +30,242 @@
#include "hangout_media.pb-c.h"
#include "hangouts_connection.h"
+#include "mediamanager.h" +/** This is a bit of a hack; + if libpurple isn't compiled with USE_VV then a bunch of functions don't end up being exported, so we'll try load them in at runtime so we dont have to have a million and one different versions of the .so +static gboolean purple_media_functions_initaliased = FALSE; +static gchar *(*_purple_media_candidate_get_username)(PurpleMediaCandidate *candidate); +static gchar *(*_purple_media_candidate_get_password)(PurpleMediaCandidate *candidate); +static PurpleMediaNetworkProtocol (*_purple_media_candidate_get_protocol)(PurpleMediaCandidate *candidate); +static guint (*_purple_media_candidate_get_component_id)(PurpleMediaCandidate *candidate); +static gchar *(*_purple_media_candidate_get_ip)(PurpleMediaCandidate *candidate); +static guint16 (*_purple_media_candidate_get_port)(PurpleMediaCandidate *candidate); +static PurpleMediaCandidateType (*_purple_media_candidate_get_candidate_type)(PurpleMediaCandidate *candidate); +static guint32 (*_purple_media_candidate_get_priority)(PurpleMediaCandidate *candidate); +// Using dlopen() instead of GModule because I don't want another dep +# define dlopen(filename, flag) GetModuleHandleA(filename) +# define dlsym(handle, symbol) GetProcAddress(handle, symbol) +# define dlclose(handle) FreeLibrary(handle) +hangouts_init_media_functions() + gpointer libpurple_module; + if (purple_media_functions_initaliased == FALSE) { + libpurple_module = dlopen(NULL, 0); + if (libpurple_module != NULL) { + _purple_media_candidate_get_username = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_username"); + _purple_media_candidate_get_password = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_password"); + _purple_media_candidate_get_component_id = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_component_id"); + _purple_media_candidate_get_protocol = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_protocol"); + _purple_media_candidate_get_ip = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_ip"); + _purple_media_candidate_get_port = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_port"); + _purple_media_candidate_get_candidate_type = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_candidate_type"); + _purple_media_candidate_get_priority = (gpointer) dlsym(libpurple_module, "purple_media_candidate_get_priority"); + purple_media_functions_initaliased = TRUE; +hangouts_pblite_media_media_session_add_cb(HangoutsAccount *ha, MediaSessionAddResponse *response, gpointer user_data) +hangouts_media_candidates_prepared_cb(PurpleMedia *media, gchar *sid, gchar *name, HangoutsMedia *hangouts_media) + MediaSessionAddRequest request; + MediaSession media_session; + MediaSession *media_sessions; + MediaContent client_content; + MediaContent *client_contents; + // MediaCryptoParams crypto_param; + // MediaCryptoParams *crypto_params; + MediaTransport transport; + MediaIceCandidate *ice_candidates; + GList *purple_candidates; + PurpleMediaSessionType purple_session_type; + media_session_add_request__init(&request); + media_session__init(&media_session); + media_content__init(&client_content); + media_transport__init(&transport); + transport.ice_version = ICE_VERSION__ICE_RFC_5245; + purple_candidates = purple_media_get_local_candidates(media, sid, name); + n_ice_candidates = g_list_length(purple_candidates); + ice_candidates = g_new0(MediaIceCandidate, n_ice_candidates); + for(i = 0; purple_candidates; purple_candidates = g_list_next(purple_candidates), i++) { + PurpleMediaCandidate *purple_candidate = purple_candidates->data; + MediaIceCandidate ice_candidate = ice_candidates[i]; + media_ice_candidate__init(&ice_candidate); + //TODO multiple passwords needed? + transport.username = _purple_media_candidate_get_username(purple_candidate); + transport.password = _purple_media_candidate_get_password(purple_candidate); + ice_candidate.has_component = TRUE; + switch(_purple_media_candidate_get_component_id(purple_candidate)) { + case PURPLE_MEDIA_COMPONENT_RTP: + ice_candidate.component = COMPONENT__RTP; + case PURPLE_MEDIA_COMPONENT_RTCP: + ice_candidate.component = COMPONENT__RTCP; + ice_candidate.has_component = FALSE; + ice_candidate.has_protocol = TRUE; + switch(_purple_media_candidate_get_protocol(purple_candidate)) { + case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP: + ice_candidate.protocol = PROTOCOL__UDP; + case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE: + case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE: + case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO: + ice_candidate.protocol = PROTOCOL__TCP; + ice_candidate.has_protocol = FALSE; + ice_candidate.ip = _purple_media_candidate_get_ip(purple_candidate); + ice_candidate.has_port = TRUE; + ice_candidate.port = _purple_media_candidate_get_port(purple_candidate); + ice_candidate.has_type = TRUE; + switch(_purple_media_candidate_get_candidate_type(purple_candidate)) { + case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: + ice_candidate.type = MEDIA_ICE_CANDIDATE_TYPE__HOST; + case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: + ice_candidate.type = MEDIA_ICE_CANDIDATE_TYPE__SERVER_REFLEXIVE; + case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: + ice_candidate.type = MEDIA_ICE_CANDIDATE_TYPE__PEER_REFLEXIVE; + case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: + ice_candidate.type = MEDIA_ICE_CANDIDATE_TYPE__RELAY; + case PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST: + ice_candidate.has_type = FALSE; + ice_candidate.priority = _purple_media_candidate_get_priority(purple_candidate); + ice_candidate.has_priority = TRUE; + transport.candidate = &ice_candidates; + transport.n_candidate = n_ice_candidates; + client_content.transport = &transport; + //media_crypto_params__init(&crypto_param); + //crypto_param.has_suite = TRUE; + //crypto_param.suite = MEDIA_CRYPTO_SUITE__AES_CM_128_HMAC_SHA1_80; + //crypto_param.key_params = 'inline:' random key + //crypto_params = &crypto_param; + //client_content.crypto_param = &crypto_params; + //TODO is this correct? + media_session.session_id = sid; + media_session.n_client_content = 1; + media_session.client_content = &client_contents; + purple_session_type = purple_media_get_session_type(media, sid); + client_content.has_media_type = TRUE; + if (purple_session_type == PURPLE_MEDIA_SEND_AUDIO) { + client_content.media_type = MEDIA_TYPE__MEDIA_TYPE_AUDIO; + } else if (purple_session_type == PURPLE_MEDIA_SEND_VIDEO) { + client_content.media_type = MEDIA_TYPE__MEDIA_TYPE_VIDEO; + } else if (purple_session_type == PURPLE_MEDIA_SEND_APPLICATION) { + client_content.media_type = MEDIA_TYPE__MEDIA_TYPE_DATA; + client_content.media_type = MEDIA_TYPE__MEDIA_TYPE_BUNDLE; + media_sessions = &media_session; + request.n_resource = 1; + request.resource = &media_sessions; + hangouts_pblite_media_media_session_add(hangouts_media->ha, &request, hangouts_pblite_media_media_session_add_cb, hangouts_media); + g_free(ice_candidates); +hangouts_initiate_media(PurpleAccount *account, const gchar *who, PurpleMediaSessionType type) + PurpleConnection *pc = purple_account_get_connection(account); + HangoutsAccount *ha = purple_connection_get_protocol_data(pc); + HangoutsMedia *hangouts_media; + GParameter *params = NULL; + hangouts_init_media_functions(); + //TODO use openwebrtc instead of fsrtpconference + PurpleMedia *media = purple_media_manager_create_media(purple_media_manager_get(), + account, "fsrtpconference", who, TRUE); + hangouts_media = g_new0(HangoutsMedia, 1); + hangouts_media->ha = ha; + hangouts_media->media = media; + purple_media_set_protocol_data(media, hangouts_media); + g_signal_connect(G_OBJECT(media), "candidates-prepared", + G_CALLBACK(hangouts_media_candidates_prepared_cb), hangouts_media); + // g_signal_connect(G_OBJECT(media), "codecs-changed", + // G_CALLBACK(hangouts_media_codecs_changed_cb), hangouts_media); + // g_signal_connect(G_OBJECT(media), "state-changed", + // G_CALLBACK(hangouts_media_state_changed_cb), hangouts_media); + // g_signal_connect(G_OBJECT(media), "stream-info", + // G_CALLBACK(hangouts_media_stream_info_cb), hangouts_media); + if(!purple_media_add_stream(media, "needs a name", who, type, TRUE, "nice", num_params, params)) { + purple_media_end(media, NULL, NULL); + /* TODO: How much clean-up is necessary here? (does calling + purple_media_end lead to cleaning up Jingle structs?) */ --- a/hangouts_media.h Tue Apr 26 16:07:23 2016 +1200
+++ b/hangouts_media.h Thu Apr 28 00:05:27 2016 +1200
@@ -38,6 +38,13 @@
HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(hangout_query, HangoutQuery, "hangouts/query");
HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_session_add, MediaSessionAdd, "media_sessions/add");
HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_session_modify, MediaSessionModify, "media_sessions/modify");
+HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_session_query, MediaSessionQuery, "media_sessions/query"); HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(hangout_participant_add, HangoutParticipantAdd, "hangout_participants/add");
+HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(hangout_invitation_add, HangoutInvitationAdd, "hangout_invitations/add"); +HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(hangout_bulk, HangoutBulk, "hangouts/bulk"); +HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_source_add, MediaSourceAdd, "media_sources/add"); +HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_stream_add, MediaStreamAdd, "media_streams/add"); +HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_stream_search, MediaStreamSearch, "media_streams/search"); +HANGOUTS_DEFINE_PBLITE_MEDIA_REQUEST_FUNC(media_stream_modify, MediaStreamModify, "media_streams/modify"); #endif /*_HANGOUTS_MEDIA_H_*/
\ No newline at end of file
--- a/libhangouts.c Tue Apr 26 16:07:23 2016 +1200
+++ b/libhangouts.c Thu Apr 28 00:05:27 2016 +1200
@@ -44,7 +44,7 @@
hangouts_get_media_caps(PurpleAccount *account, const char *who)
- return PURPLE_MEDIA_CAPS_AUDIO_VIDEO;
+ return PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_AUDIO_VIDEO | PURPLE_MEDIA_CAPS_MODIFY_SESSION; @@ -447,6 +447,12 @@
prpl_info->invite = hangouts_chat_invite;
+hangouts_protocol_media_iface_init(PurpleProtocolMediaIface *prpl_info) + prpl_info->get_caps = hangouts_get_media_caps; static PurpleProtocol *hangouts_protocol;
PURPLE_DEFINE_TYPE_EXTENDED(
@@ -466,6 +472,9 @@
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
hangouts_protocol_privacy_iface_init)
+ PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, + hangouts_protocol_media_iface_init) @@ -616,6 +625,8 @@
prpl_info->get_chat_name = hangouts_get_chat_name;
prpl_info->chat_invite = hangouts_chat_invite;
+ prpl_info->get_media_caps = hangouts_get_media_caps; prpl_info->add_deny = hangouts_block_user;
prpl_info->rem_deny = hangouts_unblock_user;
--- a/purplecompat.h Tue Apr 26 16:07:23 2016 +1200
+++ b/purplecompat.h Thu Apr 28 00:05:27 2016 +1200
@@ -116,6 +116,8 @@
#define PURPLE_IM_TYPING PURPLE_TYPING
#define PURPLE_IM_TYPED PURPLE_TYPED
+#define purple_media_set_protocol_data purple_media_set_prpl_data #undef purple_notify_error
#define purple_notify_error(handle, title, primary, secondary, cpar) \
purple_notify_message((handle), PURPLE_NOTIFY_MSG_ERROR, (title), \