summaryrefslogtreecommitdiff
path: root/src/encode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/encode.c')
-rw-r--r--src/encode.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/encode.c b/src/encode.c
new file mode 100644
index 0000000..caaaa46
--- /dev/null
+++ b/src/encode.c
@@ -0,0 +1,166 @@
+/* License: LGPL-2.1
+ * vim: set sts=2 sw=2 et : */
+
+#include "encode.h"
+
+#include <gst/pbutils/encoding-profile.h>
+
+static gboolean
+on_autoplug_continue (GstElement *decodebin,
+ GstPad *srcpad,
+ GstCaps *caps,
+ GstElement *encodebin)
+{
+ gboolean ret;
+ char *name;
+ GstPad *sinkpad;
+
+ name = gst_caps_to_string (caps);
+ g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad);
+
+ if (sinkpad != NULL)
+ ret = FALSE, g_debug ("encodebin can passthrough %s\n", name);
+ else
+ ret = TRUE, g_debug ("encodebin cannot passthrough %s\n", name);
+
+ g_free (name);
+ return ret;
+}
+
+static void
+on_decodebin_pad_added (GstElement *decodebin,
+ GstPad *srcpad,
+ GstElement *encodebin)
+{
+ GstCaps *caps;
+ GstPad *sinkpad = NULL;
+ char *name;
+
+ caps = gst_pad_query_caps (srcpad, NULL);
+ name = gst_caps_to_string (caps);
+
+ /* FIXME: We don't try to fetch a compatible pad for raw audio because
+ * that somehow always fails to link. Transmageddon does the same. */
+ if (!g_str_has_prefix (name, "audio/x-raw"))
+ /* If we successfully requested a compatible sink pad in
+ * "autoplug-continue", we can fetch that here. */
+ sinkpad = gst_element_get_compatible_pad (encodebin, srcpad, NULL);
+
+ if (!sinkpad) {
+ /* We request a sink pad for the decoded stream */
+ g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad);
+ if (!sinkpad) {
+ g_printerr ("Failed to request a new sink pad for %s\n", name);
+ goto out;
+ }
+ g_debug ("Requested a new sink pad for %s\n", name);
+ } else {
+ g_debug ("Found an existing sink pad for %s\n", name);
+ }
+
+ if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
+ g_printerr ("Couldn't link pads for %s\n", name);
+
+out:
+ g_free (name);
+ gst_caps_unref (caps);
+ return;
+}
+
+static GstEncodingProfile *
+create_webm_profile (void)
+{
+ GstEncodingProfile *t;
+ GstEncodingContainerProfile *prof;
+ GstCaps *caps;
+ GstPreset *vp8preset;
+
+ caps = gst_caps_from_string ("video/webm");
+ prof = gst_encoding_container_profile_new ("WebM audio/video", "Standard WEBM/VP8/VORBIS", caps, NULL);
+ gst_caps_unref (caps);
+
+ vp8preset = GST_PRESET (gst_element_factory_make ("vp8enc", "vp8preset"));
+ /* FIXME: This thing still doesn't encode fast enough in real time */
+ g_object_set (vp8preset,
+ "cpu-used", 5,
+ "end-usage", 1,
+ "max-quantizer", 56,
+ "threads", 4,
+ "undershoot", 95,
+ NULL);
+ gst_preset_save_preset (vp8preset, "stp_vp8preset");
+
+ caps = gst_caps_from_string ("video/x-vp8");
+ t = (GstEncodingProfile*) gst_encoding_video_profile_new (caps, "stp_vp8preset",
+ NULL, 0);
+ gst_encoding_container_profile_add_profile (prof, t);
+ gst_caps_unref (caps);
+
+ caps = gst_caps_from_string ("audio/x-vorbis");
+ t = (GstEncodingProfile*) gst_encoding_audio_profile_new (caps, NULL, NULL, 0);
+ gst_encoding_container_profile_add_profile (prof, t);
+ gst_caps_unref (caps);
+
+ /* What about application/x-ass? */
+
+ return (GstEncodingProfile*) prof;
+}
+
+void
+stp_encode_from_msg (TranscodeServerCtx *ctx)
+{
+ GstBus *bus;
+ GstElement *src, *decodebin, *encodebin;
+ GstElement *tee, *q1, *fakesink;
+ GstEncodingProfile *profile;
+
+ g_debug ("Constructing pipeline\n");
+
+ src = gst_element_factory_make ("appsrc", "src");
+ g_object_set (src, "is-live", TRUE,
+ "emit-signals", FALSE,
+ "stream-type", 0, NULL);
+ ctx->appsrc = src;
+
+ decodebin = gst_element_factory_make ("decodebin", "decodebin");
+
+ gst_bin_add_many (GST_BIN (ctx->pipeline), src, decodebin, NULL);
+ gst_element_link (src, decodebin);
+
+ /* TODO: Allow setting of a scaling factor to
+ * allow realtime encoding of all streams */
+ profile = create_webm_profile ();
+ encodebin = gst_element_factory_make ("encodebin", "encodebin");
+ g_object_set (encodebin,
+ "profile", profile,
+ "avoid-reencoding", TRUE, NULL);
+ tee = gst_element_factory_make ("tee", "tee");
+ q1 = gst_element_factory_make ("queue", "q1");
+#ifndef DEBUG
+ fakesink = gst_element_factory_make ("filesink", "filesink");
+ g_object_set (fakesink, "location", "debug-output.webm", NULL);
+#else
+ fakesink = gst_element_factory_make ("fakesink", "fakesink");
+#endif
+
+ gst_bin_add_many (GST_BIN (ctx->pipeline), encodebin, tee, q1, fakesink, NULL);
+ gst_element_link_many (encodebin, tee, q1, fakesink, NULL);
+
+ /* The pads of decodebin and encodebin are dynamic,
+ * so those will be linked when streams/pads are added */
+
+ /* When decodebin finds a stream that can be decoded, we check
+ * if we can pass that directly to encodebin instead of letting
+ * decodebin find a decoding element automatically */
+ g_signal_connect (decodebin, "autoplug-continue",
+ G_CALLBACK (on_autoplug_continue), encodebin);
+ /* When decodebin exposes a source pad, we need to request a
+ * corresponding sink pad on decodebin */
+ g_signal_connect (decodebin, "pad-added",
+ G_CALLBACK (on_decodebin_pad_added), encodebin);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (ctx->pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (bus, "message", G_CALLBACK (stp_on_gst_bus_message), ctx);
+ g_object_unref (bus);
+}