17f5f0fa0c
* NEW: MediaControl Tag event now takes a MediaTagList object as argument. That object contains all tags received at event time. Not all possible tags are supported yet. [EXAMPLES] * NEW: MediaPlayer: Add a panel that displays information about the media file get through the Tag event. git-svn-id: svn://localhost/gambas/trunk@4723 867c0c6c-44f3-4631-809d-bfa615b0a4ec
1167 lines
27 KiB
C
1167 lines
27 KiB
C
/***************************************************************************
|
|
|
|
c_media.c
|
|
|
|
gb.media component
|
|
|
|
(c) 2012 Benoît Minisini <gambas@users.sourceforge.net>
|
|
|
|
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 <gst/interfaces/xoverlay.h>
|
|
|
|
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
|
|
};
|
|
|