* SECTION:element-tensor_converter
*
* A filter that converts media stream to tensor stream for NN frameworks.
- * The output is always in the format of other/tensor.
+ * The output is always in the format of other/tensor or other/tensors.
*
* <refsect2>
* <title>Example launch line</title>
GstPad * pad, GstCaps * filter);
static gboolean gst_tensor_converter_parse_caps (GstTensorConverter * self,
const GstCaps * caps);
-
+static gboolean gst_tensor_converter_update_caps (GstTensorConverter * self,
+ GstPad * pad, GstTensorsConfig * config);
static const NNStreamerExternalConverter *findExternalConverter (const char
*media_type_name);
static const gchar *getExternalConverterName (const char *name);
case GST_EVENT_CAPS:
{
GstCaps *in_caps;
- GstCaps *out_caps;
gst_event_parse_caps (event, &in_caps);
silent_debug_caps (in_caps, "in-caps");
if (gst_tensor_converter_parse_caps (self, in_caps)) {
- out_caps = gst_tensor_caps_from_config (&self->tensor_config);
- silent_debug_caps (out_caps, "out-caps");
-
- gst_pad_set_caps (self->srcpad, out_caps);
-
+ gboolean ret = gst_tensor_converter_update_caps (self, self->srcpad,
+ &self->tensors_config);
gst_event_unref (event);
- event = gst_event_new_caps (out_caps);
-
- gst_caps_unref (out_caps);
- return gst_pad_push_event (self->srcpad, event);
+ return ret;
}
break;
}
/** @brief Chain function's private routine */
static void
_gst_tensor_converter_chain_segment (GstTensorConverter * self,
- gboolean have_framerate, GstTensorConfig * config, gsize frame_size)
+ gboolean have_framerate, GstTensorsConfig * config, gsize frame_size)
{
if (self->need_segment) {
GstSegment seg;
/** @brief Chain function's private routine */
static void
_gst_tensor_converter_chain_timestamp (GstTensorConverter * self,
- gboolean have_framerate, GstTensorConfig * config, GstBuffer * inbuf,
+ gboolean have_framerate, GstTensorsConfig * config, GstBuffer * inbuf,
GstClockTime * duration, GstClockTime * pts, guint frames_in)
{
if (self->set_timestamp) {
gst_tensor_converter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstTensorConverter *self;
- GstTensorConfig *config;
- GstTensorsConfig tensors_config;
+ GstTensorsConfig *config;
GstAdapter *adapter;
GstBuffer *inbuf;
gsize avail, buf_size, frame_size, out_size;
GstFlowReturn ret = GST_FLOW_OK;
GstClockTime pts, dts, duration;
gboolean have_framerate;
- GstCaps *curr_caps, *peer_caps, *out_caps = NULL;
buf_size = gst_buffer_get_size (buf);
g_return_val_if_fail (buf_size > 0, GST_FLOW_ERROR);
self = GST_TENSOR_CONVERTER (parent);
/** This is an internal logic error. */
- g_assert (self->tensor_configured);
- config = &self->tensor_config;
+ g_assert (self->tensors_configured);
+ config = &self->tensors_config;
have_framerate = (config->rate_n > 0 && config->rate_d > 0);
guint color, width, height;
gsize type;
- color = config->info.dimension[0];
- width = config->info.dimension[1];
- height = config->info.dimension[2];
- type = gst_tensor_get_element_size (config->info.type);
+ color = config->info.info[0].dimension[0];
+ width = config->info.info[0].dimension[1];
+ height = config->info.info[0].dimension[2];
+ type = gst_tensor_get_element_size (config->info.info[0].type);
/** colorspace * width * height * type */
frame_size = color * width * height * type;
break;
case _NNS_MEDIA_PLUGINS:
{
+ GstTensorsConfig new_config;
+
if (self->externalConverter == NULL ||
self->externalConverter->convert == NULL)
return GST_FLOW_NOT_SUPPORTED;
inbuf =
self->externalConverter->convert (buf, &frame_size, &frames_in,
- &tensors_config);
-
- self->tensors_config = tensors_config;
- if (tensors_config.info.num_tensors == 1) {
- GstStructure *st;
-
- peer_caps = gst_pad_peer_query_caps (self->srcpad, NULL);
- silent_debug_caps (peer_caps, "peer caps");
+ &new_config);
- st = gst_caps_get_structure (peer_caps, 0);
-
- if (g_strcmp0 (gst_structure_get_name (st), "other/tensor") == 0) {
- self->tensor_config.info = tensors_config.info.info[0];
- self->tensor_config.rate_n = tensors_config.rate_n;
- self->tensor_config.rate_d = tensors_config.rate_d;
- out_caps = gst_tensor_caps_from_config (&self->tensor_config);
+ if (gst_tensors_config_is_equal (config, &new_config) != TRUE) {
+ if (gst_tensor_converter_update_caps (self, self->srcpad,
+ &new_config) == TRUE) {
+ self->tensors_config = new_config;
+ } else {
+ return GST_FLOW_ERROR;
}
}
- /* caps for tensors */
- if (out_caps == NULL)
- out_caps = gst_tensors_caps_from_config (&self->tensors_config);
-
- silent_debug_caps (out_caps, "out-caps");
-
- /* Update source pad caps. If it is different */
- curr_caps = gst_pad_get_current_caps (self->srcpad);
- if (gst_caps_is_equal (curr_caps, out_caps) != TRUE) {
- gst_pad_set_caps (self->srcpad, out_caps);
- }
g_assert (inbuf != NULL);
g_assert (frame_size > 0);
- gst_caps_unref (curr_caps);
- gst_caps_unref (out_caps);
- gst_buffer_unref (buf);
+ if (inbuf != buf)
+ gst_buffer_unref (buf);
break;
}
gst_adapter_clear (self->adapter);
}
- self->tensor_configured = FALSE;
- gst_tensor_config_init (&self->tensor_config);
+ self->tensors_configured = FALSE;
+ gst_tensors_config_init (&self->tensors_config);
self->have_segment = FALSE;
self->need_segment = FALSE;
}
/**
- * @brief Set the tensor config structure from video info (internal static function)
+ * @brief Set the tensors config structure from video info (internal static function)
* @param self this pointer to GstTensorConverter
- * @param config tensor config structure to be filled
+ * @param config tensors config structure to be filled
* @param info video info structure
* @note Change dimension if tensor contains N frames.
* @return TRUE if supported type
*/
static gboolean
gst_tensor_converter_parse_video (GstTensorConverter * self,
- GstTensorConfig * config, const GstVideoInfo * info)
+ GstTensorsConfig * config, const GstVideoInfo * info)
{
/**
* Refer: https://www.tensorflow.org/api_docs/python/tf/summary/image
guint i;
g_return_val_if_fail (config != NULL, FALSE);
- gst_tensor_config_init (config);
+ gst_tensors_config_init (config);
+ config->info.num_tensors = 1;
g_return_val_if_fail (info != NULL, FALSE);
/* [color-space][width][height][frames] */
switch (format) {
case GST_VIDEO_FORMAT_GRAY8:
- config->info.type = _NNS_UINT8;
- config->info.dimension[0] = 1;
+ config->info.info[0].type = _NNS_UINT8;
+ config->info.info[0].dimension[0] = 1;
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
- config->info.type = _NNS_UINT8;
- config->info.dimension[0] = 3;
+ config->info.info[0].type = _NNS_UINT8;
+ config->info.info[0].dimension[0] = 3;
break;
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
- config->info.type = _NNS_UINT8;
- config->info.dimension[0] = 4;
+ config->info.info[0].type = _NNS_UINT8;
+ config->info.info[0].dimension[0] = 4;
break;
default:
/* unsupported format */
break;
}
- config->info.dimension[1] = GST_VIDEO_INFO_WIDTH (info);
- config->info.dimension[2] = GST_VIDEO_INFO_HEIGHT (info);
+ config->info.info[0].dimension[1] = GST_VIDEO_INFO_WIDTH (info);
+ config->info.info[0].dimension[2] = GST_VIDEO_INFO_HEIGHT (info);
/* Supposed 1 frame in tensor, change dimension[3] if tensor contains N frames. */
for (i = 3; i < NNS_TENSOR_RANK_LIMIT; i++) {
- config->info.dimension[i] = 1;
+ config->info.info[0].dimension[i] = 1;
}
config->rate_n = GST_VIDEO_INFO_FPS_N (info);
config->rate_d = GST_VIDEO_INFO_FPS_D (info);
- return (config->info.type != _NNS_END);
+ return (config->info.info[0].type != _NNS_END);
}
/**
- * @brief Set the tensor config structure from audio info (internal static function)
+ * @brief Set the tensors config structure from audio info (internal static function)
* @param self this pointer to GstTensorConverter
- * @param config tensor config structure to be filled
+ * @param config tensors config structure to be filled
* @param info audio info structure
* @note Change dimension if tensor contains N frames.
* @return TRUE if supported type
*/
static gboolean
gst_tensor_converter_parse_audio (GstTensorConverter * self,
- GstTensorConfig * config, const GstAudioInfo * info)
+ GstTensorsConfig * config, const GstAudioInfo * info)
{
/**
* Refer: https://www.tensorflow.org/api_docs/python/tf/summary/audio
guint i;
g_return_val_if_fail (config != NULL, FALSE);
- gst_tensor_config_init (config);
+ gst_tensors_config_init (config);
+ config->info.num_tensors = 1;
g_return_val_if_fail (info != NULL, FALSE);
/* [channels][frames] */
switch (format) {
case GST_AUDIO_FORMAT_S8:
- config->info.type = _NNS_INT8;
+ config->info.info[0].type = _NNS_INT8;
break;
case GST_AUDIO_FORMAT_U8:
- config->info.type = _NNS_UINT8;
+ config->info.info[0].type = _NNS_UINT8;
break;
case GST_AUDIO_FORMAT_S16:
- config->info.type = _NNS_INT16;
+ config->info.info[0].type = _NNS_INT16;
break;
case GST_AUDIO_FORMAT_U16:
- config->info.type = _NNS_UINT16;
+ config->info.info[0].type = _NNS_UINT16;
break;
case GST_AUDIO_FORMAT_S32:
- config->info.type = _NNS_INT32;
+ config->info.info[0].type = _NNS_INT32;
break;
case GST_AUDIO_FORMAT_U32:
- config->info.type = _NNS_UINT32;
+ config->info.info[0].type = _NNS_UINT32;
break;
case GST_AUDIO_FORMAT_F32:
- config->info.type = _NNS_FLOAT32;
+ config->info.info[0].type = _NNS_FLOAT32;
break;
case GST_AUDIO_FORMAT_F64:
- config->info.type = _NNS_FLOAT64;
+ config->info.info[0].type = _NNS_FLOAT64;
break;
default:
/* unsupported format */
break;
}
- config->info.dimension[0] = GST_AUDIO_INFO_CHANNELS (info);
+ config->info.info[0].dimension[0] = GST_AUDIO_INFO_CHANNELS (info);
/* Supposed 1 frame in tensor, change dimension[1] if tensor contains N frames. */
for (i = 1; i < NNS_TENSOR_RANK_LIMIT; i++) {
- config->info.dimension[i] = 1;
+ config->info.info[0].dimension[i] = 1;
}
config->rate_n = GST_AUDIO_INFO_RATE (info);
config->rate_d = 1;
- return (config->info.type != _NNS_END);
+ return (config->info.info[0].type != _NNS_END);
}
/**
- * @brief Set the tensor config structure from text info (internal static function)
+ * @brief Set the tensors config structure from text info (internal static function)
* @param self this pointer to GstTensorConverter
- * @param config tensor config structure to be filled
+ * @param config tensors config structure to be filled
* @param structure caps structure
- * @note Change dimension if tensor contains N frames.
+ * @note Change dimension if tensors contains N frames.
* @return TRUE if supported type
*/
static gboolean
gst_tensor_converter_parse_text (GstTensorConverter * self,
- GstTensorConfig * config, const GstStructure * structure)
+ GstTensorsConfig * config, const GstStructure * structure)
{
/**
* Refer: https://www.tensorflow.org/api_docs/python/tf/summary/text
guint i;
g_return_val_if_fail (config != NULL, FALSE);
- gst_tensor_config_init (config);
+ gst_tensors_config_init (config);
+ config->info.num_tensors = 1;
g_return_val_if_fail (structure != NULL, FALSE);
format_string = gst_structure_get_string (structure, "format");
if (format_string) {
if (g_ascii_strcasecmp (format_string, "utf8") == 0) {
- config->info.type = _NNS_UINT8;
+ config->info.info[0].type = _NNS_UINT8;
} else {
/* unsupported format */
GST_WARNING_OBJECT (self, "Unsupported format = %s\n", format_string);
/* [size][frames] */
/* Fixed size of string, we cannot get the size from caps. */
- config->info.dimension[0] = 0;
+ config->info.info[0].dimension[0] = 0;
/* Supposed 1 frame in tensor, change dimension[1] if tensor contains N frames. */
for (i = 1; i < NNS_TENSOR_RANK_LIMIT; i++) {
- config->info.dimension[i] = 1;
+ config->info.info[0].dimension[i] = 1;
}
if (gst_structure_has_field (structure, "framerate")) {
config->rate_d = 1;
}
- return (config->info.type != _NNS_END);
+ return (config->info.info[0].type != _NNS_END);
}
/**
- * @brief Set the tensor config structure from octet stream (internal static function)
+ * @brief Set the tensors configs structure from octet stream (internal static function)
* @param self this pointer to GstTensorConverter
- * @param config tensor config structure to be filled
+ * @param config tensors config structure to be filled
* @param structure caps structure
- * @note Change tensor dimension and type.
+ * @note Change tensors dimension and type.
* @return TRUE if supported type
*/
static gboolean
gst_tensor_converter_parse_octet (GstTensorConverter * self,
- GstTensorConfig * config, const GstStructure * structure)
+ GstTensorsConfig * config, const GstStructure * structure)
{
g_return_val_if_fail (config != NULL, FALSE);
- gst_tensor_config_init (config);
+ gst_tensors_config_init (config);
+ config->info.num_tensors = 1;
g_return_val_if_fail (structure != NULL, FALSE);
/**
* Raw byte-stream (application/octet-stream)
- * We cannot get the exact tensor info from caps.
- * All tensor info should be updated.
+ * We cannot get the exact tensors info from caps.
+ * All tensors info should be updated.
*/
- config->info.type = _NNS_UINT8;
+ config->info.info[0].type = _NNS_UINT8;
if (gst_structure_has_field (structure, "framerate")) {
gst_structure_get_fraction (structure, "framerate", &config->rate_n,
config->rate_d = 1;
}
- return (config->info.type != _NNS_END);
+ return (config->info.info[0].type != _NNS_END);
}
/**
silent_debug_caps (peer_caps, "peer caps");
if (gst_caps_get_size (peer_caps) > 0) {
- GstTensorConfig config;
+ GstTensorsConfig config;
GstStructure *st;
guint i, caps_len;
media_type type;
- /* get tensor info from peer caps */
+ /* get tensors info from peer caps */
st = gst_caps_get_structure (peer_caps, 0);
- gst_tensor_config_from_structure (&config, st);
+ gst_tensors_config_from_structure (&config, st);
/* convert peer caps to possible media caps */
caps_len = gst_caps_get_size (media_caps);
switch (type) {
case _NNS_VIDEO:
/* video caps from tensor info */
- if (is_video_supported (self) && config.info.type == _NNS_UINT8) {
+ if (is_video_supported (self)
+ && config.info.info[0].type == _NNS_UINT8) {
GValue supported_formats = G_VALUE_INIT;
gint colorspace, width, height;
- colorspace = config.info.dimension[0];
+ colorspace = config.info.info[0].dimension[0];
switch (colorspace) {
case 1:
gst_tensor_converter_get_format_list (&supported_formats,
}
g_value_unset (&supported_formats);
- if ((width = config.info.dimension[1]) > 0) {
+ if ((width = config.info.info[0].dimension[1]) > 0) {
gst_structure_set (st, "width", G_TYPE_INT, width, NULL);
}
- if ((height = config.info.dimension[2]) > 0) {
+ if ((height = config.info.info[0].dimension[2]) > 0) {
gst_structure_set (st, "height", G_TYPE_INT, height, NULL);
}
break;
case _NNS_AUDIO:
/* audio caps from tensor info */
- if (is_audio_supported (self) && config.info.type != _NNS_END) {
+ if (is_audio_supported (self)
+ && config.info.info[0].type != _NNS_END) {
gint ch, rate;
GstAudioFormat aformat;
- switch (config.info.type) {
+ switch (config.info.info[0].type) {
case _NNS_INT8:
aformat = GST_AUDIO_FORMAT_S8;
break;
gst_structure_set (st, "format", G_TYPE_STRING,
gst_audio_format_to_string (aformat), NULL);
- if ((ch = config.info.dimension[0]) > 0) {
+ if ((ch = config.info.info[0].dimension[0]) > 0) {
gst_structure_set (st, "channels", G_TYPE_INT, ch, NULL);
}
}
/**
- * @brief Parse caps and set tensor info.
+ * @brief Parse caps and set tensors info.
*/
static gboolean
gst_tensor_converter_parse_caps (GstTensorConverter * self,
const GstCaps * caps)
{
GstStructure *structure;
- GstTensorConfig config;
+ GstTensorsConfig config;
media_type in_type;
- gint frames_dim = -1; /** dimension index of frames in configured tensor */
+ gint frames_dim = -1; /** dimension index of frames in configured tensors */
g_return_val_if_fail (caps != NULL, FALSE);
g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
return FALSE;
}
- config.info.dimension[0] = self->tensor_info.dimension[0];
+ config.info.info[0].dimension[0] = self->tensor_info.dimension[0];
frames_dim = 1;
- self->frame_size = gst_tensor_info_get_size (&config.info);
+ self->frame_size = gst_tensor_info_get_size (&config.info.info[0]);
break;
}
case _NNS_OCTET:
{
if (!gst_tensor_converter_parse_octet (self, &config, structure)) {
- GST_ERROR_OBJECT (self, "Failed to configure tensor from octet info.");
+ GST_ERROR_OBJECT (self, "Failed to configure tensors from octet info.");
return FALSE;
}
return FALSE;
}
- config.info = self->tensor_info;
- self->frame_size = gst_tensor_info_get_size (&config.info);
+ config.info.info[0] = self->tensor_info;
+ self->frame_size = gst_tensor_info_get_size (&config.info.info[0]);
break;
}
default:
if (NULL == ex->get_caps || !ex->get_caps (structure, &config)) {
GST_ERROR_OBJECT (self,
- "Failed to get tensor info from %s. Check the given options.",
+ "Failed to get tensors info from %s. Check the given options.",
ext_conv_name);
ml_loge ("Please set the options property correctly.\n");
self->externalConverter = NULL;
/** set the number of frames in dimension */
if (frames_dim >= 0) {
- config.info.dimension[frames_dim] = self->frames_per_tensor;
+ config.info.info[0].dimension[frames_dim] = self->frames_per_tensor;
}
- if (!gst_tensor_config_validate (&config)) {
+ if (!gst_tensors_config_validate (&config)) {
/** not fully configured */
- GST_ERROR_OBJECT (self, "Failed to configure tensor info.\n");
+ GST_ERROR_OBJECT (self, "Failed to configure tensors info.\n");
return FALSE;
}
-
if (gst_tensor_info_validate (&self->tensor_info)) {
/** compare tensor info */
- if (!gst_tensor_info_is_equal (&self->tensor_info, &config.info)) {
+ if (!gst_tensor_info_is_equal (&self->tensor_info, &config.info.info[0])) {
GST_ERROR_OBJECT (self, "Failed, mismatched tensor info.\n");
return FALSE;
}
}
self->in_media_type = in_type;
- self->tensor_configured = TRUE;
- self->tensor_config = config;
+ self->tensors_configured = TRUE;
+ self->tensors_config = config;
+ return TRUE;
+}
+
+/**
+ * @brief Update pas caps from tensors config
+ */
+static gboolean
+gst_tensor_converter_update_caps (GstTensorConverter * self,
+ GstPad * pad, GstTensorsConfig * config)
+{
+ GstStructure *st;
+ GstCaps *curr_caps, *out_caps = NULL;
+
+ if (config->info.num_tensors == 1) {
+ GstCaps *peer_caps = gst_pad_peer_query_caps (pad, NULL);
+ silent_debug_caps (peer_caps, "peer caps");
+
+ if (peer_caps != NULL) {
+ st = gst_caps_get_structure (peer_caps, 0);
+
+ if (g_strcmp0 (gst_structure_get_name (st), "other/tensor") == 0) {
+ GstTensorConfig tensor_config;
+
+ tensor_config.info = config->info.info[0];
+ tensor_config.rate_n = config->rate_n;
+ tensor_config.rate_d = config->rate_d;
+ out_caps = gst_tensor_caps_from_config (&tensor_config);
+ }
+ gst_caps_unref (peer_caps);
+ } else {
+ GST_WARNING_OBJECT (self, "Failed to get peer caps \n");
+ return FALSE;
+ }
+ }
+
+ /* caps for tensors */
+ if (out_caps == NULL)
+ out_caps = gst_tensors_caps_from_config (config);
+ silent_debug_caps (out_caps, "out-caps");
+
+ /* Update pad caps. If it is different */
+ curr_caps = gst_pad_get_current_caps (pad);
+ if (gst_caps_is_equal (curr_caps, out_caps) != TRUE) {
+ gst_pad_set_caps (pad, out_caps);
+ }
+ gst_caps_unref (out_caps);
+ gst_caps_unref (curr_caps);
+
return TRUE;
}