pidgin-2.10.11-application-media.patch 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. # HG changeset patch
  2. # User Youness Alaoui <kakaroto@kakaroto.homelinux.net>
  3. # Date 1405979621 14400
  4. # Node ID 4fe1034f3dce1c5cd3c929ab8c58db8e27655beb
  5. # Parent d729a9b2126594df3e38647e926ac7c0a7db807b
  6. Add application media type and APIs
  7. Fixes #16315
  8. Index: pidgin-2.10.11/configure.ac
  9. ===================================================================
  10. --- pidgin-2.10.11.orig/configure.ac
  11. +++ pidgin-2.10.11/configure.ac
  12. @@ -916,6 +916,20 @@ fi
  13. AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno")
  14. dnl #######################################################################
  15. +dnl # Check for Raw data streams support in Farstream
  16. +dnl #######################################################################
  17. +if test "x$enable_vv" != "xno" -a "x$with_gstreamer" == "x1.0"; then
  18. + AC_MSG_CHECKING(for raw data support in Farstream)
  19. + PKG_CHECK_MODULES(GSTAPP, [gstreamer-app-1.0], [
  20. + AC_DEFINE(USE_GSTAPP, 1, [Use GStreamer Video Overlay support])
  21. + AC_SUBST(GSTAPP_CFLAGS)
  22. + AC_SUBST(GSTAPP_LIBS)
  23. + AC_DEFINE(HAVE_MEDIA_APPLICATION, 1, [Define if we have support for application media type.])
  24. + AC_MSG_RESULT(yes)
  25. + ], [AC_MSG_RESULT(no)])
  26. +fi
  27. +
  28. +dnl #######################################################################
  29. dnl # Check for Internationalized Domain Name support
  30. dnl #######################################################################
  31. Index: pidgin-2.10.11/libpurple/Makefile.am
  32. ===================================================================
  33. --- pidgin-2.10.11.orig/libpurple/Makefile.am
  34. +++ pidgin-2.10.11/libpurple/Makefile.am
  35. @@ -314,6 +314,7 @@ libpurple_la_LIBADD = \
  36. $(FARSTREAM_LIBS) \
  37. $(GSTREAMER_LIBS) \
  38. $(GSTVIDEO_LIBS) \
  39. + $(GSTAPP_LIBS) \
  40. $(GSTINTERFACES_LIBS) \
  41. $(IDN_LIBS) \
  42. ciphers/libpurple-ciphers.la \
  43. @@ -331,6 +332,7 @@ AM_CPPFLAGS = \
  44. $(FARSTREAM_CFLAGS) \
  45. $(GSTREAMER_CFLAGS) \
  46. $(GSTVIDEO_CFLAGS) \
  47. + $(GSTAPP_CFLAGS) \
  48. $(GSTINTERFACES_CFLAGS) \
  49. $(IDN_CFLAGS) \
  50. $(NETWORKMANAGER_CFLAGS) \
  51. Index: pidgin-2.10.11/libpurple/media-gst.h
  52. ===================================================================
  53. --- pidgin-2.10.11.orig/libpurple/media-gst.h
  54. +++ pidgin-2.10.11/libpurple/media-gst.h
  55. @@ -71,6 +71,7 @@ typedef enum {
  56. PURPLE_MEDIA_ELEMENT_SRC = 1 << 9, /** can be set as an active src */
  57. PURPLE_MEDIA_ELEMENT_SINK = 1 << 10, /** can be set as an active sink */
  58. + PURPLE_MEDIA_ELEMENT_APPLICATION = 1 << 11, /** supports application data */
  59. } PurpleMediaElementType;
  60. #ifdef __cplusplus
  61. Index: pidgin-2.10.11/libpurple/media/backend-fs2.c
  62. ===================================================================
  63. --- pidgin-2.10.11.orig/libpurple/media/backend-fs2.c
  64. +++ pidgin-2.10.11/libpurple/media/backend-fs2.c
  65. @@ -543,6 +543,10 @@ session_type_to_fs_media_type(PurpleMedi
  66. return FS_MEDIA_TYPE_AUDIO;
  67. else if (type & PURPLE_MEDIA_VIDEO)
  68. return FS_MEDIA_TYPE_VIDEO;
  69. +#ifdef HAVE_MEDIA_APPLICATION
  70. + else if (type & PURPLE_MEDIA_APPLICATION)
  71. + return FS_MEDIA_TYPE_APPLICATION;
  72. +#endif
  73. else
  74. return 0;
  75. }
  76. @@ -551,7 +555,7 @@ static FsStreamDirection
  77. session_type_to_fs_stream_direction(PurpleMediaSessionType type)
  78. {
  79. if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
  80. - (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
  81. + (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
  82. return FS_DIRECTION_BOTH;
  83. else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
  84. (type & PURPLE_MEDIA_SEND_VIDEO))
  85. @@ -559,6 +563,14 @@ session_type_to_fs_stream_direction(Purp
  86. else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
  87. (type & PURPLE_MEDIA_RECV_VIDEO))
  88. return FS_DIRECTION_RECV;
  89. +#ifdef HAVE_MEDIA_APPLICATION
  90. + else if ((type & PURPLE_MEDIA_APPLICATION) == PURPLE_MEDIA_APPLICATION)
  91. + return FS_DIRECTION_BOTH;
  92. + else if (type & PURPLE_MEDIA_SEND_APPLICATION)
  93. + return FS_DIRECTION_SEND;
  94. + else if (type & PURPLE_MEDIA_RECV_APPLICATION)
  95. + return FS_DIRECTION_RECV;
  96. +#endif
  97. else
  98. return FS_DIRECTION_NONE;
  99. }
  100. @@ -577,6 +589,13 @@ session_type_from_fs(FsMediaType type, F
  101. result |= PURPLE_MEDIA_SEND_VIDEO;
  102. if (direction & FS_DIRECTION_RECV)
  103. result |= PURPLE_MEDIA_RECV_VIDEO;
  104. +#ifdef HAVE_MEDIA_APPLICATION
  105. + } else if (type == FS_MEDIA_TYPE_APPLICATION) {
  106. + if (direction & FS_DIRECTION_SEND)
  107. + result |= PURPLE_MEDIA_SEND_APPLICATION;
  108. + if (direction & FS_DIRECTION_RECV)
  109. + result |= PURPLE_MEDIA_RECV_APPLICATION;
  110. +#endif
  111. }
  112. return result;
  113. }
  114. @@ -1333,7 +1352,8 @@ gst_handle_message_error(GstBus *bus, Gs
  115. & PURPLE_MEDIA_AUDIO)
  116. purple_media_error(priv->media,
  117. _("Error with your microphone"));
  118. - else
  119. + else if (purple_media_get_session_type(priv->media,
  120. + sessions->data) & PURPLE_MEDIA_VIDEO)
  121. purple_media_error(priv->media,
  122. _("Error with your webcam"));
  123. @@ -1756,6 +1776,21 @@ create_session(PurpleMediaBackendFs2 *se
  124. session->session = fs_conference_new_session(priv->conference,
  125. session_type_to_fs_media_type(type), &err);
  126. +#ifdef HAVE_MEDIA_APPLICATION
  127. + if (type == PURPLE_MEDIA_APPLICATION) {
  128. + GstCaps *caps;
  129. + GObject *rtpsession = NULL;
  130. +
  131. + caps = gst_caps_new_empty_simple ("application/octet-stream");
  132. + fs_session_set_allowed_caps (session->session, caps, caps, NULL);
  133. + gst_caps_unref (caps);
  134. + g_object_get (session->session, "internal-session", &rtpsession, NULL);
  135. + if (rtpsession) {
  136. + g_object_set (rtpsession, "probation", 0, NULL);
  137. + g_object_unref (rtpsession);
  138. + }
  139. + }
  140. +#endif
  141. if (err != NULL) {
  142. purple_media_error(priv->media,
  143. _("Error creating session: %s"),
  144. @@ -1970,6 +2005,21 @@ src_pad_added_cb(FsStream *fsstream, Gst
  145. gst_bin_add(GST_BIN(priv->confbin), sink);
  146. gst_element_set_state(sink, GST_STATE_PLAYING);
  147. stream->fakesink = sink;
  148. +#ifdef HAVE_MEDIA_APPLICATION
  149. + } else if (codec->media_type == FS_MEDIA_TYPE_APPLICATION) {
  150. +#if GST_CHECK_VERSION(1,0,0)
  151. + stream->src = gst_element_factory_make("funnel", NULL);
  152. +#else
  153. + stream->src = gst_element_factory_make("fsfunnel", NULL);
  154. +#endif
  155. + sink = purple_media_manager_get_element(
  156. + purple_media_get_manager(priv->media),
  157. + PURPLE_MEDIA_RECV_APPLICATION, priv->media,
  158. + stream->session->id,
  159. + stream->participant);
  160. + gst_bin_add(GST_BIN(priv->confbin), sink);
  161. + gst_element_set_state(sink, GST_STATE_PLAYING);
  162. +#endif
  163. }
  164. stream->tee = gst_element_factory_make("tee", NULL);
  165. gst_bin_add_many(GST_BIN(priv->confbin),
  166. @@ -2332,6 +2382,9 @@ purple_media_backend_fs2_codecs_ready(Pu
  167. return FALSE;
  168. if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
  169. +#ifdef HAVE_MEDIA_APPLICATION
  170. + PURPLE_MEDIA_SEND_APPLICATION |
  171. +#endif
  172. PURPLE_MEDIA_SEND_VIDEO)) {
  173. #ifdef HAVE_FARSIGHT
  174. g_object_get(session->session,
  175. @@ -2355,6 +2408,9 @@ purple_media_backend_fs2_codecs_ready(Pu
  176. PurpleMediaBackendFs2Session *session = values->data;
  177. if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
  178. +#ifdef HAVE_MEDIA_APPLICATION
  179. + PURPLE_MEDIA_SEND_APPLICATION |
  180. +#endif
  181. PURPLE_MEDIA_SEND_VIDEO)) {
  182. #ifdef HAVE_FARSIGHT
  183. g_object_get(session->session,
  184. Index: pidgin-2.10.11/libpurple/media/codec.c
  185. ===================================================================
  186. --- pidgin-2.10.11.orig/libpurple/media/codec.c
  187. +++ pidgin-2.10.11/libpurple/media/codec.c
  188. @@ -188,7 +188,7 @@ purple_media_codec_class_init(PurpleMedi
  189. g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
  190. g_param_spec_flags("media-type",
  191. "Media Type",
  192. - "Whether this is an audio of video codec.",
  193. + "Whether this is an audio, video or application codec.",
  194. PURPLE_TYPE_MEDIA_SESSION_TYPE,
  195. PURPLE_MEDIA_NONE,
  196. G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
  197. @@ -402,6 +402,8 @@ purple_media_codec_to_string(const Purpl
  198. media_type_str = "audio";
  199. else if (priv->media_type & PURPLE_MEDIA_VIDEO)
  200. media_type_str = "video";
  201. + else if (priv->media_type & PURPLE_MEDIA_APPLICATION)
  202. + media_type_str = "application";
  203. g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id,
  204. media_type_str, priv->encoding_name,
  205. Index: pidgin-2.10.11/libpurple/media/enum-types.c
  206. ===================================================================
  207. --- pidgin-2.10.11.orig/libpurple/media/enum-types.c
  208. +++ pidgin-2.10.11/libpurple/media/enum-types.c
  209. @@ -176,10 +176,16 @@ purple_media_session_type_get_type()
  210. "PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
  211. { PURPLE_MEDIA_SEND_VIDEO,
  212. "PURPLE_MEDIA_SEND_VIDEO", "send-video" },
  213. + { PURPLE_MEDIA_RECV_APPLICATION,
  214. + "PURPLE_MEDIA_RECV_APPLICATION", "recv-application" },
  215. + { PURPLE_MEDIA_SEND_APPLICATION,
  216. + "PURPLE_MEDIA_SEND_APPLICATION", "send-application" },
  217. { PURPLE_MEDIA_AUDIO,
  218. "PURPLE_MEDIA_AUDIO", "audio" },
  219. { PURPLE_MEDIA_VIDEO,
  220. "PURPLE_MEDIA_VIDEO", "video" },
  221. + { PURPLE_MEDIA_APPLICATION,
  222. + "PURPLE_MEDIA_APPLICATION", "application" },
  223. { 0, NULL, NULL }
  224. };
  225. type = g_flags_register_static(
  226. Index: pidgin-2.10.11/libpurple/media/enum-types.h
  227. ===================================================================
  228. --- pidgin-2.10.11.orig/libpurple/media/enum-types.h
  229. +++ pidgin-2.10.11/libpurple/media/enum-types.h
  230. @@ -92,8 +92,12 @@ typedef enum {
  231. PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
  232. PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
  233. PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
  234. + PURPLE_MEDIA_RECV_APPLICATION = 1 << 4,
  235. + PURPLE_MEDIA_SEND_APPLICATION = 1 << 5,
  236. PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
  237. - PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
  238. + PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO,
  239. + PURPLE_MEDIA_APPLICATION = PURPLE_MEDIA_RECV_APPLICATION |
  240. + PURPLE_MEDIA_SEND_APPLICATION
  241. } PurpleMediaSessionType;
  242. /** Media state-changed types */
  243. Index: pidgin-2.10.11/libpurple/mediamanager.c
  244. ===================================================================
  245. --- pidgin-2.10.11.orig/libpurple/mediamanager.c
  246. +++ pidgin-2.10.11/libpurple/mediamanager.c
  247. @@ -44,6 +44,9 @@
  248. #else
  249. #include <farstream/fs-element-added-notifier.h>
  250. #endif
  251. +#ifdef HAVE_MEDIA_APPLICATION
  252. +#include <gst/app/app.h>
  253. +#endif
  254. #if GST_CHECK_VERSION(1,0,0)
  255. #include <gst/video/videooverlay.h>
  256. @@ -97,14 +100,45 @@ struct _PurpleMediaManagerPrivate
  257. PurpleMediaElementInfo *video_sink;
  258. PurpleMediaElementInfo *audio_src;
  259. PurpleMediaElementInfo *audio_sink;
  260. +
  261. +#ifdef HAVE_MEDIA_APPLICATION
  262. + /* Application data streams */
  263. + GList *appdata_info; /* holds PurpleMediaAppDataInfo */
  264. + GMutex appdata_mutex;
  265. +#endif
  266. };
  267. +#ifdef HAVE_MEDIA_APPLICATION
  268. +typedef struct {
  269. + PurpleMedia *media;
  270. + GWeakRef media_ref;
  271. + gchar *session_id;
  272. + gchar *participant;
  273. + PurpleMediaAppDataCallbacks callbacks;
  274. + gpointer user_data;
  275. + GDestroyNotify notify;
  276. + GstAppSrc *appsrc;
  277. + GstAppSink *appsink;
  278. + gint num_samples;
  279. + GstSample *current_sample;
  280. + guint sample_offset;
  281. + gboolean writable;
  282. + gboolean connected;
  283. + guint writable_timer_id;
  284. + guint readable_timer_id;
  285. + GCond readable_cond;
  286. +} PurpleMediaAppDataInfo;
  287. +#endif
  288. +
  289. #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
  290. #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))
  291. static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
  292. static void purple_media_manager_init (PurpleMediaManager *media);
  293. static void purple_media_manager_finalize (GObject *object);
  294. +#ifdef HAVE_MEDIA_APPLICATION
  295. +static void free_appdata_info_locked (PurpleMediaAppDataInfo *info);
  296. +#endif
  297. static GObjectClass *parent_class = NULL;
  298. @@ -190,8 +224,10 @@ purple_media_manager_init (PurpleMediaMa
  299. media->priv->medias = NULL;
  300. media->priv->private_medias = NULL;
  301. media->priv->next_output_window_id = 1;
  302. -#ifdef USE_VV
  303. media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2;
  304. +#ifdef HAVE_MEDIA_APPLICATION
  305. + media->priv->appdata_info = NULL;
  306. + g_mutex_init (&media->priv->appdata_mutex);
  307. #endif
  308. purple_prefs_add_none("/purple/media");
  309. @@ -220,6 +256,13 @@ purple_media_manager_finalize (GObject *
  310. }
  311. if (priv->video_caps)
  312. gst_caps_unref(priv->video_caps);
  313. +#ifdef HAVE_MEDIA_APPLICATION
  314. + if (priv->appdata_info)
  315. + g_list_free_full (priv->appdata_info,
  316. + (GDestroyNotify) free_appdata_info_locked);
  317. + g_mutex_clear (&priv->appdata_mutex);
  318. +#endif
  319. +
  320. parent_class->finalize(media);
  321. }
  322. #endif
  323. @@ -440,8 +483,23 @@ purple_media_manager_remove_media(Purple
  324. medias = &manager->priv->private_medias;
  325. }
  326. - if (list)
  327. + if (list) {
  328. *medias = g_list_delete_link(*medias, list);
  329. +
  330. +#ifdef HAVE_MEDIA_APPLICATION
  331. + g_mutex_lock (&manager->priv->appdata_mutex);
  332. + for (list = manager->priv->appdata_info; list; list = list->next) {
  333. + PurpleMediaAppDataInfo *info = list->data;
  334. +
  335. + if (info->media == media) {
  336. + manager->priv->appdata_info = g_list_delete_link (
  337. + manager->priv->appdata_info, list);
  338. + free_appdata_info_locked (info);
  339. + }
  340. + }
  341. + g_mutex_unlock (&manager->priv->appdata_mutex);
  342. +#endif
  343. + }
  344. #endif
  345. }
  346. @@ -493,6 +551,92 @@ purple_media_manager_get_private_media_b
  347. return get_media_by_account (manager, account, TRUE);
  348. }
  349. +#ifdef HAVE_MEDIA_APPLICATION
  350. +static void
  351. +free_appdata_info_locked (PurpleMediaAppDataInfo *info)
  352. +{
  353. + if (info->notify)
  354. + info->notify (info->user_data);
  355. +
  356. + /* Make sure no other thread is using the structure */
  357. + g_free (info->session_id);
  358. + g_free (info->participant);
  359. +
  360. + if (info->readable_timer_id) {
  361. + purple_timeout_remove (info->readable_timer_id);
  362. + info->readable_timer_id = 0;
  363. + }
  364. +
  365. + if (info->writable_timer_id) {
  366. + purple_timeout_remove (info->writable_timer_id);
  367. + info->writable_timer_id = 0;
  368. + }
  369. +
  370. + if (info->current_sample)
  371. + gst_sample_unref (info->current_sample);
  372. + info->current_sample = NULL;
  373. +
  374. + /* Unblock any reading thread before destroying the GCond */
  375. + g_cond_broadcast (&info->readable_cond);
  376. +
  377. + g_cond_clear (&info->readable_cond);
  378. +
  379. + g_slice_free (PurpleMediaAppDataInfo, info);
  380. +}
  381. +
  382. +/*
  383. + * Get an app data info struct associated with a session and lock the mutex
  384. + * We don't want to return an info struct and unlock then it gets destroyed
  385. + * so we need to return it with the lock still taken
  386. + */
  387. +static PurpleMediaAppDataInfo *
  388. +get_app_data_info_and_lock (PurpleMediaManager *manager,
  389. + PurpleMedia *media, const gchar *session_id, const gchar *participant)
  390. +{
  391. + GList *i;
  392. +
  393. + g_mutex_lock (&manager->priv->appdata_mutex);
  394. + for (i = manager->priv->appdata_info; i; i = i->next) {
  395. + PurpleMediaAppDataInfo *info = i->data;
  396. +
  397. + if (info->media == media &&
  398. + g_strcmp0 (info->session_id, session_id) == 0 &&
  399. + (participant == NULL ||
  400. + g_strcmp0 (info->participant, participant) == 0)) {
  401. + return info;
  402. + }
  403. + }
  404. +
  405. + return NULL;
  406. +}
  407. +
  408. +/*
  409. + * Get an app data info struct associated with a session and lock the mutex
  410. + * if it doesn't exist, we create it.
  411. + */
  412. +static PurpleMediaAppDataInfo *
  413. +ensure_app_data_info_and_lock (PurpleMediaManager *manager, PurpleMedia *media,
  414. + const gchar *session_id, const gchar *participant)
  415. +{
  416. + PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, media,
  417. + session_id, participant);
  418. +
  419. + if (info == NULL) {
  420. + info = g_slice_new0 (PurpleMediaAppDataInfo);
  421. + info->media = media;
  422. + g_weak_ref_init (&info->media_ref, media);
  423. + info->session_id = g_strdup (session_id);
  424. + info->participant = g_strdup (participant);
  425. + g_cond_init (&info->readable_cond);
  426. + manager->priv->appdata_info = g_list_prepend (
  427. + manager->priv->appdata_info, info);
  428. + }
  429. +
  430. + return info;
  431. +}
  432. +#endif
  433. +
  434. +
  435. #ifdef USE_VV
  436. static void
  437. request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
  438. @@ -577,6 +721,351 @@ purple_media_manager_get_video_caps(Purp
  439. #endif
  440. }
  441. +#ifdef HAVE_MEDIA_APPLICATION
  442. +/*
  443. + * Calls the appdata writable callback from the main thread.
  444. + * This needs to grab the appdata lock and make sure it didn't get destroyed
  445. + * before calling the callback.
  446. + */
  447. +static gboolean
  448. +appsrc_writable (gpointer user_data)
  449. +{
  450. + PurpleMediaManager *manager = purple_media_manager_get ();
  451. + PurpleMediaAppDataInfo *info = user_data;
  452. + void (*writable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
  453. + const gchar *session_id, const gchar *participant, gboolean writable,
  454. + gpointer user_data);
  455. + PurpleMedia *media;
  456. + gchar *session_id;
  457. + gchar *participant;
  458. + gboolean writable;
  459. + gpointer cb_data;
  460. + guint *timer_id_ptr = &info->writable_timer_id;
  461. + guint timer_id = *timer_id_ptr;
  462. +
  463. +
  464. + g_mutex_lock (&manager->priv->appdata_mutex);
  465. + if (timer_id == 0 || timer_id != *timer_id_ptr) {
  466. + /* In case info was freed while we were waiting for the mutex to unlock
  467. + * we still have a pointer to the timer_id which should still be
  468. + * accessible since it's in the Glib slice allocator. It gets set to 0
  469. + * just after the timeout is canceled which happens also before the
  470. + * AppDataInfo is freed, so even if that memory slice gets reused, the
  471. + * timer_id would be different from its previous value (unless
  472. + * extremely unlucky). So checking if the value for the timer_id changed
  473. + * should be enough to prevent any kind of race condition in which the
  474. + * media/AppDataInfo gets destroyed in one thread while the timeout was
  475. + * triggered and is waiting on the mutex to get unlocked in this thread
  476. + */
  477. + g_mutex_unlock (&manager->priv->appdata_mutex);
  478. + return FALSE;
  479. + }
  480. + writable_cb = info->callbacks.writable;
  481. + media = g_weak_ref_get (&info->media_ref);
  482. + session_id = g_strdup (info->session_id);
  483. + participant = g_strdup (info->participant);
  484. + writable = info->writable && info->connected;
  485. + cb_data = info->user_data;
  486. +
  487. + info->writable_timer_id = 0;
  488. + g_mutex_unlock (&manager->priv->appdata_mutex);
  489. +
  490. +
  491. + if (writable_cb && media)
  492. + writable_cb (manager, media, session_id, participant, writable,
  493. + cb_data);
  494. +
  495. + g_object_unref (media);
  496. + g_free (session_id);
  497. + g_free (participant);
  498. +
  499. + return FALSE;
  500. +}
  501. +
  502. +/*
  503. + * Schedule a writable callback to be called from the main thread.
  504. + * We need to do this because need-data/enough-data signals from appsrc
  505. + * will come from the streaming thread and we need to create
  506. + * a source that we attach to the main context but we can't use
  507. + * g_main_context_invoke since we need to be able to cancel the source if the
  508. + * media gets destroyed.
  509. + * We use a timeout source instead of idle source, so the callback gets a higher
  510. + * priority
  511. + */
  512. +static void
  513. +call_appsrc_writable_locked (PurpleMediaAppDataInfo *info)
  514. +{
  515. + /* We already have a writable callback scheduled, don't create another one */
  516. + if (info->writable_timer_id || info->callbacks.writable == NULL)
  517. + return;
  518. +
  519. + info->writable_timer_id = purple_timeout_add (0, appsrc_writable, info);
  520. +}
  521. +
  522. +static void
  523. +appsrc_need_data (GstAppSrc *appsrc, guint length, gpointer user_data)
  524. +{
  525. + PurpleMediaAppDataInfo *info = user_data;
  526. + PurpleMediaManager *manager = purple_media_manager_get ();
  527. +
  528. + g_mutex_lock (&manager->priv->appdata_mutex);
  529. + if (!info->writable) {
  530. + info->writable = TRUE;
  531. + /* Only signal writable if we also established a connection */
  532. + if (info->connected)
  533. + call_appsrc_writable_locked (info);
  534. + }
  535. + g_mutex_unlock (&manager->priv->appdata_mutex);
  536. +}
  537. +
  538. +static void
  539. +appsrc_enough_data (GstAppSrc *appsrc, gpointer user_data)
  540. +{
  541. + PurpleMediaAppDataInfo *info = user_data;
  542. + PurpleMediaManager *manager = purple_media_manager_get ();
  543. +
  544. + g_mutex_lock (&manager->priv->appdata_mutex);
  545. + if (info->writable) {
  546. + info->writable = FALSE;
  547. + call_appsrc_writable_locked (info);
  548. + }
  549. + g_mutex_unlock (&manager->priv->appdata_mutex);
  550. +}
  551. +
  552. +static gboolean
  553. +appsrc_seek_data (GstAppSrc *appsrc, guint64 offset, gpointer user_data)
  554. +{
  555. + return FALSE;
  556. +}
  557. +
  558. +static void
  559. +appsrc_destroyed (PurpleMediaAppDataInfo *info)
  560. +{
  561. + PurpleMediaManager *manager = purple_media_manager_get ();
  562. +
  563. + g_mutex_lock (&manager->priv->appdata_mutex);
  564. + info->appsrc = NULL;
  565. + if (info->writable) {
  566. + info->writable = FALSE;
  567. + call_appsrc_writable_locked (info);
  568. + }
  569. + g_mutex_unlock (&manager->priv->appdata_mutex);
  570. +}
  571. +
  572. +static void
  573. +media_established_cb (PurpleMedia *media,const gchar *session_id,
  574. + const gchar *participant, PurpleMediaCandidate *local_candidate,
  575. + PurpleMediaCandidate *remote_candidate, PurpleMediaAppDataInfo *info)
  576. +{
  577. + PurpleMediaManager *manager = purple_media_manager_get ();
  578. +
  579. + g_mutex_lock (&manager->priv->appdata_mutex);
  580. + info->connected = TRUE;
  581. + /* We established the connection, if we were writable, then we need to
  582. + * signal it now */
  583. + if (info->writable)
  584. + call_appsrc_writable_locked (info);
  585. + g_mutex_unlock (&manager->priv->appdata_mutex);
  586. +}
  587. +
  588. +static GstElement *
  589. +create_send_appsrc(PurpleMedia *media,
  590. + const gchar *session_id, const gchar *participant)
  591. +{
  592. + PurpleMediaManager *manager = purple_media_manager_get ();
  593. + PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
  594. + media, session_id, participant);
  595. + GstElement *appsrc = (GstElement *)info->appsrc;
  596. +
  597. + if (appsrc == NULL) {
  598. + GstAppSrcCallbacks callbacks = {appsrc_need_data, appsrc_enough_data,
  599. + appsrc_seek_data, {NULL}};
  600. + GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
  601. +
  602. + appsrc = gst_element_factory_make("appsrc", NULL);
  603. +
  604. + info->appsrc = (GstAppSrc *)appsrc;
  605. +
  606. + gst_app_src_set_caps (info->appsrc, caps);
  607. + gst_app_src_set_callbacks (info->appsrc,
  608. + &callbacks, info, (GDestroyNotify) appsrc_destroyed);
  609. + g_signal_connect (media, "candidate-pair-established",
  610. + (GCallback) media_established_cb, info);
  611. + gst_caps_unref (caps);
  612. + }
  613. +
  614. + g_mutex_unlock (&manager->priv->appdata_mutex);
  615. + return appsrc;
  616. +}
  617. +
  618. +static void
  619. +appsink_eos (GstAppSink *appsink, gpointer user_data)
  620. +{
  621. +}
  622. +
  623. +static GstFlowReturn
  624. +appsink_new_preroll (GstAppSink *appsink, gpointer user_data)
  625. +{
  626. + return GST_FLOW_OK;
  627. +}
  628. +
  629. +static gboolean
  630. +appsink_readable (gpointer user_data)
  631. +{
  632. + PurpleMediaManager *manager = purple_media_manager_get ();
  633. + PurpleMediaAppDataInfo *info = user_data;
  634. + void (*readable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
  635. + const gchar *session_id, const gchar *participant, gpointer user_data);
  636. + PurpleMedia *media;
  637. + gchar *session_id;
  638. + gchar *participant;
  639. + gpointer cb_data;
  640. + guint *timer_id_ptr = &info->readable_timer_id;
  641. + guint timer_id = *timer_id_ptr;
  642. +
  643. + g_mutex_lock (&manager->priv->appdata_mutex);
  644. + if (timer_id == 0 || timer_id != *timer_id_ptr) {
  645. + /* Avoided a race condition (see writable callback) */
  646. + g_mutex_unlock (&manager->priv->appdata_mutex);
  647. + return FALSE;
  648. + }
  649. + /* We need to signal readable until there are no more samples */
  650. + while (info->callbacks.readable &&
  651. + (info->num_samples > 0 || info->current_sample != NULL)) {
  652. + readable_cb = info->callbacks.readable;
  653. + media = g_weak_ref_get (&info->media_ref);
  654. + session_id = g_strdup (info->session_id);
  655. + participant = g_strdup (info->participant);
  656. + cb_data = info->user_data;
  657. + g_mutex_unlock (&manager->priv->appdata_mutex);
  658. +
  659. + if (readable_cb)
  660. + readable_cb (manager, media, session_id, participant, cb_data);
  661. +
  662. + g_mutex_lock (&manager->priv->appdata_mutex);
  663. + g_object_unref (media);
  664. + g_free (session_id);
  665. + g_free (participant);
  666. + if (timer_id == 0 || timer_id != *timer_id_ptr) {
  667. + /* We got cancelled */
  668. + g_mutex_unlock (&manager->priv->appdata_mutex);
  669. + return FALSE;
  670. + }
  671. + }
  672. + info->readable_timer_id = 0;
  673. + g_mutex_unlock (&manager->priv->appdata_mutex);
  674. + return FALSE;
  675. +}
  676. +
  677. +static void
  678. +call_appsink_readable_locked (PurpleMediaAppDataInfo *info)
  679. +{
  680. + /* We must signal that a new sample has arrived to release blocking reads */
  681. + g_cond_broadcast (&info->readable_cond);
  682. +
  683. + /* We already have a writable callback scheduled, don't create another one */
  684. + if (info->readable_timer_id || info->callbacks.readable == NULL)
  685. + return;
  686. +
  687. + info->readable_timer_id = purple_timeout_add (0, appsink_readable, info);
  688. +}
  689. +
  690. +static GstFlowReturn
  691. +appsink_new_sample (GstAppSink *appsink, gpointer user_data)
  692. +{
  693. + PurpleMediaManager *manager = purple_media_manager_get ();
  694. + PurpleMediaAppDataInfo *info = user_data;
  695. +
  696. + g_mutex_lock (&manager->priv->appdata_mutex);
  697. + info->num_samples++;
  698. + call_appsink_readable_locked (info);
  699. + g_mutex_unlock (&manager->priv->appdata_mutex);
  700. +
  701. + return GST_FLOW_OK;
  702. +}
  703. +
  704. +static void
  705. +appsink_destroyed (PurpleMediaAppDataInfo *info)
  706. +{
  707. + PurpleMediaManager *manager = purple_media_manager_get ();
  708. +
  709. + g_mutex_lock (&manager->priv->appdata_mutex);
  710. + info->appsink = NULL;
  711. + info->num_samples = 0;
  712. + g_mutex_unlock (&manager->priv->appdata_mutex);
  713. +}
  714. +
  715. +static GstElement *
  716. +create_recv_appsink(PurpleMedia *media,
  717. + const gchar *session_id, const gchar *participant)
  718. +{
  719. + PurpleMediaManager *manager = purple_media_manager_get ();
  720. + PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
  721. + media, session_id, participant);
  722. + GstElement *appsink = (GstElement *)info->appsink;
  723. +
  724. + if (appsink == NULL) {
  725. + GstAppSinkCallbacks callbacks = {appsink_eos, appsink_new_preroll,
  726. + appsink_new_sample, {NULL}};
  727. + GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
  728. +
  729. + appsink = gst_element_factory_make("appsink", NULL);
  730. +
  731. + info->appsink = (GstAppSink *)appsink;
  732. +
  733. + gst_app_sink_set_caps (info->appsink, caps);
  734. + gst_app_sink_set_callbacks (info->appsink,
  735. + &callbacks, info, (GDestroyNotify) appsink_destroyed);
  736. + gst_caps_unref (caps);
  737. +
  738. + }
  739. +
  740. + g_mutex_unlock (&manager->priv->appdata_mutex);
  741. + return appsink;
  742. +}
  743. +#endif
  744. +
  745. +static PurpleMediaElementInfo *
  746. +get_send_application_element_info ()
  747. +{
  748. + static PurpleMediaElementInfo *info = NULL;
  749. +
  750. +#ifdef HAVE_MEDIA_APPLICATION
  751. + if (info == NULL) {
  752. + info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
  753. + "id", "pidginappsrc",
  754. + "name", "Pidgin Application Source",
  755. + "type", PURPLE_MEDIA_ELEMENT_APPLICATION
  756. + | PURPLE_MEDIA_ELEMENT_SRC
  757. + | PURPLE_MEDIA_ELEMENT_ONE_SRC,
  758. + "create-cb", create_send_appsrc, NULL);
  759. + }
  760. +#endif
  761. +
  762. + return info;
  763. +}
  764. +
  765. +
  766. +static PurpleMediaElementInfo *
  767. +get_recv_application_element_info ()
  768. +{
  769. + static PurpleMediaElementInfo *info = NULL;
  770. +
  771. +#ifdef HAVE_MEDIA_APPLICATION
  772. + if (info == NULL) {
  773. + info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
  774. + "id", "pidginappsink",
  775. + "name", "Pidgin Application Sink",
  776. + "type", PURPLE_MEDIA_ELEMENT_APPLICATION
  777. + | PURPLE_MEDIA_ELEMENT_SINK
  778. + | PURPLE_MEDIA_ELEMENT_ONE_SINK,
  779. + "create-cb", create_recv_appsink, NULL);
  780. + }
  781. +#endif
  782. +
  783. + return info;
  784. +}
  785. +
  786. GstElement *
  787. purple_media_manager_get_element(PurpleMediaManager *manager,
  788. PurpleMediaSessionType type, PurpleMedia *media,
  789. @@ -595,6 +1084,10 @@ purple_media_manager_get_element(PurpleM
  790. info = manager->priv->video_src;
  791. else if (type & PURPLE_MEDIA_RECV_VIDEO)
  792. info = manager->priv->video_sink;
  793. + else if (type & PURPLE_MEDIA_SEND_APPLICATION)
  794. + info = get_send_application_element_info ();
  795. + else if (type & PURPLE_MEDIA_RECV_APPLICATION)
  796. + info = get_recv_application_element_info ();
  797. if (info == NULL)
  798. return NULL;
  799. @@ -834,11 +1327,16 @@ purple_media_manager_get_active_element(
  800. return manager->priv->audio_src;
  801. else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
  802. return manager->priv->video_src;
  803. + else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
  804. + return get_send_application_element_info ();
  805. } else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
  806. if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
  807. return manager->priv->audio_sink;
  808. else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
  809. return manager->priv->video_sink;
  810. + else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
  811. + return get_recv_application_element_info ();
  812. +
  813. }
  814. #endif
  815. @@ -1138,6 +1636,174 @@ purple_media_manager_get_backend_type(Pu
  816. #endif
  817. }
  818. +void
  819. +purple_media_manager_set_application_data_callbacks(PurpleMediaManager *manager,
  820. + PurpleMedia *media, const gchar *session_id,
  821. + const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
  822. + gpointer user_data, GDestroyNotify notify)
  823. +{
  824. +#ifdef HAVE_MEDIA_APPLICATION
  825. + PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
  826. + media, session_id, participant);
  827. +
  828. + if (info->notify)
  829. + info->notify (info->user_data);
  830. +
  831. + if (info->readable_timer_id) {
  832. + purple_timeout_remove (info->readable_timer_id);
  833. + info->readable_timer_id = 0;
  834. + }
  835. +
  836. + if (info->writable_timer_id) {
  837. + purple_timeout_remove (info->writable_timer_id);
  838. + info->writable_timer_id = 0;
  839. + }
  840. +
  841. + if (callbacks) {
  842. + info->callbacks = *callbacks;
  843. + } else {
  844. + info->callbacks.writable = NULL;
  845. + info->callbacks.readable = NULL;
  846. + }
  847. + info->user_data = user_data;
  848. + info->notify = notify;
  849. +
  850. + call_appsrc_writable_locked (info);
  851. + if (info->num_samples > 0 || info->current_sample != NULL)
  852. + call_appsink_readable_locked (info);
  853. +
  854. + g_mutex_unlock (&manager->priv->appdata_mutex);
  855. +#endif
  856. +}
  857. +
  858. +gint
  859. +purple_media_manager_send_application_data (
  860. + PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
  861. + const gchar *participant, gpointer buffer, guint size, gboolean blocking)
  862. +{
  863. +#ifdef HAVE_MEDIA_APPLICATION
  864. + PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
  865. + media, session_id, participant);
  866. +
  867. + if (info && info->appsrc && info->connected) {
  868. + GstBuffer *gstbuffer = gst_buffer_new_wrapped (g_memdup (buffer, size),
  869. + size);
  870. + GstAppSrc *appsrc = gst_object_ref (info->appsrc);
  871. +
  872. + g_mutex_unlock (&manager->priv->appdata_mutex);
  873. + if (gst_app_src_push_buffer (appsrc, gstbuffer) == GST_FLOW_OK) {
  874. + if (blocking) {
  875. + GstPad *srcpad;
  876. +
  877. + srcpad = gst_element_get_static_pad (GST_ELEMENT (appsrc),
  878. + "src");
  879. + if (srcpad) {
  880. + gst_pad_peer_query (srcpad, gst_query_new_drain ());
  881. + gst_object_unref (srcpad);
  882. + }
  883. + }
  884. + gst_object_unref (appsrc);
  885. + return size;
  886. + } else {
  887. + gst_object_unref (appsrc);
  888. + return -1;
  889. + }
  890. + }
  891. + g_mutex_unlock (&manager->priv->appdata_mutex);
  892. + return -1;
  893. +#else
  894. + return -1;
  895. +#endif
  896. +}
  897. +
  898. +gint
  899. +purple_media_manager_receive_application_data (
  900. + PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
  901. + const gchar *participant, gpointer buffer, guint max_size,
  902. + gboolean blocking)
  903. +{
  904. +#ifdef HAVE_MEDIA_APPLICATION
  905. + PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
  906. + media, session_id, participant);
  907. + guint bytes_read = 0;
  908. +
  909. + if (info) {
  910. + /* If we are in a blocking read, we need to loop until max_size data
  911. + * is read into the buffer, if we're not, then we need to read as much
  912. + * data as possible
  913. + */
  914. + do {
  915. + if (!info->current_sample && info->appsink && info->num_samples > 0) {
  916. + info->current_sample = gst_app_sink_pull_sample (info->appsink);
  917. + info->sample_offset = 0;
  918. + if (info->current_sample)
  919. + info->num_samples--;
  920. + }
  921. +
  922. + if (info->current_sample) {
  923. + GstBuffer *gstbuffer = gst_sample_get_buffer (
  924. + info->current_sample);
  925. +
  926. + if (gstbuffer) {
  927. + GstMapInfo mapinfo;
  928. + guint bytes_to_copy;
  929. +
  930. + gst_buffer_map (gstbuffer, &mapinfo, GST_MAP_READ);
  931. + /* We must copy only the data remaining in the buffer without
  932. + * overflowing the buffer */
  933. + bytes_to_copy = max_size - bytes_read;
  934. + if (bytes_to_copy > mapinfo.size - info->sample_offset)
  935. + bytes_to_copy = mapinfo.size - info->sample_offset;
  936. + memcpy ((guint8 *)buffer + bytes_read,
  937. + mapinfo.data + info->sample_offset, bytes_to_copy);
  938. +
  939. + gst_buffer_unmap (gstbuffer, &mapinfo);
  940. + info->sample_offset += bytes_to_copy;
  941. + bytes_read += bytes_to_copy;
  942. + if (info->sample_offset == mapinfo.size) {
  943. + gst_sample_unref (info->current_sample);
  944. + info->current_sample = NULL;
  945. + info->sample_offset = 0;
  946. + }
  947. + } else {
  948. + /* In case there's no buffer in the sample (should never
  949. + * happen), we need to at least unref it */
  950. + gst_sample_unref (info->current_sample);
  951. + info->current_sample = NULL;
  952. + info->sample_offset = 0;
  953. + }
  954. + }
  955. +
  956. + /* If blocking, wait until there's an available sample */
  957. + while (bytes_read < max_size && blocking &&
  958. + info->current_sample == NULL && info->num_samples == 0) {
  959. + g_cond_wait (&info->readable_cond, &manager->priv->appdata_mutex);
  960. +
  961. + /* We've been signaled, we need to unlock and regrab the info
  962. + * struct to make sure nothing changed */
  963. + g_mutex_unlock (&manager->priv->appdata_mutex);
  964. + info = get_app_data_info_and_lock (manager,
  965. + media, session_id, participant);
  966. + if (info == NULL || info->appsink == NULL) {
  967. + /* The session was destroyed while we were waiting, we
  968. + * should return here */
  969. + g_mutex_unlock (&manager->priv->appdata_mutex);
  970. + return bytes_read;
  971. + }
  972. + }
  973. + } while (bytes_read < max_size &&
  974. + (blocking || info->num_samples > 0));
  975. +
  976. + g_mutex_unlock (&manager->priv->appdata_mutex);
  977. + return bytes_read;
  978. + }
  979. + g_mutex_unlock (&manager->priv->appdata_mutex);
  980. + return -1;
  981. +#else
  982. + return -1;
  983. +#endif
  984. +}
  985. +
  986. #ifdef USE_GSTREAMER
  987. /*
  988. @@ -1185,6 +1851,8 @@ purple_media_element_type_get_type()
  989. "PURPLE_MEDIA_ELEMENT_SRC", "src" },
  990. { PURPLE_MEDIA_ELEMENT_SINK,
  991. "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
  992. + { PURPLE_MEDIA_ELEMENT_APPLICATION,
  993. + "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
  994. { 0, NULL, NULL }
  995. };
  996. type = g_flags_register_static(
  997. @@ -1410,5 +2078,6 @@ purple_media_element_info_call_create(Pu
  998. return NULL;
  999. }
  1000. +
  1001. #endif /* USE_GSTREAMER */
  1002. Index: pidgin-2.10.11/libpurple/mediamanager.h
  1003. ===================================================================
  1004. --- pidgin-2.10.11.orig/libpurple/mediamanager.h
  1005. +++ pidgin-2.10.11/libpurple/mediamanager.h
  1006. @@ -38,6 +38,30 @@ typedef struct _PurpleMediaManagerClass
  1007. #include "account.h"
  1008. #include "media.h"
  1009. +/**
  1010. + * PurpleMediaAppDataCallbacks:
  1011. + * @readable: Called when the stream has received data and is readable.
  1012. + * @writable: Called when the stream has become writable or has stopped being
  1013. + * writable.
  1014. + *
  1015. + * A set of callbacks that can be installed on an Application data session with
  1016. + * purple_media_manager_set_application_data_callbacks()
  1017. + *
  1018. + * Once installed the @readable callback will get called as long as data is
  1019. + * available to read, so the data must be read completely.
  1020. + * The @writable callback will only be called when the writable state of the
  1021. + * stream changes. The @writable argument defines whether the stream has
  1022. + * become writable or stopped being writable.
  1023. + *
  1024. + */
  1025. +typedef struct {
  1026. + void (*readable) (PurpleMediaManager *manager, PurpleMedia *media,
  1027. + const gchar *session_id, const gchar *participant, gpointer user_data);
  1028. + void (*writable) (PurpleMediaManager *manager, PurpleMedia *media,
  1029. + const gchar *session_id, const gchar *participant, gboolean writable,
  1030. + gpointer user_data);
  1031. +} PurpleMediaAppDataCallbacks;
  1032. +
  1033. G_BEGIN_DECLS
  1034. #define PURPLE_TYPE_MEDIA_MANAGER (purple_media_manager_get_type())
  1035. @@ -285,6 +309,71 @@ void purple_media_manager_set_backend_ty
  1036. */
  1037. GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
  1038. +/**
  1039. + * purple_media_manager_set_application_data_callbacks:
  1040. + * @manager: The manager to register the callbacks with.
  1041. + * @media: The media instance to register the callbacks with.
  1042. + * @session_id: The session to register the callbacks with.
  1043. + * @participant: The participant to register the callbacks with.
  1044. + * @callbacks: The callbacks to be set on the session.
  1045. + * @user_data: a user_data argument for the callbacks.
  1046. + * @notify: a destroy notify function.
  1047. + *
  1048. + * Set callbacks on a session to be called when the stream becomes writable
  1049. + * or readable for media sessions of type #PURPLE_MEDIA_APPLICATION
  1050. + */
  1051. +void purple_media_manager_set_application_data_callbacks(
  1052. + PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
  1053. + const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
  1054. + gpointer user_data, GDestroyNotify notify);
  1055. +
  1056. +/**
  1057. + * purple_media_manager_send_application_data:
  1058. + * @manager: The manager to send data with.
  1059. + * @media: The media instance to which the session belongs.
  1060. + * @session_id: The session to send data to.
  1061. + * @participant: The participant to send data to.
  1062. + * @buffer: The buffer of data to send.
  1063. + * @size: The size of @buffer
  1064. + * @blocking: Whether to block until the data was send or not.
  1065. + *
  1066. + * Sends a buffer of data to a #PURPLE_MEDIA_APPLICATION session.
  1067. + * If @blocking is set, unless an error occured, the function will not return
  1068. + * until the data has been flushed into the network.
  1069. + * If the stream is not writable, the data will be queued. It is the
  1070. + * responsability of the user to stop sending data when the stream isn't
  1071. + * writable anymore. It is also the responsability of the user to only start
  1072. + * sending data after the stream has been configured correctly (encryption
  1073. + * parameters for example).
  1074. + *
  1075. + * Returns: Number of bytes sent or -1 in case of error.
  1076. + */
  1077. +gint purple_media_manager_send_application_data (
  1078. + PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
  1079. + const gchar *participant, gpointer buffer, guint size, gboolean blocking);
  1080. +
  1081. +/**
  1082. + * purple_media_manager_receive_application_data:
  1083. + * @manager: The manager to receive data with.
  1084. + * @media: The media instance to which the session belongs.
  1085. + * @session_id: The session to receive data from.
  1086. + * @participant: The participant to receive data from.
  1087. + * @buffer: The buffer to receive data into.
  1088. + * @max_size: The max_size of @buffer
  1089. + * @blocking: Whether to block until the buffer is entirely filled or return
  1090. + * with currently available data.
  1091. + *
  1092. + * Receive a buffer of data from a #PURPLE_MEDIA_APPLICATION session.
  1093. + * If @blocking is set, unless an error occured, the function will not return
  1094. + * until @max_size bytes are read.
  1095. + *
  1096. + * Returns: Number of bytes received or -1 in case of error.
  1097. + */
  1098. +gint purple_media_manager_receive_application_data (
  1099. + PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
  1100. + const gchar *participant, gpointer buffer, guint max_size,
  1101. + gboolean blocking);
  1102. +
  1103. /*}@*/
  1104. #ifdef __cplusplus