/*************************************************************************** c_media.c gb.media component (c) 2012 BenoƮt Minisini 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 1, 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 __C_MEDIA_C #include "c_media.h" #include void MEDIA_raise_event(void *_object, int event) { gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, NULL))); } /*void MEDIA_raise_event_arg(void *_object, int event, char *arg) { gst_element_post_message(ELEMENT, gst_message_new_application(GST_OBJECT(ELEMENT), gst_structure_new("SendEvent", "event", G_TYPE_INT, event, "arg", G_TYPE_STRING, arg, NULL))); }*/ CMEDIACONTROL *MEDIA_get_control_from_element(void *element) { if (!element) return NULL; else return (CMEDIACONTROL *)g_object_get_data(G_OBJECT(element), "gambas-control"); } bool MEDIA_set_state(void *_object, int state, bool error) { GstStateChangeReturn status; status = gst_element_set_state(ELEMENT, state); if (status == GST_STATE_CHANGE_ASYNC) status = gst_element_get_state(ELEMENT, NULL, NULL, GST_SECOND * 3); if (status == GST_STATE_CHANGE_FAILURE) { if (error) GB.Error("Cannot set status"); return TRUE; } return FALSE; } static GB_TYPE to_gambas_type(const GValue *value) { switch (G_VALUE_TYPE(value)) { case G_TYPE_BOOLEAN: return GB_T_BOOLEAN; case G_TYPE_INT: return GB_T_INTEGER; case G_TYPE_UINT: return GB_T_INTEGER; case G_TYPE_UINT64: return GB_T_LONG; case G_TYPE_STRING: return GB_T_STRING; case G_TYPE_FLOAT: return GB_T_FLOAT; case G_TYPE_DOUBLE: return GB_T_FLOAT; default: if (GST_VALUE_HOLDS_DATE(value)) return GB_T_DATE; else if (GST_VALUE_HOLDS_DATE_TIME(value)) return GB_T_DATE; else { //GB.Error("Unsupported property datatype"); return GB_T_NULL; } } } static void to_gambas_value(const GValue *value, GB_VALUE *gvalue) { switch (G_VALUE_TYPE(value)) { case G_TYPE_BOOLEAN: gvalue->type = GB_T_BOOLEAN; gvalue->_boolean.value = g_value_get_boolean(value) == 0 ? 0 : -1; break; case G_TYPE_INT: gvalue->type = GB_T_INTEGER; gvalue->_integer.value = g_value_get_int(value); break; case G_TYPE_UINT: gvalue->type = GB_T_INTEGER; gvalue->_integer.value = (int)g_value_get_uint(value); break; case G_TYPE_UINT64: gvalue->type = GB_T_LONG; gvalue->_long.value = (int64_t)g_value_get_uint64(value); break; case G_TYPE_STRING: gvalue->type = GB_T_STRING; gvalue->_string.value.addr = GB.NewZeroString(g_value_get_string(value)); gvalue->_string.value.start = 0; gvalue->_string.value.len = GB.StringLength(gvalue->_string.value.addr); break; case G_TYPE_FLOAT: gvalue->type = GB_T_FLOAT; gvalue->_float.value = (double)g_value_get_float(value); break; case G_TYPE_DOUBLE: gvalue->type = GB_T_FLOAT; gvalue->_float.value = g_value_get_double(value); break; default: if (GST_VALUE_HOLDS_DATE(value)) { GB_DATE_SERIAL ds; GDate *date = (GDate *)g_value_get_boxed(value); CLEAR(&ds); ds.year = date->year; ds.month = date->month; ds.day = date->day; GB.MakeDate(&ds, (GB_DATE *)gvalue); break; } else if (GST_VALUE_HOLDS_DATE_TIME(value)) { GB_DATE_SERIAL ds; GstDateTime *date = (GstDateTime *)g_value_get_boxed(value); CLEAR(&ds); ds.year = gst_date_time_get_year(date); ds.month = gst_date_time_get_month(date); ds.day = gst_date_time_get_day(date); ds.hour = gst_date_time_get_hour(date); ds.min = gst_date_time_get_minute(date); ds.sec = gst_date_time_get_second(date); ds.msec = gst_date_time_get_microsecond(date); fprintf(stderr, "gb.media: warning: timezone = %g\n", gst_date_time_get_time_zone_offset(date)); GB.MakeDate(&ds, (GB_DATE *)gvalue); break; } else gvalue->type = GB_T_NULL; } } static void return_value(const GValue *value) { switch (G_VALUE_TYPE(value)) { case G_TYPE_BOOLEAN: GB.ReturnBoolean(g_value_get_boolean(value)); break; case G_TYPE_INT: GB.ReturnInteger(g_value_get_int(value)); break; case G_TYPE_UINT: GB.ReturnInteger(g_value_get_uint(value)); break; case G_TYPE_UINT64: GB.ReturnLong(g_value_get_uint64(value)); break; case G_TYPE_STRING: GB.ReturnNewZeroString(g_value_get_string(value)); break; case G_TYPE_FLOAT: GB.ReturnFloat(g_value_get_float(value)); break; case G_TYPE_DOUBLE: GB.ReturnFloat(g_value_get_double(value)); break; default: if (GST_VALUE_HOLDS_DATE(value) || GST_VALUE_HOLDS_DATE_TIME(value)) { GB_VALUE date; to_gambas_value(value, &date); GB.ReturnDate((GB_DATE *)&date); } else { fprintf(stderr, "gb.media: warning: unsupported datatype: %s\n", g_type_name(G_VALUE_TYPE(value))); GB.ReturnNull(); } } GB.ReturnConvVariant(); } static bool set_value(GValue *value, GB_VALUE *v) { //g_value_init(&value, desc->value_type); switch (G_VALUE_TYPE(value)) { case G_TYPE_BOOLEAN: if (GB.Conv(v, GB_T_BOOLEAN)) return TRUE; g_value_set_boolean(value, v->_boolean.value); break; case G_TYPE_INT: if (GB.Conv(v, GB_T_INTEGER)) return TRUE; g_value_set_int(value, v->_integer.value); break; case G_TYPE_UINT: if (GB.Conv(v, GB_T_INTEGER)) return TRUE; g_value_set_uint(value, (uint)v->_integer.value); break; case G_TYPE_UINT64: if (GB.Conv(v, GB_T_LONG)) return TRUE; g_value_set_uint64(value, (guint64)v->_long.value); break; case G_TYPE_STRING: if (GB.Conv(v, GB_T_STRING)) return TRUE; g_value_set_string(value, GB.ToZeroString((GB_STRING *)v)); break; case G_TYPE_FLOAT: if (GB.Conv(v, GB_T_FLOAT)) return TRUE; g_value_set_float(value, v->_float.value); break; case G_TYPE_DOUBLE: if (GB.Conv(v, GB_T_FLOAT)) return TRUE; g_value_set_double(value, v->_float.value); break; default: GB.Error("Unsupported property datatype"); return TRUE; } return FALSE; } #if 0 //---- MediaSignalArguments ----------------------------------------------- static int check_signal_arguments(void *_object) { return THIS_ARG->param_values == NULL; } BEGIN_METHOD(MediaSignalArguments_get, GB_INTEGER index) int index = VARG(index); if (index < 0 || index >= THIS_ARG->n_param_values) { GB.Error(GB_ERR_BOUND); return; } return_value(&THIS_ARG->param_values[index]); END_METHOD #endif //---- MediaTagList ------------------------------------------------------- static int MediaTagList_check(void *_object) { return THIS_TAGLIST->tags == NULL; } static CMEDIATAGLIST *create_tag_list(GstTagList *tags) { CMEDIATAGLIST *ob; ob = GB.New(GB.FindClass("MediaTagList"), NULL, NULL); ob->tags = tags; return ob; } BEGIN_METHOD(MediaTagList_get, GB_STRING name) char *name = GB.ToZeroString(ARG(name)); GstTagList *tags = THIS_TAGLIST->tags; const GValue *value; int nvalue; nvalue = gst_tag_list_get_tag_size(tags, name); if (nvalue <= 0) { GB.ReturnNull(); GB.ReturnConvVariant(); } else if (nvalue == 1) { value = gst_tag_list_get_value_index(tags, name, 0); return_value(value); } else { GB_ARRAY array; GB_TYPE type; GB_VALUE gvalue; int i; value = gst_tag_list_get_value_index(tags, name, 0); type = to_gambas_type(value); if (type == GB_T_NULL) return; GB.Array.New(&array, type, nvalue); for (i = 0; i < nvalue; i++) { value = gst_tag_list_get_value_index(tags, name, i); to_gambas_value(value, &gvalue); GB.Store(type, GB.Array.Get(array, i), &gvalue); } GB.ReturnObject(array); GB.ReturnConvVariant(); } END_METHOD BEGIN_PROPERTY(MediaTagList_Tags) GB_ARRAY array; GstTagList *tags = THIS_TAGLIST->tags; int ntags, i; ntags = gst_structure_n_fields(GST_STRUCTURE(tags)); GB.Array.New(&array, GB_T_STRING, ntags); for (i = 0; i < ntags; i++) *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(gst_structure_nth_field_name(GST_STRUCTURE(tags), i)); GB.ReturnObject(array); END_PROPERTY //---- MediaControl ------------------------------------------------------- DECLARE_EVENT(EVENT_State); //DECLARE_EVENT(EVENT_Signal); typedef struct { char *klass; char *type; } MEDIA_TYPE; static MEDIA_TYPE _types[] = { { "MediaContainer", "bin" }, { "MediaPipeline", "pipeline" }, { "Media", "pipeline" }, { "MediaPlayer", "playbin2" }, //{ "MediaDecoder", "decodebin2" }, { NULL, NULL } }; static void cb_pad_added(GstElement *element, GstPad *pad, CMEDIACONTROL *_object) { char *name; //GstPad *sinkpad; //fprintf(stderr, "cb_pad_added: %s\n", gst_element_factory_get_klass(gst_element_get_factory(ELEMENT))); if (!THIS->dest) return; /* We can now link this pad with the vorbis-decoder sink pad */ //sinkpad = gst_element_get_static_pad (decoder, "sink"); //gst_pad_link (pad, sinkpad); name = gst_pad_get_name(pad); gst_element_link_pads(ELEMENT, name, ((CMEDIACONTROL *)THIS->dest)->elt, NULL); g_free(name); //GB.Unref(POINTER(&THIS->dest)); //gst_object_unref (sinkpad); } BEGIN_METHOD(MediaControl_new, GB_OBJECT parent; GB_STRING type) char *type; CMEDIACONTAINER *parent; MEDIA_TYPE *mtp; GB_CLASS klass; THIS->tag.type = GB_T_NULL; if (MISSING(type)) { klass = GB.GetClass(THIS); type = NULL; for (mtp = _types; mtp->klass; mtp++) { if (GB.FindClass(mtp->klass) == klass) { type = mtp->type; break; } } if (!type) { GB.Error("The type must be specified"); return; } } else type = GB.ToZeroString(ARG(type)); THIS->state = GST_STATE_NULL; THIS->type = GB.NewZeroString(type); ELEMENT = gst_element_factory_make(type, NULL); if (!ELEMENT) { GB.Error("Unable to create media control"); return; } gst_object_ref(GST_OBJECT(ELEMENT)); g_object_set_data(G_OBJECT(ELEMENT), "gambas-control", THIS); parent = VARGOPT(parent, NULL); if (parent) { gst_bin_add(GST_BIN(parent->elt), ELEMENT); gst_element_sync_state_with_parent(ELEMENT); } else if (!GST_IS_PIPELINE(ELEMENT)) GB.CheckObject(parent); END_METHOD BEGIN_METHOD_VOID(MediaControl_free) GB.Unref(POINTER(&THIS->dest)); GB.FreeString(&THIS->type); GB.StoreVariant(NULL, &THIS->tag); if (ELEMENT) { gst_element_set_state(ELEMENT, GST_STATE_NULL); gst_object_unref(GST_OBJECT(ELEMENT)); } END_METHOD BEGIN_PROPERTY(MediaControl_Tag) if (READ_PROPERTY) GB.ReturnVariant(&THIS->tag); else GB.StoreVariant(PROP(GB_VARIANT), POINTER(&THIS->tag)); END_METHOD BEGIN_PROPERTY(MediaControl_Type) GB.ReturnString(THIS->type); //NewZeroString(gst_element_factory_get_klass(gst_element_get_factory(ELEMENT))); END_PROPERTY BEGIN_PROPERTY(MediaControl_Name) if (READ_PROPERTY) GB.ReturnNewZeroString(gst_element_get_name(ELEMENT)); else gst_element_set_name(ELEMENT, GB.ToZeroString(PROP(GB_STRING))); END_PROPERTY BEGIN_PROPERTY(MediaControl_State) if (READ_PROPERTY) { GB.ReturnInteger(THIS->state); /*GstState state; status = gst_element_get_state(ELEMENT, &state, NULL, GST_SECOND * 3); if (status != GST_STATE_CHANGE_SUCCESS) GB.ReturnInteger(-1); else GB.ReturnInteger(state);*/ } else { MEDIA_set_state(THIS, VPROP(GB_INTEGER), TRUE); } END_PROPERTY static GParamSpec *get_property(GstElement *element, char *property) { GParamSpec *desc; desc = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(element)), property); if (!desc) GB.Error("Unknown property: '&1'", property); return desc; } BEGIN_METHOD(MediaControl_get, GB_STRING property) char *property = GB.ToZeroString(ARG(property)); GParamSpec *desc; GValue value = G_VALUE_INIT; desc = get_property(ELEMENT, property); if (!desc) return; //fprintf(stderr, "type = %s\n", g_type_name(desc->value_type)); g_value_init(&value, desc->value_type); g_object_get_property(G_OBJECT(ELEMENT), property, &value); return_value(&value); g_value_unset(&value); END_METHOD BEGIN_METHOD(MediaControl_put, GB_VARIANT value; GB_STRING property) char *property = GB.ToZeroString(ARG(property)); GParamSpec *desc; GValue value = G_VALUE_INIT; GB_VALUE *v = (GB_VALUE *)ARG(value); desc = get_property(ELEMENT, property); if (!desc) return; g_value_init(&value, desc->value_type); if (set_value(&value, v)) return; g_object_set_property(G_OBJECT(ELEMENT), property, &value); g_value_unset(&value); END_METHOD BEGIN_METHOD(MediaControl_LinkTo, GB_OBJECT dest; GB_STRING output; GB_STRING input) CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); char *output; char *input; if (GB.CheckObject(dest)) return; output = MISSING(output) ? NULL : GB.ToZeroString(ARG(output)); if (output && !*output) output = NULL; input = MISSING(input) ? NULL : GB.ToZeroString(ARG(input)); if (input && !*input) input = NULL; gst_element_link_pads(ELEMENT, output, dest->elt, input); END_METHOD BEGIN_METHOD(MediaControl_LinkLaterTo, GB_OBJECT dest) CMEDIACONTROL *dest = (CMEDIACONTROL *)VARG(dest); if (GB.CheckObject(dest)) return; GB.Unref(POINTER(&THIS->dest)); GB.Ref(dest); THIS->dest = dest; g_signal_connect(ELEMENT, "pad-added", G_CALLBACK(cb_pad_added), THIS); END_METHOD static void fill_pad_list(GB_ARRAY array, GstIterator *iter) { bool done = FALSE; GstPad *pad; char *name; while (!done) { switch (gst_iterator_next(iter, (gpointer *)&pad)) { case GST_ITERATOR_OK: name = gst_pad_get_name(pad); *((char **)GB.Array.Add(array)) = GB.NewZeroString(name); g_free(name); gst_object_unref(pad); break; case GST_ITERATOR_RESYNC: gst_iterator_resync(iter); break; case GST_ITERATOR_ERROR: case GST_ITERATOR_DONE: done = TRUE; break; } } gst_iterator_free(iter); } BEGIN_PROPERTY(MediaControl_Inputs) GstIterator *iter; GB_ARRAY array; GB.Array.New(&array, GB_T_STRING, 0); iter = gst_element_iterate_sink_pads(ELEMENT); fill_pad_list(array, iter); GB.ReturnObject(array); END_PROPERTY BEGIN_PROPERTY(MediaControl_Outputs) GstIterator *iter; GB_ARRAY array; GB.Array.New(&array, GB_T_STRING, 0); iter = gst_element_iterate_src_pads(ELEMENT); fill_pad_list(array, iter); GB.ReturnObject(array); END_PROPERTY #if 0 static void closure_marshal(GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { GObject *src; CMEDIACONTROL *_object; CMEDIASIGNALARGUMENTS *arg; src = g_value_peek_pointer (param_values + 0); _object = get_control_from_element(src); arg = GB.New(GB.FindClass("MediaSignalArguments"), NULL, NULL); arg->n_param_values = n_param_values; arg->param_values = param_values; GB.Ref(arg); GB.Raise(THIS, EVENT_Signal, 1, GB_T_OBJECT, arg); MEDIA_raise_event_arg(THIS, EVENT_Signal, ); arg->n_param_values = 0; arg->param_values = NULL; GB.Unref(POINTER(&arg)); } static GClosure *get_closure() { static GClosure *closure = NULL; if (!closure) { closure = g_closure_new_simple(sizeof(GClosure), NULL); g_closure_set_marshal(closure, closure_marshal); } return closure; } BEGIN_METHOD(MediaControl_Activate, GB_STRING signal) char *signal = GB.ToZeroString(ARG(signal)); GClosure *closure = get_closure(); if (g_signal_handler_find(ELEMENT, G_SIGNAL_MATCH_CLOSURE | G_SIGNAL_MATCH_ID, g_signal_lookup(signal, G_OBJECT_TYPE(ELEMENT)), (GQuark)0, closure, NULL, NULL)) { GB.Error("Signal is already activated"); return; } g_signal_connect_closure(ELEMENT, GB.ToZeroString(ARG(signal)), closure, FALSE); END_METHOD #endif BEGIN_METHOD(MediaControl_SetWindow, GB_OBJECT control; GB_INTEGER x; GB_INTEGER y; GB_INTEGER w; GB_INTEGER h) void *control = VARG(control); long wid; int x, y, w, h; if (!gst_element_implements_interface(ELEMENT, GST_TYPE_X_OVERLAY)) { GB.Error("Not supported on this control"); return; } if (control && GB.CheckObject(control)) return; if (control) { wid = MAIN_get_x11_handle(control); if (wid == 0) return; } else wid = 0; gst_x_overlay_set_window_handle(GST_X_OVERLAY(ELEMENT), (guintptr)wid); if (wid && !MISSING(x) && !MISSING(y) && !MISSING(w) && !MISSING(h)) { x = VARG(x); y = VARG(y); w = VARG(w); h = VARG(h); if (w > 0 && h > 0) gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(ELEMENT), x, y, w, h); } gst_x_overlay_expose(GST_X_OVERLAY(ELEMENT)); END_PROPERTY //---- MediaContainer ----------------------------------------------------- static bool add_input_output(void *_object, CMEDIACONTROL *child, char *name, int direction, const char *dir_error, const char *unknown_error) { GstPad *pad; GstIterator *iter; GstIteratorResult res; if (GB.CheckObject(child)) return TRUE; if (!name) { if (direction == GST_PAD_SINK) iter = gst_element_iterate_sink_pads(child->elt); else iter = gst_element_iterate_src_pads(child->elt); for(;;) { res = gst_iterator_next(iter, (gpointer *)&pad); if (res == GST_ITERATOR_RESYNC) gst_iterator_resync(iter); else break; } gst_iterator_free(iter); if (res != GST_ITERATOR_OK) { GB.Error(unknown_error); return TRUE; } } else { pad = gst_element_get_static_pad(child->elt, name); if (!pad) { GB.Error(unknown_error); return TRUE; } if (gst_pad_get_direction(pad) != direction) { gst_object_unref (GST_OBJECT(pad)); GB.Error(dir_error); return TRUE; } } gst_element_add_pad(ELEMENT, gst_ghost_pad_new(name, pad)); gst_object_unref(GST_OBJECT(pad)); return FALSE; } BEGIN_METHOD(MediaContainer_AddInput, GB_OBJECT child; GB_STRING name) add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SINK, "Not an input", "Unknown input"); END_METHOD BEGIN_METHOD(MediaContainer_AddOutput, GB_OBJECT child; GB_STRING name) add_input_output(THIS, (CMEDIACONTROL *)VARG(child), MISSING(name) ? NULL : GB.ToZeroString(ARG(name)), GST_PAD_SRC, "Not an output", "Unknown output"); END_METHOD //---- MediaPipeline ------------------------------------------------------ DECLARE_EVENT(EVENT_End); DECLARE_EVENT(EVENT_Message); DECLARE_EVENT(EVENT_Tag); DECLARE_EVENT(EVENT_Buffering); DECLARE_EVENT(EVENT_Duration); DECLARE_EVENT(EVENT_Progress); static int cb_message(CMEDIAPIPELINE *_object) { GstMessage *msg; GstMessageType type; int msg_type; GstBus *bus; CMEDIACONTROL *control; bus = gst_pipeline_get_bus(PIPELINE); while((msg = gst_bus_pop(bus)) != NULL) { type = GST_MESSAGE_TYPE(msg); control = MEDIA_get_control_from_element(GST_MESSAGE_SRC(msg)); if (type == GST_MESSAGE_APPLICATION) { //CMEDIACONTROL *target = (CMEDIACONTROL *)g_value_get_pointer(gst_structure_get_value(gst_message_get_structure(msg), "control")); int event = g_value_get_int(gst_structure_get_value(gst_message_get_structure(msg), "event")); GB.Raise(control, event, 0); } else if (type == GST_MESSAGE_STATE_CHANGED && control) { GstState old_state, new_state; gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); control->state = new_state; if (new_state == GST_STATE_NULL) control->error = FALSE; GB.Raise(control, EVENT_State, 0); } else //if (GST_MESSAGE_SRC(msg) == GST_OBJECT(PIPELINE)) { switch (type) { case GST_MESSAGE_EOS: GB.Raise(THIS, EVENT_End, 0); break; case GST_MESSAGE_ERROR: case GST_MESSAGE_WARNING: case GST_MESSAGE_INFO: { gchar *debug; GError *error; if (type == GST_MESSAGE_ERROR) { gst_message_parse_error(msg, &error, &debug); msg_type = 2; control->error = TRUE; THIS->error = TRUE; } else if (type == GST_MESSAGE_WARNING) { gst_message_parse_warning(msg, &error, &debug); msg_type = 1; } else { gst_message_parse_info(msg, &error, &debug); msg_type = 0; } g_free(debug); GB.Ref(control); GB.Raise(THIS, EVENT_Message, 3, GB_T_OBJECT, control, GB_T_INTEGER, msg_type, GB_T_STRING, error->message, -1); g_error_free(error); GB.Unref(POINTER(&control)); break; } case GST_MESSAGE_TAG: { GstTagList *tags = NULL; CMEDIATAGLIST *ob; char *list; gst_message_parse_tag(msg, &tags); list = gst_tag_list_to_string(tags); //fprintf(stderr, "--> %s\n", list); g_free(list); ob = create_tag_list(tags); GB.Ref(ob); GB.Raise(THIS, EVENT_Tag, 1, GB_T_OBJECT, ob); ob->tags = NULL; GB.Unref(POINTER(&ob)); gst_tag_list_free(tags); break; } case GST_MESSAGE_BUFFERING: GB.Raise(THIS, EVENT_Buffering, 0); break; case GST_MESSAGE_DURATION: GB.Raise(THIS, EVENT_Duration, 0); break; case GST_MESSAGE_PROGRESS: GB.Raise(THIS, EVENT_Progress, 0); break; default: break; } } gst_message_unref(msg); } gst_object_unref(bus); return FALSE; } BEGIN_METHOD_VOID(MediaPipeline_new) THIS->watch = GB.Every(250, (GB_TIMER_CALLBACK)cb_message, (intptr_t)THIS); END_METHOD BEGIN_METHOD_VOID(MediaPipeline_free) GB.Unref(POINTER(&THIS->watch)); END_METHOD BEGIN_METHOD_VOID(MediaPipeline_Play) MEDIA_set_state(THIS, GST_STATE_PLAYING, TRUE); END_METHOD BEGIN_METHOD_VOID(MediaPipeline_Stop) MEDIA_set_state(THIS, GST_STATE_READY, TRUE); END_METHOD BEGIN_METHOD_VOID(MediaPipeline_Close) MEDIA_set_state(THIS, GST_STATE_NULL, TRUE); END_METHOD BEGIN_METHOD_VOID(MediaPipeline_Pause) MEDIA_set_state(THIS, GST_STATE_PAUSED, TRUE); END_METHOD BEGIN_PROPERTY(MediaPipeline_Position) if (READ_PROPERTY) { GstFormat format = GST_FORMAT_TIME; gint64 pos; if (THIS->state == GST_STATE_NULL || THIS->state == GST_STATE_READY || THIS->error || !gst_element_query_position(ELEMENT, &format, &pos) || format != GST_FORMAT_TIME) GB.ReturnFloat(0); else GB.ReturnFloat((double)(pos / 1000) / 1E6); } else { guint64 pos = VPROP(GB_FLOAT) * 1E9; if (pos < 0) pos = 0; gst_element_seek_simple(ELEMENT, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, pos); } END_PROPERTY BEGIN_PROPERTY(MediaPipeline_Duration) GstFormat format = GST_FORMAT_TIME; gint64 dur; if (THIS->state == GST_STATE_NULL || THIS->error || !gst_element_query_duration(ELEMENT, &format, &dur) || format != GST_FORMAT_TIME) GB.ReturnFloat(0); else GB.ReturnFloat((double)(dur / 1000) / 1E6); END_PROPERTY //---- Media -------------------------------------------------------------- BEGIN_METHOD(Media_Link, GB_OBJECT controls) GB_OBJECT *controls = ARG(controls); int i; CMEDIACONTROL *c1, *c2; if (GB.CheckObject(VARG(controls))) return; // GB.NParam() returns 0 when MediaPipeline_Link has just two arguments for (i = 0; i <= GB.NParam(); i++) { c1 = VALUE(&controls[i]); c2 = VALUE(&controls[i + 1]); if (GB.CheckObject(c1)) return; gst_element_link(c1->elt, c2->elt); } END_METHOD BEGIN_METHOD(Media_Time, GB_FLOAT second) GB.ReturnLong(VARG(second) * 1E9); END_METHOD BEGIN_METHOD(Media_URL, GB_STRING path) char *path = GB.RealFileName(STRING(path), LENGTH(path)); path = g_filename_to_uri(path, NULL, NULL); GB.ReturnNewZeroString(path); g_free(path); END_METHOD //------------------------------------------------------------------------- GB_DESC MediaTagListDesc[] = { GB_DECLARE("MediaTagList", sizeof(CMEDIATAGLIST)), GB_NOT_CREATABLE(), GB_HOOK_CHECK(MediaTagList_check), GB_METHOD("_get", "v", MediaTagList_get, "(Name)s"), GB_PROPERTY_READ("Tags", "String[]", MediaTagList_Tags), GB_END_DECLARE }; GB_DESC MediaControlDesc[] = { GB_DECLARE("MediaControl", sizeof(CMEDIACONTROL)), GB_METHOD("_new", NULL, MediaControl_new, "[(Parent)MediaContainer;(Type)s]"), GB_METHOD("_free", NULL, MediaControl_free, NULL), GB_PROPERTY("Name", "s", MediaControl_Name), GB_PROPERTY_READ("Type", "s", MediaControl_Type), GB_PROPERTY("State", "i", MediaControl_State), GB_PROPERTY("Tag", "v", MediaControl_Tag), GB_METHOD("_put", NULL, MediaControl_put, "(Value)v(Property)s"), GB_METHOD("_get", "v", MediaControl_get, "(Property)s"), GB_METHOD("LinkTo", NULL, MediaControl_LinkTo, "(Destination)MediaControl;[(Output)s(Input)s]"), GB_METHOD("LinkLaterTo", NULL, MediaControl_LinkLaterTo, "(Destination)MediaControl;"), GB_PROPERTY_READ("Inputs", "String[]", MediaControl_Inputs), GB_PROPERTY_READ("Outputs", "String[]", MediaControl_Outputs), GB_METHOD("SetWindow", NULL, MediaControl_SetWindow, "(Control)Control;[(X)i(Y)i(Width)i(Height)i]"), GB_EVENT("State", NULL, NULL, &EVENT_State), //GB_EVENT("Signal", NULL, "(Arg)MediaSignalArguments", &EVENT_Signal), GB_END_DECLARE }; GB_DESC MediaContainerDesc[] = { GB_DECLARE("MediaContainer", sizeof(CMEDIACONTAINER)), GB_INHERITS("MediaControl"), GB_METHOD("AddInput", NULL, MediaContainer_AddInput, "(Child)MediaControl;[(Name)s]"), GB_METHOD("AddOutput", NULL, MediaContainer_AddOutput, "(Child)MediaControl;[(Name)s]"), GB_END_DECLARE }; GB_DESC MediaPipelineDesc[] = { GB_DECLARE("MediaPipeline", sizeof(CMEDIAPIPELINE)), GB_INHERITS("MediaContainer"), GB_METHOD("_new", NULL, MediaPipeline_new, NULL), GB_METHOD("_free", NULL, MediaPipeline_free, NULL), GB_CONSTANT("Null", "i", GST_STATE_NULL), GB_CONSTANT("Ready", "i", GST_STATE_READY), GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), GB_CONSTANT("Info", "i", 0), GB_CONSTANT("Warning", "i", 1), GB_CONSTANT("Error", "i", 2), GB_PROPERTY("Position", "f", MediaPipeline_Position), GB_PROPERTY_READ("Duration", "f", MediaPipeline_Duration), GB_PROPERTY_READ("Length", "f", MediaPipeline_Duration), GB_METHOD("Play", NULL, MediaPipeline_Play, NULL), GB_METHOD("Stop", NULL, MediaPipeline_Stop, NULL), GB_METHOD("Pause", NULL, MediaPipeline_Pause, NULL), GB_METHOD("Close", NULL, MediaPipeline_Close, NULL), GB_EVENT("End", NULL, NULL, &EVENT_End), GB_EVENT("Message", NULL, "(Source)MediaControl;(Type)i(Message)s", &EVENT_Message), GB_EVENT("Tag", NULL, "(TagList)MediaTagList;", &EVENT_Tag), GB_EVENT("Buffering", NULL, NULL, &EVENT_Buffering), GB_EVENT("Duration", NULL, NULL, &EVENT_Duration), GB_EVENT("Progress", NULL, NULL, &EVENT_Progress), GB_END_DECLARE }; GB_DESC MediaDesc[] = { GB_DECLARE("Media", sizeof(CMEDIAPIPELINE)), GB_INHERITS("MediaPipeline"), GB_CONSTANT("Null", "i", GST_STATE_NULL), GB_CONSTANT("Unknown", "i", -1), GB_CONSTANT("Ready", "i", GST_STATE_READY), GB_CONSTANT("Paused", "i", GST_STATE_PAUSED), GB_CONSTANT("Playing", "i", GST_STATE_PLAYING), GB_CONSTANT("Info", "i", 0), GB_CONSTANT("Warning", "i", 1), GB_CONSTANT("Error", "i", 2), GB_STATIC_METHOD("Link", NULL, Media_Link, "(FirstControl)MediaControl;(SecondControl)MediaControl;."), GB_STATIC_METHOD("Time", "l", Media_Time, "(Seconds)f"), GB_STATIC_METHOD("URL", "s", Media_URL, "(Path)s"), GB_END_DECLARE };