$ git clone https://tcclient.ion.nu/tc_client.git
commit c4f4691cc6d935f1ad5c782a05d54123614bba3d
Author: Alicia <...>
Date: Wed Oct 12 01:36:53 2016 +0200
tc_client-gtk: added support for broadcasting audio.
diff --git a/ChangeLog b/ChangeLog
index b01aec9..b2690fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -22,6 +22,7 @@ tc_client-gtk: fixed resampling of incoming audio.
tc_client-gtk: added GTK+2 compatibility code related to the greenscreen camera color picker.
tc_client-gtk: added compatibility code for windows' lack of pipe()
tc_client-gtk: added workaround for libao not handling the "client_name" option on windows.
+tc_client-gtk: added support for broadcasting audio.
libcamera: added support for a virtual X11 camera.
tc_client-gtk and camviewer: updated to libavcodec's avcodec_{send,receive}_{frame,packet} API.
camviewer: removed the old, buggy audio code.
diff --git a/Makefile b/Makefile
index ae86c88..9eee982 100644
--- a/Makefile
+++ b/Makefile
@@ -30,13 +30,36 @@ ifdef SWSCALE_LIBS
INSTALLDEPS+=tc_client-gtk gtkgui.glade
ifdef AO_LIBS
ifdef AVRESAMPLE_LIBS
- CONFINFO+=| Will enable (incoming) mic support
+ CONFINFO+=| Will enable incoming mic support
CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_AVRESAMPLE=1 $(AVRESAMPLE_CFLAGS) $(AO_CFLAGS)
endif
ifdef SWRESAMPLE_LIBS
- CONFINFO+=| Will enable (incoming) mic support
+ CONFINFO+=| Will enable incoming mic support
CFLAGS+=-DHAVE_LIBAO=1 -DHAVE_SWRESAMPLE=1 $(SWRESAMPLE_CFLAGS) $(AO_CFLAGS)
endif
+ ifndef AVRESAMPLE_LIBS
+ ifndef SWRESAMPLE_LIBS
+ CONFINFO+=| Incoming mic support will not be enabled
+ endif
+ endif
+ endif
+ ifdef PULSE_LIBS
+ CONFINFO+=| Will enable outgoing mic support
+ CFLAGS+=-DHAVE_PULSEAUDIO=1 $(PULSE_CFLAGS)
+ else
+ CONFINFO+=| Outgoing mic support will not be enabled
+ endif
+ ifdef LIBV4L2_LIBS
+ CONFINFO+=| Will enable v4l2 camera support
+ CFLAGS+=-DHAVE_V4L2 $(LIBV4L2_CFLAGS)
+ LIBCAMERA_OBJ+=utilities/libcamera/camera_v4l2.o
+ else
+ CONFINFO+=| v4l2 camera support will not be enabled
+ endif
+ ifdef LIBX11_LIBS
+ CONFINFO+=| Will enable X11 virtual camera support
+ CFLAGS+=-DHAVE_X11 $(LIBX11_CFLAGS)
+ LIBCAMERA_OBJ+=utilities/libcamera/camera_x11.o
endif
ifneq ($(findstring MINGW,$(shell uname -s)),)
ifeq ($(findstring mingw,$(shell $(CC) -v 2>&1 | grep Target)),)
@@ -65,18 +88,6 @@ ifdef SWSCALE_LIBS
@echo
@echo 'To build the gtk+ GUI, enter this directory from a MinGW shell and type make'
endif
- ifdef LIBV4L2_LIBS
- CONFINFO+=| Will enable v4l2 camera support
- CFLAGS+=-DHAVE_V4L2 $(LIBV4L2_CFLAGS)
- LIBCAMERA_OBJ+=utilities/libcamera/camera_v4l2.o
- else
- CONFINFO+=| v4l2 camera support will not be enabled
- endif
- ifdef LIBX11_LIBS
- CONFINFO+=| Will enable X11 virtual camera support
- CFLAGS+=-DHAVE_X11 $(LIBX11_CFLAGS)
- LIBCAMERA_OBJ+=utilities/libcamera/camera_x11.o
- endif
endif
endif
endif
@@ -118,7 +129,7 @@ cursedchat: $(CURSEDCHAT_OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) $(READLINE_LIBS) $(CURSES_LIBS) -o $@
tc_client-gtk: $(TC_CLIENT_GTK_OBJ) camplaceholder.gif
- $(CC) $(LDFLAGS) $(TC_CLIENT_GTK_OBJ) $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) $(SWRESAMPLE_LIBS) $(AO_LIBS) $(LIBV4L2_LIBS) $(LIBX11_LIBS) -o $@
+ $(CC) $(LDFLAGS) $(TC_CLIENT_GTK_OBJ) $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) $(SWRESAMPLE_LIBS) $(AO_LIBS) $(LIBV4L2_LIBS) $(LIBX11_LIBS) $(PULSE_LIBS) -o $@
camplaceholder.gif: utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf
utilities/gtk/gencamplaceholder.sh
diff --git a/configure b/configure
index 8868c34..3300265 100755
--- a/configure
+++ b/configure
@@ -69,6 +69,23 @@ testbuild()
fi
}
+testpkgconfig()
+{
+ pkgname="$1"
+ varprefix="$2"
+ printf "Checking for ${pkgname}... "
+ testlibs="`pkg-config --libs "$pkgname" 2> /dev/null`"
+ if [ "x$testlibs" != "x" ]; then
+ echo "${varprefix}_LIBS=${testlibs}" >> config.mk
+ echo "${varprefix}_CFLAGS=`pkg-config --cflags "$pkgname"`" >> config.mk
+ echo yes
+ return 0
+ else
+ echo no
+ return 1
+ fi
+}
+
if ! testbuild 'strndup' 'char* x=strndup("abc", 2);' 'string.h'; then
echo '#define NO_STRNDUP 1' >> config.h
fi
@@ -77,23 +94,8 @@ if ! testbuild 'dprintf' 'dprintf(1,"test");' 'stdio.h'; then
echo '#define NO_DPRINTF 1' >> config.h
fi
-printf 'Checking for gtk+-3.0... '
-gtklibs="`pkg-config --libs gtk+-3.0 2> /dev/null`"
-if [ "x$gtklibs" != "x" ]; then
- echo "GTK_LIBS=${gtklibs}" >> config.mk
- echo "GTK_CFLAGS=`pkg-config --cflags gtk+-3.0`" >> config.mk
- echo yes
-else
- echo no
- printf 'Checking for gtk+-2.0... '
- gtklibs="`pkg-config --libs gtk+-2.0 2> /dev/null`"
- if [ "x$gtklibs" != "x" ]; then
- echo "GTK_LIBS=${gtklibs}" >> config.mk
- echo "GTK_CFLAGS=`pkg-config --cflags gtk+-2.0`" >> config.mk
- echo yes
- else
- echo no
- fi
+if ! testpkgconfig 'gtk+-3.0' GTK; then
+ testpkgconfig 'gtk+-2.0' GTK
fi
printf 'Checking for libavcodec... '
@@ -134,15 +136,7 @@ else
echo no
fi
-printf 'Checking for libswscale... '
-swscalelibs="`pkg-config --libs libswscale 2> /dev/null`"
-if [ "x$swscalelibs" != "x" ]; then
- echo "SWSCALE_LIBS=${swscalelibs}" >> config.mk
- echo "SWSCALE_CFLAGS=`pkg-config --cflags libswscale`" >> config.mk
- echo yes
-else
- echo no
-fi
+testpkgconfig libswscale SWSCALE
printf 'Checking for libavutil... '
avutillibs="`pkg-config --libs libavutil 2> /dev/null`"
@@ -211,15 +205,7 @@ else
fi
if "$haveresample"; then
- printf 'Checking for libao... '
- aolibs="`pkg-config --libs ao 2> /dev/null`"
- if [ "x$aolibs" != "x" ]; then
- echo "AO_LIBS=${aolibs}" >> config.mk
- echo "AO_CFLAGS=`pkg-config --cflags ao`" >> config.mk
- echo yes
- else
- echo no
- fi
+ testpkgconfig ao AO
fi
printf 'Checking for ncurses... '
@@ -242,25 +228,9 @@ if testbuild 'readline' 'rl_initialize();return 0;' 'stdio.h readline/readline.h
echo "READLINE_LIBS=-lreadline" >> config.mk
fi
-printf 'Checking for libv4l2... '
-v4l2libs="`pkg-config --libs libv4l2 2> /dev/null`"
-if [ "x$v4l2libs" != "x" ]; then
- echo "LIBV4L2_LIBS=${v4l2libs}" >> config.mk
- echo "LIBV4L2_CFLAGS=`pkg-config --cflags libv4l2`" >> config.mk
- echo yes
-else
- echo no
-fi
-
-printf 'Checking for X11... '
-x11libs="`pkg-config --libs x11 2> /dev/null`"
-if [ "x$x11libs" != "x" ]; then
- echo "LIBX11_LIBS=${x11libs}" >> config.mk
- echo "LIBX11_CFLAGS=`pkg-config --cflags x11`" >> config.mk
- echo yes
-else
- echo no
-fi
+testpkgconfig libv4l2 LIBV4L2
+testpkgconfig x11 LIBX11
+testpkgconfig libpulse-simple PULSE
# TODO: handle crosscompiling better
printf 'Checking if endianness macros work... '
diff --git a/gtkgui.glade b/gtkgui.glade
index b481225..77a3cba 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -1083,6 +1083,22 @@
<property name="use_underline">True</property>
</object>
</child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menuitem_broadcast_mic">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Microphone</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem_broadcast_stop">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Stop broadcasting</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
</object>
</child>
</object>
@@ -1231,10 +1247,34 @@
</packing>
</child>
<child>
- <object class="GtkEntry" id="inputfield">
+ <object class="GtkBox" id="box16">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="pushtotalk">
+ <property name="label" translatable="yes">Push-to-talk</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="inputfield">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">False</property>
@@ -1568,4 +1608,73 @@
</object>
</child>
</object>
+ <object class="GtkDialog" id="pushtotalkdialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Broadcast</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">main</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label" translatable="yes">Push-to-talk</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label" translatable="yes">Open mic</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Broadcast mic in push-to-talk mode (only broadcast while a button is pressed) or open mic?</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="1">button1</action-widget>
+ <action-widget response="0">button2</action-widget>
+ </action-widgets>
+ </object>
</interface>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index 8bdfaf4..019260f 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -145,6 +145,12 @@ void togglecam_cancel(void)
gtk_check_menu_item_set_active(item, 0);
}
+void stopbroadcasting(GtkMenuItem* x, void* y)
+{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera")), 0);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_mic")), 0);
+}
+
unsigned int cameventsource=0;
char buf[1024];
gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer datap)
@@ -425,7 +431,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
{
if(config_get_bool("camdownonjoin"))
{
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera")), 0);
+ stopbroadcasting(0, 0);
}
space[0]=0;
adduser(nick);
@@ -544,7 +550,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
}
if(!strcmp(buf, "Outgoing media stream was closed"))
{
- togglecam_cancel();
+ stopbroadcasting(0, 0);
return 1;
}
if(!strncmp(buf, "Room topic: ", 12) ||
@@ -592,13 +598,15 @@ void* audiothread(void* fdp)
}
#endif
-void togglecam(GtkCheckMenuItem* item, struct viddata* data)
+void togglecam(GtkCheckMenuItem* item, void* x)
{
if(!gtk_check_menu_item_get_active(item))
{
if(!camout_cam){return;}
cam_close(camout_cam);
camout_cam=0;
+ if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_mic")))){return;}
+ // TODO: if switching from cam+mic to just mic, send something to tell other clients to drop the last video frame and show a mic instead
if(camera_find("out"))
{
dprintf(tc_client_in[1], "/camdown\n");
@@ -609,6 +617,62 @@ void togglecam(GtkCheckMenuItem* item, struct viddata* data)
camselect_open(startcamout, togglecam_cancel);
}
+#ifdef HAVE_PULSEAUDIO
+void togglemic(GtkCheckMenuItem* item, void* x)
+{
+ static int micpipe[2];
+ static int eventsource=0;
+ if(!gtk_check_menu_item_get_active(item)) // Stop mic broadcast
+ {
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalk")));
+ pushtotalk_enabled=0;
+ if(!eventsource){return;}
+ close(micpipe[0]);
+ close(micpipe[1]);
+ g_source_remove(eventsource);
+ eventsource=0;
+ if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera")))){return;}
+ if(camera_find("out"))
+ {
+ dprintf(tc_client_in[1], "/camdown\n");
+ camera_remove("out", 0); // Close our local display
+ }
+ return;
+ }
+ // Choose between push-to-talk and open mic
+ int choice=gtk_dialog_run(GTK_DIALOG(gtk_builder_get_object(gui, "pushtotalkdialog")));
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalkdialog")));
+ if(choice==GTK_RESPONSE_DELETE_EVENT)
+ {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_mic")), 0);
+ return;
+ }
+ if(choice) // 1=push-to-talk, 0=open mic
+ {
+ gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalk")));
+ pushtotalk_enabled=1;
+ pushtotalk_pushed=0;
+ }
+ // Start mic broadcast
+ if(!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_camera"))))
+ { // Only /camup if we're not already broadcasting video
+ dprintf(tc_client_in[1], "/camup\n");
+ }
+ pipe(micpipe);
+ g_thread_new("audio_in", audiothread_in, &micpipe[1]);
+ // Attach micpipe[0] to mic_encode()
+ GIOChannel* channel=g_io_channel_unix_new(micpipe[0]);
+ g_io_channel_set_encoding(channel, 0, 0);
+ eventsource=g_io_add_watch(channel, G_IO_IN, mic_encode, 0);
+}
+
+gboolean mic_pushtotalk(GtkWidget* button, GdkEvent* event, void* pushed)
+{
+ pushtotalk_pushed=!!pushed;
+ return 0;
+}
+#endif
+
gboolean handleresize(GtkWidget* widget, GdkEventConfigure* event, struct viddata* data)
{
if(event->width!=gtk_widget_get_allocated_width(cambox))
@@ -821,6 +885,7 @@ void startsession(GtkButton* button, void* x)
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "channelconfig")));
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "channelpasswordwindow")));
gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalk")));
const char* nick=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")));
channel=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")));
const char* chanpass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "channelpassword")));
@@ -895,7 +960,7 @@ void startsession(GtkButton* button, void* x)
void captcha_done(GtkWidget* button, void* x)
{
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "captcha")));
- gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
+ gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
write(tc_client_in[1], "\n", 1);
}
@@ -931,7 +996,18 @@ int main(int argc, char** argv)
unsigned int i;
GtkWidget* item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_broadcast_camera"));
- g_signal_connect(item, "toggled", G_CALLBACK(togglecam), data);
+ g_signal_connect(item, "toggled", G_CALLBACK(togglecam), 0);
+ item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_broadcast_mic"));
+ #ifdef HAVE_PULSEAUDIO
+ g_signal_connect(item, "toggled", G_CALLBACK(togglemic), 0);
+ item=GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalk"));
+ g_signal_connect(item, "button-press-event", G_CALLBACK(mic_pushtotalk), (void*)1);
+ g_signal_connect(item, "button-release-event", G_CALLBACK(mic_pushtotalk), 0);
+ #else
+ gtk_widget_destroy(item);
+ #endif
+ item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_broadcast_stop"));
+ g_signal_connect(item, "activate", G_CALLBACK(stopbroadcasting), 0);
data->vencoder=avcodec_find_encoder(AV_CODEC_ID_FLV1);
// Set up cam selection and preview
campreview.cam=GTK_WIDGET(gtk_builder_get_object(gui, "camselect_preview"));
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index aaab747..d506190 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -24,6 +24,9 @@
#else
#include <libavcore/imgutils.h>
#endif
+#ifdef HAVE_PULSEAUDIO
+ #include <pulse/simple.h>
+#endif
#include "../libcamera/camera.h"
#include "compat.h"
#include "../compat.h"
@@ -49,6 +52,10 @@ unsigned int camrowcount=0;
GdkPixbufAnimation* camplaceholder=0;
GdkPixbufAnimationIter* camplaceholder_iter=0;
CAM* camout_cam=0;
+#ifdef HAVE_PULSEAUDIO
+char pushtotalk_enabled=0;
+char pushtotalk_pushed=0;
+#endif
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
// Experimental mixer, not sure if it really works
@@ -226,7 +233,10 @@ void freebuffer(guchar* pixels, gpointer data){free(pixels);}
unsigned int camout_delay;
void startcamout(CAM* cam)
{
- dprintf(tc_client_in[1], "/camup\n");
+ if(!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(gui, "menuitem_broadcast_mic"))))
+ { // Only /camup if we're not already broadcasting mic
+ dprintf(tc_client_in[1], "/camup\n");
+ }
camout_cam=cam;
camout_delay=500;
camsize_out.width=320;
@@ -523,3 +533,82 @@ GdkPixbuf* scaled_gdk_pixbuf_from_cam(CAM* cam, unsigned int width, unsigned int
}
return gdkframe;
}
+
+#ifdef HAVE_PULSEAUDIO
+void* audiothread_in(void* fdp)
+{
+ int fd=*(int*)fdp;
+ pa_simple* pulse;
+ pa_sample_spec pulsespec;
+ pulsespec.format=PA_SAMPLE_FLOAT32;
+ pulsespec.channels=1;
+ pulsespec.rate=44100;
+ pulse=pa_simple_new(0, "tc_client-gtk", PA_STREAM_RECORD, 0, "mic", &pulsespec, 0, 0, 0);
+ char buf[1024];
+ // Just read/listen and write to the main thread until either reading/listening or writing fails
+ while(1)
+ {
+ if(pa_simple_read(pulse, buf, 1024, 0)<0){break;}
+ if(write(fd, buf, 1024)<1024){break;}
+ }
+ pa_simple_free(pulse);
+ close(fd);
+ return 0;
+}
+
+gboolean mic_encode(GIOChannel* iochannel, GIOCondition condition, gpointer datap)
+{
+ static AVFrame* micframe=0;
+ static AVCodecContext* avctx=0;
+ if(!micframe)
+ {
+ micframe=av_frame_alloc();
+ micframe->format=AV_SAMPLE_FMT_FLT;
+ micframe->sample_rate=44100;
+ unsigned int i;
+ for(i=0; i<AV_NUM_DATA_POINTERS; ++i)
+ {
+ micframe->data[i]=0;
+ micframe->linesize[i]=0;
+ }
+ AVCodec* encoder=avcodec_find_encoder(AV_CODEC_ID_NELLYMOSER);
+ avctx=avcodec_alloc_context3(encoder);
+ avctx->sample_fmt=AV_SAMPLE_FMT_FLT;
+ avctx->sample_rate=44100;
+ avctx->channels=1;
+ avctx->time_base.num=1;
+ avctx->time_base.den=10;
+ avcodec_open2(avctx, encoder, 0);
+ }
+ // Read up to 1024 bytes (256 samples) and start encoding
+ unsigned char buf[1024];
+ gsize r;
+ int status=g_io_channel_read_chars(iochannel, (char*)buf, 1024, &r, 0);
+ if(status!=G_IO_STATUS_NORMAL){return 0;}
+ // If push-to-talk is enabled but not pushed, return as soon as we've consumed the incoming data
+ if(pushtotalk_enabled && !pushtotalk_pushed){return 1;}
+ micframe->nb_samples=r/4; // 32bit floats
+ micframe->data[0]=buf;
+ micframe->linesize[0]=r;
+ AVPacket packet={
+#ifdef AVPACKET_HAS_BUF
+ .buf=0,
+#endif
+ .data=0,
+ .size=0,
+ .dts=AV_NOPTS_VALUE,
+ .pts=AV_NOPTS_VALUE
+ };
+ av_init_packet(&packet);
+ avcodec_send_frame(avctx, micframe);
+ if(avcodec_receive_packet(avctx, &packet)){return 1;}
+ unsigned char frameinfo=0x6c; // 6=Nellymoser, 3<<2=44100 samplerate
+ // Send audio
+ dprintf(tc_client_in[1], "/audio %i\n", packet.size+1);
+ write(tc_client_in[1], &frameinfo, 1);
+ write(tc_client_in[1], packet.data, packet.size);
+
+ av_packet_unref(&packet);
+ return 1;
+}
+#endif
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 44d15e3..3e06f78 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -61,6 +61,8 @@ extern GtkWidget* cambox;
extern GdkPixbufAnimation* camplaceholder;
extern GdkPixbufAnimationIter* camplaceholder_iter;
extern CAM* camout_cam;
+extern char pushtotalk_enabled;
+extern char pushtotalk_pushed;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
extern void camera_playsnd(int audiopipe, struct camera* cam, short* samples, unsigned int samplecount);
@@ -83,4 +85,6 @@ extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int
extern void updatescaling(unsigned int width, unsigned int height, char changedcams);
extern gboolean camplaceholder_update(void* id);
extern GdkPixbuf* scaled_gdk_pixbuf_from_cam(CAM* cam, unsigned int width, unsigned int height, unsigned int maxwidth, unsigned int maxheight);
+extern void* audiothread_in(void* fdp);
+extern gboolean mic_encode(GIOChannel* iochannel, GIOCondition condition, gpointer datap);
#endif