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);
789 new->language_code = g_strdup(attr_value);
792 new->language_key = g_strdup(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] = g_strdup (tempKey);
1196 langKey_length[keyCount]=keyLength;
1211 GST_DEBUG_OBJECT (self, " ..increment con_temp %s",con_temp);
1214 GST_DEBUG_OBJECT (self, " At end keyCount no of langs is %d ",keyCount);
1215 for(i=0;i<keyCount;i++) {
1217 GstLangStruct *new = g_new0 (GstLangStruct, 1);
1218 GST_DEBUG_OBJECT (self, "Adding ign case to the langKey keyCount %d and lang %s ",i, langkey[i]);
1219 new->language_code = (gchar*)malloc (3);
1220 if(!(new->language_code)){
1221 GST_DEBUG_OBJECT (self, " .Error 2");
1224 gchar *attr_val=new->language_code ;
1225 strncpy (attr_val, "un", 2);
1228 new->language_key = g_strdup(langKey_length[i]);
1229 self->priv->lang_list = g_list_append (self->priv->lang_list, new);
1230 GST_DEBUG_OBJECT (self, " new...Successfull");
1236 g_free(file_path_type);
1242 GST_DEBUG_OBJECT (self, " In Error");
1245 g_free(file_path_type);
1253 validate_langlist_body(GList * lang_list, Gstsubmux * self){
1254 gchar * file_path_type = NULL;
1255 gchar * file_path = NULL;
1258 guint i = 0, found_count = 0,k = 0;
1259 const guint list_len = g_list_length(lang_list);
1260 gboolean counter[MAX_LANGUAGE];
1261 GstPad *sinkpad = NULL;
1264 gchar *language_code;
1265 gchar *language_key;
1267 sinkpad = (GstPad *) g_list_nth_data(self->sinkpad, 0);
1269 GstStructure *structure;
1270 const GValue *value;
1271 structure = gst_structure_new ("FileSrcURI", "file-uri", G_TYPE_STRING, NULL, NULL);
1273 cquery = gst_query_new_application (GST_QUERY_CUSTOM, structure);
1275 if (!gst_pad_peer_query (sinkpad, cquery)) {
1276 GST_ERROR_OBJECT (self, "Failed to query SMI file path");
1277 gst_query_unref (cquery);
1280 structure = gst_query_get_structure (cquery);
1281 value = gst_structure_get_value (structure, "file-uri");
1282 file_path = g_strdup (g_value_get_string (value));
1284 if (file_path == NULL){
1285 GST_ERROR_OBJECT (self, "Could not parse the SMI file path");
1286 gst_query_unref (cquery);
1290 if (self->external_filepath == NULL) {
1291 self->external_filepath = file_path;
1294 if (!g_strcmp0 (file_path, self->external_filepath)) {
1295 GST_INFO_OBJECT (self, "Same external file URI, no need to parse again");
1296 gst_query_unref (cquery);
1301 g_free (self->external_filepath);
1302 self->external_filepath = NULL;
1303 self->external_filepath = file_path;
1307 gst_query_unref (cquery);
1308 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1310 file_path_type = g_strndup ((gchar *) file_path, 4);
1311 GST_INFO_OBJECT (self, "Received file path by query = %s, %s", file_path, file_path_type);
1312 if (!g_strcmp0(file_path_type, "file")){
1314 GST_INFO_OBJECT (self, "File path comes as %s", file_path);
1316 fp = fopen (file_path, "r");
1318 GST_ERROR_OBJECT (self, "Failed to open file");
1319 g_free(file_path_type);
1323 for (i = 0; i < list_len; i++){
1327 while (!feof (fp) && found_count < list_len){
1329 gsize * consumed = NULL;
1331 guint charCount = 0;
1332 gchar* result = NULL;
1334 gchar* temp_lang = NULL;
1335 gchar* con_temp_end = NULL;
1336 gchar* con_temp_start = NULL;
1337 gchar* new_key = NULL;
1338 gint new_key_length = 0;
1339 gboolean new_key_found = FALSE;
1340 gchar * temp1 = NULL;
1341 gchar *con_temp_lang = NULL;
1342 gchar *con_temp = NULL;
1343 gboolean conversion = TRUE;
1344 charCount = fread (line, sizeof(char), 1024, fp);
1345 line[charCount] = '\0';
1347 GST_WARNING_OBJECT (self, "fread returned zero bytes");
1351 GST_DEBUG_OBJECT (self, "value of detected encoding is %s and self encoding is %s",
1352 self->detected_encoding,self->encoding);
1353 if (self->detected_encoding && strcmp (self->detected_encoding, "UTF-8") && conversion){
1354 result = convert_to_utf8 (line, charCount, self->detected_encoding, consumed, &err, self);
1356 if (result == NULL) {
1360 con_temp = g_utf8_strdown (result, strlen (result));
1363 con_temp = g_strstr_len(con_temp, strlen (con_temp), "class=");
1365 temp1 = g_strstr_len(con_temp+1, strlen (con_temp), "class=");
1367 if (temp1 && con_temp){
1368 gap = strlen (con_temp) - strlen (temp1);
1369 } else if (con_temp) {
1370 gap = strlen (con_temp);
1375 for (i = 0; i < list_len; i++){
1376 if (counter[i] == TRUE) {
1377 con_temp = con_temp + 1;
1380 lang = (struct LangStruct *) g_list_nth_data (lang_list, i);
1382 temp_lang = g_strdup(lang->language_key);
1383 con_temp_lang = g_utf8_strdown (temp_lang, strlen (temp_lang));
1384 if (g_strstr_len (con_temp, gap, con_temp_lang)) {
1387 GST_INFO_OBJECT (self, "Valid Language in list : [%s]", lang->language_key);
1388 con_temp = con_temp + 1;
1390 /* Fix Me: Cases where there is no body for a specific language
1391 * inside a single language .smi file */
1394 con_temp_start = con_temp;
1395 con_temp_end = con_temp;
1396 while(con_temp_end) {
1397 if(*con_temp_end == '=') {
1398 con_temp_start = con_temp_end+1;
1400 }else if(*con_temp_end == '>') {
1401 con_temp_end = con_temp_end;
1402 new_key_found = TRUE;
1406 new_key_found = FALSE;
1410 new_key_length = strlen(con_temp_start)-strlen(con_temp_end);
1411 new_key = g_malloc(new_key_length +1);
1412 for(k=0;k<new_key_length;k++){
1413 *(new_key+k)=*(con_temp_start+k);
1415 *(new_key+new_key_length)='\0';
1416 GST_INFO("new lang key is %s",lang->language_key);
1420 con_temp = con_temp + 1;
1425 g_free (con_temp_lang);
1436 if (found_count < list_len) {
1437 for (i = 0; i < list_len; i++) {
1438 if (counter[i] == FALSE)
1439 lang_list = g_list_delete_link (lang_list, g_list_nth (lang_list, i));
1443 GST_ERROR_OBJECT (self, "File is not a local file");
1444 g_free(file_path_type);
1448 g_free(file_path_type);
1454 ** Description : Chain function used to push the subtitle buffer to internal pipelines of submux element
1455 ** Params : (1) sink pad on which buffer is arriving (2) the buffer itself
1456 ** return : GST_FLOW_OK on successfully pushing subtitle buffer to next element
1459 static GstFlowReturn
1460 gst_submux_chain(GstPad *pad, GstBuffer *buffer)
1464 GstPad *checkpad = NULL;
1465 Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
1466 gboolean ret = FALSE;
1467 GstFlowReturn fret = GST_FLOW_ERROR;
1468 GstSubmuxStream *stream = NULL;
1469 GstMessage *m = NULL;
1471 if (GST_BUFFER_IS_DISCONT (buffer))
1472 GST_DEBUG_OBJECT(submux, "Discont buffer came in chain function");
1473 GST_DEBUG_OBJECT (submux, "^^^^^entering in chain^^^^^^");
1474 if (!submux->priv->is_internal) {
1475 if (!submux->priv->first_buffer) {
1476 submux->detected_encoding = detect_encoding ((gchar*)GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
1478 if (!submux->langlist_msg_posted && submux->priv->lang_list) {
1479 if (!validate_langlist_body (submux->priv->lang_list, submux)){
1480 GST_WARNING_OBJECT(submux, "Error occured while validating language list. Posting without validation");
1482 if (submux->priv->lang_list) {
1483 GList* temp_list_to_post = NULL;
1484 temp_list_to_post = g_list_copy (submux->priv->lang_list);
1485 m = gst_message_new_element (GST_OBJECT_CAST (submux), gst_structure_new("Ext_Sub_Language_List",
1486 "lang_list", G_TYPE_POINTER, temp_list_to_post, NULL));
1488 gst_element_post_message (GST_ELEMENT_CAST (submux), m);
1489 submux->langlist_msg_posted = TRUE;
1491 GST_DEBUG_OBJECT (submux, "LANGLIST POSTED");
1493 if (submux->need_segment) {
1494 ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
1495 submux->segment.format, submux->segment.start, submux->segment.stop,
1496 submux->segment.time));
1497 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1499 GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
1500 return GST_FLOW_ERROR;
1502 GST_DEBUG_OBJECT (submux, "Starting the loop again");
1503 if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
1504 GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
1505 GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
1506 return GST_FLOW_ERROR;
1508 submux->need_segment = FALSE;
1510 GST_DEBUG_OBJECT (submux, "before pushing buffer to each apprsrc");
1511 if (!submux->priv->lang_list && submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
1512 GST_ERROR_OBJECT (submux, "lang list is not there");
1513 return GST_FLOW_ERROR;
1515 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI)
1516 length = g_list_length (submux->priv->lang_list);
1520 for (i = 0; i < length; i++) {
1521 stream = g_list_nth_data(submux->streams, i);
1522 if ((submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) && !submux->priv->first_buffer){
1523 GstLangStruct *lang = g_list_nth_data(submux->priv->lang_list, i);
1524 if(submux->msl_streams){
1525 GstLangStruct *lang1 = g_list_nth_data(submux->msl_streams, i);
1526 lang->active = lang1->active;
1529 lang->active = TRUE;
1531 lang->active = FALSE;
1534 GST_DEBUG_OBJECT (submux, "making stream need segment false");
1535 stream->need_segment = FALSE;
1538 for (i = 0; i < length; i++) {
1539 stream = g_list_nth_data(submux->streams, i);
1541 GST_ERROR_OBJECT (submux, "stream not found");
1542 return GST_FLOW_ERROR;
1545 if (!stream->pipe_struc.appsrc) {
1546 GST_ERROR_OBJECT (submux, "appsrc not found");
1547 return GST_FLOW_ERROR;
1549 if (i < (length - 1))
1550 gst_buffer_ref (buffer);
1552 fret = gst_app_src_push_buffer ((GstAppSrc*)stream->pipe_struc.appsrc, buffer);
1554 if (fret != GST_FLOW_OK) {
1555 GST_ERROR_OBJECT (submux, "push buffer failed with fret is %d", fret);
1558 GST_DEBUG_OBJECT (submux, "pad_push successfull to appsrc %p buffer", buffer);
1561 length = submux->sinkpads_count;
1562 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
1563 if (checkpad == pad) {
1564 if (submux->need_segment) {
1565 ret = gst_pad_push_event (submux->srcpad, gst_event_new_new_segment (FALSE, submux->segment.rate,
1566 submux->segment.format, submux->segment.start, submux->segment.stop,
1567 submux->segment.time));
1568 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1570 GST_ERROR_OBJECT (submux, "Sending newsegment to next element is failed");
1571 return GST_FLOW_ERROR;
1573 GST_DEBUG_OBJECT (submux, "Starting the loop again");
1574 if (!gst_pad_start_task (submux->srcpad, (GstTaskFunction) gst_submux_loop, submux)) {
1575 GST_ERROR_OBJECT (submux, "failed to start srcpad task...");
1576 GST_ELEMENT_ERROR (submux, RESOURCE, FAILED, ("failed to create push loop"), (NULL));
1577 return GST_FLOW_ERROR;
1579 submux->need_segment = FALSE;
1582 for (i = 0; i < length; i++) {
1583 checkpad = (GstPad *) g_list_nth_data(submux->sinkpad, i);
1584 if (checkpad == pad) {
1585 stream = g_list_nth_data (submux->streams, i);
1587 GST_ERROR_OBJECT (submux, "Stream not available...");
1588 return GST_FLOW_ERROR;
1590 if (stream->flushing){
1591 GST_DEBUG_OBJECT (submux, "flushing going on in appsink");
1592 return GST_FLOW_OK ;
1595 g_mutex_lock (stream->queue_lock);
1596 g_queue_push_tail (stream->queue, buffer);
1597 g_cond_signal (stream->queue_empty);
1598 g_mutex_unlock (stream->queue_lock);
1600 GST_DEBUG_OBJECT (submux, "push buffer success to appsrc with fret is %d for stream[%d]", fret, i);
1606 if (!submux->priv->first_buffer) {
1607 GST_DEBUG_OBJECT (submux, "got the first buffer");
1608 submux->priv->first_buffer = TRUE;
1609 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
1613 GST_DEBUG_OBJECT (submux, "^^^^^exiting in chain^^^^^^");
1619 gst_submux_stream_deinit (GstSubmuxStream *stream,Gstsubmux *submux)
1621 GstBuffer *buf = NULL;
1624 if (stream->queue) {
1625 while (!g_queue_is_empty (stream->queue)) {
1626 buf = g_queue_pop_head (stream->queue);
1627 gst_buffer_unref (buf);
1630 g_queue_free (stream->queue);
1631 stream->queue = NULL;
1634 if (stream->pipe_struc.pipe) {
1635 gst_element_set_state (stream->pipe_struc.pipe, GST_STATE_NULL);
1636 gst_element_get_state (stream->pipe_struc.pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
1637 gst_object_unref(GST_OBJECT(stream->pipe_struc.appsrc));
1638 gst_object_unref(GST_OBJECT(stream->pipe_struc.appsink));
1639 gst_object_unref (stream->pipe_struc.pipe);
1642 if (stream->queue_lock) {
1643 g_cond_broadcast(stream->queue_empty);
1644 g_mutex_free (stream->queue_lock);
1645 stream->queue_lock = NULL;
1648 if (stream->queue_empty) {
1649 g_cond_free (stream->queue_empty);
1650 stream->queue_empty= NULL;
1654 GST_DEBUG_OBJECT (submux, "stream deinit completed");
1658 /* releasing the requested pad */
1660 gst_submux_release_pad (GstElement * element, GstPad * pad)
1662 Gstsubmux *submux = GST_SUBMUX_CAST (element);
1666 GST_INFO_OBJECT(element,"entering in the release pad");
1667 length = g_list_length(submux->sinkpad);
1668 GST_DEBUG_OBJECT (element, "Releasing %s:%s", GST_DEBUG_PAD_NAME (pad));
1670 for (i=1;i<=length;i++) {
1671 check_pad = (struct GstPad *) g_list_nth_data(submux->sinkpad,i);
1672 if (check_pad == pad) {
1673 /* this is it, remove */
1674 submux->sinkpad = g_list_remove_link (submux->sinkpad, pad);
1675 gst_element_remove_pad (element, pad);
1681 /* request new pad */
1683 gst_submux_request_new_pad (GstElement * element,
1684 GstPadTemplate * templ, const gchar * req_name)
1686 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
1687 Gstsubmux *submux = GST_SUBMUX_CAST (element);
1688 GstPad *newpad = NULL;
1691 if (templ->direction != GST_PAD_SINK) {
1692 GST_ERROR_OBJECT (submux, "templ direction is not sinkpad, returning from here");
1693 goto wrong_direction;
1696 if (templ == gst_element_class_get_pad_template (klass, "sink%d")) {
1697 name = g_strdup_printf ("sink%d", submux->sinkpads_count++);
1700 GST_DEBUG_OBJECT (submux, "Requested pad: %s", name);
1701 newpad = (GstPad*)g_new0 (GstPad*, 1);
1702 /* create pad and add to collections */
1703 newpad = gst_pad_new_from_template (templ, name);
1705 if(!submux->priv->is_internal) {
1706 submux->external_sinkpad = TRUE;
1708 submux->sinkpad = g_list_append (submux->sinkpad, newpad);
1711 /* set up pad functions */
1712 gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_setcaps));
1713 gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_submux_handle_sink_event));
1714 gst_pad_set_chain_function(newpad, GST_DEBUG_FUNCPTR (gst_submux_chain));
1715 gst_pad_set_active (newpad, TRUE);
1716 gst_element_add_pad (element, newpad);
1722 GST_WARNING_OBJECT (submux, "Request pad that is not a SINK pad.");
1727 gst_submux_handle_src_event (GstPad * pad, GstEvent * event)
1729 Gstsubmux *submux = GST_SUBMUX(GST_PAD_PARENT(pad));
1730 gboolean ret = FALSE;
1734 GstSubmuxStream *cur_stream = NULL;
1736 GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
1737 length = g_list_length(submux->streams);
1739 switch (GST_EVENT_TYPE (event)) {
1740 /* this event indicates speed change or seek */
1741 case GST_EVENT_SEEK: {
1743 GstSeekType start_type, stop_type;
1746 GstPad *sinkpad = (GstPad *) g_list_nth_data (submux->sinkpad, 0);
1747 gst_event_parse_seek (event, &rate, &format, &submux->segment_flags,
1748 &start_type, &start, &stop_type, &stop);
1749 gst_segment_set_seek (&submux->segment, rate, format, submux->segment_flags,
1750 start_type, start, stop_type, stop, &update);
1751 if (submux->priv->is_internal || submux->priv->parser_type != GST_SUB_PARSE_FORMAT_SAMI) {
1752 length = g_list_length (submux->sinkpad);
1754 length = g_list_length(submux->streams);
1756 if (!submux->priv->is_internal) {
1757 ret = gst_pad_push_event (sinkpad, gst_event_new_seek (rate, GST_FORMAT_BYTES, submux->segment_flags,
1758 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, 0));
1759 gst_event_unref (event);
1761 GST_DEBUG_OBJECT (submux, "handling seek in case of internal");
1762 ret = gst_pad_event_default (pad, event);
1766 GST_ERROR_OBJECT (submux, "sending seek event to sink pad failed");
1769 GST_DEBUG_OBJECT (submux, "sending seek event to sink pad passed");
1775 ret = gst_pad_event_default (pad, event);
1784 gst_submux_handle_sink_event (GstPad * pad, GstEvent * event)
1786 Gstsubmux *submux = GST_SUBMUX (GST_PAD_PARENT (pad));
1787 gboolean ret = FALSE;
1789 GstBuffer *buf = NULL;
1790 GstPad *checkpad = NULL;
1792 GstSubmuxStream *cur_stream = NULL;
1794 GST_DEBUG_OBJECT (submux, "Handling %s event", GST_EVENT_TYPE_NAME (event));
1796 switch (GST_EVENT_TYPE (event)) {
1797 case GST_EVENT_EOS:{
1798 length = g_list_length (submux->sinkpad);
1799 GST_OBJECT_LOCK (submux);
1800 for (i = 0; i < length; i++) {
1801 GST_DEBUG_OBJECT(submux, "inside the handling of EOS event");
1802 cur_stream = g_list_nth_data(submux->streams,i);
1803 if (!cur_stream->eos_sent) {
1804 GstBuffer *buf = gst_buffer_new_and_alloc (3 + 1);
1805 GST_DEBUG_OBJECT(submux, "sending EOS buffer to chain\n");
1806 GST_DEBUG_OBJECT (submux, "EOS. Pushing remaining text (if any)");
1807 GST_BUFFER_DATA (buf)[0] = 'e';
1808 GST_BUFFER_DATA (buf)[1] = 'o';
1809 GST_BUFFER_DATA (buf)[2] = 's';
1810 GST_BUFFER_DATA (buf)[3] = '\0';
1812 GST_BUFFER_SIZE (buf) = 3;
1813 GST_BUFFER_FLAG_SET(buf,GST_BUFFER_FLAG_GAP);
1814 gst_submux_chain (g_list_nth_data(submux->sinkpad,i), buf);//
1815 cur_stream->eos_sent = TRUE;
1818 GST_OBJECT_UNLOCK (submux);
1819 gst_event_unref(event);
1823 case GST_EVENT_NEWSEGMENT: {
1826 gint64 start, stop, time;
1828 GST_OBJECT_LOCK (submux);
1829 if (!submux->pipeline_made) {
1830 if (!gst_submux_format_autodetect (submux)) {
1831 GST_ERROR_OBJECT (submux, "auto detect function failed");
1834 if (!gst_calculate_number_languages(submux)) {
1835 GST_ERROR_OBJECT (submux, "failed to calculate number of languages");
1838 if (!gst_submux_create_pipelines (submux, pad)) {
1839 GST_ERROR_OBJECT (submux, "failed to create pipelines");
1844 if (!submux->priv->is_internal) {
1845 gst_event_unref(event);
1846 length = g_list_length(submux->streams);
1847 for (i = 0; i < length; i++) {
1848 GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
1849 cur_stream = g_list_nth_data(submux->streams,i);
1850 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1851 if (!cur_stream->pipe_struc.pipe) {
1852 GST_ERROR_OBJECT (submux, "pipeline is null");
1855 cur_stream = g_list_nth_data(submux->streams,i);
1856 cur_stream->seek_ts = submux->segment.start;
1857 ret = gst_element_send_event (cur_stream->pipe_struc.pipe, gst_event_new_new_segment (FALSE,
1858 submux->segment.rate, submux->segment.format,
1859 submux->segment.start, submux->segment.stop, submux->segment.time));
1861 GST_ERROR_OBJECT(submux, "sending newsegment event to stream[%d] failed", i);
1865 submux->need_segment = TRUE;
1867 length = g_list_length (submux->sinkpad);
1868 if (length == g_list_length (submux->streams) && submux->need_segment) {
1869 for (i = 0; i < length; i++) {
1870 GST_DEBUG_OBJECT (submux, "inside the handling of new_segment event");
1871 cur_stream = g_list_nth_data(submux->streams, i);
1872 if (cur_stream->need_segment) {
1873 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time);
1874 gst_segment_set_newsegment_full (&submux->segment, update, rate, arate, format, start, stop, time);
1875 GST_DEBUG_OBJECT (submux, "pushing newsegment event with %" GST_SEGMENT_FORMAT, &submux->segment);
1877 cur_stream->need_segment = FALSE;
1880 submux->need_segment = TRUE;
1881 gst_event_unref(event);
1884 GST_OBJECT_UNLOCK (submux);
1887 case GST_EVENT_FLUSH_START: {
1888 length = g_list_length(submux->streams);
1889 if (!submux->priv->is_internal) {
1890 gst_event_unref(event);
1891 ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
1892 for (i = 0;i < length;i++) {
1893 cur_stream = g_list_nth_data(submux->streams,i);
1894 cur_stream->flushing = TRUE;
1895 cur_stream->discont_came = FALSE;
1896 GST_DEBUG_OBJECT (submux, "making discont false");
1897 GST_DEBUG_OBJECT (submux, "making flushing TRUE");
1898 cur_stream->eos_came = FALSE;
1899 cur_stream->eos_sent = FALSE;
1900 GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
1902 for (i = 0; i < length; i++) {
1903 cur_stream = g_list_nth_data(submux->streams,i);
1904 ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_start ());
1905 ret= gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_flush_stop ());
1906 ret = gst_element_send_event(cur_stream->pipe_struc.appsrc,gst_event_new_eos ());
1907 GST_INFO_OBJECT(cur_stream,"flush stop and start and eos is done with ret %d",ret);
1908 submux->flushing =TRUE;
1909 g_mutex_lock (cur_stream->queue_lock);
1910 g_cond_signal (cur_stream->queue_empty);
1911 g_mutex_unlock(cur_stream->queue_lock);
1912 cur_stream->flush_done = TRUE;
1913 GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
1915 GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
1919 GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
1923 if (submux && GST_PAD_TASK(submux->srcpad)) {
1924 GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
1925 GST_PAD_STREAM_LOCK (submux->srcpad);
1926 GST_INFO_OBJECT (submux, "acquired stream lock");
1927 GST_PAD_STREAM_UNLOCK (submux->srcpad);
1929 /*changes for new design*/
1930 for (i = 0;i < length;i++) {
1931 cur_stream = g_list_nth_data(submux->streams,i);
1932 gst_submux_stream_deinit(cur_stream,submux);
1934 g_list_free(submux->streams);
1935 submux->streams = NULL;
1936 gst_submux_deinit_private_values (submux);
1938 submux->stop_loop = FALSE;
1939 submux->need_segment = TRUE;
1940 submux->langlist_msg_posted = FALSE;
1941 GST_DEBUG_OBJECT (submux, "flush start successfully send to next element");
1943 GST_DEBUG_OBJECT(submux, "flusht start in case of internal subtitle");
1944 gst_event_unref (event);
1945 submux->flushing = TRUE;
1946 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
1947 if (checkpad == pad) {
1948 ret = gst_pad_event_default (pad, gst_event_new_flush_start ());
1949 for (i = 0; i < length; i++) {
1950 cur_stream = g_list_nth_data(submux->streams, i);
1951 cur_stream->flushing = TRUE;
1952 GST_DEBUG_OBJECT (submux, "in case of internal making discont unchanged");
1953 GST_DEBUG_OBJECT (submux, "making flushing TRUE");
1955 for (i = 0; i < length; i++) {
1956 cur_stream = g_list_nth_data(submux->streams, i);
1957 submux->flushing = TRUE;
1958 g_mutex_lock (cur_stream->queue_lock);
1959 while (!g_queue_is_empty (cur_stream->queue)) {
1960 buf = g_queue_pop_head (cur_stream->queue);
1961 gst_buffer_unref (buf);
1963 GST_DEBUG_OBJECT (submux, "cleared stream cur_stream->queue");
1964 g_queue_clear (cur_stream->queue);
1965 g_cond_signal (cur_stream->queue_empty);
1966 g_mutex_unlock(cur_stream->queue_lock);
1967 GST_DEBUG_OBJECT (cur_stream, "signaling queue empty signal from flush start");
1968 cur_stream->eos_came = FALSE;
1969 cur_stream->eos_sent = FALSE;
1970 GST_DEBUG_OBJECT (submux, "making eos_came and eos_sent FALSE");
1972 GST_DEBUG_OBJECT (submux, "sending flush start event to stream[%d] success", i);
1975 GST_ERROR_OBJECT (submux, "sending flush start event to srcpad pad failed");
1979 if (submux && GST_PAD_TASK (submux->srcpad)) {
1980 GST_INFO_OBJECT (submux, "trying acquire srcpad lock");
1981 GST_PAD_STREAM_LOCK (submux->srcpad);
1982 GST_INFO_OBJECT (submux, "acquired srcpad lock");
1983 GST_PAD_STREAM_UNLOCK (submux->srcpad);
1985 GST_DEBUG_OBJECT(submux, "flush start successfully send to next element");
1990 case GST_EVENT_FLUSH_STOP: {
1991 gst_event_unref(event);
1992 if (!submux->priv->is_internal) {
1994 submux->flushing = FALSE;
1995 ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
1997 GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
2000 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2001 submux->cur_buf_array[idx] = NULL;
2003 GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
2005 length = g_list_length(submux->streams);
2006 GST_DEBUG_OBJECT (submux, "flusht stop in case of internal subtitle");
2007 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
2008 if (checkpad == pad) {
2009 for (i = 0; i < length; i++) {
2010 cur_stream = g_list_nth_data(submux->streams, i);
2011 cur_stream->need_segment = TRUE;
2012 submux->cur_buf_array[i] = NULL;
2013 submux->need_segment = TRUE;
2014 GST_DEBUG_OBJECT (submux, "making need_segment true");
2015 submux->flushing = FALSE;
2016 cur_stream->flushing = FALSE;
2017 GST_DEBUG_OBJECT (submux, "making flushing FALSE");
2019 GST_DEBUG_OBJECT (submux, "sending %s event to stream[%d] success", GST_EVENT_TYPE_NAME (event), i);
2021 ret = gst_pad_event_default (pad, gst_event_new_flush_stop ());
2023 GST_ERROR_OBJECT (submux, "sending flush-stop event to srcpad pad failed");
2026 GST_DEBUG_OBJECT (submux, "flush stop successfully send to next element");
2032 if (!submux->priv->is_internal) {
2033 ret = gst_pad_event_default (pad, event);
2035 checkpad = (GstPad *) g_list_nth_data (submux->sinkpad, length - 1);
2036 if (checkpad == pad) {
2037 ret = gst_pad_event_default (pad, event);
2043 GST_ERROR_OBJECT (submux, "sending %s event to srcpad pad failed", GST_EVENT_TYPE_NAME (event));
2052 static gint gst_submux_buffer_list_sorting (gconstpointer a, gconstpointer b)
2054 GstBuffer *buf_a = (GstBuffer *) a;
2055 GstBuffer *buf_b = (GstBuffer *) b;
2056 if (GST_BUFFER_TIMESTAMP(buf_a)>GST_BUFFER_TIMESTAMP(buf_b))
2058 else if(GST_BUFFER_TIMESTAMP(buf_a)<GST_BUFFER_TIMESTAMP(buf_b))
2065 gst_submux_is_muxing_needed (GstBuffer *ref_buffer, GstBuffer *cur_buf)
2067 GstClockTime ref_start = GST_BUFFER_TIMESTAMP(ref_buffer);
2068 GstClockTime ref_stop = GST_BUFFER_TIMESTAMP(ref_buffer) + GST_BUFFER_DURATION(ref_buffer);
2069 GstClockTime start = GST_BUFFER_TIMESTAMP(cur_buf);
2070 GstClockTime stop = GST_BUFFER_TIMESTAMP(cur_buf) + GST_BUFFER_DURATION(cur_buf);
2072 /* if we have a stop position and a valid start and start is bigger,
2073 * we're outside of the segment */
2074 if (G_UNLIKELY (ref_stop != -1 && start != -1 && start >= ref_stop))
2077 /* if a stop position is given and is before the segment start,
2078 * we're outside of the segment. Special case is were start
2079 * and stop are equal to the segment start. In that case we
2080 * are inside the segment. */
2081 if (G_UNLIKELY (stop != -1 && (stop < ref_start || (start != stop && stop == ref_start))))
2087 /* This function do the actual muxing of buffer on the basis of timestamps */
2089 gst_submux_muxing (Gstsubmux *submux)
2091 GstClockTime min_timestamp = 0;
2094 GstClockTime next_min_time = 0;
2096 GList *push_list = NULL;
2098 /* Finding least timestamp of all streams and their stream ID */
2099 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2100 if(submux->cur_buf_array[idx] && !min_timestamp) {
2101 min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2104 if(submux->cur_buf_array[idx] && (GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < min_timestamp)) {
2105 min_timestamp = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2110 GST_DEBUG_OBJECT (submux, "Identified least timestamp: %"GST_TIME_FORMAT" for stream: %d",
2111 GST_TIME_ARGS(min_timestamp), min_stream);
2113 /* Finding overlap buffers and next least timestamp */
2114 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2115 if(submux->cur_buf_array[idx] && (idx != min_stream) && (!next_min_time)) {
2116 next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2118 if(submux->cur_buf_array[idx] && (idx != min_stream)) {
2119 if(gst_submux_is_muxing_needed (submux->cur_buf_array[min_stream], submux->cur_buf_array[idx])) {
2120 overlap = overlap | (1<<idx); // bit setting of overlap variable with stream ID
2121 GST_DEBUG_OBJECT (submux, "overlapped with stream = %d", idx);
2122 if(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) < next_min_time)
2123 next_min_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]);
2128 GST_DEBUG_OBJECT (submux, "Identified overlap: %d next least timestamp: %"GST_TIME_FORMAT" ", overlap, GST_TIME_ARGS(next_min_time));
2130 /* If no overlap send buffer as it is */
2132 GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[min_stream]));
2133 push_list = g_list_append(push_list, submux->cur_buf_array[min_stream]);
2134 GST_DEBUG_OBJECT (submux, "No overlap found pushing buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2135 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
2136 submux->cur_buf_array[min_stream] = NULL;
2138 GstBuffer *push_buf = NULL;
2139 GstClockTime stop_time = 0;
2141 GstBuffer *overlap_buf = NULL;
2142 guint overlap_buf_length = 0;
2143 GString *overlap_text = NULL;
2146 if(next_min_time > GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])) {
2147 GST_DEBUG_OBJECT (submux, "Before duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2148 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream])), GST_TIME_ARGS(GST_BUFFER_DURATION(submux->cur_buf_array[min_stream])));
2149 push_buf = gst_buffer_copy (submux->cur_buf_array[min_stream]);
2150 push_buf->duration = next_min_time - GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]);
2151 GST_BUFFER_TIMESTAMP(submux->cur_buf_array[min_stream]) = next_min_time;
2152 GST_BUFFER_DURATION(submux->cur_buf_array[min_stream]) -= push_buf->duration;
2153 GST_DEBUG_OBJECT (submux, "After duration change ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2154 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 min_timestamp = next_min_time;
2157 GST_INFO_OBJECT (submux, "pushing string: %s...", (gchar*)GST_BUFFER_DATA(push_buf));
2158 push_list = g_list_append(push_list, push_buf);
2159 GST_DEBUG_OBJECT (submux, "Overlap found pushing initial partial buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2160 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(push_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(push_buf)));
2163 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2164 if(submux->cur_buf_array[idx] && !stop_time) {
2165 stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
2168 if(submux->cur_buf_array[idx] &&
2169 ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) < stop_time)) {
2170 stop_time = GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx]) + GST_BUFFER_DURATION(submux->cur_buf_array[idx]);
2174 GST_DEBUG_OBJECT (submux, "Identified least stop timestamp: %"GST_TIME_FORMAT" for stream: %d",
2175 GST_TIME_ARGS(stop_time), stop_idx);
2177 overlap_text = g_string_new ("");
2178 overlap = overlap | (1<<min_stream);
2179 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2180 int finder = 1<<idx;
2181 if(overlap & finder) {
2182 GST_DEBUG_OBJECT (submux, "append string: %s....", (gchar*)GST_BUFFER_DATA(submux->cur_buf_array[idx]));
2183 g_string_append (overlap_text, (gchar*)GST_BUFFER_DATA (submux->cur_buf_array[idx]));
2184 GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+= (stop_time - min_timestamp);
2185 GST_BUFFER_DURATION(submux->cur_buf_array[idx])-= (stop_time - min_timestamp);
2186 if(overlap > (1<<(idx+1))) g_string_append_c (overlap_text, '\n');
2189 text = g_string_free (overlap_text, FALSE);
2190 overlap_buf_length = strlen(text);
2191 overlap_buf = gst_buffer_new_and_alloc (overlap_buf_length + 1);
2192 memcpy (GST_BUFFER_DATA (overlap_buf), text, overlap_buf_length + 1);
2193 overlap_buf->timestamp = min_timestamp;
2194 overlap_buf->duration = stop_time - min_timestamp;
2197 submux->cur_buf_array[stop_idx] = NULL;
2198 GST_DEBUG_OBJECT (submux, "pushing string: %s....", (gchar*)GST_BUFFER_DATA(overlap_buf));
2199 push_list = g_list_append(push_list, overlap_buf);
2200 GST_DEBUG_OBJECT (submux, "Overlap found pushing merged buffer of ts = %"GST_TIME_FORMAT", dur = %"GST_TIME_FORMAT,
2201 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(overlap_buf)), GST_TIME_ARGS(GST_BUFFER_DURATION(overlap_buf)));
2202 GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", stop_idx);
2203 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2204 if(submux->cur_buf_array[idx] &&
2205 ((GST_BUFFER_TIMESTAMP(submux->cur_buf_array[idx])+ GST_BUFFER_DURATION(submux->cur_buf_array[idx])) <= stop_time)) {
2206 submux->cur_buf_array[idx] = NULL;
2207 GST_DEBUG_OBJECT (submux, "request for new buffer for stream %d", idx);
2214 static void gst_submux_loop (Gstsubmux *submux)
2217 GstBuffer *src_buffer = NULL;
2218 GstBuffer *temp_buffer = NULL;
2219 GstBuffer *check_buffer = NULL;
2220 GstSubmuxStream *cur_stream = NULL;
2221 GstSubmuxStream *check_stream = NULL;
2222 gboolean match = FALSE;
2223 GstFlowReturn fret = GST_FLOW_OK;
2224 GstClockTime cur_duration = 0 ;
2225 GstClockTime cur_ts = 0;
2226 gboolean eos = TRUE;
2228 GList *push_list = NULL;
2229 GstBuffer *push_buf = NULL;
2231 if (!submux->priv->first_buffer) {
2232 GST_INFO_OBJECT (submux, "exiting from lopp");
2236 if (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI) {
2237 length = g_list_length (submux->priv->lang_list);
2239 length = submux->sinkpads_count;
2242 gboolean made = FALSE;
2245 for (i = 0; i < length; i++) {
2248 cur_stream = g_list_nth_data (submux->streams, i);
2249 GST_DEBUG_OBJECT (submux, "Before lock acquired in loop stream[%d]", i);
2250 g_mutex_lock (cur_stream->queue_lock);
2251 GST_DEBUG_OBJECT (submux, "Lock acquired in loop stream[%d]", i);
2253 if (g_queue_is_empty (cur_stream->queue) && !submux->flushing) {
2254 GST_DEBUG_OBJECT (submux, "Queue is empty, waiting for the condition signal stream[%d]", i);
2255 g_cond_wait (cur_stream->queue_empty, cur_stream->queue_lock);
2257 GST_DEBUG_OBJECT (submux, "Got the queue condition signal stream[%d]", i);
2259 if (submux->flushing || submux->stop_loop) {
2260 GST_DEBUG_OBJECT (submux, "Flushing going on in loop");
2261 GST_DEBUG_OBJECT (submux, "Got the condition signal");
2262 g_mutex_unlock (cur_stream->queue_lock);
2266 check_buffer = g_queue_peek_head (cur_stream->queue);
2267 if (!strcmp ((const char*)GST_BUFFER_DATA (check_buffer), "eos")){
2268 cur_stream->eos_came = TRUE;
2269 GST_DEBUG_OBJECT (submux, "Eos recieved for stream");
2271 for (k = 0; k < length; k++) {
2272 check_stream = g_list_nth_data(submux->streams, k);
2273 if (!check_stream->eos_came) {
2281 GST_DEBUG_OBJECT (submux, "Sending EOS to submux srcpad");
2282 gst_pad_push_event(submux->srcpad, gst_event_new_eos ());
2283 g_mutex_unlock (cur_stream->queue_lock);
2287 if (!cur_stream->eos_came && (submux->priv->parser_type == GST_SUB_PARSE_FORMAT_SAMI ||
2288 submux->priv->is_internal)) {
2289 GstLangStruct *lang = NULL;
2290 if (submux->priv->lang_list) {
2291 if (submux->cur_buf_array[i] == NULL) {
2292 check_buffer = g_queue_pop_head (cur_stream->queue);
2293 lang = g_list_nth_data(submux->priv->lang_list, i);
2294 if (!lang->active) {
2296 gst_buffer_unref(check_buffer);
2297 check_buffer = NULL;
2298 GST_DEBUG_OBJECT (submux, "unreffing the non-active stream[%d] buffer", i);
2300 submux->cur_buf_array[i] = NULL;
2301 g_mutex_unlock (cur_stream->queue_lock);
2302 GST_DEBUG_OBJECT(submux,"rejecting not selected language");
2305 if (!check_buffer) {
2306 GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
2307 g_mutex_unlock (cur_stream->queue_lock);
2310 if (!GST_BUFFER_DURATION(check_buffer)) {
2311 GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
2312 gst_buffer_unref (check_buffer);
2313 g_mutex_unlock (cur_stream->queue_lock);
2316 submux->cur_buf_array[i] = check_buffer;
2317 GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
2318 i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
2322 g_mutex_unlock (cur_stream->queue_lock);
2323 GST_DEBUG_OBJECT(submux,"Coming to Else case lang submux->priv->lang_list %x ",submux->priv->lang_list);
2326 } else if (!cur_stream->eos_came) {
2327 /* External subtitle format other than smi */
2328 if (submux->sinkpad) {
2329 if (submux->cur_buf_array[i] == NULL) {
2330 check_buffer = g_queue_pop_head (cur_stream->queue);
2331 if (!check_buffer) {
2332 GST_WARNING_OBJECT (submux, "checkbuffer null.. repop");
2333 g_mutex_unlock (cur_stream->queue_lock);
2336 if (!GST_BUFFER_DURATION (check_buffer)) {
2337 GST_WARNING_OBJECT (submux, "duration of buffer is zero..re-pop");
2338 gst_buffer_unref (check_buffer);
2339 g_mutex_unlock (cur_stream->queue_lock);
2342 submux->cur_buf_array[i] = check_buffer;
2343 GST_DEBUG_OBJECT (submux, "consuming active stream [%d] buffer : ts = %"GST_TIME_FORMAT"and dur = %"GST_TIME_FORMAT,
2344 i, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (check_buffer)), GST_TIME_ARGS(GST_BUFFER_DURATION (check_buffer)));
2347 g_mutex_unlock (cur_stream->queue_lock);
2348 GST_DEBUG_OBJECT(submux,"Coming to Else case submux->sinkpad %x ",submux->sinkpad);
2352 GST_DEBUG_OBJECT (submux, "already received EOS on this stream[%d] and cur_buf_array[idx] = NULL", i);
2353 submux->cur_buf_array[i] = NULL;
2356 g_mutex_unlock (cur_stream->queue_lock);
2357 GST_DEBUG_OBJECT (submux, "After unlocking in loop and signal queue full");
2360 push_list = gst_submux_muxing (submux);
2363 GST_LOG_OBJECT (submux, "length of push list = %d", g_list_length (push_list));
2365 for (idx = 0; idx < g_list_length (push_list); idx++) {
2366 push_buf = g_list_nth_data (push_list, idx);
2369 GST_DEBUG_OBJECT (submux, "pushing buffer : ts = %"GST_TIME_FORMAT", "
2370 "dur = %"GST_TIME_FORMAT" and data %s ...",
2371 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (push_buf)),
2372 GST_TIME_ARGS (GST_BUFFER_DURATION (push_buf)), (gchar*)GST_BUFFER_DATA (push_buf));
2374 fret = gst_pad_push (submux->srcpad, push_buf);
2375 if (fret != GST_FLOW_OK) {
2376 GST_ERROR_OBJECT (submux, "failed to push buffer. reason : %s", gst_flow_get_name (fret));
2377 /* clean any left buffers in push_list */
2379 for (; idx < g_list_length (push_list); idx++) {
2380 push_buf = g_list_nth_data (push_list, idx);
2381 gst_buffer_unref (push_buf);
2383 g_list_free (push_list);
2389 g_list_free (push_list);
2392 GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
2398 GST_WARNING_OBJECT (submux->srcpad, "Pausing the push task...");
2399 if (fret < GST_FLOW_UNEXPECTED) {
2400 GST_ERROR_OBJECT (submux, "Crtical error in push loop....");
2401 GST_ELEMENT_ERROR (submux, CORE, PAD, ("failed to push. reason - %s", gst_flow_get_name (fret)), (NULL));
2403 gst_pad_pause_task (submux->srcpad);
2404 GST_DEBUG_OBJECT (submux, "Exiting from lopp in last");
2408 ////////////////////////////////////////////////////////
2409 // Plugin Utility Functions //
2410 ////////////////////////////////////////////////////////
2413 ** Description : De-Initializing the submux private structure
2414 ** Params : (1) submux instance
2420 gst_submux_deinit_private_values(Gstsubmux *submux)
2423 GST_DEBUG_OBJECT (submux, "deinit priv values");
2425 submux->priv->first_buffer = FALSE;
2426 submux->priv->parser_type = 0;
2427 if (submux->priv->lang_list && !submux->priv->is_internal) {
2428 g_list_free (submux->priv->lang_list);
2429 submux->priv->lang_list = NULL;
2431 for (idx = 0; idx < submux->priv->stream_count; idx++) {
2432 submux->cur_buf_array[idx] = NULL;
2434 if (submux->cur_buf_array) {
2435 g_free (submux->cur_buf_array);
2436 submux->cur_buf_array = NULL;
2439 submux->priv->is_internal = FALSE;
2440 submux->priv->stream_count = 0;
2446 gst_submux_plugin_init (GstPlugin * plugin)
2448 return gst_element_register (plugin, "submux", GST_RANK_PRIMARY, GST_TYPE_SUBMUX);
2451 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")