taglist, plugins: fix compiler warnings with GLib >= 2.76
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / teletextdec / gstteletextdec.c
1 /*
2  * GStreamer
3  * Copyright (C) 2009 Sebastian Pölsterl <sebp@k-d-w.org>
4  * Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  */
21
22 /**
23  * SECTION:element-teletextdec
24  * @title: teletextdec
25  *
26  * Decode a stream of raw VBI packets containing teletext information to a RGBA
27  * stream.
28  *
29  * ## Example launch line
30  * |[
31  * gst-launch-1.0 -v -m filesrc location=recording.mpeg ! tsdemux ! teletextdec ! videoconvert ! ximagesink
32  * ]|
33  *
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <gst/gst.h>
41 #include <gst/video/video.h>
42 #include <string.h>
43 #include <stdlib.h>
44
45 #include "gstteletextdec.h"
46
47 GST_DEBUG_CATEGORY_STATIC (gst_teletextdec_debug);
48 #define GST_CAT_DEFAULT gst_teletextdec_debug
49
50 #define parent_class gst_teletextdec_parent_class
51
52 #define SUBTITLES_PAGE 888
53 #define MAX_SLICES 32
54 #define DEFAULT_FONT_DESCRIPTION "verdana 12"
55 #define PANGO_TEMPLATE "<span font_desc=\"%s\" foreground=\"%s\"> %s \n</span>"
56
57 /* Filter signals and args */
58 enum
59 {
60   LAST_SIGNAL
61 };
62
63 enum
64 {
65   PROP_0,
66   PROP_PAGENO,
67   PROP_SUBNO,
68   PROP_SUBTITLES_MODE,
69   PROP_SUBS_TEMPLATE,
70   PROP_FONT_DESCRIPTION
71 };
72
73 enum
74 {
75   VBI_ERROR = -1,
76   VBI_SUCCESS = 0,
77   VBI_NEW_FRAME = 1
78 };
79
80 typedef enum
81 {
82   DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE = 0x02,
83   DATA_UNIT_EBU_TELETEXT_SUBTITLE = 0x03,
84   DATA_UNIT_EBU_TELETEXT_INVERTED = 0x0C,
85
86   DATA_UNIT_ZVBI_WSS_CPR1204 = 0xB4,
87   DATA_UNIT_ZVBI_CLOSED_CAPTION_525 = 0xB5,
88   DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525 = 0xB6,
89
90   DATA_UNIT_VPS = 0xC3,
91   DATA_UNIT_WSS = 0xC4,
92   DATA_UNIT_CLOSED_CAPTION = 0xC5,
93   DATA_UNIT_MONOCHROME_SAMPLES = 0xC6,
94
95   DATA_UNIT_STUFFING = 0xFF,
96 } data_unit_id;
97
98 typedef struct
99 {
100   int pgno;
101   int subno;
102 } page_info;
103
104 typedef enum
105 {
106   SYSTEM_525 = 0,
107   SYSTEM_625
108 } systems;
109
110 /*
111  *  ETS 300 706 Table 30: Colour Map
112  */
113 static const gchar *default_color_map[40] = {
114   "#000000", "#FF0000", "#00FF00", "#FFFF00", "#0000FF",
115   "#FF00FF", "#00FFFF", "#FFFFFF", "#000000", "#770000",
116   "#007700", "#777700", "#000077", "#770077", "#007777",
117   "#777777", "#FF0055", "#FF7700", "#00FF77", "#FFFFBB",
118   "#00CCAA", "#550000", "#665522", "#CC7777", "#333333",
119   "#FF7777", "#77FF77", "#FFFF77", "#7777FF", "#FF77FF",
120   "#77FFFF", "#DDD0DD",
121
122   /* Private colors */
123   "#000000", "#FFAA99", "#44EE00", "#FFDD00", "#FFAA99",
124   "#FF00FF", "#00FFFF", "#EEEEEE"
125 };
126
127 /* in RGBA mode, one character occupies 12 x 10 pixels. */
128 #define COLUMNS_TO_WIDTH(cols) ((cols) * 12)
129 #define ROWS_TO_HEIGHT(rows) ((rows) * 10)
130
131 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
132     GST_PAD_SINK,
133     GST_PAD_ALWAYS,
134     GST_STATIC_CAPS ("application/x-teletext;")
135     );
136
137 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
138     GST_PAD_SRC,
139     GST_PAD_ALWAYS,
140     GST_STATIC_CAPS
141     (GST_VIDEO_CAPS_MAKE ("RGBA") ";"
142         "text/x-raw, format={utf-8,pango-markup} ;")
143     );
144
145 G_DEFINE_TYPE (GstTeletextDec, gst_teletextdec, GST_TYPE_ELEMENT);
146
147 static void gst_teletextdec_set_property (GObject * object, guint prop_id,
148     const GValue * value, GParamSpec * pspec);
149 static void gst_teletextdec_get_property (GObject * object, guint prop_id,
150     GValue * value, GParamSpec * pspec);
151 static void gst_teletextdec_finalize (GObject * object);
152
153 static GstStateChangeReturn gst_teletextdec_change_state (GstElement *
154     element, GstStateChange transition);
155
156 static GstFlowReturn gst_teletextdec_chain (GstPad * pad, GstObject * parent,
157     GstBuffer * buffer);
158 static gboolean gst_teletextdec_sink_event (GstPad * pad, GstObject * parent,
159     GstEvent * event);
160 static gboolean gst_teletextdec_src_event (GstPad * pad, GstObject * parent,
161     GstEvent * event);
162
163 static void gst_teletextdec_event_handler (vbi_event * ev, void *user_data);
164
165 static GstFlowReturn gst_teletextdec_push_page (GstTeletextDec * teletext);
166 static GstFlowReturn gst_teletextdec_export_text_page (GstTeletextDec *
167     teletext, vbi_page * page, GstBuffer ** buf);
168 static GstFlowReturn gst_teletextdec_export_rgba_page (GstTeletextDec *
169     teletext, vbi_page * page, GstBuffer ** buf);
170 static GstFlowReturn gst_teletextdec_export_pango_page (GstTeletextDec *
171     teletext, vbi_page * page, GstBuffer ** buf);
172
173 static void gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext,
174     GstBuffer * buf);
175 static gboolean gst_teletextdec_extract_data_units (GstTeletextDec *
176     teletext, GstTeletextFrame * f, const guint8 * packet, guint * offset,
177     gsize size);
178
179 static void gst_teletextdec_zvbi_init (GstTeletextDec * teletext);
180 static void gst_teletextdec_zvbi_clear (GstTeletextDec * teletext);
181 static void gst_teletextdec_reset_frame (GstTeletextDec * teletext);
182
183 /* initialize the gstteletext's class */
184 static void
185 gst_teletextdec_class_init (GstTeletextDecClass * klass)
186 {
187   GObjectClass *gobject_class;
188   GstElementClass *gstelement_class;
189
190   gobject_class = G_OBJECT_CLASS (klass);
191   gobject_class->set_property = gst_teletextdec_set_property;
192   gobject_class->get_property = gst_teletextdec_get_property;
193   gobject_class->finalize = gst_teletextdec_finalize;
194
195   gstelement_class = GST_ELEMENT_CLASS (klass);
196   gstelement_class->change_state =
197       GST_DEBUG_FUNCPTR (gst_teletextdec_change_state);
198
199   g_object_class_install_property (gobject_class, PROP_PAGENO,
200       g_param_spec_int ("page", "Page number",
201           "Number of page that should displayed",
202           100, 999, 100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203
204   g_object_class_install_property (gobject_class, PROP_SUBNO,
205       g_param_spec_int ("subpage", "Sub-page number",
206           "Number of sub-page that should displayed (-1 for all)",
207           -1, 0x99, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208
209   g_object_class_install_property (gobject_class, PROP_SUBTITLES_MODE,
210       g_param_spec_boolean ("subtitles-mode", "Enable subtitles mode",
211           "Enables subtitles mode for text output stripping the blank lines and "
212           "the teletext state lines", FALSE,
213           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214
215   g_object_class_install_property (gobject_class, PROP_SUBS_TEMPLATE,
216       g_param_spec_string ("subtitles-template", "Subtitles output template",
217           "Output template used to print each one of the subtitles lines",
218           "%s\\n", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219
220   g_object_class_install_property (gobject_class, PROP_FONT_DESCRIPTION,
221       g_param_spec_string ("font-description", "Pango font description",
222           "Font description used for the pango output.",
223           DEFAULT_FONT_DESCRIPTION,
224           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
225
226   gst_element_class_set_static_metadata (gstelement_class,
227       "Teletext decoder",
228       "Decoder",
229       "Decode a raw VBI stream containing teletext information to RGBA and text",
230       "Sebastian Pölsterl <sebp@k-d-w.org>, "
231       "Andoni Morales Alastruey <ylatuya@gmail.com>");
232
233   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
234   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
235 }
236
237 /* initialize the new element
238  * initialize instance structure
239  */
240 static void
241 gst_teletextdec_init (GstTeletextDec * teletext)
242 {
243   /* Create sink pad */
244   teletext->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
245   gst_pad_set_chain_function (teletext->sinkpad,
246       GST_DEBUG_FUNCPTR (gst_teletextdec_chain));
247   gst_pad_set_event_function (teletext->sinkpad,
248       GST_DEBUG_FUNCPTR (gst_teletextdec_sink_event));
249   gst_element_add_pad (GST_ELEMENT (teletext), teletext->sinkpad);
250
251   /* Create src pad */
252   teletext->srcpad = gst_pad_new_from_static_template (&src_template, "src");
253   gst_pad_set_event_function (teletext->srcpad,
254       GST_DEBUG_FUNCPTR (gst_teletextdec_src_event));
255   gst_element_add_pad (GST_ELEMENT (teletext), teletext->srcpad);
256
257   teletext->segment = NULL;
258   teletext->decoder = NULL;
259   teletext->pageno = 0x100;
260   teletext->subno = -1;
261   teletext->subtitles_mode = FALSE;
262   teletext->subtitles_template = g_strescape ("%s\n", NULL);
263   teletext->font_description = g_strdup (DEFAULT_FONT_DESCRIPTION);
264
265   teletext->in_timestamp = GST_CLOCK_TIME_NONE;
266   teletext->in_duration = GST_CLOCK_TIME_NONE;
267
268   teletext->rate_numerator = 0;
269   teletext->rate_denominator = 1;
270
271   teletext->queue = NULL;
272   g_mutex_init (&teletext->queue_lock);
273
274   gst_teletextdec_reset_frame (teletext);
275
276   teletext->last_ts = 0;
277
278   teletext->export_func = NULL;
279   teletext->buf_pool = NULL;
280 }
281
282 static void
283 gst_teletextdec_finalize (GObject * object)
284 {
285   GstTeletextDec *teletext = GST_TELETEXTDEC (object);
286
287   g_mutex_clear (&teletext->queue_lock);
288
289   g_free (teletext->font_description);
290   g_free (teletext->subtitles_template);
291   g_free (teletext->frame);
292
293   G_OBJECT_CLASS (parent_class)->finalize (object);
294 }
295
296 static void
297 gst_teletextdec_zvbi_init (GstTeletextDec * teletext)
298 {
299   g_return_if_fail (teletext != NULL);
300
301   GST_LOG_OBJECT (teletext, "Initializing structures");
302
303   teletext->decoder = vbi_decoder_new ();
304
305   vbi_event_handler_register (teletext->decoder,
306       VBI_EVENT_TTX_PAGE | VBI_EVENT_CAPTION,
307       gst_teletextdec_event_handler, teletext);
308
309   g_mutex_lock (&teletext->queue_lock);
310   teletext->queue = g_queue_new ();
311   g_mutex_unlock (&teletext->queue_lock);
312 }
313
314 static void
315 gst_teletextdec_zvbi_clear (GstTeletextDec * teletext)
316 {
317   g_return_if_fail (teletext != NULL);
318
319   GST_LOG_OBJECT (teletext, "Clearing structures");
320
321   if (teletext->decoder != NULL) {
322     vbi_decoder_delete (teletext->decoder);
323     teletext->decoder = NULL;
324   }
325   if (teletext->frame != NULL) {
326     if (teletext->frame->sliced_begin)
327       g_free (teletext->frame->sliced_begin);
328     g_free (teletext->frame);
329     teletext->frame = NULL;
330   }
331
332   g_mutex_lock (&teletext->queue_lock);
333   if (teletext->queue != NULL) {
334     g_queue_free (teletext->queue);
335     teletext->queue = NULL;
336   }
337   g_mutex_unlock (&teletext->queue_lock);
338
339   teletext->in_timestamp = GST_CLOCK_TIME_NONE;
340   teletext->in_duration = GST_CLOCK_TIME_NONE;
341   teletext->pageno = 0x100;
342   teletext->subno = -1;
343   teletext->last_ts = 0;
344 }
345
346 static void
347 gst_teletextdec_set_property (GObject * object, guint prop_id,
348     const GValue * value, GParamSpec * pspec)
349 {
350   GstTeletextDec *teletext = GST_TELETEXTDEC (object);
351
352   switch (prop_id) {
353     case PROP_PAGENO:
354       teletext->pageno = (gint) vbi_bin2bcd (g_value_get_int (value));
355       break;
356     case PROP_SUBNO:
357       teletext->subno = g_value_get_int (value);
358       break;
359     case PROP_SUBTITLES_MODE:
360       teletext->subtitles_mode = g_value_get_boolean (value);
361       break;
362     case PROP_SUBS_TEMPLATE:
363       g_free (teletext->subtitles_template);
364       teletext->subtitles_template = g_value_dup_string (value);
365       break;
366     case PROP_FONT_DESCRIPTION:
367       g_free (teletext->font_description);
368       teletext->font_description = g_value_dup_string (value);
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373   }
374 }
375
376 static void
377 gst_teletextdec_get_property (GObject * object, guint prop_id,
378     GValue * value, GParamSpec * pspec)
379 {
380   GstTeletextDec *teletext = GST_TELETEXTDEC (object);
381
382   switch (prop_id) {
383     case PROP_PAGENO:
384       g_value_set_int (value, (gint) vbi_bcd2dec (teletext->pageno));
385       break;
386     case PROP_SUBNO:
387       g_value_set_int (value, teletext->subno);
388       break;
389     case PROP_SUBTITLES_MODE:
390       g_value_set_boolean (value, teletext->subtitles_mode);
391       break;
392     case PROP_SUBS_TEMPLATE:
393       g_value_set_string (value, teletext->subtitles_template);
394       break;
395     case PROP_FONT_DESCRIPTION:
396       g_value_set_string (value, teletext->font_description);
397       break;
398     default:
399       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400       break;
401   }
402 }
403
404 static gboolean
405 gst_teletextdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
406 {
407   gboolean ret;
408   GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
409
410   GST_DEBUG_OBJECT (teletext, "got event %s",
411       gst_event_type_get_name (GST_EVENT_TYPE (event)));
412
413   switch (GST_EVENT_TYPE (event)) {
414     case GST_EVENT_SEGMENT:
415       /* maybe save and/or update the current segment (e.g. for output
416        * clipping) or convert the event into one in a different format
417        * (e.g. BYTES to TIME) or drop it and set a flag to send a newsegment
418        * event in a different format later */
419       if (NULL == teletext->export_func) {
420         /* save the segment event and send it after sending caps. replace the
421          * old event if present. */
422         if (teletext->segment) {
423           gst_event_unref (teletext->segment);
424         }
425         teletext->segment = event;
426         ret = TRUE;
427       } else {
428         ret = gst_pad_push_event (teletext->srcpad, event);
429       }
430       break;
431     case GST_EVENT_EOS:
432       /* end-of-stream, we should close down all stream leftovers here */
433       gst_teletextdec_zvbi_clear (teletext);
434       ret = gst_pad_push_event (teletext->srcpad, event);
435       break;
436     case GST_EVENT_FLUSH_STOP:
437       gst_teletextdec_zvbi_clear (teletext);
438       gst_teletextdec_zvbi_init (teletext);
439       ret = gst_pad_push_event (teletext->srcpad, event);
440       break;
441     default:
442       ret = gst_pad_event_default (pad, parent, event);
443       break;
444   }
445
446   return ret;
447 }
448
449 static gboolean
450 gst_teletextdec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
451 {
452   gboolean ret;
453   GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
454
455   switch (GST_EVENT_TYPE (event)) {
456     case GST_EVENT_RECONFIGURE:
457       /* setting export_func to NULL will cause the element to renegotiate caps
458        * before pushing a buffer. */
459       teletext->export_func = NULL;
460       ret = TRUE;
461       break;
462
463     default:
464       ret = gst_pad_event_default (pad, parent, event);
465       break;
466   }
467   return ret;
468 }
469
470 static GstStateChangeReturn
471 gst_teletextdec_change_state (GstElement * element, GstStateChange transition)
472 {
473   GstStateChangeReturn ret;
474   GstTeletextDec *teletext;
475
476   teletext = GST_TELETEXTDEC (element);
477
478   switch (transition) {
479     case GST_STATE_CHANGE_READY_TO_PAUSED:
480       gst_teletextdec_zvbi_init (teletext);
481       break;
482     default:
483       break;
484   }
485
486   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
487   if (ret != GST_STATE_CHANGE_SUCCESS)
488     return ret;
489
490   switch (transition) {
491     case GST_STATE_CHANGE_PAUSED_TO_READY:
492       gst_teletextdec_zvbi_clear (teletext);
493       break;
494     default:
495       break;
496   }
497
498   return ret;
499 }
500
501 static void
502 gst_teletextdec_reset_frame (GstTeletextDec * teletext)
503 {
504   if (teletext->frame == NULL)
505     teletext->frame = g_new0 (GstTeletextFrame, 1);
506   if (teletext->frame->sliced_begin == NULL)
507     teletext->frame->sliced_begin = g_new (vbi_sliced, MAX_SLICES);
508   teletext->frame->current_slice = teletext->frame->sliced_begin;
509   teletext->frame->sliced_end = teletext->frame->sliced_begin + MAX_SLICES;
510   teletext->frame->last_field = 0;
511   teletext->frame->last_field_line = 0;
512   teletext->frame->last_frame_line = 0;
513 }
514
515 static void
516 gst_teletextdec_process_telx_buffer (GstTeletextDec * teletext, GstBuffer * buf)
517 {
518   GstMapInfo buf_map;
519   guint offset = 0;
520   gint res;
521   gst_buffer_map (buf, &buf_map, GST_MAP_READ);
522
523   teletext->in_timestamp = GST_BUFFER_TIMESTAMP (buf);
524   teletext->in_duration = GST_BUFFER_DURATION (buf);
525
526   if (teletext->frame == NULL)
527     gst_teletextdec_reset_frame (teletext);
528
529   while (offset < buf_map.size) {
530     res =
531         gst_teletextdec_extract_data_units (teletext, teletext->frame,
532         buf_map.data, &offset, buf_map.size);
533
534     if (res == VBI_NEW_FRAME) {
535       /* We have a new frame, it's time to feed the decoder */
536       vbi_sliced *s;
537       gint n_lines;
538
539       n_lines = teletext->frame->current_slice - teletext->frame->sliced_begin;
540       GST_LOG_OBJECT (teletext, "Completed frame, decoding new %d lines",
541           n_lines);
542       s = g_memdup2 (teletext->frame->sliced_begin,
543           n_lines * sizeof (vbi_sliced));
544       vbi_decode (teletext->decoder, s, n_lines, teletext->last_ts);
545       /* From vbi_decode():
546        * timestamp shall advance by 1/30 to 1/25 seconds whenever calling this
547        * function. Failure to do so will be interpreted as frame dropping, which
548        * starts a resynchronization cycle, eventually a channel switch may be assumed
549        * which resets even more decoder state. So even if a frame did not contain
550        * any useful data this function must be called, with lines set to zero.
551        */
552       teletext->last_ts += 0.04;
553
554       g_free (s);
555       gst_teletextdec_reset_frame (teletext);
556     } else if (res == VBI_ERROR) {
557       gst_teletextdec_reset_frame (teletext);
558       goto beach;
559     }
560   }
561 beach:
562   gst_buffer_unmap (buf, &buf_map);
563   return;
564 }
565
566 static void
567 gst_teletextdec_event_handler (vbi_event * ev, void *user_data)
568 {
569   page_info *pi;
570   vbi_pgno pgno;
571   vbi_subno subno;
572
573   GstTeletextDec *teletext = GST_TELETEXTDEC (user_data);
574
575   switch (ev->type) {
576     case VBI_EVENT_TTX_PAGE:
577       pgno = ev->ev.ttx_page.pgno;
578       subno = ev->ev.ttx_page.subno;
579
580       if (pgno != teletext->pageno
581           || (teletext->subno != -1 && subno != teletext->subno))
582         return;
583
584       GST_DEBUG_OBJECT (teletext, "Received teletext page %03d.%02d",
585           (gint) vbi_bcd2dec (pgno), (gint) vbi_bcd2dec (subno));
586
587       pi = g_new (page_info, 1);
588       pi->pgno = pgno;
589       pi->subno = subno;
590
591       g_mutex_lock (&teletext->queue_lock);
592       g_queue_push_tail (teletext->queue, pi);
593       g_mutex_unlock (&teletext->queue_lock);
594       break;
595     case VBI_EVENT_CAPTION:
596       /* TODO: Handle subtitles in caption teletext pages */
597       GST_DEBUG_OBJECT (teletext, "Received caption page. Not implemented");
598       break;
599     default:
600       break;
601   }
602   return;
603 }
604
605 static void
606 gst_teletextdec_try_get_buffer_pool (GstTeletextDec * teletext, GstCaps * caps,
607     gssize size)
608 {
609   guint pool_bufsize, min_bufs, max_bufs;
610   GstStructure *poolcfg;
611   GstBufferPool *new_pool;
612   GstQuery *alloc = gst_query_new_allocation (caps, TRUE);
613
614   if (teletext->buf_pool) {
615     /* this function is called only on a caps/size change, so it's practically
616      * impossible that we'll be able to reuse the old pool. */
617     gst_buffer_pool_set_active (teletext->buf_pool, FALSE);
618     gst_object_unref (teletext->buf_pool);
619   }
620
621   if (!gst_pad_peer_query (teletext->srcpad, alloc)) {
622     GST_DEBUG_OBJECT (teletext, "Failed to query peer pad for allocation "
623         "parameters");
624     teletext->buf_pool = NULL;
625     goto beach;
626   }
627
628   if (gst_query_get_n_allocation_pools (alloc) > 0) {
629     gst_query_parse_nth_allocation_pool (alloc, 0, &new_pool, &pool_bufsize,
630         &min_bufs, &max_bufs);
631   } else {
632     new_pool = gst_buffer_pool_new ();
633     max_bufs = 0;
634     min_bufs = 1;
635   }
636
637   poolcfg = gst_buffer_pool_get_config (new_pool);
638   gst_buffer_pool_config_set_params (poolcfg, gst_caps_copy (caps), size,
639       min_bufs, max_bufs);
640   if (!gst_buffer_pool_set_config (new_pool, poolcfg)) {
641     GST_DEBUG_OBJECT (teletext, "Failed to configure the buffer pool");
642     gst_object_unref (new_pool);
643     teletext->buf_pool = NULL;
644     goto beach;
645   }
646   if (!gst_buffer_pool_set_active (new_pool, TRUE)) {
647     GST_DEBUG_OBJECT (teletext, "Failed to make the buffer pool active");
648     gst_object_unref (new_pool);
649     teletext->buf_pool = NULL;
650     goto beach;
651   }
652
653   teletext->buf_pool = new_pool;
654
655 beach:
656   gst_query_unref (alloc);
657 }
658
659 static gboolean
660 gst_teletextdec_negotiate_caps (GstTeletextDec * teletext, guint width,
661     guint height)
662 {
663   gboolean rv = FALSE;
664   /* get the peer's caps filtered by our own ones. */
665   GstCaps *ourcaps = gst_pad_query_caps (teletext->srcpad, NULL);
666   GstCaps *peercaps = gst_pad_peer_query_caps (teletext->srcpad, ourcaps);
667   GstStructure *caps_struct;
668   const gchar *caps_name, *caps_fmt;
669
670   gst_caps_unref (ourcaps);
671
672   if (gst_caps_is_empty (peercaps)) {
673     goto beach;
674   }
675
676   /* make them writable in case we need to fixate them (video/x-raw). */
677   peercaps = gst_caps_make_writable (peercaps);
678   caps_struct = gst_caps_get_structure (peercaps, 0);
679   caps_name = gst_structure_get_name (caps_struct);
680   caps_fmt = gst_structure_get_string (caps_struct, "format");
681
682   if (!g_strcmp0 (caps_name, "video/x-raw")) {
683     teletext->width = width;
684     teletext->height = height;
685     teletext->export_func = gst_teletextdec_export_rgba_page;
686     gst_structure_set (caps_struct,
687         "width", G_TYPE_INT, width,
688         "height", G_TYPE_INT, height,
689         "framerate", GST_TYPE_FRACTION, 0, 1, NULL);
690   } else if (!g_strcmp0 (caps_name, "text/x-raw") &&
691       !g_strcmp0 (caps_fmt, "utf-8")) {
692     teletext->export_func = gst_teletextdec_export_text_page;
693   } else if (!g_strcmp0 (caps_name, "text/x-raw") &&
694       !g_strcmp0 (caps_fmt, "pango-markup")) {
695     teletext->export_func = gst_teletextdec_export_pango_page;
696   } else {
697     goto beach;
698   }
699
700   if (!gst_pad_push_event (teletext->srcpad, gst_event_new_caps (peercaps))) {
701     goto beach;
702   }
703
704   /* try to get a bufferpool from the peer pad in case of RGBA output. */
705   if (gst_teletextdec_export_rgba_page == teletext->export_func) {
706     gst_teletextdec_try_get_buffer_pool (teletext, peercaps,
707         width * height * sizeof (vbi_rgba));
708   }
709
710   /* we can happily return a success now. */
711   rv = TRUE;
712
713 beach:
714   gst_caps_unref (peercaps);
715   return rv;
716 }
717
718 /* this function does the actual processing
719  */
720 static GstFlowReturn
721 gst_teletextdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
722 {
723   GstTeletextDec *teletext = GST_TELETEXTDEC (parent);
724   GstFlowReturn ret = GST_FLOW_OK;
725
726   teletext->in_timestamp = GST_BUFFER_TIMESTAMP (buf);
727   teletext->in_duration = GST_BUFFER_DURATION (buf);
728
729   gst_teletextdec_process_telx_buffer (teletext, buf);
730   gst_buffer_unref (buf);
731
732   g_mutex_lock (&teletext->queue_lock);
733   if (!g_queue_is_empty (teletext->queue)) {
734     ret = gst_teletextdec_push_page (teletext);
735     if (ret != GST_FLOW_OK) {
736       g_mutex_unlock (&teletext->queue_lock);
737       goto error;
738     }
739   }
740   g_mutex_unlock (&teletext->queue_lock);
741
742   return ret;
743
744 /* ERRORS */
745 error:
746   {
747     if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED
748         && ret != GST_FLOW_FLUSHING) {
749       GST_ELEMENT_FLOW_ERROR (teletext, ret);
750       return GST_FLOW_ERROR;
751     }
752     return ret;
753   }
754 }
755
756 static GstFlowReturn
757 gst_teletextdec_push_page (GstTeletextDec * teletext)
758 {
759   GstFlowReturn ret = GST_FLOW_OK;
760   GstBuffer *buf;
761   vbi_page page;
762   page_info *pi;
763   gint pgno, subno;
764   gboolean success;
765   guint width, height;
766
767   pi = g_queue_pop_head (teletext->queue);
768   pgno = vbi_bcd2dec (pi->pgno);
769   subno = vbi_bcd2dec (pi->subno);
770
771   GST_INFO_OBJECT (teletext, "Fetching teletext page %03d.%02d", pgno, subno);
772
773   success = vbi_fetch_vt_page (teletext->decoder, &page, pi->pgno, pi->subno,
774       VBI_WST_LEVEL_3p5, 25, FALSE);
775   g_free (pi);
776   if (G_UNLIKELY (!success))
777     goto fetch_page_failed;
778
779   width = COLUMNS_TO_WIDTH (page.columns);
780   height = ROWS_TO_HEIGHT (page.rows);
781
782   /* if output_func is NULL, we need to (re-)negotiate. also, it is possible
783    * (though unlikely) that we received a page of a different size. */
784   if (G_UNLIKELY (NULL == teletext->export_func ||
785           teletext->width != width || teletext->height != height)) {
786     /* if negotiate_caps returns FALSE, that means we weren't able to
787      * negotiate. */
788     if (G_UNLIKELY (!gst_teletextdec_negotiate_caps (teletext, width, height))) {
789       ret = GST_FLOW_NOT_NEGOTIATED;
790       goto push_failed;
791     }
792     if (G_UNLIKELY (teletext->segment)) {
793       gst_pad_push_event (teletext->srcpad, teletext->segment);
794       teletext->segment = NULL;
795     }
796   }
797
798   teletext->export_func (teletext, &page, &buf);
799   vbi_unref_page (&page);
800
801   GST_BUFFER_TIMESTAMP (buf) = teletext->in_timestamp;
802   GST_BUFFER_DURATION (buf) = teletext->in_duration;
803
804   GST_INFO_OBJECT (teletext, "Pushing buffer of size %" G_GSIZE_FORMAT,
805       gst_buffer_get_size (buf));
806
807   ret = gst_pad_push (teletext->srcpad, buf);
808   if (ret != GST_FLOW_OK)
809     goto push_failed;
810
811   return GST_FLOW_OK;
812
813 fetch_page_failed:
814   {
815     GST_ELEMENT_ERROR (teletext, RESOURCE, READ, (NULL), (NULL));
816     return GST_FLOW_ERROR;
817   }
818
819 push_failed:
820   {
821     GST_ERROR_OBJECT (teletext, "Pushing buffer failed, reason %s",
822         gst_flow_get_name (ret));
823     return ret;
824   }
825 }
826
827 static gchar **
828 gst_teletextdec_vbi_page_to_text_lines (guint start, guint stop, vbi_page *
829     page)
830 {
831   const guint lines_count = stop - start + 1;
832   const guint line_length = page->columns;
833   gchar **lines;
834   guint i;
835
836   /* allocate a new NULL-terminated array of strings */
837   lines = (gchar **) g_malloc (sizeof (gchar *) * (lines_count + 1));
838   lines[lines_count] = NULL;
839
840   /* export each line in the range of the teletext page in text format */
841   for (i = start; i <= stop; i++) {
842     lines[i - start] = (gchar *) g_malloc (sizeof (gchar) * (line_length + 1));
843     vbi_print_page_region (page, lines[i - start], line_length + 1, "UTF-8",
844         TRUE, 0, 0, i, line_length, 1);
845     /* Add the null character */
846     lines[i - start][line_length] = '\0';
847   }
848
849   return lines;
850 }
851
852 static GstFlowReturn
853 gst_teletextdec_export_text_page (GstTeletextDec * teletext, vbi_page * page,
854     GstBuffer ** buf)
855 {
856   gchar *text;
857   guint size;
858
859   if (teletext->subtitles_mode) {
860     gchar **lines;
861     GString *subs;
862     guint i;
863
864     lines = gst_teletextdec_vbi_page_to_text_lines (1, 23, page);
865     subs = g_string_new ("");
866     /* Strip white spaces and squash blank lines */
867     for (i = 0; i < 23; i++) {
868       g_strstrip (lines[i]);
869       if (g_strcmp0 (lines[i], ""))
870         g_string_append_printf (subs, teletext->subtitles_template, lines[i]);
871     }
872     /* if the page is blank and doesn't contain any line of text, just add a
873      * line break */
874     if (!g_strcmp0 (subs->str, ""))
875       g_string_append (subs, "\n");
876
877     size = subs->len + 1;
878     text = g_string_free (subs, FALSE);
879     g_strfreev (lines);
880   } else {
881     size = page->columns * page->rows;
882     text = g_malloc (size);
883     vbi_print_page (page, text, size, "UTF-8", FALSE, TRUE);
884   }
885
886   /* Allocate new buffer */
887   *buf = gst_buffer_new_wrapped (text, size);
888
889   return GST_FLOW_OK;
890 }
891
892 static GstFlowReturn
893 gst_teletextdec_export_rgba_page (GstTeletextDec * teletext, vbi_page * page,
894     GstBuffer ** buf)
895 {
896   guint size;
897   GstBuffer *lbuf;
898   GstMapInfo buf_map;
899
900   size = teletext->width * teletext->height * sizeof (vbi_rgba);
901
902   /* Allocate new buffer, using the negotiated pool if available. */
903   if (teletext->buf_pool) {
904     GstFlowReturn acquire_rv =
905         gst_buffer_pool_acquire_buffer (teletext->buf_pool, &lbuf, NULL);
906     if (acquire_rv != GST_FLOW_OK) {
907       return acquire_rv;
908     }
909   } else {
910     lbuf = gst_buffer_new_allocate (NULL, size, NULL);
911     if (NULL == lbuf)
912       return GST_FLOW_ERROR;
913   }
914
915   if (!gst_buffer_map (lbuf, &buf_map, GST_MAP_WRITE)) {
916     gst_buffer_unref (lbuf);
917     return GST_FLOW_ERROR;
918   }
919
920   vbi_draw_vt_page (page, VBI_PIXFMT_RGBA32_LE, buf_map.data, FALSE, TRUE);
921   gst_buffer_unmap (lbuf, &buf_map);
922   *buf = lbuf;
923
924   return GST_FLOW_OK;
925 }
926
927 static GstFlowReturn
928 gst_teletextdec_export_pango_page (GstTeletextDec * teletext, vbi_page * page,
929     GstBuffer ** buf)
930 {
931   vbi_char *acp;
932   const guint rows = page->rows;
933   gchar **colors;
934   gchar **lines;
935   GString *subs;
936   guint start, stop, k;
937   gsize len;
938   gint i, j;
939
940   colors = (gchar **) g_malloc (sizeof (gchar *) * (rows + 1));
941   colors[rows] = NULL;
942
943   /* parse all the lines and approximate it's foreground color using the first
944    * non null character */
945   for (acp = page->text, i = 0; i < page->rows; acp += page->columns, i++) {
946     for (j = 0; j < page->columns; j++) {
947       colors[i] = g_strdup (default_color_map[7]);
948       if (acp[j].unicode != 0x20) {
949         colors[i] = g_strdup (default_color_map[acp[j].foreground]);
950         break;
951       }
952     }
953   }
954
955   /* get an array of strings with each line of the telext page */
956   start = teletext->subtitles_mode ? 1 : 0;
957   stop = teletext->subtitles_mode ? rows - 2 : rows - 1;
958   lines = gst_teletextdec_vbi_page_to_text_lines (start, stop, page);
959
960   /* format each line in pango markup */
961   subs = g_string_new ("");
962   for (k = start; k <= stop; k++) {
963     g_string_append_printf (subs, PANGO_TEMPLATE,
964         teletext->font_description, colors[k], lines[k - start]);
965   }
966
967   /* Allocate new buffer */
968   len = subs->len + 1;
969   *buf = gst_buffer_new_wrapped (g_string_free (subs, FALSE), len);
970
971   g_strfreev (lines);
972   g_strfreev (colors);
973   return GST_FLOW_OK;
974 }
975
976 /* Converts the line_offset / field_parity byte of a VBI data unit. */
977 static void
978 gst_teletextdec_lofp_to_line (guint * field, guint * field_line,
979     guint * frame_line, guint lofp, systems system)
980 {
981   guint line_offset;
982
983   /* field_parity */
984   *field = !(lofp & (1 << 5));
985
986   line_offset = lofp & 31;
987
988   if (line_offset > 0) {
989     static const guint field_start[2][2] = {
990       {0, 263},
991       {0, 313},
992     };
993
994     *field_line = line_offset;
995     *frame_line = field_start[system][*field] + line_offset;
996   } else {
997     *field_line = 0;
998     *frame_line = 0;
999   }
1000 }
1001
1002 static int
1003 gst_teletextdec_line_address (GstTeletextDec * teletext,
1004     GstTeletextFrame * frame, vbi_sliced ** spp, guint lofp, systems system)
1005 {
1006   guint field;
1007   guint field_line;
1008   guint frame_line;
1009
1010   if (G_UNLIKELY (frame->current_slice >= frame->sliced_end)) {
1011     GST_LOG_OBJECT (teletext, "Out of sliced VBI buffer space (%d lines).",
1012         (int) (frame->sliced_end - frame->sliced_begin));
1013     return VBI_ERROR;
1014   }
1015
1016   gst_teletextdec_lofp_to_line (&field, &field_line, &frame_line, lofp, system);
1017
1018   GST_LOG_OBJECT (teletext, "Line %u/%u=%u.", field, field_line, frame_line);
1019
1020   if (frame_line != 0) {
1021     GST_LOG_OBJECT (teletext, "Last frame Line %u.", frame->last_frame_line);
1022     if (frame_line <= frame->last_frame_line) {
1023       GST_LOG_OBJECT (teletext, "New frame");
1024       return VBI_NEW_FRAME;
1025     }
1026
1027     /* FIXME : This never happens, since lofp is a guint8 */
1028 #if 0
1029     /* new segment flag */
1030     if (lofp < 0) {
1031       GST_LOG_OBJECT (teletext, "New frame");
1032       return VBI_NEW_FRAME;
1033     }
1034 #endif
1035
1036     frame->last_field = field;
1037     frame->last_field_line = field_line;
1038     frame->last_frame_line = frame_line;
1039
1040     *spp = frame->current_slice++;
1041     (*spp)->line = frame_line;
1042   } else {
1043     /* Undefined line. */
1044     return VBI_ERROR;
1045   }
1046
1047   return VBI_SUCCESS;
1048 }
1049
1050 static gboolean
1051 gst_teletextdec_extract_data_units (GstTeletextDec * teletext,
1052     GstTeletextFrame * f, const guint8 * packet, guint * offset, gsize size)
1053 {
1054   const guint8 *data_unit;
1055   guint i;
1056
1057   while (*offset < size) {
1058     vbi_sliced *s = NULL;
1059     gint data_unit_id, data_unit_length;
1060
1061     data_unit = packet + *offset;
1062     data_unit_id = data_unit[0];
1063     data_unit_length = data_unit[1];
1064     GST_LOG_OBJECT (teletext, "vbi header %02x %02x %02x", data_unit[0],
1065         data_unit[1], data_unit[2]);
1066
1067     switch (data_unit_id) {
1068       case DATA_UNIT_STUFFING:
1069       {
1070         *offset += 2 + data_unit_length;
1071         break;
1072       }
1073
1074       case DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE:
1075       case DATA_UNIT_EBU_TELETEXT_SUBTITLE:
1076       {
1077         gint res;
1078
1079         if (G_UNLIKELY (data_unit_length != 1 + 1 + 42)) {
1080           /* Skip this data unit */
1081           GST_WARNING_OBJECT (teletext, "The data unit length is not 44 bytes");
1082           *offset += 2 + data_unit_length;
1083           break;
1084         }
1085
1086         res =
1087             gst_teletextdec_line_address (teletext, f, &s, data_unit[2],
1088             SYSTEM_625);
1089         if (G_UNLIKELY (res == VBI_ERROR)) {
1090           /* Can't retrieve line address, skip this data unit */
1091           GST_WARNING_OBJECT (teletext,
1092               "Could not retrieve line address for this data unit");
1093           return VBI_ERROR;
1094         }
1095         if (G_UNLIKELY (f->last_field_line > 0
1096                 && (f->last_field_line - 7 >= 23 - 7))) {
1097           GST_WARNING_OBJECT (teletext, "Bad line: %d", f->last_field_line - 7);
1098           return VBI_ERROR;
1099         }
1100         if (res == VBI_NEW_FRAME) {
1101           /* New frame */
1102           return VBI_NEW_FRAME;
1103         }
1104         s->id = VBI_SLICED_TELETEXT_B;
1105         for (i = 0; i < 42; i++)
1106           s->data[i] = vbi_rev8 (data_unit[4 + i]);
1107         *offset += 46;
1108         break;
1109       }
1110
1111       case DATA_UNIT_ZVBI_WSS_CPR1204:
1112       case DATA_UNIT_ZVBI_CLOSED_CAPTION_525:
1113       case DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525:
1114       case DATA_UNIT_VPS:
1115       case DATA_UNIT_WSS:
1116       case DATA_UNIT_CLOSED_CAPTION:
1117       case DATA_UNIT_MONOCHROME_SAMPLES:
1118       {
1119         /*Not supported yet */
1120         *offset += 2 + data_unit_length;
1121         break;
1122       }
1123
1124       default:
1125       {
1126         /* corrupted stream, increase the offset by one until we sync */
1127         GST_LOG_OBJECT (teletext, "Corrupted, increasing offset by one");
1128         *offset += 1;
1129         break;
1130       }
1131     }
1132   }
1133   return VBI_SUCCESS;
1134 }
1135
1136 static gboolean
1137 teletext_init (GstPlugin * teletext)
1138 {
1139   GST_DEBUG_CATEGORY_INIT (gst_teletextdec_debug, "teletext", 0,
1140       "Teletext decoder");
1141   return gst_element_register (teletext, "teletextdec", GST_RANK_NONE,
1142       GST_TYPE_TELETEXTDEC);
1143 }
1144
1145 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1146     GST_VERSION_MINOR,
1147     teletext,
1148     "Teletext plugin",
1149     teletext_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)