Skip to content

Instantly share code, notes, and snippets.

@mohan43u
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mohan43u/870d34786ecd809ac998 to your computer and use it in GitHub Desktop.
Save mohan43u/870d34786ecd809ac998 to your computer and use it in GitHub Desktop.
/*
* compile: gcc -O0 -g $(pkg-config --cflags --libs glib-2.0 gobject-2.0 gstreamer-1.0) -o gstextractor gstextractor.c
* pipeline: gnlcomposition. ( gnlsource. ( bin. ( filesrc ! decodebin ) ), gnlsource. ( bin. ( filesrc ! decodebin ) ) ... ) ! \
* audioconvert ! faac ! qtmux ! filesink
* gnlcomposition. ( gnlsource. ( bin. ( filesrc ! decodebin ) ), gnlsource. ( bin. ( filesrc ! decodebin ) ) ... ) ! \
* videoconvert ! x264enc ! qtmux0.
*/
#include <stdio.h>
#include <stdlib.h>
#include <glib-unix.h>
#include <gst/gst.h>
typedef struct {
GstElement *linkelement;
GstElement *bin;
} GstExtractorCallbackArgs;
typedef struct {
GstElement *audiocomp;
GstElement *audioconvert;
GstElement *audioenc;
GstElement *mux;
GstElement *out;
GstElement *videocomp;
GstElement *videoconvert;
GstElement *videoenc;
} GstExtractorElements;
typedef struct {
GstExtractorElements *elements;
GstElement *pipeline;
GMainLoop *mainloop;
GstBus *msgbus;
} GstExtractor;
static guint64 gstextractor_timestamp_to_nanoseconds(gchar *timestamp) {
gchar **timev = g_strsplit(timestamp, ":", -1);
gint timec = g_strv_length(timev);
gdouble time = 0;
time = time + ((timec - 1) >= 0 && timev[timec - 1] ? g_strtod(timev[timec - 1], NULL) : 0);
time = time + ((timec - 2) >= 0 && timev[timec - 2] ? g_strtod(timev[timec - 2], NULL) * 60 : 0);
time = time + ((timec - 3) >= 0 && timev[timec - 3] ? g_strtod(timev[timec - 3], NULL) * 60 * 60 : 0);
g_strfreev(timev);
return (guint64) (time * GST_SECOND);
}
static gstextractor_no_more_pads_cb(GstElement *element, gpointer data) {
GstElement *linkelement = (GstElement *) data;
if(!gst_element_link(element, linkelement)) {
g_printerr("element->linkelement not linked\n");
}
}
static gstextractor_pad_added_cb(GstElement *element, GstPad *pad, gpointer data) {
GstExtractorCallbackArgs *args = (GstExtractorCallbackArgs *) data;
if(gst_caps_is_always_compatible(gst_pad_query_caps(pad, NULL),
gst_pad_query_caps(gst_element_get_static_pad(args->linkelement,
"sink"),
NULL))) {
GstPad *gpad = gst_ghost_pad_new(NULL, pad);
gst_pad_set_active(gpad, TRUE);
gst_element_add_pad(args->bin, gpad);
}
}
static GstElement* gstextractor_gen_comp(GstExtractor *this, GstElement *linkelement, gchar *filesinfo) {
GstElement *comp = gst_element_factory_make("gnlcomposition", NULL);
gchar **filesv = g_strsplit(filesinfo, ",", -1);
gint filesc = g_strv_length(filesv);
gint i = 0;
guint64 offset = 0;
gboolean ret = TRUE;
for(; i < filesc; i++) {
gchar **detailsv = g_strsplit(filesv[i], "|", -1);
GstElement *in = gst_element_factory_make("filesrc", NULL);
GstElement *decode = gst_element_factory_make("decodebin", NULL);
char *binname = g_strdup_printf("bin%d", i);
GstElement *bin = gst_bin_new(binname);
GstElement *source = gst_element_factory_make("gnlsource", NULL);
GstExtractorCallbackArgs *args = g_new(GstExtractorCallbackArgs, 1);
guint64 start = offset;
guint64 inpoint = gstextractor_timestamp_to_nanoseconds(detailsv[1]);
guint64 duration = gstextractor_timestamp_to_nanoseconds(detailsv[2]) - inpoint;
gst_bin_add(GST_BIN(comp), source);
gst_bin_add(GST_BIN(source), bin);
gst_bin_add_many(GST_BIN(bin), in, decode, NULL);
gst_element_link(in, decode);
args->linkelement = linkelement;
args->bin = bin;
g_signal_connect(decode,
"pad-added",
G_CALLBACK(gstextractor_pad_added_cb),
args);
g_object_set(in, "location", detailsv[0], NULL);
g_object_set(source, "start", start, NULL);
g_object_set(source, "inpoint", inpoint, NULL);
g_object_set(source, "duration", duration, NULL);
g_object_set(source, "priority", 1, NULL);
offset += duration;
g_strfreev(detailsv);
g_free(binname);
}
g_signal_connect(comp,
"no-more-pads",
G_CALLBACK(gstextractor_no_more_pads_cb),
linkelement);
g_signal_emit_by_name(comp, "commit", TRUE, &ret);
g_strfreev(filesv);
return comp;
}
static gstextractor_elements_init(GstExtractor *this, gchar *filesinfo, gchar *outfile) {
this->elements = g_new(GstExtractorElements, 1);
this->elements->audioconvert = gst_element_factory_make("audioconvert", NULL);
this->elements->audioenc = gst_element_factory_make("faac", NULL);
this->elements->mux = gst_element_factory_make("qtmux", NULL);
this->elements->out = gst_element_factory_make("filesink", NULL);
this->elements->videoconvert = gst_element_factory_make("videoconvert", NULL);
this->elements->videoenc = gst_element_factory_make("x264enc", NULL);
this->elements->audiocomp = gstextractor_gen_comp(this, this->elements->audioconvert, filesinfo);
this->elements->videocomp = gstextractor_gen_comp(this, this->elements->videoconvert, filesinfo);
gst_bin_add_many(GST_BIN(this->pipeline),
this->elements->audiocomp,
this->elements->audioconvert,
this->elements->audioenc,
this->elements->mux,
this->elements->out,
this->elements->videocomp,
this->elements->videoconvert,
this->elements->videoenc,
NULL);
gst_element_link_many(this->elements->audioconvert,
this->elements->audioenc,
this->elements->mux,
this->elements->out,
NULL);
gst_element_link_many(this->elements->videoconvert,
this->elements->videoenc,
this->elements->mux,
NULL);
g_object_set(this->elements->out, "location", outfile, NULL);
}
static void gstextractor_run(GstExtractor *this) {
gst_element_set_state(this->pipeline, GST_STATE_PLAYING);
g_main_loop_run(this->mainloop);
}
static void gstextractor_stop(GstExtractor *this) {
gst_element_set_state(this->pipeline, GST_STATE_NULL);
g_main_loop_quit(this->mainloop);
}
static gboolean gstextractor_msgbus_cb(GstBus *bus, GstMessage *message, gpointer *data) {
GstExtractor *this = (GstExtractor *) data;
switch(GST_MESSAGE_TYPE(message)) {
case(GST_MESSAGE_EOS): gstextractor_stop(this);
break;
case(GST_MESSAGE_STATE_CHANGED): {
GstState old;
GstState new;
gst_message_parse_state_changed(message, &old, &new, NULL);
g_printerr("%d: state changed from %s to %s on %s\n",
GST_MESSAGE_SEQNUM(message),
gst_element_state_get_name(old),
gst_element_state_get_name(new),
GST_MESSAGE_SRC_NAME(message));
}
break;
default: g_printerr("%d: received %s from %s..\n",
GST_MESSAGE_SEQNUM(message),
GST_MESSAGE_TYPE_NAME(message),
GST_MESSAGE_SRC_NAME(message));
}
return TRUE;
}
static void gstextractor_init(GstExtractor *this, gchar *filesinfo, gchar *outfile) {
if(! gst_is_initialized()) gst_init(NULL, NULL);
this->mainloop = g_main_loop_new(NULL, FALSE);
this->pipeline = gst_pipeline_new("pipeline0");
gstextractor_elements_init(this, filesinfo, outfile);
this->msgbus = gst_element_get_bus(GST_ELEMENT(this->pipeline));
gst_bus_add_watch_full(this->msgbus,
G_PRIORITY_DEFAULT,
(GstBusFunc) gstextractor_msgbus_cb,
this,
NULL);
}
int main(int argc, char *argv[]) {
if(argc < 3) {
g_printerr("[usage] %s \"file|start([hh:mm:]ss)|stop([hh:mm:]ss)[,...]\" outfile\n", argv[0]);
exit(EXIT_FAILURE);
}
GstExtractor gstextractor;
gstextractor_init(&gstextractor, argv[1], argv[2]);
gstextractor_run(&gstextractor);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment