Last active
August 29, 2015 14:05
-
-
Save mohan43u/870d34786ecd809ac998 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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