$ git clone http://tcclient.ion.nu/tc_client.git
commit d62ea903c69850ec532e8ae09ed972ff6c2c1238
Author: Alicia <...>
Date: Sun Dec 18 20:16:15 2016 +0100
tc_client-gtk: implemented broadcasting to greenroom while awaiting approval.
diff --git a/ChangeLog b/ChangeLog
index febc61c..08fcde4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,7 @@ tc_client-gtk: added an option to hide join/quit/nickname notifications.
tc_client-gtk: made the user list sorted.
tc_client-gtk: added support for viewing and approving greenroom cameras.
tc_client-gtk: added an option to show the greenroom menu even when you're not a moderator.
+tc_client-gtk: implemented broadcasting to greenroom while awaiting approval.
dist/appimage.sh: fix audio in appimages by building ffmpeg with support for nellymoser and speex, and depending on the system's libao and libpulse instead of including it in the appimage.
libcamera(escapi): handle failure to open camera more gracefully.
irchack: pass along "<user> cammed up" notifications.
diff --git a/Makefile b/Makefile
index e667f06..dbd3239 100644
--- a/Makefile
+++ b/Makefile
@@ -132,7 +132,7 @@ camviewer: $(CAMVIEWER_OBJ)
cursedchat: $(CURSEDCHAT_OBJ)
$(CC) $(LDFLAGS) $^ $(LIBS) $(READLINE_LIBS) $(CURSES_LIBS) -o $@
-tc_client-gtk: $(TC_CLIENT_GTK_OBJ) camplaceholder.gif modicon.png
+tc_client-gtk: $(TC_CLIENT_GTK_OBJ) camplaceholder.gif modicon.png greenroomindicator.png
$(CC) $(LDFLAGS) $(TC_CLIENT_GTK_OBJ) $(LIBS) $(GTK_LIBS) $(AVCODEC_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) $(AVRESAMPLE_LIBS) $(SWRESAMPLE_LIBS) $(AVFORMAT_LIBS) $(AO_LIBS) $(LIBV4L2_LIBS) $(LIBX11_LIBS) $(PULSE_LIBS) -o $@
camplaceholder.gif: utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf
@@ -141,6 +141,9 @@ camplaceholder.gif: utilities/gtk/gencamplaceholder.sh utilities/gtk/camplacehol
modicon.png: $(SRCDIR)utilities/gtk/modicon.xcf
convert -background none $< -layers Merge -scale x20 $@
+greenroomindicator.png: $(SRCDIR)utilities/gtk/greenroomindicator.xcf
+ convert $< -layers Merge $@
+
libcamera.a: $(LIBCAMERA_OBJ)
$(AR) cru $@ $^
$(RANLIB) $@
@@ -154,7 +157,7 @@ SOURCES+=utilities/modbot/modbot.c utilities/modbot/queue.c utilities/modbot/que
SOURCES+=utilities/camviewer/camviewer.c
SOURCES+=utilities/cursedchat/cursedchat.c utilities/cursedchat/buffer.c utilities/cursedchat/buffer.h
SOURCES+=utilities/gtk/camviewer.c utilities/gtk/userlist.c utilities/gtk/media.c utilities/gtk/compat.c utilities/gtk/configfile.c utilities/gtk/gui.c utilities/gtk/logging.c utilities/gtk/postproc.c utilities/gtk/inputhistory.c utilities/gtk/playmedia.c utilities/gtk/greenroom.c utilities/gtk/main.h utilities/gtk/userlist.h utilities/gtk/media.h utilities/gtk/compat.h utilities/gtk/configfile.h utilities/gtk/gui.h utilities/gtk/logging.h utilities/gtk/postproc.h utilities/gtk/inputhistory.h utilities/gtk/playmedia.h utilities/gtk/greenroom.h gtkgui.glade
-SOURCES+=utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf utilities/gtk/modicon.xcf
+SOURCES+=utilities/gtk/gencamplaceholder.sh utilities/gtk/camplaceholder.xcf utilities/gtk/spinnerdot.xcf utilities/gtk/modicon.xcf utilities/gtk/greenroomindicator.xcf
SOURCES+=utilities/compat.c utilities/compat.h utilities/list.c utilities/list.h utilities/stringutils.c utilities/stringutils.h utilities/compat_av.c utilities/compat_av.h
SOURCES+=utilities/libcamera/camera.c utilities/libcamera/camera.h utilities/libcamera/camera_v4l2.c utilities/libcamera/camera_v4l2.h utilities/libcamera/camera_img.c utilities/libcamera/camera_img.h utilities/libcamera/camera_escapi.cpp utilities/libcamera/camera_escapi.h utilities/libcamera/camera_x11.c utilities/libcamera/camera_x11.h
tarball:
@@ -170,6 +173,7 @@ ifdef SWSCALE_LIBS
install -D gtkgui.glade "$(PREFIX)/share/tc_client/gtkgui.glade"
install -D camplaceholder.gif "$(PREFIX)/share/tc_client/camplaceholder.gif"
install -D modicon.png "$(PREFIX)/share/tc_client/modicon.png"
+ install -D greenroomindicator.png "$(PREFIX)/share/tc_client/greenroomindicator.png"
endif
endif
endif
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index d42cbef..c91a67b 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -80,6 +80,7 @@ char hasgreenroom=0;
char frombuild=0; // Running from the build directory
#ifdef _WIN32
PROCESS_INFORMATION coreprocess={.hProcess=0};
+ PROCESS_INFORMATION grprocess={.hProcess=0}; // Greenroom
#endif
void printchat(const char* text, const char* color, unsigned int offset, const char* pm)
@@ -284,6 +285,11 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer x)
}
return 1;
}
+ if(!strcmp(buf, "Authenticated to broadcast"))
+ {
+ greenroom_allowed();
+ return 1;
+ }
if(!strcmp(buf, "Password required"))
{
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
@@ -516,6 +522,7 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer x)
}
if(!strcmp(buf, "Starting outgoing media stream"))
{
+ if(camera_find("out")){return 1;} // In case we're just moving out of greenroom
struct camera* cam=camera_new(nickname, "out", CAMFLAG_NONE);
cam->vctx=avcodec_alloc_context3(data->vencoder);
cam->vctx->pix_fmt=AV_PIX_FMT_YUV420P;
@@ -611,7 +618,8 @@ void togglecam(GtkCheckMenuItem* item, void* x)
// 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");
+ int fd=((hasgreenroom && !greenroom_gotpass)?greenroompipe_in[1]:tc_client_in[1]);
+ dprintf(fd, "/camdown\n");
camera_remove("out", 0); // Close our local display
}
return;
@@ -624,6 +632,7 @@ void togglemic(GtkCheckMenuItem* item, void* x)
{
static int micpipe[2];
static int eventsource=0;
+ int fd=((hasgreenroom && !greenroom_gotpass)?greenroompipe_in[1]:tc_client_in[1]);
if(!gtk_check_menu_item_get_active(item)) // Stop mic broadcast
{
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "pushtotalk")));
@@ -636,7 +645,7 @@ void togglemic(GtkCheckMenuItem* item, void* x)
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");
+ dprintf(fd, "/camdown\n");
camera_remove("out", 0); // Close our local display
}
return;
@@ -658,7 +667,7 @@ void togglemic(GtkCheckMenuItem* item, void* x)
// 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");
+ dprintf(fd, "/camup\n");
}
pipe(micpipe);
g_thread_new("audio_in", audiothread_in, &micpipe[1]);
@@ -1009,6 +1018,10 @@ int main(int argc, char** argv)
{
TerminateProcess(coreprocess.hProcess, 0);
}
+ if(grprocess.hProcess)
+ {
+ TerminateProcess(grprocess.hProcess, 0);
+ }
#endif
camera_cleanup();
return 0;
diff --git a/utilities/gtk/greenroom.c b/utilities/gtk/greenroom.c
index 754fa3c..0ccf434 100644
--- a/utilities/gtk/greenroom.c
+++ b/utilities/gtk/greenroom.c
@@ -38,11 +38,14 @@
#include "configfile.h"
#include "userlist.h"
#include "greenroom.h"
+#ifdef _WIN32
+ #include <wtypes.h>
+ extern PROCESS_INFORMATION grprocess;
+#endif
int greenroompipe[2];
int greenroompipe_in[2]={-1,-1};
-// TODO: Handle outgoing cam, sending to greenroom if we don't have the broadcast password yet
-// TODO: Option to show greenroom as non-mod
+char greenroom_gotpass=0;
struct greenmap
{
@@ -248,6 +251,27 @@ static gboolean greenroom_handleline(GIOChannel* iochannel, GIOCondition conditi
greenroom_updatecount();
return 1;
}
+ if(!strcmp(buf, "Starting outgoing media stream"))
+ {
+ struct camera* cam=camera_new(nickname, "out", CAMFLAG_NONE);
+ AVCodec* codec=avcodec_find_encoder(AV_CODEC_ID_FLV1);
+ cam->vctx=avcodec_alloc_context3(codec);
+ cam->vctx->pix_fmt=AV_PIX_FMT_YUV420P;
+ cam->vctx->time_base.num=1;
+ cam->vctx->time_base.den=10;
+ cam->vctx->width=camsize_out.width;
+ cam->vctx->height=camsize_out.height;
+ avcodec_open2(cam->vctx, codec, 0);
+ cam->frame->data[0]=0;
+ cam->frame->width=0;
+ cam->frame->height=0;
+ cam->dstframe->data[0]=0;
+
+ cam->actx=0;
+ updatescaling(0, 0, 1);
+ gtk_widget_show_all(cam->box);
+ return 1;
+ }
if(!strncmp(buf, "Captcha: ", 9))
{
// If we're a mod, don't bother with the captcha (since we're only here to look at cams)
@@ -314,7 +338,7 @@ void greenroom_join(const char* id)
strcat(cmd, channel);
strcat(cmd, " ");
strcat(cmd, id);
- w32_runcmdpipes(cmd, greenroompipe_in, greenroompipe, coreprocess);
+ w32_runcmdpipes(cmd, greenroompipe_in, greenroompipe, grprocess);
#else
pipe(greenroompipe);
pipe(greenroompipe_in);
@@ -361,3 +385,31 @@ void greenroom_changenick(const char* from, const char* to)
}
}
}
+
+void greenroom_allowed(void)
+{
+ greenroom_gotpass=1;
+ if(camera_find("out"))
+ {
+ camout_delay=600;
+ write(greenroompipe_in[1], "/camdown\n", 9);
+ write(tc_client_in[1], "/camup\n", 7);
+ }
+}
+
+void greenroom_indicator(GdkPixbuf* frame)
+{
+ static GdkPixbuf* indicator=0;
+ static double indicatorwidth;
+ static double indicatorheight;
+ if(!indicator)
+ {
+ indicator=gdk_pixbuf_new_from_file(frombuild ? "greenroomindicator.png" : PREFIX "/share/tc_client/greenroomindicator.png", 0);
+ if(!indicator){return;}
+ indicatorwidth=gdk_pixbuf_get_width(indicator);
+ indicatorheight=gdk_pixbuf_get_height(indicator);
+ }
+ int framewidth=gdk_pixbuf_get_width(frame);
+ int frameheight=gdk_pixbuf_get_height(frame);
+ gdk_pixbuf_composite(indicator, frame, 0, frameheight*3/4, framewidth, frameheight/4, 0, frameheight*3/4, (double)framewidth/indicatorwidth, (double)frameheight/indicatorheight/4.0, GDK_INTERP_BILINEAR, 255);
+}
diff --git a/utilities/gtk/greenroom.h b/utilities/gtk/greenroom.h
index 98d12d0..676b22e 100644
--- a/utilities/gtk/greenroom.h
+++ b/utilities/gtk/greenroom.h
@@ -15,6 +15,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
extern int greenroompipe_in[2];
+extern char greenroom_gotpass;
extern char greenroom_gotnick(const char* id, const char* nick);
extern void greenroom_join(const char* id);
extern void greenroom_changenick(const char* from, const char* to);
+extern void greenroom_allowed(void);
+extern void greenroom_indicator(GdkPixbuf* frame);
diff --git a/utilities/gtk/greenroomindicator.xcf b/utilities/gtk/greenroomindicator.xcf
new file mode 100644
index 0000000..28594fa
Binary files /dev/null and b/utilities/gtk/greenroomindicator.xcf differ
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index b3d89c2..37b69f2 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -33,6 +33,8 @@
#include "../compat.h"
#include "../compat_av.h"
#include "gui.h"
+#include "main.h"
+#include "greenroom.h"
#include "media.h"
#define PREVIEW_MAX_WIDTH 640
@@ -57,6 +59,7 @@ CAM* camout_cam=0;
char pushtotalk_enabled=0;
char pushtotalk_pushed=0;
#endif
+unsigned int camout_delay;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
void camera_playsnd(struct camera* cam, int16_t* samples, unsigned int samplecount)
@@ -258,12 +261,12 @@ void camera_cleanup(void)
void freebuffer(guchar* pixels, gpointer data){free(pixels);}
-unsigned int camout_delay;
void startcamout(CAM* cam)
{
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");
+ int fd=((hasgreenroom && !greenroom_gotpass)?greenroompipe_in[1]:tc_client_in[1]);
+ dprintf(fd, "/camup\n");
}
camout_cam=cam;
camout_delay=500;
@@ -310,6 +313,14 @@ gboolean cam_encode(void* camera_)
// Scale to fit
gdkframe=gdk_pixbuf_scale_simple(gdkframe, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
volume_indicator(gdkframe, cam); // Add volume indicator
+ int fd;
+ if(hasgreenroom && !greenroom_gotpass)
+ {
+ fd=greenroompipe_in[1];
+ greenroom_indicator(gdkframe); // Add greenroom indicator
+ }else{
+ fd=tc_client_in[1];
+ }
gtk_image_set_from_pixbuf(GTK_IMAGE(cam->cam), gdkframe);
g_object_unref(oldpixbuf);
// Encode
@@ -333,9 +344,9 @@ gboolean cam_encode(void* camera_)
char key=!!(packet.flags&AV_PKT_FLAG_KEY);
unsigned char frameinfo=(key?0x12:0x22); // In the first 4 bits: 1=keyframe, 2=interframe
// Send video
- dprintf(tc_client_in[1], "/video %i\n", packet.size+1);
- write(tc_client_in[1], &frameinfo, 1);
- ssize_t w=write(tc_client_in[1], packet.data, packet.size);
+ dprintf(fd, "/video %i\n", packet.size+1);
+ write(fd, &frameinfo, 1);
+ ssize_t w=write(fd, packet.data, packet.size);
if(w!=packet.size){printf("Error: wrote %zi of %i bytes\n", w, packet.size);}
av_packet_unref(&packet);
@@ -661,9 +672,10 @@ gboolean mic_encode(GIOChannel* iochannel, GIOCondition condition, gpointer data
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);
+ int fd=((hasgreenroom && !greenroom_gotpass)?greenroompipe_in[1]:tc_client_in[1]);
+ dprintf(fd, "/audio %i\n", packet.size+1);
+ write(fd, &frameinfo, 1);
+ write(fd, packet.data, packet.size);
av_packet_unref(&packet);
return 1;
@@ -691,10 +703,12 @@ void volume_indicator(GdkPixbuf* frame, struct camera* cam)
guchar* pixels=gdk_pixbuf_get_pixels(frame);
unsigned int channels=gdk_pixbuf_get_n_channels(frame);
unsigned int stride=gdk_pixbuf_get_rowstride(frame);
- unsigned int size_x=camsize_scale.width/24;
- unsigned int size_y=camsize_scale.height/5;
- unsigned int pos_x=camsize_scale.width*47/48-size_x;
- unsigned int pos_y=camsize_scale.height*47/48-size_y;
+ unsigned int width=gdk_pixbuf_get_width(frame);
+ unsigned int height=gdk_pixbuf_get_height(frame);
+ unsigned int size_x=width/24;
+ unsigned int size_y=height/5;
+ unsigned int pos_x=width*47/48-size_x;
+ unsigned int pos_y=height*47/48-size_y;
int volumebar=size_y-cam->volume*size_y;
if(volumebar<0){volumebar=0;}
unsigned int x, y;
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index c205428..f9a16aa 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -70,6 +70,7 @@ extern GdkPixbufAnimationIter* camplaceholder_iter;
extern CAM* camout_cam;
extern char pushtotalk_enabled;
extern char pushtotalk_pushed;
+extern unsigned int camout_delay;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
extern void camera_playsnd(struct camera* cam, int16_t* samples, unsigned int samplecount);