diff options
Diffstat (limited to 'src/encode.c')
-rw-r--r-- | src/encode.c | 166 |
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); +} |