comparison libpurple/media.c @ 26117:6cf36f68033c

Only create output windows once there's data to be output. Also, allow multiple output windows per session/stream.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Tue, 24 Feb 2009 08:11:24 +0000
parents 9b0761b77218
children 0a6f1f796d21
comparison
equal deleted inserted replaced
26116:cb3c6ec9291e 26117:6cf36f68033c
37 #include "debug.h" 37 #include "debug.h"
38 38
39 #ifdef USE_VV 39 #ifdef USE_VV
40 40
41 #include <gst/interfaces/propertyprobe.h> 41 #include <gst/interfaces/propertyprobe.h>
42 #include <gst/interfaces/xoverlay.h>
43 #include <gst/farsight/fs-conference-iface.h> 42 #include <gst/farsight/fs-conference-iface.h>
44 43
45 /** @copydoc _PurpleMediaSession */ 44 /** @copydoc _PurpleMediaSession */
46 typedef struct _PurpleMediaSession PurpleMediaSession; 45 typedef struct _PurpleMediaSession PurpleMediaSession;
47 /** @copydoc _PurpleMediaStream */ 46 /** @copydoc _PurpleMediaStream */
1589 1588
1590 g_return_if_fail(recvbin != NULL && recvlevel != NULL); 1589 g_return_if_fail(recvbin != NULL && recvlevel != NULL);
1591 1590
1592 *recvbin = gst_bin_new("pidginrecvaudiobin"); 1591 *recvbin = gst_bin_new("pidginrecvaudiobin");
1593 sink = gst_element_factory_make("alsasink", "asink"); 1592 sink = gst_element_factory_make("alsasink", "asink");
1594 g_object_set(G_OBJECT(sink), "sync", FALSE, NULL); 1593 g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL);
1595 volume = gst_element_factory_make("volume", "purpleaudiooutputvolume"); 1594 volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
1596 g_object_set(volume, "volume", output_volume, NULL); 1595 g_object_set(volume, "volume", output_volume, NULL);
1597 *recvlevel = gst_element_factory_make("level", "recvlevel"); 1596 *recvlevel = gst_element_factory_make("level", "recvlevel");
1598 queue = gst_element_factory_make("queue", NULL); 1597 queue = gst_element_factory_make("queue", NULL);
1599 gst_bin_add_many(GST_BIN(*recvbin), sink, volume, 1598 gst_bin_add_many(GST_BIN(*recvbin), sink, volume,
1608 } 1607 }
1609 1608
1610 void 1609 void
1611 purple_media_video_init_recv(GstElement **recvbin) 1610 purple_media_video_init_recv(GstElement **recvbin)
1612 { 1611 {
1613 GstElement *sink;
1614 GstPad *pad, *ghost;
1615
1616 g_return_if_fail(recvbin != NULL); 1612 g_return_if_fail(recvbin != NULL);
1617 1613
1618 *recvbin = gst_bin_new("fakebin"); 1614 *recvbin = gst_element_factory_make("autovideosink", NULL);
1619 sink = gst_element_factory_make("fakesink", NULL);
1620 gst_bin_add(GST_BIN(*recvbin), sink);
1621 pad = gst_element_get_pad(sink, "sink");
1622 ghost = gst_ghost_pad_new("ghostsink", pad);
1623 gst_element_add_pad(*recvbin, ghost);
1624 } 1615 }
1625 1616
1626 static void 1617 static void
1627 purple_media_new_local_candidate_cb(FsStream *stream, 1618 purple_media_new_local_candidate_cb(FsStream *stream,
1628 FsCandidate *local_candidate, 1619 FsCandidate *local_candidate,
1734 1725
1735 static gboolean 1726 static gboolean
1736 purple_media_connected_cb(PurpleMediaStream *stream) 1727 purple_media_connected_cb(PurpleMediaStream *stream)
1737 { 1728 {
1738 g_return_val_if_fail(stream != NULL, FALSE); 1729 g_return_val_if_fail(stream != NULL, FALSE);
1730
1731 purple_media_manager_create_output_window(
1732 stream->session->media->priv->manager,
1733 stream->session->media,
1734 stream->session->id, stream->participant);
1735
1739 g_signal_emit(stream->session->media, 1736 g_signal_emit(stream->session->media,
1740 purple_media_signals[STATE_CHANGED], 1737 purple_media_signals[STATE_CHANGED],
1741 0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED, 1738 0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED,
1742 stream->session->id, stream->participant); 1739 stream->session->id, stream->participant);
1743 return FALSE; 1740 return FALSE;
1882 session_type = purple_media_from_fs(type, FS_DIRECTION_SEND); 1879 session_type = purple_media_from_fs(type, FS_DIRECTION_SEND);
1883 purple_media_set_src(media, session->id, 1880 purple_media_set_src(media, session->id,
1884 purple_media_manager_get_element( 1881 purple_media_manager_get_element(
1885 media->priv->manager, session_type)); 1882 media->priv->manager, session_type));
1886 gst_element_set_state(session->src, GST_STATE_PLAYING); 1883 gst_element_set_state(session->src, GST_STATE_PLAYING);
1884
1885 purple_media_manager_create_output_window(
1886 media->priv->manager,
1887 session->media,
1888 session->id, NULL);
1887 } 1889 }
1888 1890
1889 if (!(participant = purple_media_add_participant(media, who))) { 1891 if (!(participant = purple_media_add_participant(media, who))) {
1890 purple_media_remove_session(media, session); 1892 purple_media_remove_session(media, session);
1891 g_free(session); 1893 g_free(session);
2301 g_object_set(volume, "volume", level, NULL); 2303 g_object_set(volume, "volume", level, NULL);
2302 } 2304 }
2303 } 2305 }
2304 } 2306 }
2305 2307
2306 typedef struct 2308 gulong
2307 {
2308 gchar *name;
2309 gulong window_id;
2310 gulong handler_id;
2311 } PurpleMediaXOverlayData;
2312
2313 static void
2314 window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaXOverlayData *data)
2315 {
2316 gchar *name;
2317
2318 if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
2319 !gst_structure_has_name(msg->structure,
2320 "prepare-xwindow-id"))
2321 return;
2322
2323 name = gst_object_get_name(GST_MESSAGE_SRC(msg));
2324
2325 if (!strncmp(name, data->name, strlen(data->name))) {
2326 g_signal_handler_disconnect(bus, data->handler_id);
2327
2328 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
2329 GST_MESSAGE_SRC(msg)), data->window_id);
2330
2331 g_free(data->name);
2332 g_free(data);
2333 }
2334
2335 g_free(name);
2336
2337 return;
2338 }
2339
2340 gboolean
2341 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, 2309 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
2342 const gchar *participant, gulong window_id) 2310 const gchar *participant, gulong window_id)
2343 { 2311 {
2344 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); 2312 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
2345 2313
2346 if (session_id != NULL && participant == NULL) { 2314 return purple_media_manager_set_output_window(media->priv->manager,
2347 2315 media, session_id, participant, window_id);
2348 PurpleMediaSession *session;
2349 session = purple_media_get_session(media, session_id);
2350
2351 if (session == NULL)
2352 return FALSE;
2353
2354 session->window_id = window_id;
2355
2356 if (session->sink == NULL) {
2357 PurpleMediaXOverlayData *data;
2358 GstBus *bus;
2359 GstElement *bin, *queue, *sink;
2360 GstPad *request_pad, *sinkpad, *ghostpad;
2361 gchar *name;
2362
2363 /* Create sink */
2364 bin = gst_bin_new(NULL);
2365 gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(
2366 session->tee)), bin);
2367
2368 queue = gst_element_factory_make("queue", NULL);
2369 name = g_strdup_printf(
2370 "session-sink_%s", session_id);
2371 sink = gst_element_factory_make(
2372 "autovideosink", name);
2373
2374 gst_bin_add_many(GST_BIN(bin), queue, sink, NULL);
2375 gst_element_link(queue, sink);
2376
2377 sinkpad = gst_element_get_static_pad(queue, "sink");
2378 ghostpad = gst_ghost_pad_new("ghostsink", sinkpad);
2379 gst_object_unref(sinkpad);
2380 gst_element_add_pad(bin, ghostpad);
2381
2382 /* Connect callback for prepared-xwindow-id signal */
2383 data = g_new0(PurpleMediaXOverlayData, 1);
2384 data->name = name;
2385 data->window_id = window_id;
2386
2387 bus = gst_pipeline_get_bus(GST_PIPELINE(
2388 purple_media_manager_get_pipeline(
2389 media->priv->manager)));
2390 data->handler_id = g_signal_connect(bus,
2391 "sync-message::element",
2392 G_CALLBACK(window_id_cb), data);
2393 gst_object_unref(bus);
2394
2395 gst_element_set_state(bin, GST_STATE_PLAYING);
2396
2397 request_pad = gst_element_get_request_pad(
2398 session->tee, "src%d");
2399 gst_pad_link(request_pad, ghostpad);
2400 gst_object_unref(request_pad);
2401
2402 session->sink = bin;
2403 return TRUE;
2404 } else {
2405 /* Changing the XOverlay output window */
2406 GstElement *xoverlay = gst_bin_get_by_interface(
2407 GST_BIN(session->sink),
2408 GST_TYPE_X_OVERLAY);
2409 if (xoverlay != NULL) {
2410 gst_x_overlay_set_xwindow_id(
2411 GST_X_OVERLAY(xoverlay),
2412 window_id);
2413 }
2414 return FALSE;
2415 }
2416 } else if (session_id != NULL && participant != NULL) {
2417 PurpleMediaStream *stream = purple_media_get_stream(
2418 media, session_id, participant);
2419 GstBus *bus;
2420 GstElement *bin, *queue, *sink;
2421 GstPad *pad, *peer = NULL, *ghostpad;
2422 PurpleMediaXOverlayData *data;
2423 gchar *name;
2424
2425 if (stream == NULL)
2426 return FALSE;
2427
2428 stream->window_id = window_id;
2429
2430 if (stream->sink != NULL) {
2431 gboolean is_fakebin;
2432 name = gst_element_get_name(stream->sink);
2433 is_fakebin = !strcmp(name, "fakebin");
2434 g_free(name);
2435
2436 if (is_fakebin) {
2437 pad = gst_element_get_static_pad(
2438 stream->sink, "ghostsink");
2439 peer = gst_pad_get_peer(pad);
2440
2441 gst_pad_unlink(peer, pad);
2442 gst_object_unref(pad);
2443 gst_element_set_state(stream->sink,
2444 GST_STATE_NULL);
2445 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(
2446 stream->sink)), stream->sink);
2447 } else {
2448 /* Changing the XOverlay output window */
2449 GstElement *xoverlay =
2450 gst_bin_get_by_interface(
2451 GST_BIN(stream->sink),
2452 GST_TYPE_X_OVERLAY);
2453 if (xoverlay != NULL) {
2454 gst_x_overlay_set_xwindow_id(
2455 GST_X_OVERLAY(xoverlay),
2456 window_id);
2457 return TRUE;
2458 }
2459 return FALSE;
2460 }
2461 }
2462
2463 bin = gst_bin_new(NULL);
2464
2465 name = g_strdup_printf("stream-sink_%s_%s",
2466 session_id, participant);
2467 queue = gst_element_factory_make("queue", NULL);
2468 sink = gst_element_factory_make("autovideosink", name);
2469
2470 gst_bin_add_many(GST_BIN(bin), queue, sink, NULL);
2471 gst_element_link(queue, sink);
2472 pad = gst_element_get_static_pad(queue, "sink");
2473 ghostpad = gst_ghost_pad_new("ghostsink", pad);
2474 gst_element_add_pad(bin, ghostpad);
2475
2476 /* Connect callback for prepared-xwindow-id signal */
2477 data = g_new0(PurpleMediaXOverlayData, 1);
2478 data->name = name;
2479 data->window_id = window_id;
2480
2481 bus = gst_pipeline_get_bus(GST_PIPELINE(
2482 purple_media_manager_get_pipeline(
2483 media->priv->manager)));
2484 data->handler_id = g_signal_connect(bus,
2485 "sync-message::element",
2486 G_CALLBACK(window_id_cb), data);
2487 gst_object_unref(bus);
2488
2489 if (stream->tee != NULL) {
2490 gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(
2491 stream->tee)), bin);
2492 gst_element_set_state(bin, GST_STATE_PLAYING);
2493 gst_element_link(stream->tee, bin);
2494 }
2495
2496 stream->sink = bin;
2497 return TRUE;
2498 }
2499 return FALSE;
2500 }
2501
2502 static void
2503 dummy_block_cb(GstPad *pad, gboolean blocked, gpointer user_data)
2504 {
2505 }
2506
2507 gboolean
2508 purple_media_remove_output_window(PurpleMedia *media, const gchar *session_id,
2509 const gchar *participant)
2510 {
2511 GstElement *parent, *fakesink, *sink;
2512 GstPad *pad, *peer;
2513
2514 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
2515
2516 if (session_id != NULL && participant == NULL) {
2517 PurpleMediaSession *session;
2518 GstPad *pad, *peer;
2519
2520 session = purple_media_get_session(media, session_id);
2521
2522 if (session == NULL)
2523 return FALSE;
2524
2525 sink = session->sink;
2526
2527 if (!GST_IS_ELEMENT(sink))
2528 return FALSE;
2529
2530 pad = gst_element_get_static_pad(sink, "ghostsink");
2531 peer = gst_pad_get_peer(pad);
2532 gst_object_unref(pad);
2533
2534 gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
2535 gst_object_unref(peer);
2536
2537 gst_element_set_locked_state(sink, TRUE);
2538 gst_element_set_state(sink, GST_STATE_NULL);
2539
2540 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(sink)), sink);
2541 session->sink = NULL;
2542 return TRUE;
2543 } else if (session_id != NULL && participant != NULL) {
2544 PurpleMediaStream *stream;
2545 stream = purple_media_get_stream(media,
2546 session_id, participant);
2547
2548 if (stream == NULL)
2549 return FALSE;
2550
2551 sink = stream->sink;
2552 } else
2553 return FALSE;
2554
2555 if (!GST_IS_ELEMENT(sink))
2556 return FALSE;
2557
2558 /* Remove sink */
2559 parent = GST_ELEMENT(gst_element_get_parent(sink));
2560
2561 if (parent == NULL) {
2562 /* It's not added and therefore not linked */
2563 gst_object_unref(sink);
2564 return FALSE;
2565 }
2566
2567 pad = gst_element_get_static_pad(sink, "ghostsink");
2568
2569 if (pad == NULL) {
2570 /* It's already a fakesink */
2571 gst_object_unref(parent);
2572 return FALSE;
2573 }
2574
2575 peer = gst_pad_get_peer(pad);
2576 gst_object_unref(pad);
2577 gst_pad_set_blocked_async(peer, TRUE, dummy_block_cb, NULL);
2578 gst_element_set_locked_state(sink, TRUE);
2579 gst_element_set_state(sink, GST_STATE_NULL);
2580 gst_bin_remove(GST_BIN(parent), sink);
2581
2582 /* Add fakesink */
2583 fakesink = gst_element_factory_make("fakesink", NULL);
2584 gst_bin_add(GST_BIN(parent), fakesink);
2585 gst_element_sync_state_with_parent(fakesink);
2586 gst_object_unref(parent);
2587 pad = gst_element_get_static_pad(fakesink, "sink");
2588 gst_pad_link(peer, pad);
2589 gst_object_unref(pad);
2590 gst_pad_set_blocked_async(peer, FALSE, dummy_block_cb, NULL);
2591 gst_object_unref(peer);
2592 return TRUE;
2593 } 2316 }
2594 2317
2595 void 2318 void
2596 purple_media_remove_output_windows(PurpleMedia *media) 2319 purple_media_remove_output_windows(PurpleMedia *media)
2597 { 2320 {
2598 GList *iter = media->priv->streams; 2321 GList *iter = media->priv->streams;
2599 for (; iter; iter = g_list_next(iter)) { 2322 for (; iter; iter = g_list_next(iter)) {
2600 PurpleMediaStream *stream = iter->data; 2323 PurpleMediaStream *stream = iter->data;
2601 purple_media_remove_output_window(media, 2324 purple_media_manager_remove_output_windows(
2325 media->priv->manager, media,
2602 stream->session->id, stream->participant); 2326 stream->session->id, stream->participant);
2603 } 2327 }
2604 2328
2605 iter = purple_media_get_session_names(media); 2329 iter = purple_media_get_session_names(media);
2606 for (; iter; iter = g_list_delete_link(iter, iter)) { 2330 for (; iter; iter = g_list_delete_link(iter, iter)) {
2607 gchar *session_name = iter->data; 2331 gchar *session_name = iter->data;
2608 purple_media_remove_output_window(media, session_name, NULL); 2332 purple_media_manager_remove_output_windows(
2609 } 2333 media->priv->manager, media,
2334 session_name, NULL);
2335 }
2336 }
2337
2338 GstElement *
2339 purple_media_get_tee(PurpleMedia *media,
2340 const gchar *session_id, const gchar *participant)
2341 {
2342 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
2343
2344 if (session_id != NULL && participant == NULL) {
2345 PurpleMediaSession *session =
2346 purple_media_get_session(media, session_id);
2347 return (session != NULL) ? session->tee : NULL;
2348 } else if (session_id != NULL && participant != NULL) {
2349 PurpleMediaStream *stream =
2350 purple_media_get_stream(media,
2351 session_id, participant);
2352 return (stream != NULL) ? stream->tee : NULL;
2353 }
2354 g_return_val_if_reached(NULL);
2610 } 2355 }
2611 2356
2612 #endif /* USE_VV */ 2357 #endif /* USE_VV */