3 * Copyright (c) 2011 Samsung Electronics, Inc.
6 * This software is a confidential and proprietary information
7 * of Samsung Electronics, Inc. ("Confidential Information"). You
8 * shall not disclose such Confidential Information and shall use
9 * it only in accordance with the terms of the license agreement
10 * you entered into with Samsung Electronics.
15 * @author Deepak Singh (deep.singh@samsung.com)
17 * @brief This source code implements the gstreamer plugin for subtitle muxing requirement in media player.
22 *! ---------------------------------------------------------------------------
23 *! DATE | AUTHOR | COMMENTS
24 *! ---------------------------------------------------------------------------
25 *! 1-4-2014 deep.singh@samsung.com created.
29 #include "gstsubmux.h"
30 #include <gst/base/gstadapter.h>
31 #include <glib/gstdio.h>
33 #include <gst/app/gstappsink.h>
35 #include <sys/types.h>
37 #include <gst/tag/tag.h>
41 static const GstElementDetails gst_submux_plugin_details = GST_ELEMENT_DETAILS(
43 "Codec/Parser/Subtitle",
44 "muxing of different subtitle stream",
45 "Samsung Electronics <www.samsung.com>"
47 static GstStaticPadTemplate gst_submux_sink_template = GST_STATIC_PAD_TEMPLATE(
51 GST_STATIC_CAPS("video/x-dvd-subpicture; text/plain; application/x-subtitle; "
52 "text/x-pango-markup;"
53 "application/x-usf; subpicture/x-pgs; subtitle/x-kate; application/x-subtitle; "
54 "application/x-subtitle-sami; application/x-subtitle-tmplayer; "
55 "application/x-subtitle-mpl2; application/x-subtitle-dks; "
56 "application/x-subtitle-qttext")
58 static GstStaticPadTemplate gst_submux_src_template = GST_STATIC_PAD_TEMPLATE(
62 GST_STATIC_CAPS ("text/plain; text/x-pango-markup")
69 PROP_EXTSUB_CURRENT_LANGUAGE,
75 GST_DEBUG_CATEGORY_STATIC (gst_submux_debug);
76 #define GST_CAT_DEFAULT gst_submux_debug
77 #define _do_init(bla) \
78 GST_DEBUG_CATEGORY_INIT (gst_submux_debug, "submux", 0, "submux");
79 ////////////////////////////////////////////////////////
80 // Gstreamer Base Prototype //
81 ////////////////////////////////////////////////////////
83 GST_BOILERPLATE_FULL(Gstsubmux, gst_submux, GstElement, GST_TYPE_ELEMENT, _do_init);
85 #define GST_SUBMUX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SUBMUX, GstsubmuxPrivate))
86 #define MAX_LANGUAGE 10
87 static gint gst_submux_buffer_list_sorting (gconstpointer a, gconstpointer b);
88 static gboolean gst_submux_create_pipelines(Gstsubmux *self,GstPad * pad);
89 static GstPad* gst_submux_request_new_pad (GstElement * element,
90 GstPadTemplate * templ, const gchar * req_name);
91 static void gst_submux_release_pad (GstElement * element, GstPad * pad);
92 static gchar* gst_submux_extract_data (Gstsubmux *submux);
93 static gboolean gst_create_own_language_list (Gstsubmux *submux) ;
94 static GstSubMuxFormat gst_submux_data_format_autodetect (gchar * match_str);
95 static gpointer gst_submux_data_format_autodetect_regex_once (GstSubMuxRegex regtype);
96 static gboolean gst_submux_format_autodetect (Gstsubmux * self);
97 static void gst_submux_base_init(gpointer klass);
98 static void gst_submux_class_init(GstsubmuxClass *klass);
99 static GstStateChangeReturn gst_submux_change_state (GstElement * element, GstStateChange transition);
100 static void gst_submux_init(Gstsubmux *submux, GstsubmuxClass *klass);
101 static gboolean gst_submux_setcaps(GstPad *pad, GstCaps *caps);
102 static GstFlowReturn gst_submux_chain (GstPad *pad, GstBuffer *buffer);
103 static void gst_submux_dispose(GObject *object);
104 static void gst_submux_loop (Gstsubmux * submux);
105 static gboolean gst_submux_stream_init(GstSubmuxStream * stream);
106 static void gst_submux_stream_deinit(GstSubmuxStream * stream,Gstsubmux * submux);
107 static void gst_submux_on_new_buffer (GstElement *appsink, void *data);
108 static gboolean gst_submux_handle_src_event (GstPad * pad, GstEvent * event);
110 static gboolean gst_submux_handle_sink_event (GstPad * pad, GstEvent * event);
111 ////////////////////////////////////////////////////////
112 // Plugin Utility Prototype //
113 ////////////////////////////////////////////////////////
114 static void gst_submux_set_property (GObject * object, guint prop_id,
115 const GValue * value, GParamSpec * pspec);
116 static void gst_submux_get_property (GObject * object, guint prop_id,
117 GValue * value, GParamSpec * pspec);
118 static gboolean gst_submux_deinit_private_values(Gstsubmux *submux);
119 static gchar *convert_to_utf8 (const gchar * str, gsize len, const gchar * encoding,
120 gsize * consumed, GError ** err, Gstsubmux * self);
121 #define DEFAULT_ENCODING NULL
122 #define DEFAULT_CURRENT_LANGUAGE NULL
124 ////////////////////////////////////////////////////////
125 // Gstreamer Base Functions //
126 ////////////////////////////////////////////////////////
130 ** Description : base init
131 ** Params : (1) instance of gclass
133 ** Comments : The following code registers templates for src and sink pad.
137 gst_submux_base_init(gpointer g_class)
139 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
140 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_submux_sink_template));
141 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&gst_submux_src_template));
142 gst_element_class_set_details(element_class, &gst_submux_plugin_details);
147 ** Description : Initilizes the Gstsubmux's class
148 ** Params : @ klass instance of submux plugin's class
150 ** Comments : Declaring properties and over-writing function pointers
154 gst_submux_class_init(GstsubmuxClass *klass)
156 GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass);
157 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
158 g_type_class_add_private (klass, sizeof (GstsubmuxPrivate));
159 gobject_class->set_property = gst_submux_set_property;
160 gobject_class->get_property = gst_submux_get_property;
161 g_object_class_install_property (gobject_class, PROP_ENCODING,
162 g_param_spec_string ("subtitle-encoding", "subtitle charset encoding",
163 "Encoding to assume if input subtitles are not in UTF-8 or any other "
164 "Unicode encoding. If not set, the GST_SUBTITLE_ENCODING environment "
165 "variable will be checked for an encoding to use. If that is not set "
166 "either, ISO-8859-15 will be assumed.", DEFAULT_ENCODING,
167 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
169 g_object_class_install_property (gobject_class, PROP_VIDEOFPS,
170 gst_param_spec_fraction ("video-fps", "Video framerate",
171 "Framerate of the video stream. This is needed by some subtitle "
172 "formats to synchronize subtitles and video properly. If not set "
173 "and the subtitle format requires it subtitles may be out of sync.",
174 0, 1, G_MAXINT, 1, 24000, 1001,
175 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
176 g_object_class_install_property (gobject_class, PROP_EXTSUB_CURRENT_LANGUAGE,
177 g_param_spec_string ("current-language", "Current language",
178 "Current language of the subtitle in external subtitle case.",
179 DEFAULT_CURRENT_LANGUAGE,
180 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
181 g_object_class_install_property (gobject_class, PROP_IS_INTERNAL,
182 g_param_spec_boolean ("is-internal", "is internal",
183 "TRUE for internal subtitle case",
184 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185 g_object_class_install_property (gobject_class, PROP_LANG_LIST,
186 g_param_spec_pointer ("lang-list", "language list", "List of languages selected/not selected",
187 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188 parent_class = g_type_class_peek_parent (klass);
189 gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_submux_request_new_pad);
190 gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_submux_dispose);
191 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_submux_change_state);
192 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_submux_release_pad);
197 ** Description : Initilizes the submux element
198 ** Params : (1)instance of submux (2) instance of submux class
200 ** Comments : instantiate pads and add them to element, set pad calback functions
204 gst_submux_init(Gstsubmux *submux, GstsubmuxClass *klass)
206 GST_DEBUG_OBJECT (submux, "Entering in init");
207 submux->priv = GST_SUBMUX_GET_PRIVATE(submux);
208 submux->srcpad = gst_pad_new_from_static_template(&gst_submux_src_template, "src");
209 gst_pad_set_event_function (submux->srcpad,
210 GST_DEBUG_FUNCPTR (gst_submux_handle_src_event));
211 submux->priv->first_buffer = FALSE;
212 gst_segment_init (&submux->segment, GST_FORMAT_TIME);
213 submux->flushing = FALSE;
214 submux->msl_streams = NULL;
215 submux->stop_loop = FALSE;
216 submux->need_segment = TRUE;
217 submux->pipeline_made = FALSE;
218 submux->external_sinkpad = FALSE;
219 submux->detected_encoding = NULL;
220 submux->encoding = NULL;
221 submux->seek_came = FALSE;
222 submux->sinkpads_count = 0;
223 submux->langlist_msg_posted = FALSE;
224 submux->cur_buf_array = NULL;
225 GST_DEBUG_OBJECT (submux, "Making flushing FALSE");
226 submux->priv->is_internal = FALSE;
227 submux->external_filepath = NULL;
228 gst_element_add_pad (GST_ELEMENT (submux), submux->srcpad);
229 GST_DEBUG_OBJECT (submux, "Exiting in init");
234 ** Description : for setting the property of submux
236 ** Comments : To set the various properties of submux
240 gst_submux_set_property (GObject * object, guint prop_id,
241 const GValue * value, GParamSpec * pspec)
243 Gstsubmux *submux = GST_SUBMUX (object);
246 GstLangStruct *cur_language=NULL;
247 GstSubmuxStream *cur_stream = NULL;
248 GST_OBJECT_LOCK (submux);
249 length = g_list_length(submux->priv->lang_list);
252 g_free (submux->encoding);
253 submux->encoding = g_value_dup_string (value);
254 GST_DEBUG_OBJECT (submux, "subtitle encoding set to %s",
255 GST_STR_NULL (submux->encoding));
256 for(i = 0;i < length;i++) {
257 cur_stream = g_list_nth_data(submux->streams,i);
258 GST_DEBUG_OBJECT (submux, "setting the subtitle-encoding to %s", submux->encoding);
259 g_object_set (G_OBJECT (cur_stream->pipe_struc.parser), "subtitle-encoding", submux->encoding, NULL);
264 submux->fps_n = gst_value_get_fraction_numerator (value);
265 submux->fps_d = gst_value_get_fraction_denominator (value);
266 GST_DEBUG_OBJECT (submux, "video framerate set to %d/%d", submux->fps_n, submux->fps_d);
269 case PROP_EXTSUB_CURRENT_LANGUAGE: {
270 for (i = 0; i < length; i++) {
271 cur_stream = g_list_nth_data(submux->streams, i);
272 cur_language = g_list_nth_data(submux->priv->lang_list, i);
273 GST_DEBUG_OBJECT (submux, "value of current-language key is %s", cur_language->language_key);
274 g_object_set (G_OBJECT (cur_stream->pipe_struc.parser), "current-language",
275 cur_language->language_key, NULL);
277 gchar *dup = g_value_dup_string (value);
278 GST_DEBUG_OBJECT (submux, "Setting property to %s", dup);
282 case PROP_IS_INTERNAL: {
283 submux->priv->is_internal = g_value_get_boolean (value);
284 GST_DEBUG_OBJECT (submux, "Setting the is_internal prop to %d", submux->priv->is_internal);
287 case PROP_LANG_LIST: {
288 submux->priv->lang_list = (GList*) g_value_get_pointer (value);
289 GST_DEBUG_OBJECT (submux, "updating the languages list and length is %d", g_list_length (submux->priv->lang_list));
290 submux->msl_streams = g_list_copy (submux->priv->lang_list);
295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298 GST_OBJECT_UNLOCK (submux);
303 ** Description : for getting the property of submux
305 ** Comments : To get the various properties of submux in case called by MSL
309 gst_submux_get_property (GObject * object, guint prop_id,
310 GValue * value, GParamSpec * pspec)
312 Gstsubmux *submux = GST_SUBMUX (object);
314 GST_OBJECT_LOCK (submux);
317 g_value_set_string (value, submux->encoding);
320 gst_value_set_fraction (value, submux->fps_n, submux->fps_d);
322 case PROP_EXTSUB_CURRENT_LANGUAGE:
323 GST_DEBUG_OBJECT (submux, "Getting the current language");
325 case PROP_IS_INTERNAL: {
326 g_value_set_boolean(value,submux->priv->is_internal);
329 case PROP_LANG_LIST: {
330 g_value_set_pointer(value,(gpointer)(submux->priv->lang_list));
334 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337 GST_OBJECT_UNLOCK (submux);
341 gst_submux_dispose (GObject *object)
343 Gstsubmux *submux = GST_SUBMUX(object);
345 gchar *pad_name = gst_pad_get_name (submux->srcpad);
346 if (submux && GST_PAD_TASK(submux->srcpad)) {
347 GST_INFO_OBJECT (submux, "Stopping pad task : %s", pad_name);
348 GST_DEBUG_OBJECT (submux, "Stopping pad task : on src pad %p", submux->srcpad);
349 gst_pad_stop_task (submux->srcpad);
350 GST_INFO_OBJECT (submux, "stopped pad task : %s", pad_name);
353 if (submux->srcpad) {
354 gst_element_remove_pad (GST_ELEMENT_CAST (submux), submux->srcpad);
355 submux->srcpad = NULL;
357 if (submux->priv->is_internal) {
358 for (i = 0; i < (submux->sinkpads_count); i++){
359 gst_submux_stream_deinit (g_list_nth_data (submux->streams, i), submux);
362 for (i = 0; i < g_list_length (submux->priv->lang_list); i++) {
363 gst_submux_stream_deinit (g_list_nth_data (submux->streams, i), submux);
366 gst_submux_deinit_private_values (submux);
368 GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
369 GST_DEBUG_OBJECT (submux, "Returning from finalize");
373 gst_submux_stop (Gstsubmux* submux)
375 GstSubmuxStream *new_stream = NULL;
377 submux->stop_loop = TRUE;
378 GST_INFO_OBJECT (submux, "stopping the loop");
379 if (submux->priv->is_internal) {
380 for (i = 0; i < (submux->sinkpads_count); i++) {
381 new_stream = g_list_nth_data (submux->streams, i);
383 g_mutex_lock (new_stream->queue_lock);
384 g_cond_signal (new_stream->queue_empty);
385 g_mutex_unlock (new_stream->queue_lock);
389 for (i = 0; i < g_list_length (submux->priv->lang_list); i++) {
390 new_stream = g_list_nth_data (submux->streams, i);
392 g_mutex_lock (new_stream->queue_lock);
393 g_cond_signal (new_stream->queue_empty);
394 g_mutex_unlock (new_stream->queue_lock);
400 static GstStateChangeReturn
401 gst_submux_change_state (GstElement * element, GstStateChange transition)
403 GstStateChangeReturn ret;
404 Gstsubmux *submux = GST_SUBMUX (element);
405 gboolean bret = FALSE;
407 switch (transition) {
408 case GST_STATE_CHANGE_READY_TO_PAUSED:
410 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
411 GST_INFO_OBJECT (submux,"PAUSED->PLAYING");
413 case GST_STATE_CHANGE_PAUSED_TO_READY:
414 GST_INFO_OBJECT (submux,"PAUSED->READY");
415 gst_submux_stop (submux);
421 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
423 switch (transition) {
424 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
425 GST_INFO_OBJECT (submux,"PLAYING->PAUSED");
427 case GST_STATE_CHANGE_PAUSED_TO_READY:
428 GST_INFO_OBJECT (submux,"PAUSED->READY");
429 if(submux->msl_streams) {
430 g_list_free(submux->msl_streams);
431 submux->msl_streams = NULL;
433 if (submux->priv->lang_list && !submux->priv->is_internal) {
434 g_list_free (submux->priv->lang_list);
435 submux->priv->lang_list = NULL;
437 if (submux->external_filepath) {
438 g_free (submux->external_filepath);
439 submux->external_filepath = NULL;
441 GST_WARNING_OBJECT(submux,"stopping has been called ...Moved after change_state");
443 case GST_STATE_CHANGE_READY_TO_NULL:
444 GST_INFO_OBJECT (submux,"READY->NULL");
454 ** Description : Setting the caps on sink pad based on upstream element's src pad
455 ** Params : (1) GstPad to set the capabilities of
456 ** (2) caps to be set
457 ** return : TRUE on success
458 ** Comments : this function handles the link with other elements
462 gst_submux_setcaps (GstPad *pad, GstCaps *caps)
467 /*extracting data for file format detection*/
468 static gchar* gst_submux_extract_data (Gstsubmux *submux){
469 gchar * file_path_type = NULL;
470 gchar * file_path = NULL;
471 gchar * temp_path = NULL;
473 gboolean is_converted = FALSE;
474 gchar *converted = NULL;
478 gsize * consumed = NULL;
481 GstStructure *structure;
483 GstPad *sinkpad = (GstPad *)g_list_nth_data (submux->sinkpad, 0);
484 structure = gst_structure_new ("FileSrcURI",
485 "file-uri", G_TYPE_STRING, NULL, NULL);
487 cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
489 if (!gst_pad_peer_query (sinkpad, cquery))
491 GST_ERROR_OBJECT (submux, "Failed to query SMI file path");
492 gst_query_unref (cquery);
495 structure = gst_query_get_structure (cquery);
496 value = gst_structure_get_value (structure, "file-uri");
497 file_path = g_strdup (g_value_get_string (value));
499 if (file_path == NULL){
500 GST_ERROR_OBJECT (submux, "Could not parse the SMI file path");
501 gst_query_unref (cquery);
505 gst_query_unref (cquery);
506 temp_path = file_path;
507 GST_INFO_OBJECT (submux, "File path comes as %s", file_path);
509 file_path_type = g_strndup ((gchar *) file_path, 4);
510 GST_INFO_OBJECT (submux, "Received file path by query = %s, %s", file_path, file_path_type);
511 if (!g_strcmp0(file_path_type, "file")){
513 GST_INFO_OBJECT (submux, "File path comes as %s", file_path);
515 fp = fopen (file_path, "r");
517 GST_ERROR_OBJECT (submux, "Failed to open file");
518 g_free(file_path_type);
523 GST_ERROR_OBJECT (submux, "File is not local");
524 g_free(file_path_type);
528 line = (gchar*)g_malloc (2049);
529 charCount = fread (line, sizeof(char), 2048, fp);
530 line[charCount] = '\0';
531 if (submux->encoding && strcmp (submux->encoding, "UTF-8"))
532 converted = convert_to_utf8 (line, charCount, submux->encoding, consumed, &err, submux);
536 GST_INFO("returned from conversion and length of converted string is[%d]", strlen(converted));
540 GST_WARNING_OBJECT (submux, "fread returned zero bytes");
542 g_free(file_path_type);
550 g_free(file_path_type);
560 gst_submux_data_format_autodetect_regex_once (GstSubMuxRegex regtype)
562 gpointer result = NULL;
565 case GST_SUB_PARSE_REGEX_MDVDSUB:
567 (gpointer) g_regex_new ("^\\{[0-9]+\\}\\{[0-9]+\\}",
568 G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
569 if (result == NULL) {
570 g_warning ("Compilation of mdvd regex failed: %s", gerr->message);
574 case GST_SUB_PARSE_REGEX_SUBRIP:
575 result = (gpointer) g_regex_new ("^ {0,3}[ 0-9]{1,4}\\s*(\x0d)?\x0a"
576 " ?[0-9]{1,2}: ?[0-9]{1,2}: ?[0-9]{1,2}[,.] {0,2}[0-9]{1,3}"
577 " +--> +[0-9]{1,2}: ?[0-9]{1,2}: ?[0-9]{1,2}[,.] {0,2}[0-9]{1,2}",
578 G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
579 if (result == NULL) {
580 g_warning ("Compilation of subrip regex failed: %s", gerr->message);
584 case GST_SUB_PARSE_REGEX_DKS:
585 result = (gpointer) g_regex_new ("^\\[[0-9]+:[0-9]+:[0-9]+\\].*",
586 G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &gerr);
587 if (result == NULL) {
588 g_warning ("Compilation of dks regex failed: %s", gerr->message);
593 GST_WARNING ("Trying to allocate regex of unknown type %u", regtype);
598 static GstSubMuxFormat
599 gst_submux_data_format_autodetect (gchar * match_str)
603 static GOnce mdvd_rx_once = G_ONCE_INIT;
604 static GOnce subrip_rx_once = G_ONCE_INIT;
605 static GOnce dks_rx_once = G_ONCE_INIT;
611 g_once (&mdvd_rx_once,
612 (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
613 (gpointer) GST_SUB_PARSE_REGEX_MDVDSUB);
614 g_once (&subrip_rx_once,
615 (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
616 (gpointer) GST_SUB_PARSE_REGEX_SUBRIP);
617 g_once (&dks_rx_once,
618 (GThreadFunc) gst_submux_data_format_autodetect_regex_once,
619 (gpointer) GST_SUB_PARSE_REGEX_DKS);
621 mdvd_grx = (GRegex *) mdvd_rx_once.retval;
622 subrip_grx = (GRegex *) subrip_rx_once.retval;
623 dks_grx = (GRegex *) dks_rx_once.retval;
625 if (g_regex_match (mdvd_grx, match_str, 0, NULL) == TRUE) {
626 GST_LOG ("MicroDVD (frame based) format detected");
627 return GST_SUB_PARSE_FORMAT_MDVDSUB;
629 if (g_regex_match (subrip_grx, match_str, 0, NULL) == TRUE) {
630 GST_LOG ("SubRip (time based) format detected");
631 return GST_SUB_PARSE_FORMAT_SUBRIP;
633 if (g_regex_match (dks_grx, match_str, 0, NULL) == TRUE) {
634 GST_LOG ("DKS (time based) format detected");
635 return GST_SUB_PARSE_FORMAT_DKS;
638 if (!strncmp (match_str, "FORMAT=TIME", 11)) {
639 GST_LOG ("MPSub (time based) format detected");
640 return GST_SUB_PARSE_FORMAT_MPSUB;
642 if (strstr (match_str, "<SAMI>") != NULL ||
643 strstr (match_str, "<sami>") != NULL) {
644 GST_LOG ("SAMI (time based) format detected");
645 return GST_SUB_PARSE_FORMAT_SAMI;
647 /* we're boldly assuming the first subtitle appears within the first hour */
648 if (sscanf (match_str, "0:%02u:%02u:", &n1, &n2) == 2 ||
649 sscanf (match_str, "0:%02u:%02u=", &n1, &n2) == 2 ||
650 sscanf (match_str, "00:%02u:%02u:", &n1, &n2) == 2 ||
651 sscanf (match_str, "00:%02u:%02u=", &n1, &n2) == 2 ||
652 sscanf (match_str, "00:%02u:%02u,%u=", &n1, &n2, &n3) == 3) {
653 GST_LOG ("TMPlayer (time based) format detected");
654 return GST_SUB_PARSE_FORMAT_TMPLAYER;
656 if (sscanf (match_str, "[%u][%u]", &n1, &n2) == 2) {
657 GST_LOG ("MPL2 (time based) format detected");
658 return GST_SUB_PARSE_FORMAT_MPL2;
660 if (strstr (match_str, "[INFORMATION]") != NULL) {
661 GST_LOG ("SubViewer (time based) format detected");
662 return GST_SUB_PARSE_FORMAT_SUBVIEWER;
664 if (strstr (match_str, "{QTtext}") != NULL) {
665 GST_LOG ("QTtext (time based) format detected");
666 return GST_SUB_PARSE_FORMAT_QTTEXT;
669 GST_WARNING ("no subtitle format detected");
670 return GST_SUB_PARSE_FORMAT_UNKNOWN;
673 /*checking the type of subtitle*/
675 gst_submux_format_autodetect (Gstsubmux *self)
678 GstSubMuxFormat format;
680 if (self->priv->is_internal) {
681 GST_DEBUG_OBJECT (self, "File is of internal type");
684 line = gst_submux_extract_data (self);
687 if (strlen (line) < 30) {
688 GST_WARNING_OBJECT (self, "File too small to be a subtitles file");
693 data = g_strndup (line, 35);
694 format = gst_submux_data_format_autodetect (data);
697 self->priv->parser_type = format;
703 /*to validate the number of languages in case of sami files*/
705 gst_calculate_number_languages(Gstsubmux *self) {
711 gchar * name_temp = NULL;
714 GST_DEBUG_OBJECT (self, "Entering in language number");
716 if ((self->priv->parser_type != GST_SUB_PARSE_FORMAT_SAMI) || self->priv->is_internal)
719 text = gst_submux_extract_data (self);
720 start = g_strstr_len (text, strlen (text), "!--");
722 GST_ERROR_OBJECT (self, "Could not found the language start code in smi file");
723 return gst_create_own_language_list(self);
725 end = g_strstr_len (start, strlen (start), "-->");
727 GST_ERROR_OBJECT (self, "Could not found the language end code in smi file");
734 found = (gchar*)strcasestr (found, "lang");
743 return gst_create_own_language_list(self);
746 for (i = 0; i < count; i++) {
747 gchar *attr_name = NULL, *attr_value = NULL;
748 GstLangStruct *new = NULL;
750 start = (gchar*)strcasestr (start, "lang:");
751 attr_value = (gchar*)malloc (3);
753 GST_ERROR_OBJECT (self, "memory could not be allocated through malloc call");
757 strncpy (attr_value, start, 2);
758 attr_value[2] = '\0';
759 GST_DEBUG_OBJECT (self, "Language value comes as %s", attr_value);
762 if (*name_temp == '{') {
763 int character_count = 0;
767 if (*name_temp == '.') {
768 attr_name = (gchar*) malloc (character_count + 1);
770 } else if (*name_temp != ' ')
778 GST_ERROR_OBJECT (self, "Could not find the languages field in the file");
783 for (j = 0; *(name_temp + j) != ' '; j++) {
784 attr_name[j] = *(name_temp + j);
787 new = g_new0 (GstLangStruct, 1);
788 new->language_code = (gchar*) malloc (strlen (attr_value) + 1);
789 if (new->language_code && attr_value)
790 strcpy (new->language_code, attr_value);
791 new->language_key = (gchar*) malloc (strlen (attr_name) + 1);
792 if (new->language_key && attr_name)
793 strcpy (new->language_key, attr_name);
796 self->priv->lang_list = g_list_append (self->priv->lang_list, new);
805 /*to initialize stream*/
806 static gboolean gst_submux_stream_init(GstSubmuxStream * stream)
808 stream->duration = 0;
809 stream->need_segment = TRUE;
810 stream->flushing = FALSE;
811 stream->eos_sent = FALSE;
812 stream->eos_came = FALSE;
813 stream->discont_came = FALSE;
815 stream->last_ts = -1;
816 stream->queue = g_queue_new ();
817 stream->queue_empty = g_cond_new ();
818 stream->queue_lock = g_mutex_new ();
819 stream->flush_done = FALSE;
823 /*to create pipelines according to internal and external subtitle*/
824 gboolean gst_submux_create_pipelines(Gstsubmux *self, GstPad * pad)
827 GstStateChangeReturn ret;
828 GstSubmuxStream *new_stream;
831 if (!self->priv->is_internal) {
832 GstLangStruct *cur_language=NULL;
834 GST_DEBUG_OBJECT (self, "creating the pipeline for external pipeline");
835 if (self->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
836 if (!self->priv->lang_list) {
837 GST_ERROR_OBJECT(self, "failed to get the lang list");
840 length = g_list_length (self->priv->lang_list);
845 GST_DEBUG_OBJECT (self, "number of tentative languages present are %d", length);
847 for (i = 0; i < length; i++) {
848 new_stream = g_new0 (GstSubmuxStream, 1);
849 if (!gst_submux_stream_init(new_stream)) {
850 GST_ERROR_OBJECT (self, "stream init is failed");
853 GST_DEBUG_OBJECT (self, "stream init has been done for stream[%d]", i);
855 new_stream->pipe_struc.pipe = gst_pipeline_new ("subtitle-pipeline");
856 if (!new_stream->pipe_struc.pipe) {
857 GST_ERROR_OBJECT (self, "failed to create pipeline");
860 GST_DEBUG_OBJECT (self, "creating appsrc");
862 /* creating source element */
863 new_stream->pipe_struc.appsrc = gst_element_factory_make ("appsrc", "pipe_appsrc");
864 if (!new_stream->pipe_struc.appsrc) {
865 GST_ERROR_OBJECT (self, "failed to create appsrc");
869 g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "block", 1, NULL);
870 g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "max-bytes", (guint64)1, NULL);
871 /* create sink element */
872 new_stream->pipe_struc.appsink = gst_element_factory_make ("appsink", "pipe_appsink");
873 if (!new_stream->pipe_struc.appsink) {
874 GST_ERROR_OBJECT (self, "failed to create appsink");
877 g_object_set (G_OBJECT (new_stream->pipe_struc.appsink), "sync", FALSE, "emit-signals", TRUE, NULL);
878 g_object_set(G_OBJECT (new_stream->pipe_struc.appsrc),"emit-signals", TRUE, NULL);
881 /* create parsing element */
882 new_stream->pipe_struc.parser = gst_element_factory_make("subparse","pipe_parser");
883 if (!new_stream->pipe_struc.parser) {
884 GST_ERROR_OBJECT (self, "failed to create parser");
887 if (self->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
888 cur_language = g_list_nth_data(self->priv->lang_list, i);
889 g_object_set (G_OBJECT (new_stream->pipe_struc.parser), "current-language",
890 cur_language->language_key, NULL);
892 GST_DEBUG_OBJECT (self, "value of subtitle-encoding is %s", self->encoding);
893 g_object_set (G_OBJECT (new_stream->pipe_struc.parser), "subtitle-encoding", self->encoding, NULL);
894 g_object_set (G_OBJECT (new_stream->pipe_struc.appsrc), "stream-type",0,"format",GST_FORMAT_TIME, NULL);
895 gst_bin_add_many (GST_BIN ( new_stream->pipe_struc.pipe), new_stream->pipe_struc.appsink, new_stream->pipe_struc.appsrc,new_stream->pipe_struc.parser, NULL);
896 if (!gst_element_link_many (new_stream->pipe_struc.appsrc, new_stream->pipe_struc.parser,new_stream->pipe_struc.appsink, NULL)) {
897 GST_ERROR_OBJECT (self, "failed to link elements");
901 GST_DEBUG_OBJECT (self, "reached here and linking successful");
903 ret = gst_element_set_state (new_stream->pipe_struc.pipe, GST_STATE_PLAYING);
904 if (ret == GST_STATE_CHANGE_FAILURE) {
905 GST_ERROR_OBJECT (self, "set_state failed...");
908 GST_DEBUG_OBJECT (self, "state has been changed succesfully");
909 self->streams = g_list_append(self->streams, new_stream);
910 self->priv->stream_count++;
911 g_signal_connect (new_stream->pipe_struc.appsink, "new-buffer", G_CALLBACK (gst_submux_on_new_buffer), g_list_nth_data(self->streams,i) );
914 length = self->sinkpads_count;
915 for (i = 0; i < length; i++) {
916 new_stream = g_new0 (GstSubmuxStream, 1);
917 if (!gst_submux_stream_init (new_stream)) {
918 GST_ERROR_OBJECT (self, "stream init is failed");
922 self->streams=g_list_append(self->streams,new_stream);
923 self->priv->stream_count++;
925 self->pipeline_made = TRUE;
928 self->cur_buf_array = g_malloc0 (self->priv->stream_count * (sizeof (GstBuffer *)));
929 if (!self->cur_buf_array) {
930 GST_ERROR_OBJECT (self, "failed to allocate memory..");
936 /* call back on recieving the new buffer in appsink pad */
938 gst_submux_on_new_buffer (GstElement *appsink, void *data)
940 GstSubmuxStream *stream = (GstSubmuxStream *)data;
941 GstBuffer *inbuf = NULL;
944 GST_WARNING("Stream not available...");
947 g_mutex_lock (stream->queue_lock);
948 inbuf = gst_app_sink_pull_buffer ((GstAppSink *)appsink);
950 GST_WARNING_OBJECT (stream, "Input buffer not available...");
951 g_mutex_unlock (stream->queue_lock);
954 if(stream->eos_ts == -1) {
955 if (!strcmp ((const char*)GST_BUFFER_DATA (inbuf), "eos") && GST_BUFFER_FLAG_IS_SET(inbuf,GST_BUFFER_FLAG_GAP)){
956 stream->eos_ts = stream->last_ts;
957 if (stream->eos_ts <= stream->seek_ts) {
958 g_queue_push_tail (stream->queue, inbuf);
959 g_cond_signal (stream->queue_empty);
960 g_mutex_unlock (stream->queue_lock);
961 GST_INFO_OBJECT (stream, "signaling queue empty signal as we are seeking beyond last subtitle");
964 gst_buffer_unref(inbuf);
966 stream->last_ts = GST_BUFFER_DURATION(inbuf) + GST_BUFFER_TIMESTAMP(inbuf);
968 } else if (stream->eos_ts <= stream->seek_ts) {
969 gst_buffer_unref(inbuf);
970 GstBuffer *buf = gst_buffer_new_and_alloc (3 + 1);
971 GST_DEBUG_OBJECT(stream, "sending EOS buffer to chain\n");
972 GST_DEBUG_OBJECT (stream, "EOS. Pushing remaining text (if any)");
973 GST_BUFFER_DATA (buf)[0] = 'e';
974 GST_BUFFER_DATA (buf)[1] = 'o';
975 GST_BUFFER_DATA (buf)[2] = 's';
976 GST_BUFFER_DATA (buf)[3] = '\0';
978 GST_BUFFER_SIZE (buf) = 3;
979 GST_BUFFER_FLAG_SET(buf,GST_BUFFER_FLAG_GAP);
980 g_queue_push_tail (stream->queue, buf);
981 g_cond_signal (stream->queue_empty);
982 g_mutex_unlock (stream->queue_lock);
983 GST_INFO_OBJECT (stream,"signaling queue empty signal as we are seeking beyond last subtitle");
986 if (!stream->discont_came) {
987 stream->discont_came = GST_BUFFER_IS_DISCONT (inbuf);
988 if (stream->discont_came) {
989 GST_DEBUG_OBJECT (stream, "first buffer with discont on new_buffer for stream with ts = %"
990 GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)),
991 GST_TIME_ARGS(GST_BUFFER_DURATION(inbuf)));
995 if (!stream->discont_came) {
996 GST_DEBUG_OBJECT (stream, "rejecting the buffer in appsink on new_buffer for stream with ts = %"
997 GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(inbuf)),
998 GST_TIME_ARGS(GST_BUFFER_DURATION(inbuf)));
999 gst_buffer_unref(inbuf);
1000 g_mutex_unlock (stream->queue_lock);
1003 g_queue_push_tail (stream->queue, inbuf);
1004 g_cond_signal (stream->queue_empty);
1005 g_mutex_unlock (stream->queue_lock);
1006 GST_DEBUG_OBJECT (stream, "signaling queue empty signal");
1011 convert_to_utf8 (const gchar * str, gsize len, const gchar * encoding,
1012 gsize * consumed, GError ** err, Gstsubmux * self)
1016 /* The char cast is necessary in glib < 2.24 */
1018 g_convert_with_fallback (str, len, "UTF-8", encoding, (char *) "*",
1019 consumed, NULL, err);
1023 GST_DEBUG_OBJECT (self, "g_convert_with_fallback returns NULL");
1027 /* + 3 to skip UTF-8 BOM if it was added */
1029 if (len >= 3 && (guint8) ret[0] == 0xEF && (guint8) ret[1] == 0xBB
1030 && (guint8) ret[2] == 0xBF)
1031 g_memmove (ret, ret + 3, len + 1 - 3);
1037 detect_encoding (const gchar * str, gsize len)
1039 if (len >= 3 && (guint8) str[0] == 0xEF && (guint8) str[1] == 0xBB
1040 && (guint8) str[2] == 0xBF)
1041 return g_strdup ("UTF-8");
1043 if (len >= 2 && (guint8) str[0] == 0xFE && (guint8) str[1] == 0xFF)
1044 return g_strdup ("UTF-16BE");
1046 if (len >= 2 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE)
1047 return g_strdup ("UTF-16LE");
1049 if (len >= 4 && (guint8) str[0] == 0x00 && (guint8) str[1] == 0x00
1050 && (guint8) str[2] == 0xFE && (guint8) str[3] == 0xFF)
1051 return g_strdup ("UTF-32BE");
1053 if (len >= 4 && (guint8) str[0] == 0xFF && (guint8) str[1] == 0xFE
1054 && (guint8) str[2] == 0x00 && (guint8) str[3] == 0x00)
1055 return g_strdup ("UTF-32LE");
1060 /* If language list is not present in smi file, check the body and create our own list */
1062 gst_create_own_language_list (Gstsubmux *self)
1064 gchar * file_path_type = NULL;
1065 gchar * temp_path = NULL;
1066 gchar * file_path = NULL;
1067 gsize * consumed = NULL;
1070 GstPad *sinkpad = (GstPad *) g_list_nth_data(self->sinkpad, 0);
1072 GstStructure *structure;
1073 const GValue *value;
1074 gchar* langkey[MAX_LANG];
1075 gint langKey_length[MAX_LANG];
1078 structure = gst_structure_new ("FileSrcURI", "file-uri", G_TYPE_STRING, NULL, NULL);
1080 cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
1082 if (!gst_pad_peer_query (sinkpad, cquery)) {
1083 GST_ERROR_OBJECT (self, "Failed to query SMI file path");
1084 gst_query_unref (cquery);
1087 structure = gst_query_get_structure (cquery);
1088 value = gst_structure_get_value (structure, "file-uri");
1089 file_path = g_strdup (g_value_get_string (value));
1091 if (file_path == NULL){
1092 GST_ERROR_OBJECT (self, "Could not parse the SMI file path");
1093 gst_query_unref (cquery);
1096 gst_query_unref (cquery);
1097 temp_path = file_path;
1098 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1100 file_path_type = g_strndup ((gchar *) file_path, 4);
1101 GST_INFO_OBJECT (self, "Received file path by query = %s, %s", file_path, file_path_type);
1102 if (!g_strcmp0(file_path_type, "file")){
1104 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1106 fp = fopen (file_path, "r");
1108 GST_ERROR_OBJECT (self, "Failed to open file");
1110 g_free(file_path_type);
1114 for( i=0;i<MAX_LANG;i++){
1116 langKey_length[i]=0;
1118 gboolean lang_found= FALSE;
1119 while (!feof (fp) ){
1121 guint charCount = 0;
1122 gboolean conversion = TRUE;
1123 gchar *result = NULL;
1124 gchar *con_temp = NULL;
1125 gchar *delimiter = NULL;
1127 guint keyLength = 0;
1129 charCount = fread (line, sizeof(char), 1024, fp);
1130 line[charCount] = '\0';
1132 GST_WARNING_OBJECT (self, "fread returned zero bytes");
1135 GST_DEBUG_OBJECT (self, " Read charCount %d bytes Successfully",charCount);
1136 GST_DEBUG_OBJECT (self, "value of detected encoding is %s and self encoding is %s",
1137 self->detected_encoding,self->encoding);
1138 if (self->detected_encoding && strcmp (self->detected_encoding, "UTF-8") && conversion){
1139 result = convert_to_utf8 (line, charCount, self->detected_encoding, consumed, &err, self);
1140 GST_DEBUG_OBJECT (self, " Converted convert_to_utf8 result %d ",result);
1142 if (result == NULL) {
1150 gchar* tempKey = NULL;
1153 con_temp = strcasestr(con_temp,"class=");
1155 delimiter = strcasestr(con_temp, ">");
1156 GST_DEBUG_OBJECT (self, " Delimiter ...Entering if %s",con_temp);
1157 if (con_temp && (delimiter!=NULL)){
1158 gchar* tempChar = con_temp + 6;
1159 GST_DEBUG_OBJECT (self, "Has class= reading string %s",tempChar);
1160 GST_DEBUG_OBJECT (self, "Has class= ");
1161 while (*tempChar != '>'){
1164 GST_DEBUG_OBJECT (self, " keyLength %d tempChar %c",keyLength,*tempChar);
1166 GST_DEBUG_OBJECT (self, " keyLength %d",keyLength);
1167 tempChar -= keyLength;
1168 tempKey = (gchar*) g_malloc (keyLength + 1);
1170 GST_DEBUG_OBJECT (self, "Error 1");
1173 gchar* temp1 =tempKey;
1174 GST_DEBUG_OBJECT (self, "tempChar %s keyLength %d",tempChar,keyLength);
1175 while (*tempChar != '>'){
1176 *tempKey = *tempChar;
1181 tempKey[keyLength]='\0';
1182 GST_DEBUG_OBJECT (self, "tempKey %s keyLength %d keyCount %d",tempKey,keyLength,keyCount);
1184 for (k = 0; k < keyCount; k++){
1186 if (!strcasecmp (tempKey,langkey[k]))
1188 GST_DEBUG_OBJECT (self, "Has the key already so breaking..Entry %d tempKey %s langkey[i] %s ",k,tempKey,langkey[k]);
1194 if(lang_found == FALSE){
1195 langkey[keyCount] = (gchar*) g_malloc (keyLength);
1196 if(! langkey[keyCount])
1198 strcpy(langkey[keyCount],tempKey);
1199 langKey_length[keyCount]=keyLength;
1214 GST_DEBUG_OBJECT (self, " ..increment con_temp %s",con_temp);
1217 GST_DEBUG_OBJECT (self, " At end keyCount no of langs is %d ",keyCount);
1218 for(i=0;i<keyCount;i++) {
1220 GstLangStruct *new = g_new0 (GstLangStruct, 1);
1221 GST_DEBUG_OBJECT (self, "Adding ign case to the langKey keyCount %d and lang %s ",i, langkey[i]);
1222 new->language_code = (gchar*)malloc (3);
1223 if(!(new->language_code)){
1224 GST_DEBUG_OBJECT (self, " .Error 2");
1227 gchar *attr_val=new->language_code ;
1228 strcpy (attr_val, "un");
1231 new->language_key = (gchar*) malloc (langKey_length[i] + 1);
1232 if(!(new->language_key)){
1233 GST_DEBUG_OBJECT (self, " ..Error 3");
1236 strcpy (new->language_key, langkey[i]);
1237 self->priv->lang_list = g_list_append (self->priv->lang_list, new);
1238 GST_DEBUG_OBJECT (self, " new...Successfull");
1244 g_free(file_path_type);
1249 GST_DEBUG_OBJECT (self, " In Error");
1252 g_free(file_path_type);
1259 validate_langlist_body(GList * lang_list, Gstsubmux * self){
1260 gchar * file_path_type = NULL;
1261 gchar * file_path = NULL;
1264 guint i = 0, found_count = 0,k = 0;
1265 const guint list_len = g_list_length(lang_list);
1266 gboolean counter[MAX_LANGUAGE];
1267 GstPad *sinkpad = NULL;
1270 gchar *language_code;
1271 gchar *language_key;
1273 sinkpad = (GstPad *) g_list_nth_data(self->sinkpad, 0);
1275 GstStructure *structure;
1276 const GValue *value;
1277 structure = gst_structure_new ("FileSrcURI", "file-uri", G_TYPE_STRING, NULL, NULL);
1279 cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
1281 if (!gst_pad_peer_query (sinkpad, cquery)) {
1282 GST_ERROR_OBJECT (self, "Failed to query SMI file path");
1283 gst_query_unref (cquery);
1286 structure = gst_query_get_structure (cquery);
1287 value = gst_structure_get_value (structure, "file-uri");
1288 file_path = g_strdup (g_value_get_string (value));
1290 if (file_path == NULL){
1291 GST_ERROR_OBJECT (self, "Could not parse the SMI file path");
1292 gst_query_unref (cquery);
1296 if (self->external_filepath == NULL) {
1297 self->external_filepath = file_path;
1300 if (!g_strcmp0 (file_path, self->external_filepath)) {
1301 GST_INFO_OBJECT (self, "Same external file URI, no need to parse again");
1302 gst_query_unref (cquery);
1307 g_free (self->external_filepath);
1308 self->external_filepath = NULL;
1309 self->external_filepath = file_path;
1313 gst_query_unref (cquery);
1314 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1316 file_path_type = g_strndup ((gchar *) file_path, 4);
1317 GST_INFO_OBJECT (self, "Received file path by query = %s, %s", file_path, file_path_type);
1318 if (!g_strcmp0(file_path_type, "file")){
1320 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1322 fp = fopen (file_path, "r");
1324 GST_ERROR_OBJECT (self, "Failed to open file");
1325 g_free(file_path_type);
1329 for (i = 0; i < list_len; i++){
1333 while (!feof (fp) && found_count < list_len){
1335 gsize * consumed = NULL;
1337 guint charCount = 0;
1338 gchar* result = NULL;
1340 gchar* temp_lang = NULL;
1341 gchar* con_temp_end = NULL;
1342 gchar* con_temp_start = NULL;
1343 gchar* new_key = NULL;
1344 gint new_key_length = 0;
1345 gboolean new_key_found = FALSE;
1346 gchar * temp1 = NULL;
1347 gchar *con_temp_lang = NULL;
1348 gchar *con_temp = NULL;
1349 gboolean conversion = TRUE;
1350 charCount = fread (line, sizeof(char), 1024, fp);
1351 line[charCount] = '\0';
1353 GST_WARNING_OBJECT (self, "fread returned zero bytes");
1357 GST_DEBUG_OBJECT (self, "value of detected encoding is %s and self encoding is %s",
1358 self->detected_encoding,self->encoding);
1359 if (self->detected_encoding && strcmp (self->detected_encoding, "UTF-8") && conversion){
1360 result = convert_to_utf8 (line, charCount, self->detected_encoding, consumed, &err, self);
1362 if (result == NULL) {
1366 con_temp = g_utf8_strdown (result, strlen (result));
1369 con_temp = g_strstr_len(con_temp, strlen (con_temp), "class=");
1371 temp1 = g_strstr_len(con_temp+1, strlen (con_temp), "class=");
1373 if (temp1 && con_temp){
1374 gap = strlen (con_temp) - strlen (temp1);
1375 } else if (con_temp) {
1376 gap = strlen (con_temp);
1381 for (i = 0; i < list_len; i++){
1382 if (counter[i] == TRUE) {
1383 con_temp = con_temp + 1;
1386 lang = (struct LangStruct *) g_list_nth_data (lang_list, i);
1388 temp_lang = (gchar*)g_malloc (strlen (lang->language_key) + 1);
1389 strcpy (temp_lang, lang->language_key);
1390 con_temp_lang = g_utf8_strdown (temp_lang, strlen (temp_lang));
1391 if (g_strstr_len (con_temp, gap, con_temp_lang)) {
1394 GST_INFO_OBJECT (self, "Valid Language in list : [%s]", lang->language_key);
1395 con_temp = con_temp + 1;
1397 /* Fix Me: Cases where there is no body for a specific language
1398 * inside a single language .smi file */
1401 con_temp_start = con_temp;
1402 con_temp_end = con_temp;
1403 while(con_temp_end) {
1404 if(*con_temp_end == '=') {
1405 con_temp_start = con_temp_end+1;
1407 }else if(*con_temp_end == '>') {
1408 con_temp_end = con_temp_end;
1409 new_key_found = TRUE;
1413 new_key_found = FALSE;
1417 new_key_length = strlen(con_temp_start)-strlen(con_temp_end);
1418 new_key = g_malloc(new_key_length +1);
1419 for(k=0;k<new_key_length;k++){
1420 *(new_key+k)=*(con_temp_start+k);
1422 *(new_key+new_key_length)='\0';
1423 GST_INFO("new lang key is %s",lang->language_key);
1427 con_temp = con_temp + 1;
1432 g_free (con_temp_lang);
1443 if (found_count < list_len) {
1444 for (i = 0; i < list_len; i++) {
1445 if (counter[i] == FALSE)
1446 lang_list = g_list_delete_link (lang_list, g_list_nth (lang_list, i));
1450 GST_ERROR_OBJECT (self, "File is not a local file");
1451 g_free(file_path_type);
1455 g_free(file_path_type);
1461 ** Description : Chain function used to push the subtitle buffer to internal pipelines of submux element
1462 ** Params : (1) sink pad on which buffer is arriving (2) the buffer itself
1463 ** return : GST_FLOW_OK on successfully pushing subtitle buffer to next element
1466 static GstFlowReturn
1467 gst_submux_chain(GstPad *pad, GstBuffer *buffer)
1471 GstPad *checkpad = NULL;
1472 Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
1473 gboolean ret = FALSE;
1474 GstFlowReturn fret = GST_FLOW_ERROR;
1475 GstSubmuxStream *stream = NULL;
1476 GstMessage *m = NULL;
1478 if (GST_BUFFER_IS_DISCONT (buffer))
1479 GST_DEBUG_OBJECT(submux, "Discont buffer came in chain function");
1480 GST_DEBUG_OBJECT (submux, "^^^^^entering in chain^^^^^^");
1481 if (!submux->priv->is_internal) {
1482 if (!submux->priv->first_buffer) {
1483 submux->detected_encoding = detect_encoding ((gchar*)GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1485 if (!submux->langlist_msg_posted && submux->priv->lang_list) {
1486 if (!validate_langlist_body (submux->priv->lang_list, submux)){
1487 GST_WARNING_OBJECT(submux, "Error occured while validating language list. Posting without validation");
1489 if (submux->priv->lang_list) {
1490 GList* temp_list_to_post = NULL;
1491 temp_list_to_post = g_list_copy (submux->priv->lang_list);
1492 m = gst_message_new_element (GST_OBJECT_CAST (submux), gst_structure_new("Ext_Sub_Language_List",
1493 "lang_list", G_TYPE_POINTER, temp_list_to_post, NULL));
1495 gst_element_post_message (GST_ELEMENT_CAST (submux), m);
1496 submux->langlist_msg_posted = TRUE;
1498 GST_DEBUG_OBJECT (submux, "LANGLIST POSTED");
1500 if (submux->need_segment) {
1501 ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
1502 submux->segment.format, submux->segment.start, submux->segment.stop,
1503 submux->segment.time));
1504 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1506 GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
1507 return GST_FLOW_ERROR;
1509 GST_DEBUG_OBJECT (submux, "Starting the loop again");
1510 if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
1511 GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
1512 GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
1513 return GST_FLOW_ERROR;
1515 submux->need_segment = FALSE;
1517 GST_DEBUG_OBJECT (submux, "before pushing buffer to each apprsrc");
1518 if (!submux->priv->lang_list && submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
1519 GST_ERROR_OBJECT (submux, "lang list is not there");
1520 return GST_FLOW_ERROR;
1522 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI)
1523 length = g_list_length (submux->priv->lang_list);
1527 for (i = 0; i < length; i++) {
1528 stream = g_list_nth_data(submux->streams, i);
1529 if ((submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) && !submux->priv->first_buffer){
1530 GstLangStruct *lang = g_list_nth_data(submux->priv->lang_list, i);
1531 if(submux->msl_streams){
1532 GstLangStruct *lang1 = g_list_nth_data(submux->msl_streams, i);
1533 lang->active = lang1->active;
1536 lang->active = TRUE;
1538 lang->active = FALSE;
1541 GST_DEBUG_OBJECT (submux, "making stream need segment false");
1542 stream->need_segment = FALSE;
1545 for (i = 0; i < length; i++) {
1546 stream = g_list_nth_data(submux->streams, i);
1548 GST_ERROR_OBJECT (submux, "stream not found");
1549 return GST_FLOW_ERROR;
1552 if (!stream->pipe_struc.appsrc) {
1553 GST_ERROR_OBJECT (submux, "appsrc not found");
1554 return GST_FLOW_ERROR;
1556 if (i < (length - 1))
1557 gst_buffer_ref (buffer);
1559 fret = gst_app_src_push_buffer ((GstAppSrc*)stream->pipe_struc.appsrc, buffer);
1561 if (fret != GST_FLOW_OK) {
1562 GST_ERROR_OBJECT (submux, "push buffer failed with fret is %d", fret);
1565 GST_DEBUG_OBJECT (submux, "pad_push successfull to appsrc %p buffer", buffer);
1568 length = submux->sinkpads_count;
1569 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
1570 if (checkpad == pad) {
1571 if (submux->need_segment) {
1572 ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
1573 submux->segment.format, submux->segment.start, submux->segment.stop,
1574 submux->segment.time));
1575 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1577 GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
1578 return GST_FLOW_ERROR;
1580 GST_DEBUG_OBJECT (submux, "Starting the loop again");
1581 if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
1582 GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
1583 GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
1584 return GST_FLOW_ERROR;
1586 submux->need_segment = FALSE;
1589 for (i = 0; i < length; i++) {
1590 checkpad = (GstPad *) g_list_nth_data(submux->sinkpad, i);
1591 if (checkpad == pad) {
1592 stream = g_list_nth_data (submux->streams, i);
1594 GST_ERROR_OBJECT (submux, "Stream not available...");
1595 return GST_FLOW_ERROR;
1597 if (stream->flushing){
1598 GST_DEBUG_OBJECT (submux, "flushing going on in appsink");
1599 return GST_FLOW_OK ;
1602 g_mutex_lock (stream->queue_lock);
1603 g_queue_push_tail (stream->queue, buffer);
1604 g_cond_signal (stream->queue_empty);
1605 g_mutex_unlock (stream->queue_lock);
1607 GST_DEBUG_OBJECT (submux, "push buffer success to appsrc with fret is %d for stream[%d]", fret, i);
1613 if (!submux->priv->first_buffer) {
1614 GST_DEBUG_OBJECT (submux, "got the first buffer");
1615 submux->priv->first_buffer = TRUE;
1616 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
1620 GST_DEBUG_OBJECT (submux, "^^^^^exiting in chain^^^^^^");
1626 gst_submux_stream_deinit (GstSubmuxStream *stream,Gstsubmux *submux)
1628 GstBuffer *buf = NULL;
1631 if (stream->queue) {
1632 while (!g_queue_is_empty (stream->queue)) {
1633 buf = g_queue_pop_head (stream->queue);
1634 gst_buffer_unref (buf);
1637 g_queue_free (stream->queue);
1638 stream->queue = NULL;
1641 if (stream->pipe_struc.pipe) {
1642 gst_element_set_state (stream->pipe_struc.pipe, GST_STATE_NULL);
1643 gst_element_get_state (stream->pipe_struc.pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
1644 gst_object_unref(GST_OBJECT(stream->pipe_struc.appsrc));
1645 gst_object_unref(GST_OBJECT(stream->pipe_struc.appsink));
1646 gst_object_unref (stream->pipe_struc.pipe);
1649 if (stream->queue_lock) {
1650 g_cond_broadcast(stream->queue_empty);
1651 g_mutex_free (stream->queue_lock);
1652 stream->queue_lock = NULL;
1655 if (stream->queue_empty) {
1656 g_cond_free (stream->queue_empty);
1657 stream->queue_empty= NULL;
1661 GST_DEBUG_OBJECT (submux, "stream deinit completed");
1665 /* releasing the requested pad */
1667 gst_submux_release_pad (GstElement * element, GstPad * pad)
1669 Gstsubmux *submux = GST_SUBMUX_CAST (element);
1673 GST_INFO_OBJECT(element,"entering in the release pad");
1674 length = g_list_length(submux->sinkpad);
1675 GST_DEBUG_OBJECT (element, "Releasing %s:%s", GST_DEBUG_PAD_NAME (pad));
1677 for (i=1;i<=length;i++) {
1678 check_pad = (struct GstPad *) g_list_nth_data(submux->sinkpad,i);
1679 if (check_pad == pad) {
1680 /* this is it, remove */
1681 submux->sinkpad = g_list_remove_link (submux->sinkpad, pad);
1682 gst_element_remove_pad (element, pad);
1688 /* request new pad */
1690 gst_submux_request_new_pad (GstElement * element,
1691 GstPadTemplate * templ, const gchar * req_name)
1693 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1694 Gstsubmux *submux = GST_SUBMUX_CAST (element);
1695 GstPad *newpad = NULL;
1698 if (templ->direction != GST_PAD_SINK) {
1699 GST_ERROR_OBJECT (submux, "templ direction is not sinkpad, returning from here");
1700 goto wrong_direction;
1703 if (templ == gst_element_class_get_pad_template (klass, "sink%d")) {
1704 name = g_strdup_printf ("sink%d", submux->sinkpads_count++);
1707 GST_DEBUG_OBJECT (submux, "Requested pad: %s", name);
1708 newpad = (GstPad*)g_new0 (GstPad*, 1);
1709 /* create pad and add to collections */
1710 newpad = gst_pad_new_from_template (templ, name);
1712 if(!submux->priv->is_internal) {
1713 submux->external_sinkpad = TRUE;
1715 submux->sinkpad = g_list_append (submux->sinkpad, newpad);
1718 /* set up pad functions */
1719 gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_setcaps));
1720 gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_handle_sink_event));
1721 gst_pad_set_chain_function(newpad, GST_DEBUG_FUNCPTR (gst_submux_chain));
1722 gst_pad_set_active (newpad, TRUE);
1723 gst_element_add_pad (element, newpad);
1729 GST_WARNING_OBJECT (submux, "Request pad that is not a SINK pad.");
1734 gst_submux_handle_src_event (GstPad * pad, GstEvent * event)
1736 Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
1737 gboolean ret = FALSE;
1741 GstSubmuxStream *cur_stream = NULL;
1743 GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
1744 length = g_list_length(submux->streams);
1746 switch (GST_EVENT_TYPE (event)) {
1747 /* this event indicates speed change or seek */
1748 case GST_EVENT_SEEK: {
1750 GstSeekType start_type, stop_type;
1753 GstPad *sinkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
1754 gst_event_parse_seek (event, &rate, &format, &submux->segment_flags,
1755 &start_type, &start, &stop_type, &stop);
1756 gst_segment_set_seek (&submux->segment, rate, format, submux->segment_flags,
1757 start_type, start, stop_type, stop, &update);
1758 if (submux->priv->is_internal || submux->priv->parser_type != GST_SUB_PARSE_FORMAT_SAMI) {
1759 length = g_list_length (submux->sinkpad);
1761 length = g_list_length(submux->streams);
1763 if (!submux->priv->is_internal) {
1764 ret = gst_pad_push_event (sinkpad, gst_event_new_seek (rate, GST_FORMAT_BYTES, submux->segment_flags,
1765 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, 0));
1766 gst_event_unref (event);
1768 GST_DEBUG_OBJECT (submux, "handling seek in case of internal");
1769 ret = gst_pad_event_default (pad, event);
1773 GST_ERROR_OBJECT (submux, "sending seek event to sink pad failed");
1776 GST_DEBUG_OBJECT (submux, "sending seek event to sink pad passed");
1782 ret = gst_pad_event_default (pad, event);
1791 gst_submux_handle_sink_event (GstPad * pad, GstEvent * event)
1793 Gstsubmux *submux = GST_SUBMUX (GST_PAD_PARENT (pad));
1794 gboolean ret = FALSE;
1796 GstBuffer *buf = NULL;
1797 GstPad *checkpad = NULL;
1799 GstSubmuxStream *cur_stream = NULL;
1801 GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
1803 switch (GST_EVENT_TYPE (event)) {
1804 case GST_EVENT_EOS:{
1805 length = g_list_length (submux->sinkpad);
1806 GST_OBJECT_LOCK (submux);
1807 for (i = 0; i < length; i++) {
1808 GST_DEBUG_OBJECT(submux, "inside the handling of EOS event");
1809 cur_stream = g_list_nth_data(submux->streams,i);
1810 if (!cur_stream->eos_sent) {
1811 GstBuffer *buf = gst_buffer_new_and_alloc (3 + 1);
1812 GST_DEBUG_OBJECT(submux, "sending EOS buffer to chain\n");
1813 GST_DEBUG_OBJECT (submux, "EOS. Pushing remaining text (if any)");
1814 GST_BUFFER_DATA (buf)[0] = 'e';
1815 GST_BUFFER_DATA (buf)[1] = 'o';
1816 GST_BUFFER_DATA (buf)[2] = 's';
1817 GST_BUFFER_DATA (buf)[3] = '\0';
1819 GST_BUFFER_SIZE (buf) = 3;
1820 GST_BUFFER_FLAG_SET(buf,GST_BUFFER_FLAG_GAP);
1821 gst_submux_chain (g_list_nth_data(submux->sinkpad,i), buf);//
1822 cur_stream->eos_sent = TRUE;
1825 GST_OBJECT_UNLOCK (submux);
1826 gst_event_unref(event);
1830 case GST_EVENT_NEWSEGMENT: {
1833 gint64 start, stop, time;
1835 GST_OBJECT_LOCK (submux);
1836 if (!submux->pipeline_made) {
1837 if (!gst_submux_format_autodetect (submux)) {
1838 GST_ERROR_OBJECT (submux, "auto detect function failed");
1841 if (!gst_calculate_number_languages(submux)) {
1842 GST_ERROR_OBJECT (submux, "failed to calculate number of languages");
1845 if (!gst_submux_create_pipelines (submux, pad)) {
1846 GST_ERROR_OBJECT (submux, "failed to create pipelines");
1851 if (!submux->priv->is_internal) {
1852 gst_event_unref(event);
1853 length = g_list_length(submux->streams);
1854 for (i = 0; i < length; i++) {
1855 GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
1856 cur_stream = g_list_nth_data(submux->streams,i);
1857 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1858 if (!cur_stream->pipe_struc.pipe) {
1859 GST_ERROR_OBJECT (submux, "pipeline is null");
1862 cur_stream = g_list_nth_data(submux->streams,i);
1863 cur_stream->seek_ts = submux->segment.start;
1864 ret = gst_element_send_event (cur_stream->pipe_struc.pipe, gst_event_new_new_segment (FALSE,
1865 submux->segment.rate, submux->segment.format,
1866 submux->segment.start, submux->segment.stop, submux->segment.time));
1868 GST_ERROR_OBJECT(submux, "sending newsegment event to stream[%d] failed", i);
1872 submux->need_segment = TRUE;
1874 length = g_list_length (submux->sinkpad);
1875 if (length == g_list_length (submux->streams) && submux->need_segment) {
1876 for (i = 0; i < length; i++) {
1877 GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
1878 cur_stream = g_list_nth_data(submux->streams, i);
1879 if (cur_stream->need_segment) {
1880 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time);
1881 gst_segment_set_newsegment_full (&submux->segment, update, rate, arate, format, start, stop, time);
1882 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1884 cur_stream->need_segment = FALSE;
1887 submux->need_segment = TRUE;
1888 gst_event_unref(event);
1891 GST_OBJECT_UNLOCK (submux);
1894 case GST_EVENT_FLUSH_START: {
1895 length = g_list_length(submux->streams);
1896 if (!submux->priv->is_internal) {
1897 gst_event_unref(event);
1898 ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
1899 for (i = 0;i < length;i++) {
1900 cur_stream = g_list_nth_data(submux->streams,i);
1901 cur_stream->flushing = TRUE;
1902 cur_stream->discont_came = FALSE;
1903 GST_DEBUG_OBJECT (submux, "making discont false");
1904 GST_DEBUG_OBJECT (submux, "making flushing TRUE");
1905 cur_stream->eos_came = FALSE;
1906 cur_stream->eos_sent = FALSE;
1907 GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
1909 for (i = 0; i < length; i++) {
1910 cur_stream = g_list_nth_data(submux->streams,i);
1911 ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_start ());
1912 ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_stop ());
1913 ret = gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_eos ());
1914 GST_INFO_OBJECT(cur_stream,"flush stop and start and eos is done with ret %d",ret);
1915 submux->flushing =TRUE;
1916 g_mutex_lock (cur_stream->queue_lock);
1917 g_cond_signal (cur_stream->queue_empty);
1918 g_mutex_unlock(cur_stream->queue_lock);
1919 cur_stream->flush_done = TRUE;
1920 GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
1922 GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
1926 GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
1930 if (submux && GST_PAD_TASK(submux->srcpad)) {
1931 GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
1932 GST_PAD_STREAM_LOCK (submux->srcpad);
1933 GST_INFO_OBJECT (submux, "acquired stream lock");
1934 GST_PAD_STREAM_UNLOCK (submux->srcpad);
1936 /*changes for new design*/
1937 for (i = 0;i < length;i++) {
1938 cur_stream = g_list_nth_data(submux->streams,i);
1939 gst_submux_stream_deinit(cur_stream,submux);
1941 g_list_free(submux->streams);
1942 submux->streams = NULL;
1943 gst_submux_deinit_private_values (submux);
1945 submux->stop_loop = FALSE;
1946 submux->need_segment = TRUE;
1947 submux->langlist_msg_posted = FALSE;
1948 GST_DEBUG_OBJECT (submux, "flush start successfully send to next element");
1950 GST_DEBUG_OBJECT(submux, "flusht start in case of internal subtitle");
1951 gst_event_unref (event);
1952 submux->flushing = TRUE;
1953 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
1954 if (checkpad == pad) {
1955 ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
1956 for (i = 0; i < length; i++) {
1957 cur_stream = g_list_nth_data(submux->streams, i);
1958 cur_stream->flushing = TRUE;
1959 GST_DEBUG_OBJECT (submux, "in case of internal making discont unchanged");
1960 GST_DEBUG_OBJECT (submux, "making flushing TRUE");
1962 for (i = 0; i < length; i++) {
1963 cur_stream = g_list_nth_data(submux->streams, i);
1964 submux->flushing = TRUE;
1965 g_mutex_lock (cur_stream->queue_lock);
1966 while (!g_queue_is_empty (cur_stream->queue)) {
1967 buf = g_queue_pop_head (cur_stream->queue);
1968 gst_buffer_unref (buf);
1970 GST_DEBUG_OBJECT (submux, "cleared stream cur_stream->queue");
1971 g_queue_clear (cur_stream->queue);
1972 g_cond_signal (cur_stream->queue_empty);
1973 g_mutex_unlock(cur_stream->queue_lock);
1974 GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
1975 cur_stream->eos_came = FALSE;
1976 cur_stream->eos_sent = FALSE;
1977 GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
1979 GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
1982 GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
1986 if (submux && GST_PAD_TASK (submux->srcpad)) {
1987 GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
1988 GST_PAD_STREAM_LOCK (submux->srcpad);
1989 GST_INFO_OBJECT (submux, "acquired srcpad lock");
1990 GST_PAD_STREAM_UNLOCK (submux->srcpad);
1992 GST_DEBUG_OBJECT(submux, "flush start successfully send to next element");
1997 case GST_EVENT_FLUSH_STOP: {
1998 gst_event_unref(event);
1999 if (!submux->priv->is_internal) {
2001 submux->flushing = FALSE;
2002 ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
2004 GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
2007 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2008 submux->cur_buf_array[idx] = NULL;
2010 GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
2012 length = g_list_length(submux->streams);
2013 GST_DEBUG_OBJECT (submux, "flusht stop in case of internal subtitle");
2014 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
2015 if (checkpad == pad) {
2016 for (i = 0; i < length; i++) {
2017 cur_stream = g_list_nth_data(submux->streams, i);
2018 cur_stream->need_segment = TRUE;
2019 submux->cur_buf_array[i] = NULL;
2020 submux->need_segment = TRUE;
2021 GST_DEBUG_OBJECT (submux, "making need_segment true");
2022 submux->flushing = FALSE;
2023 cur_stream->flushing = FALSE;
2024 GST_DEBUG_OBJECT (submux, "making flushing FALSE");
2026 GST_DEBUG_OBJECT (submux, "sending %s event to stream[%d] success", GST_EVENT_TYPE_NAME (event), i);
2028 ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
2030 GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
2033 GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
2039 if (!submux->priv->is_internal) {
2040 ret = gst_pad_event_default (pad, event);
2042 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
2043 if (checkpad == pad) {
2044 ret = gst_pad_event_default (pad, event);
2050 GST_ERROR_OBJECT (submux, "sending %s event to srcpad pad failed", GST_EVENT_TYPE_NAME (event));
2059 static gint gst_submux_buffer_list_sorting (gconstpointer a, gconstpointer b)
2061 GstBuffer *buf_a = (GstBuffer *) a;
2062 GstBuffer *buf_b = (GstBuffer *) b;
2063 if (GST_BUFFER_TIMESTAMP(buf_a)>GST_BUFFER_TIMESTAMP(buf_b))
2065 else if(GST_BUFFER_TIMESTAMP(buf_a)<GST_BUFFER_TIMESTAMP(buf_b))
2072 gst_submux_is_muxing_needed (GstBuffer *ref_buffer, GstBuffer *cur_buf)
2074 GstClockTime ref_start = GST_BUFFER_TIMESTAMP(ref_buffer);
2075 GstClockTime ref_stop = GST_BUFFER_TIMESTAMP(ref_buffer) + GST_BUFFER_DURATION(ref_buffer);
2076 GstClockTime start = GST_BUFFER_TIMESTAMP(cur_buf);
2077 GstClockTime stop = GST_BUFFER_TIMESTAMP(cur_buf) + GST_BUFFER_DURATION(cur_buf);
2079 /* if we have a stop position and a valid start and start is bigger,
2080 * we're outside of the segment */
2081 if (G_UNLIKELY (ref_stop != -1 && start != -1 && start >= ref_stop))
2084 /* if a stop position is given and is before the segment start,
2085 * we're outside of the segment. Special case is were start
2086 * and stop are equal to the segment start. In that case we
2087 * are inside the segment. */
2088 if (G_UNLIKELY (stop != -1 && (stop < ref_start || (start != stop && stop == ref_start))))
2094 /* This function do the actual muxing of buffer on the basis of timestamps */
2096 gst_submux_muxing (Gstsubmux *submux)
2098 GstClockTime min_timestamp = 0;
2101 GstClockTime next_min_time = 0;
2103 GList *push_list = NULL;
2105 /* Finding least timestamp of all streams and their stream ID */
2106 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2107 if(submux->cur_buf_array[idx] && !min_timestamp) {
2108 min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2111 if(submux->cur_buf_array[idx] && (GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < min_timestamp)) {
2112 min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2117 GST_DEBUG_OBJECT (submux, "Identified least timestamp: %"GST_TIME_FORMAT" for stream: %d",
2118 GST_TIME_ARGS(min_timestamp), min_stream);
2120 /* Finding overlap buffers and next least timestamp */
2121 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2122 if(submux->cur_buf_array[idx] && (idx != min_stream) && (!next_min_time)) {
2123 next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2125 if(submux->cur_buf_array[idx] && (idx != min_stream)) {
2126 if(gst_submux_is_muxing_needed (submux->cur_buf_array[min_stream], submux->cur_buf_array[idx])) {
2127 overlap = overlap | (1<<idx); // bit setting of overlap variable with stream ID
2128 GST_DEBUG_OBJECT (submux, "overlapped with stream = %d", idx);
2129 if(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < next_min_time)
2130 next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2135 GST_DEBUG_OBJECT (submux, "Identified overlap: %d next least timestamp: %"GST_TIME_FORMAT" ", overlap, GST_TIME_ARGS(next_min_time));
2137 /* If no overlap send buffer as it is */
2139 GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[min_stream]));
2140 push_list = g_list_append(push_list, submux->cur_buf_array[min_stream]);
2141 GST_DEBUG_OBJECT (submux, "No overlap found pushing buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2142 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
2143 submux->cur_buf_array[min_stream] = NULL;
2145 GstBuffer *push_buf = NULL;
2146 GstClockTime stop_time = 0;
2148 GstBuffer *overlap_buf = NULL;
2149 guint overlap_buf_length = 0;
2150 GString *overlap_text = NULL;
2153 if(next_min_time > GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])) {
2154 GST_DEBUG_OBJECT (submux, "Before duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2155 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
2156 push_buf = gst_buffer_copy (submux->cur_buf_array[min_stream]);
2157 push_buf->duration = next_min_time - GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]);
2158 GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]) = next_min_time;
2159 GST_BUFFER_DURATION(submux->cur_buf_array[min_stream]) -= push_buf->duration;
2160 GST_DEBUG_OBJECT (submux, "After duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2161 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
2163 min_timestamp = next_min_time;
2164 GST_INFO_OBJECT (submux, "pushing string: %s...", (gchar*)GST_BUFFER_DATA(push_buf));
2165 push_list = g_list_append(push_list, push_buf);
2166 GST_DEBUG_OBJECT (submux, "Overlap found pushing initial partial buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2167 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(push_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(push_buf)));
2170 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2171 if(submux->cur_buf_array[idx] && !stop_time) {
2172 stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
2175 if(submux->cur_buf_array[idx] &&
2176 ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) < stop_time)) {
2177 stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
2181 GST_DEBUG_OBJECT (submux, "Identified least stop timestamp: %"GST_TIME_FORMAT" for stream: %d",
2182 GST_TIME_ARGS(stop_time), stop_idx);
2184 overlap_text = g_string_new ("");
2185 overlap = overlap | (1<<min_stream);
2186 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2187 int finder = 1<<idx;
2188 if(overlap & finder) {
2189 GST_DEBUG_OBJECT (submux, "append string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[idx]));
2190 g_string_append (overlap_text, (gchar*)GST_BUFFER_DATA (submux->cur_buf_array[idx]));
2191 GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+= (stop_time - min_timestamp);
2192 GST_BUFFER_DURATION(submux->cur_buf_array[idx])-= (stop_time - min_timestamp);
2193 if(overlap > (1<<(idx+1))) g_string_append_c (overlap_text, '\n');
2196 text = g_string_free (overlap_text, FALSE);
2197 overlap_buf_length = strlen(text);
2198 overlap_buf = gst_buffer_new_and_alloc (overlap_buf_length + 1);
2199 memcpy (GST_BUFFER_DATA (overlap_buf), text, overlap_buf_length + 1);
2200 overlap_buf->timestamp = min_timestamp;
2201 overlap_buf->duration = stop_time - min_timestamp;
2204 submux->cur_buf_array[stop_idx] = NULL;
2205 GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(overlap_buf));
2206 push_list = g_list_append(push_list, overlap_buf);
2207 GST_DEBUG_OBJECT (submux, "Overlap found pushing merged buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2208 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(overlap_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(overlap_buf)));
2209 GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", stop_idx);
2210 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2211 if(submux->cur_buf_array[idx] &&
2212 ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) <= stop_time)) {
2213 submux->cur_buf_array[idx] = NULL;
2214 GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", idx);
2221 static void gst_submux_loop (Gstsubmux *submux)
2224 GstBuffer *src_buffer = NULL;
2225 GstBuffer *temp_buffer = NULL;
2226 GstBuffer *check_buffer = NULL;
2227 GstSubmuxStream *cur_stream = NULL;
2228 GstSubmuxStream *check_stream = NULL;
2229 gboolean match = FALSE;
2230 GstFlowReturn fret = GST_FLOW_OK;
2231 GstClockTime cur_duration = 0 ;
2232 GstClockTime cur_ts = 0;
2233 gboolean eos = TRUE;
2235 GList *push_list = NULL;
2236 GstBuffer *push_buf = NULL;
2238 if (!submux->priv->first_buffer) {
2239 GST_INFO_OBJECT (submux, "exiting from lopp");
2243 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
2244 length = g_list_length (submux->priv->lang_list);
2246 length = submux->sinkpads_count;
2249 gboolean made = FALSE;
2252 for (i = 0; i < length; i++) {
2255 cur_stream = g_list_nth_data (submux->streams, i);
2256 GST_DEBUG_OBJECT (submux, "Before lock acquired in loop stream[%d]", i);
2257 g_mutex_lock (cur_stream->queue_lock);
2258 GST_DEBUG_OBJECT (submux, "Lock acquired in loop stream[%d]", i);
2260 if (g_queue_is_empty (cur_stream->queue) && !submux->flushing) {
2261 GST_DEBUG_OBJECT (submux, "Queue is empty, waiting for the condition signal stream[%d]", i);
2262 g_cond_wait (cur_stream->queue_empty, cur_stream->queue_lock);
2264 GST_DEBUG_OBJECT (submux, "Got the queue condition signal stream[%d]", i);
2266 if (submux->flushing || submux->stop_loop) {
2267 GST_DEBUG_OBJECT (submux, "Flushing going on in loop");
2268 GST_DEBUG_OBJECT (submux, "Got the condition signal");
2269 g_mutex_unlock (cur_stream->queue_lock);
2273 check_buffer = g_queue_peek_head (cur_stream->queue);
2274 if (!strcmp ((const char*)GST_BUFFER_DATA (check_buffer), "eos")){
2275 cur_stream->eos_came = TRUE;
2276 GST_DEBUG_OBJECT (submux, "Eos recieved for stream");
2278 for (k = 0; k < length; k++) {
2279 check_stream = g_list_nth_data(submux->streams, k);
2280 if (!check_stream->eos_came) {
2288 GST_DEBUG_OBJECT (submux, "Sending EOS to submux srcpad");
2289 gst_pad_push_event(submux->srcpad, gst_event_new_eos ());
2290 g_mutex_unlock (cur_stream->queue_lock);
2294 if (!cur_stream->eos_came && (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI ||
2295 submux->priv->is_internal)) {
2296 GstLangStruct *lang = NULL;
2297 if (submux->priv->lang_list) {
2298 if (submux->cur_buf_array[i] == NULL) {
2299 check_buffer = g_queue_pop_head (cur_stream->queue);
2300 lang = g_list_nth_data(submux->priv->lang_list, i);
2301 if (!lang->active) {
2303 gst_buffer_unref(check_buffer);
2304 check_buffer = NULL;
2305 GST_DEBUG_OBJECT (submux, "unreffing the non-active stream[%d] buffer", i);
2307 submux->cur_buf_array[i] = NULL;
2308 g_mutex_unlock (cur_stream->queue_lock);
2309 GST_DEBUG_OBJECT(submux,"rejecting not selected language");
2312 if (!check_buffer) {
2313 GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
2314 g_mutex_unlock (cur_stream->queue_lock);
2317 if (!GST_BUFFER_DURATION(check_buffer)) {
2318 GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
2319 gst_buffer_unref (check_buffer);
2320 g_mutex_unlock (cur_stream->queue_lock);
2323 submux->cur_buf_array[i] = check_buffer;
2324 GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
2325 i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
2329 g_mutex_unlock (cur_stream->queue_lock);
2330 GST_DEBUG_OBJECT(submux,"Coming to Else case lang submux->priv->lang_list %x ",submux->priv->lang_list);
2333 } else if (!cur_stream->eos_came) {
2334 /* External subtitle format other than smi */
2335 if (submux->sinkpad) {
2336 if (submux->cur_buf_array[i] == NULL) {
2337 check_buffer = g_queue_pop_head (cur_stream->queue);
2338 if (!check_buffer) {
2339 GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
2340 g_mutex_unlock (cur_stream->queue_lock);
2343 if (!GST_BUFFER_DURATION (check_buffer)) {
2344 GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
2345 gst_buffer_unref (check_buffer);
2346 g_mutex_unlock (cur_stream->queue_lock);
2349 submux->cur_buf_array[i] = check_buffer;
2350 GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
2351 i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
2354 g_mutex_unlock (cur_stream->queue_lock);
2355 GST_DEBUG_OBJECT(submux,"Coming to Else case submux->sinkpad %x ",submux->sinkpad);
2359 GST_DEBUG_OBJECT (submux, "already received EOS on this stream[%d] and cur_buf_array[idx] = NULL", i);
2360 submux->cur_buf_array[i] = NULL;
2363 g_mutex_unlock (cur_stream->queue_lock);
2364 GST_DEBUG_OBJECT (submux, "After unlocking in loop and signal queue full");
2367 push_list = gst_submux_muxing (submux);
2370 GST_LOG_OBJECT (submux, "length of push list = %d", g_list_length (push_list));
2372 for (idx = 0; idx < g_list_length (push_list); idx++) {
2373 push_buf = g_list_nth_data (push_list, idx);
2376 GST_DEBUG_OBJECT (submux, "pushing buffer : ts = %"GST_TIME_FORMAT", "
2377 "dur = %"GST_TIME_FORMAT" and data %s ...",
2378 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (push_buf)),
2379 GST_TIME_ARGS (GST_BUFFER_DURATION (push_buf)), (gchar*)GST_BUFFER_DATA (push_buf));
2381 fret = gst_pad_push (submux->srcpad, push_buf);
2382 if (fret != GST_FLOW_OK) {
2383 GST_ERROR_OBJECT (submux, "failed to push buffer. reason : %s", gst_flow_get_name (fret));
2384 /* clean any left buffers in push_list */
2386 for (; idx < g_list_length (push_list); idx++) {
2387 push_buf = g_list_nth_data (push_list, idx);
2388 gst_buffer_unref (push_buf);
2390 g_list_free (push_list);
2396 g_list_free (push_list);
2399 GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
2405 GST_WARNING_OBJECT (submux->srcpad, "Pausing the push task...");
2406 if (fret < GST_FLOW_UNEXPECTED) {
2407 GST_ERROR_OBJECT (submux, "Crtical error in push loop....");
2408 GST_ELEMENT_ERROR (submux, CORE, PAD, ("failed to push. reason - %s", gst_flow_get_name (fret)), (NULL));
2410 gst_pad_pause_task (submux->srcpad);
2411 GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
2415 ////////////////////////////////////////////////////////
2416 // Plugin Utility Functions //
2417 ////////////////////////////////////////////////////////
2420 ** Description : De-Initializing the submux private structure
2421 ** Params : (1) submux instance
2427 gst_submux_deinit_private_values(Gstsubmux *submux)
2430 GST_DEBUG_OBJECT (submux, "deinit priv values");
2432 submux->priv->first_buffer = FALSE;
2433 submux->priv->parser_type = 0;
2434 if (submux->priv->lang_list && !submux->priv->is_internal) {
2435 g_list_free (submux->priv->lang_list);
2436 submux->priv->lang_list = NULL;
2438 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2439 submux->cur_buf_array[idx] = NULL;
2441 if (submux->cur_buf_array) {
2442 g_free (submux->cur_buf_array);
2443 submux->cur_buf_array = NULL;
2446 submux->priv->is_internal = FALSE;
2447 submux->priv->stream_count = 0;
2453 gst_submux_plugin_init (GstPlugin * plugin)
2455 return gst_element_register (plugin, "submux", GST_RANK_PRIMARY, GST_TYPE_SUBMUX);
2458 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,GST_VERSION_MINOR,"submux","submux",gst_submux_plugin_init,"0.10.36","Proprietary","Samsung Electronics Co","http://www.samsung.com")