summaryrefslogtreecommitdiffstats
path: root/src/anchorman.c
diff options
context:
space:
mode:
authorTrever Fischer <wm161@wm161.net>2011-03-01 18:14:00 -0500
committerTrever Fischer <wm161@wm161.net>2011-03-01 18:14:00 -0500
commit68c4353c1b0513e2efaa42abc804309cb3ed3430 (patch)
treeddd3b83ba47be87fa206446e82de6c16ab07e727 /src/anchorman.c
parent2152912ca84f51aa347ceb9ede2827d5ab978d4d (diff)
downloadcamstream-68c4353c1b0513e2efaa42abc804309cb3ed3430.tar.gz
camstream-68c4353c1b0513e2efaa42abc804309cb3ed3430.tar.xz
camstream-68c4353c1b0513e2efaa42abc804309cb3ed3430.zip
Rename project to 'anchorman', and prepare for public consumption.
Inspired by the movie of the same name, anchorman now aims to be a decent tool for running your own broadcasting studio.
Diffstat (limited to 'src/anchorman.c')
-rw-r--r--src/anchorman.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/src/anchorman.c b/src/anchorman.c
new file mode 100644
index 0000000..6ed979b
--- /dev/null
+++ b/src/anchorman.c
@@ -0,0 +1,238 @@
+// anchorman - A program to stream webcams.
+//
+// Copyright (C) 2011 Trever Fischer <tdfischer@fedoraproject.org>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+
+#include <gst/gst.h>
+#include <gudev/gudev.h>
+#include <string.h>
+
+// Original gst-launch command:
+// gst-launch v4l2src device=/dev/video0 ! ffmpegcolorspace ! videorate ! videoscale ! video/x-raw-yuv,width=320,height=240 ! theoraenc bitrate=64 ! queue leaky=1 ! oggmux name=mux alsasrc device=hw:1 ! audioconvert ! vorbisenc ! queue leaky=1 ! mux. mux. ! queue ! shout2send ip=acm.cs.uakron.edu mount=lab.ogg
+
+GMainLoop *loop;
+
+static gchar *videoDevice = 0;
+static gchar *audioDevice = 0;
+static gchar *icecastServer = 0;
+static gchar *icecastMount = 0;
+
+static GOptionEntry entries[] =
+{
+ { "video-device", 'v', 0, G_OPTION_ARG_STRING, &videoDevice, "Video device to use", "device" },
+ { "audio-device", 'a', 0, G_OPTION_ARG_STRING, &audioDevice, "Audio device to use", "device" },
+ { "server", 's', 0, G_OPTION_ARG_STRING, &icecastServer, "Icecast server to stream to", "server" },
+ { "mount", 'm', 0, G_OPTION_ARG_STRING, &icecastMount, "Icecast mount to stream to on the server", "mount" },
+ { NULL }
+};
+
+static const gchar *subsystems[] =
+{
+ "video4linux",
+ NULL
+};
+
+gboolean startPipeline(GstElement *pipe) {
+ GstStateChangeReturn stateChange;
+ stateChange = gst_element_set_state(pipe, GST_STATE_PLAYING);
+ switch(stateChange) {
+ case GST_STATE_CHANGE_SUCCESS:
+ case GST_STATE_CHANGE_ASYNC:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+gboolean stopPipeline(GstElement *pipe) {
+ GstStateChangeReturn stateChange;
+ stateChange = gst_element_set_state(pipe, GST_STATE_NULL);
+ switch(stateChange) {
+ case GST_STATE_CHANGE_SUCCESS:
+ case GST_STATE_CHANGE_ASYNC:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+void newDevice_cb(GUdevClient *client, gchar *action, GUdevDevice *device, gpointer data)
+{
+ g_debug("Got %s event for %s", action, g_udev_device_get_name(device));
+ if (strcmp(g_udev_device_get_device_file(device), videoDevice) == 0) {
+ if (strcmp(action, "add") == 0) {
+ g_print("Device added.\n");
+ while(!startPipeline((GstElement*)data));
+ } else if (strcmp(action, "remove") == 0) {
+ g_print("Device removed.\n");
+ while(!stopPipeline((GstElement*)data));
+ }
+ }
+}
+
+static gboolean message_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ g_debug("Got message %s", GST_MESSAGE_TYPE_NAME(msg));
+}
+
+static gboolean state_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstState oldState;
+ GstState newState;
+ GstState pendingState;
+ gchar *name;
+
+ gst_message_parse_state_changed(msg, &oldState, &newState, &pendingState);
+ name = gst_element_get_name(msg->src);
+ g_debug("Element %s changed state from %s to %s", name, gst_element_state_get_name(oldState), gst_element_state_get_name(newState));
+ g_free(name);
+}
+
+static gboolean error_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GError *err;
+ gchar *debug;
+ gst_message_parse_error(msg, &err, &debug);
+ g_free(debug);
+ if (err->domain == GST_RESOURCE_ERROR) {
+ if (err->code == GST_RESOURCE_ERROR_OPEN_READ) {
+ g_debug("Could not open a device: %s", err->message);
+ goto out;
+ }
+ } else {
+ g_error("%d: %s", err->code, err->message);
+ }
+ g_main_loop_quit(loop);
+out:
+ g_error_free(err);
+}
+
+static gboolean warning_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GError *err;
+ gchar *debug;
+ gst_message_parse_warning(msg, &err, &debug);
+ g_free(debug);
+ g_warning("%d %d: %s", err->domain, err->code, err->message);
+ if (err->domain == 992 && err->code == 13)
+ while(!stopPipeline((GstElement*)data));
+ g_error_free(err);
+}
+
+GstElement *buildPipeline(const gchar *videoDev, const gchar *audioDev, const gchar *server, const gchar *mount)
+{
+ GstElement *pipe = gst_pipeline_new(NULL);
+
+ // Video pipeline
+ GstElement *v4lsrc = gst_element_factory_make("v4l2src", NULL);
+ GstElement *convert = gst_element_factory_make("ffmpegcolorspace", NULL);
+ GstElement *rate = gst_element_factory_make("videorate", NULL);
+ GstElement *scale = gst_element_factory_make("videoscale", NULL);
+ GstElement *encode = gst_element_factory_make("theoraenc", NULL);
+ GstElement *videoQueue = gst_element_factory_make("queue", NULL);
+
+ gst_bin_add_many(GST_BIN(pipe), v4lsrc, convert, rate, scale, encode, videoQueue, NULL);
+ gst_element_link(encode, videoQueue);
+ gst_element_link_many(v4lsrc, convert, rate, scale);
+ GstCaps *caps = gst_caps_new_simple("video/x-raw-yuv", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, NULL);
+ gst_element_link_filtered(scale, encode, caps);
+
+ // Audio pipeline
+ GstElement *alsasrc = gst_element_factory_make("alsasrc", NULL);
+ GstElement *audioConvert = gst_element_factory_make("audioconvert", NULL);
+ GstElement *vorbisenc = gst_element_factory_make("vorbisenc", NULL);
+ GstElement *audioQueue = gst_element_factory_make("queue", NULL);
+
+ gst_bin_add_many(GST_BIN(pipe), alsasrc, audioConvert, vorbisenc, audioQueue, NULL);
+ gst_element_link_many(alsasrc, audioConvert, vorbisenc, audioQueue, NULL);
+
+
+ // Combine
+ GstElement *oggMux = gst_element_factory_make("oggmux", NULL);
+ gst_bin_add(GST_BIN(pipe), oggMux);
+ gst_element_link(videoQueue, oggMux);
+ gst_element_link(audioQueue, oggMux);
+
+ // Broadcast!
+ GstElement *shout = gst_element_factory_make("shout2send", NULL);
+
+ gst_bin_add(GST_BIN(pipe), shout);
+ gst_element_link(oggMux, shout);
+
+ GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe));
+ gst_bus_add_signal_watch(bus);
+ g_signal_connect(bus, "message::error", G_CALLBACK(error_cb), pipe);
+ g_signal_connect(bus, "message::warning", G_CALLBACK(warning_cb), pipe);
+ g_signal_connect(bus, "message::state-changed", G_CALLBACK(state_cb), pipe);
+ g_signal_connect(bus, "message", G_CALLBACK(message_cb), pipe);
+
+ if (audioDevice)
+ g_object_set(G_OBJECT(alsasrc), "device", audioDev, NULL);
+ if (videoDevice)
+ g_object_set(G_OBJECT(v4lsrc), "device", videoDev, NULL);
+ if (icecastServer)
+ g_object_set(G_OBJECT(shout), "ip", server, NULL);
+ if (icecastMount)
+ g_object_set(G_OBJECT(shout), "mount", mount, NULL);
+
+ g_object_set(G_OBJECT(encode), "bitrate", 64, NULL);
+ g_object_set(G_OBJECT(videoQueue), "leaky", 1, NULL);
+ g_object_set(G_OBJECT(audioQueue), "leaky", 1, NULL);
+
+ return pipe;
+}
+
+int main(int argc, char* argv[])
+{
+ GError *error;
+ GOptionContext *context;
+ GstStateChangeReturn stateChange;
+ GUdevDevice *dev;
+ GUdevClient *udevClient;
+ GstElement *pipe;
+
+ g_thread_init(NULL);
+
+ context = g_option_context_new(" - Stream a webcam to an icecast server");
+ g_option_context_add_main_entries(context, entries, NULL);
+ g_option_context_add_group(context, gst_init_get_option_group());
+
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ g_print("Bad arguments: %s\n", error->message);
+ exit(1);
+ }
+
+ if (!gst_init_check(&argc, &argv, &error)) {
+ g_print("Could not initialize GStreamer: %s\n", error->message);
+ exit(1);
+ }
+
+ if (!videoDevice) {
+ videoDevice = strdup("/dev/video0");
+ }
+
+ pipe = buildPipeline(videoDevice, audioDevice, icecastServer, icecastMount);
+ udevClient = g_udev_client_new(subsystems);
+ g_signal_connect(udevClient, "uevent", G_CALLBACK(newDevice_cb), pipe);
+
+ startPipeline(pipe);
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
+}