1 /* GStreamer DVD Sub-Picture Unit
2  * Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:element-dvdspu
21  * @title: dvdspu
22  *
23  * DVD sub picture overlay element.
24  *
25  * ## Example launch line
26  * |[
27  * FIXME: gst-launch-1.0 ...
28  * ]| FIXME: description for the sample launch pipeline
29  *
30  */
31 #ifdef HAVE_CONFIG_H
32 #  include <config.h>
33 #endif
34 
35 #include <gst/gst-i18n-plugin.h>
36 #include <gst/video/video.h>
37 
38 #include <string.h>
39 
40 #include <gst/gst.h>
41 
42 #include "gstdvdspu.h"
43 
44 GST_DEBUG_CATEGORY (dvdspu_debug);
45 #define GST_CAT_DEFAULT dvdspu_debug
46 
47 GstDVDSPUDebugFlags dvdspu_debug_flags;
48 
49 /* Filter signals and args */
50 enum
51 {
52   /* FILL ME */
53   LAST_SIGNAL
54 };
55 
56 static GstStaticPadTemplate video_sink_factory =
57 GST_STATIC_PAD_TEMPLATE ("video",
58     GST_PAD_SINK,
59     GST_PAD_ALWAYS,
60     GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
61         "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
62     );
63 
64 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
65     GST_PAD_SRC,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
68         "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
69     );
70 
71 static GstStaticPadTemplate subpic_sink_factory =
72     GST_STATIC_PAD_TEMPLATE ("subpicture",
73     GST_PAD_SINK,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS ("subpicture/x-dvd; subpicture/x-pgs")
76     );
77 
78 #define gst_dvd_spu_parent_class parent_class
79 G_DEFINE_TYPE (GstDVDSpu, gst_dvd_spu, GST_TYPE_ELEMENT);
80 
81 static void gst_dvd_spu_dispose (GObject * object);
82 static void gst_dvd_spu_finalize (GObject * object);
83 static GstStateChangeReturn gst_dvd_spu_change_state (GstElement * element,
84     GstStateChange transition);
85 
86 static gboolean gst_dvd_spu_src_event (GstPad * pad, GstObject * parent,
87     GstEvent * event);
88 static gboolean gst_dvd_spu_src_query (GstPad * pad, GstObject * parent,
89     GstQuery * query);
90 
91 static GstCaps *gst_dvd_spu_video_proxy_getcaps (GstPad * pad,
92     GstCaps * filter);
93 static gboolean gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad,
94     GstCaps * caps);
95 static GstFlowReturn gst_dvd_spu_video_chain (GstPad * pad, GstObject * parent,
96     GstBuffer * buf);
97 static gboolean gst_dvd_spu_video_event (GstPad * pad, GstObject * parent,
98     GstEvent * event);
99 static gboolean gst_dvd_spu_video_query (GstPad * pad, GstObject * parent,
100     GstQuery * query);
101 static void gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force);
102 
103 static void gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu);
104 static GstFlowReturn gst_dvd_spu_subpic_chain (GstPad * pad, GstObject * parent,
105     GstBuffer * buf);
106 static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent,
107     GstEvent * event);
108 static gboolean gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad,
109     GstCaps * caps);
110 
111 static void gst_dvd_spu_clear (GstDVDSpu * dvdspu);
112 static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
113     gboolean process_events);
114 static void gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts);
115 static void gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf);
116 static GstFlowReturn
117 dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
118 static void gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event);
119 
120 static void
gst_dvd_spu_class_init(GstDVDSpuClass * klass)121 gst_dvd_spu_class_init (GstDVDSpuClass * klass)
122 {
123   GObjectClass *gobject_class;
124   GstElementClass *gstelement_class;
125 
126   gobject_class = (GObjectClass *) klass;
127   gstelement_class = (GstElementClass *) klass;
128 
129   gobject_class->dispose = (GObjectFinalizeFunc) gst_dvd_spu_dispose;
130   gobject_class->finalize = (GObjectFinalizeFunc) gst_dvd_spu_finalize;
131 
132   gstelement_class->change_state = gst_dvd_spu_change_state;
133 
134   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
135   gst_element_class_add_static_pad_template (gstelement_class,
136       &video_sink_factory);
137   gst_element_class_add_static_pad_template (gstelement_class,
138       &subpic_sink_factory);
139 
140   gst_element_class_set_static_metadata (gstelement_class,
141       "Sub-picture Overlay", "Mixer/Video/Overlay/SubPicture/DVD/Bluray",
142       "Parses Sub-Picture command streams and renders the SPU overlay "
143       "onto the video as it passes through",
144       "Jan Schmidt <thaytan@noraisin.net>");
145 }
146 
147 static void
gst_dvd_spu_init(GstDVDSpu * dvdspu)148 gst_dvd_spu_init (GstDVDSpu * dvdspu)
149 {
150   dvdspu->videosinkpad =
151       gst_pad_new_from_static_template (&video_sink_factory, "video");
152   gst_pad_set_chain_function (dvdspu->videosinkpad, gst_dvd_spu_video_chain);
153   gst_pad_set_event_function (dvdspu->videosinkpad, gst_dvd_spu_video_event);
154   gst_pad_set_query_function (dvdspu->videosinkpad, gst_dvd_spu_video_query);
155 
156   dvdspu->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
157   gst_pad_set_event_function (dvdspu->srcpad, gst_dvd_spu_src_event);
158   gst_pad_set_query_function (dvdspu->srcpad, gst_dvd_spu_src_query);
159 
160   dvdspu->subpic_sinkpad =
161       gst_pad_new_from_static_template (&subpic_sink_factory, "subpicture");
162   gst_pad_set_chain_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_chain);
163   gst_pad_set_event_function (dvdspu->subpic_sinkpad, gst_dvd_spu_subpic_event);
164 
165   GST_PAD_SET_PROXY_ALLOCATION (dvdspu->videosinkpad);
166 
167   gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->videosinkpad);
168   gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->subpic_sinkpad);
169   gst_element_add_pad (GST_ELEMENT (dvdspu), dvdspu->srcpad);
170 
171   g_mutex_init (&dvdspu->spu_lock);
172   dvdspu->pending_spus = g_queue_new ();
173 
174   gst_dvd_spu_clear (dvdspu);
175 }
176 
177 static void
gst_dvd_spu_clear(GstDVDSpu * dvdspu)178 gst_dvd_spu_clear (GstDVDSpu * dvdspu)
179 {
180   gst_dvd_spu_flush_spu_info (dvdspu, FALSE);
181   gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
182 
183   dvdspu->spu_input_type = SPU_INPUT_TYPE_NONE;
184 
185   gst_buffer_replace (&dvdspu->ref_frame, NULL);
186   gst_buffer_replace (&dvdspu->pending_frame, NULL);
187 
188   dvdspu->spu_state.info.fps_n = 25;
189   dvdspu->spu_state.info.fps_d = 1;
190 
191   gst_segment_init (&dvdspu->video_seg, GST_FORMAT_UNDEFINED);
192 }
193 
194 static void
gst_dvd_spu_dispose(GObject * object)195 gst_dvd_spu_dispose (GObject * object)
196 {
197   GstDVDSpu *dvdspu = GST_DVD_SPU (object);
198 
199   /* need to hold the SPU lock in case other stuff is still running... */
200   DVD_SPU_LOCK (dvdspu);
201   gst_dvd_spu_clear (dvdspu);
202   DVD_SPU_UNLOCK (dvdspu);
203 
204   G_OBJECT_CLASS (parent_class)->dispose (object);
205 }
206 
207 static void
gst_dvd_spu_finalize(GObject * object)208 gst_dvd_spu_finalize (GObject * object)
209 {
210   GstDVDSpu *dvdspu = GST_DVD_SPU (object);
211   gint i;
212 
213   for (i = 0; i < 3; i++) {
214     if (dvdspu->spu_state.comp_bufs[i] != NULL) {
215       g_free (dvdspu->spu_state.comp_bufs[i]);
216       dvdspu->spu_state.comp_bufs[i] = NULL;
217     }
218   }
219   g_queue_free (dvdspu->pending_spus);
220   g_mutex_clear (&dvdspu->spu_lock);
221 
222   G_OBJECT_CLASS (parent_class)->finalize (object);
223 }
224 
225 /* With SPU lock held, clear the queue of SPU packets */
226 static void
gst_dvd_spu_flush_spu_info(GstDVDSpu * dvdspu,gboolean keep_events)227 gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu, gboolean keep_events)
228 {
229   SpuPacket *packet;
230   SpuState *state = &dvdspu->spu_state;
231   GQueue tmp_q = G_QUEUE_INIT;
232 
233   GST_INFO_OBJECT (dvdspu, "Flushing SPU information");
234 
235   if (dvdspu->partial_spu) {
236     gst_buffer_unref (dvdspu->partial_spu);
237     dvdspu->partial_spu = NULL;
238   }
239 
240   packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
241   while (packet != NULL) {
242     if (packet->buf) {
243       gst_buffer_unref (packet->buf);
244       g_assert (packet->event == NULL);
245       g_free (packet);
246     } else if (packet->event) {
247       if (keep_events) {
248         g_queue_push_tail (&tmp_q, packet);
249       } else {
250         gst_event_unref (packet->event);
251         g_free (packet);
252       }
253     }
254     packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
255   }
256   /* Push anything we decided to keep back onto the pending_spus list */
257   for (packet = g_queue_pop_head (&tmp_q); packet != NULL;
258       packet = g_queue_pop_head (&tmp_q))
259     g_queue_push_tail (dvdspu->pending_spus, packet);
260 
261   state->flags &= ~(SPU_STATE_FLAGS_MASK);
262   state->next_ts = GST_CLOCK_TIME_NONE;
263 
264   switch (dvdspu->spu_input_type) {
265     case SPU_INPUT_TYPE_VOBSUB:
266       gstspu_vobsub_flush (dvdspu);
267       break;
268     case SPU_INPUT_TYPE_PGS:
269       gstspu_pgs_flush (dvdspu);
270       break;
271     default:
272       break;
273   }
274 }
275 
276 static gboolean
gst_dvd_spu_src_event(GstPad * pad,GstObject * parent,GstEvent * event)277 gst_dvd_spu_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
278 {
279   GstDVDSpu *dvdspu = GST_DVD_SPU (parent);
280   GstPad *peer;
281   gboolean res = TRUE;
282 
283   peer = gst_pad_get_peer (dvdspu->videosinkpad);
284   if (peer) {
285     res = gst_pad_send_event (peer, event);
286     gst_object_unref (peer);
287   } else
288     gst_event_unref (event);
289 
290   return res;
291 }
292 
293 static gboolean
gst_dvd_spu_src_query(GstPad * pad,GstObject * parent,GstQuery * query)294 gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
295 {
296   gboolean res = FALSE;
297 
298   switch (GST_QUERY_TYPE (query)) {
299     case GST_QUERY_CAPS:
300     {
301       GstCaps *filter, *caps;
302 
303       gst_query_parse_caps (query, &filter);
304       caps = gst_dvd_spu_video_proxy_getcaps (pad, filter);
305       gst_query_set_caps_result (query, caps);
306       gst_caps_unref (caps);
307       res = TRUE;
308       break;
309     }
310     default:
311       res = gst_pad_query_default (pad, parent, query);
312       break;
313   }
314 
315   return res;
316 }
317 
318 static gboolean
gst_dvd_spu_video_set_caps(GstDVDSpu * dvdspu,GstPad * pad,GstCaps * caps)319 gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps)
320 {
321   gboolean res = FALSE;
322   GstVideoInfo info;
323   gint i;
324   SpuState *state;
325 
326   if (!gst_video_info_from_caps (&info, caps))
327     goto done;
328 
329   DVD_SPU_LOCK (dvdspu);
330 
331   state = &dvdspu->spu_state;
332 
333   state->info = info;
334   for (i = 0; i < 3; i++) {
335     state->comp_bufs[i] = g_realloc (state->comp_bufs[i],
336         sizeof (guint32) * info.width);
337   }
338   DVD_SPU_UNLOCK (dvdspu);
339 
340   res = TRUE;
341 done:
342   return res;
343 }
344 
345 static GstCaps *
gst_dvd_spu_video_proxy_getcaps(GstPad * pad,GstCaps * filter)346 gst_dvd_spu_video_proxy_getcaps (GstPad * pad, GstCaps * filter)
347 {
348   GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
349   GstCaps *caps;
350   GstPad *otherpad;
351 
352   /* Proxy the getcaps between videosink and the srcpad, ignoring the
353    * subpicture sink pad */
354   otherpad = (pad == dvdspu->srcpad) ? dvdspu->videosinkpad : dvdspu->srcpad;
355 
356   caps = gst_pad_peer_query_caps (otherpad, filter);
357   if (caps) {
358     GstCaps *temp, *templ;
359 
360     templ = gst_pad_get_pad_template_caps (otherpad);
361     temp = gst_caps_intersect (caps, templ);
362     gst_caps_unref (templ);
363     gst_caps_unref (caps);
364     caps = temp;
365   } else {
366     caps = gst_pad_get_pad_template_caps (pad);
367   }
368 
369   gst_object_unref (dvdspu);
370   return caps;
371 }
372 
373 /* With SPU lock held */
374 static void
update_video_to_position(GstDVDSpu * dvdspu,GstClockTime new_pos)375 update_video_to_position (GstDVDSpu * dvdspu, GstClockTime new_pos)
376 {
377   SpuState *state = &dvdspu->spu_state;
378 #if 0
379   g_print ("Segment update for video. Advancing from %" GST_TIME_FORMAT
380       " to %" GST_TIME_FORMAT "\n",
381       GST_TIME_ARGS (dvdspu->video_seg.position), GST_TIME_ARGS (start));
382 #endif
383   while (dvdspu->video_seg.position < new_pos &&
384       !(state->flags & SPU_STATE_STILL_FRAME)) {
385     DVD_SPU_UNLOCK (dvdspu);
386     if (dvdspu_handle_vid_buffer (dvdspu, NULL) != GST_FLOW_OK) {
387       DVD_SPU_LOCK (dvdspu);
388       break;
389     }
390     DVD_SPU_LOCK (dvdspu);
391   }
392 }
393 
394 static gboolean
gst_dvd_spu_video_event(GstPad * pad,GstObject * parent,GstEvent * event)395 gst_dvd_spu_video_event (GstPad * pad, GstObject * parent, GstEvent * event)
396 {
397   GstDVDSpu *dvdspu = (GstDVDSpu *) parent;
398   SpuState *state = &dvdspu->spu_state;
399   gboolean res = TRUE;
400 
401   switch (GST_EVENT_TYPE (event)) {
402     case GST_EVENT_CAPS:
403     {
404       GstCaps *caps;
405 
406       gst_event_parse_caps (event, &caps);
407       res = gst_dvd_spu_video_set_caps (dvdspu, pad, caps);
408       if (res)
409         res = gst_pad_push_event (dvdspu->srcpad, event);
410       else
411         gst_event_unref (event);
412       break;
413     }
414     case GST_EVENT_CUSTOM_DOWNSTREAM:
415     case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
416     {
417       gboolean in_still;
418 
419       if (gst_video_event_parse_still_frame (event, &in_still)) {
420         GstBuffer *to_push = NULL;
421 
422         /* Forward the event before handling */
423         res = gst_pad_event_default (pad, parent, event);
424 
425         GST_DEBUG_OBJECT (dvdspu,
426             "Still frame event on video pad: in-still = %d", in_still);
427 
428         DVD_SPU_LOCK (dvdspu);
429         if (in_still) {
430           state->flags |= SPU_STATE_STILL_FRAME;
431           /* Entering still. Advance the SPU to make sure the state is
432            * up to date */
433           gst_dvd_spu_check_still_updates (dvdspu);
434           /* And re-draw the still frame to make sure it appears on
435            * screen, otherwise the last frame  might have been discarded
436            * by QoS */
437           gst_dvd_spu_redraw_still (dvdspu, TRUE);
438           to_push = dvdspu->pending_frame;
439           dvdspu->pending_frame = NULL;
440         } else {
441           state->flags &= ~(SPU_STATE_STILL_FRAME);
442         }
443         DVD_SPU_UNLOCK (dvdspu);
444         if (to_push)
445           gst_pad_push (dvdspu->srcpad, to_push);
446       } else {
447         GST_DEBUG_OBJECT (dvdspu,
448             "Custom event %" GST_PTR_FORMAT " on video pad", event);
449         res = gst_pad_event_default (pad, parent, event);
450       }
451       break;
452     }
453     case GST_EVENT_SEGMENT:
454     {
455       GstSegment seg;
456 
457       gst_event_copy_segment (event, &seg);
458 
459       if (seg.format != GST_FORMAT_TIME) {
460         gst_event_unref (event);
461         return FALSE;
462       }
463 
464       /* Only print updates if they have an end time (don't print start_time
465        * updates */
466       GST_DEBUG_OBJECT (dvdspu, "video pad Segment: %" GST_SEGMENT_FORMAT,
467           &seg);
468 
469       DVD_SPU_LOCK (dvdspu);
470 
471       if (seg.start > dvdspu->video_seg.position) {
472         update_video_to_position (dvdspu, seg.start);
473       }
474 
475       dvdspu->video_seg = seg;
476       DVD_SPU_UNLOCK (dvdspu);
477 
478       res = gst_pad_event_default (pad, parent, event);
479       break;
480     }
481     case GST_EVENT_GAP:
482     {
483       GstClockTime timestamp, duration;
484       gst_event_parse_gap (event, &timestamp, &duration);
485       if (GST_CLOCK_TIME_IS_VALID (duration))
486         timestamp += duration;
487 
488       DVD_SPU_LOCK (dvdspu);
489       GST_LOG_OBJECT (dvdspu, "Received GAP. Advancing to %" GST_TIME_FORMAT,
490           GST_TIME_ARGS (timestamp));
491       update_video_to_position (dvdspu, timestamp);
492       DVD_SPU_UNLOCK (dvdspu);
493 
494       gst_event_unref (event);
495       break;
496     }
497     case GST_EVENT_FLUSH_START:
498       res = gst_pad_event_default (pad, parent, event);
499       goto done;
500     case GST_EVENT_FLUSH_STOP:
501       res = gst_pad_event_default (pad, parent, event);
502 
503       DVD_SPU_LOCK (dvdspu);
504       gst_segment_init (&dvdspu->video_seg, GST_FORMAT_UNDEFINED);
505       gst_buffer_replace (&dvdspu->ref_frame, NULL);
506       gst_buffer_replace (&dvdspu->pending_frame, NULL);
507 
508       DVD_SPU_UNLOCK (dvdspu);
509       goto done;
510     default:
511       res = gst_pad_event_default (pad, parent, event);
512       break;
513   }
514 
515 done:
516   return res;
517 #if 0
518 error:
519   gst_event_unref (event);
520   return FALSE;
521 #endif
522 }
523 
524 static gboolean
gst_dvd_spu_video_query(GstPad * pad,GstObject * parent,GstQuery * query)525 gst_dvd_spu_video_query (GstPad * pad, GstObject * parent, GstQuery * query)
526 {
527   gboolean res = FALSE;
528 
529   switch (GST_QUERY_TYPE (query)) {
530     case GST_QUERY_CAPS:
531     {
532       GstCaps *filter, *caps;
533 
534       gst_query_parse_caps (query, &filter);
535       caps = gst_dvd_spu_video_proxy_getcaps (pad, filter);
536       gst_query_set_caps_result (query, caps);
537       gst_caps_unref (caps);
538       res = TRUE;
539       break;
540     }
541     default:
542       res = gst_pad_query_default (pad, parent, query);
543       break;
544   }
545 
546   return res;
547 }
548 
549 static GstFlowReturn
gst_dvd_spu_video_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)550 gst_dvd_spu_video_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
551 {
552   GstDVDSpu *dvdspu = (GstDVDSpu *) parent;
553   GstFlowReturn ret;
554 
555   g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
556 
557   GST_LOG_OBJECT (dvdspu, "video buffer %p with TS %" GST_TIME_FORMAT,
558       buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
559 
560   ret = dvdspu_handle_vid_buffer (dvdspu, buf);
561 
562   return ret;
563 }
564 
565 static GstFlowReturn
dvdspu_handle_vid_buffer(GstDVDSpu * dvdspu,GstBuffer * buf)566 dvdspu_handle_vid_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
567 {
568   GstClockTime new_ts;
569   GstFlowReturn ret;
570   gboolean using_ref = FALSE;
571 
572   DVD_SPU_LOCK (dvdspu);
573 
574   if (buf == NULL) {
575     GstClockTime next_ts = dvdspu->video_seg.position;
576 
577     next_ts += gst_util_uint64_scale_int (GST_SECOND,
578         dvdspu->spu_state.info.fps_d, dvdspu->spu_state.info.fps_n);
579 
580     /* NULL buffer was passed - use the reference frame and update the timestamp,
581      * or else there's nothing to draw, and just return GST_FLOW_OK */
582     if (dvdspu->ref_frame == NULL) {
583       dvdspu->video_seg.position = next_ts;
584       goto no_ref_frame;
585     }
586 
587     buf = gst_buffer_copy (dvdspu->ref_frame);
588 
589 #if 0
590     g_print ("Duping frame %" GST_TIME_FORMAT " with new TS %" GST_TIME_FORMAT
591         "\n", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
592         GST_TIME_ARGS (next_ts));
593 #endif
594 
595     GST_BUFFER_TIMESTAMP (buf) = next_ts;
596     using_ref = TRUE;
597   }
598 
599   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
600     dvdspu->video_seg.position = GST_BUFFER_TIMESTAMP (buf);
601   }
602 
603   new_ts = gst_segment_to_running_time (&dvdspu->video_seg, GST_FORMAT_TIME,
604       dvdspu->video_seg.position);
605 
606 #if 0
607   g_print ("TS %" GST_TIME_FORMAT " running: %" GST_TIME_FORMAT "\n",
608       GST_TIME_ARGS (dvdspu->video_seg.position), GST_TIME_ARGS (new_ts));
609 #endif
610 
611   gst_dvd_spu_advance_spu (dvdspu, new_ts);
612 
613   /* If we have an active SPU command set, we store a copy of the frame in case
614    * we hit a still and need to draw on it. Otherwise, a reference is
615    * sufficient in case we later encounter a still */
616   if ((dvdspu->spu_state.flags & SPU_STATE_FORCED_DSP) ||
617       ((dvdspu->spu_state.flags & SPU_STATE_FORCED_ONLY) == 0 &&
618           (dvdspu->spu_state.flags & SPU_STATE_DISPLAY))) {
619     if (using_ref == FALSE) {
620       GstBuffer *copy;
621 
622       /* Take a copy in case we hit a still frame and need the pristine
623        * frame around */
624       copy = gst_buffer_copy (buf);
625       gst_buffer_replace (&dvdspu->ref_frame, copy);
626       gst_buffer_unref (copy);
627     }
628 
629     /* Render the SPU overlay onto the buffer */
630     buf = gst_buffer_make_writable (buf);
631 
632     gstspu_render (dvdspu, buf);
633   } else {
634     if (using_ref == FALSE) {
635       /* Not going to draw anything on this frame, just store a reference
636        * in case we hit a still frame and need it */
637       gst_buffer_replace (&dvdspu->ref_frame, buf);
638     }
639   }
640 
641   if (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME) {
642     GST_DEBUG_OBJECT (dvdspu, "Outputting buffer with TS %" GST_TIME_FORMAT
643         "from chain while in still",
644         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
645   }
646 
647   DVD_SPU_UNLOCK (dvdspu);
648 
649   /* just push out the incoming buffer without touching it */
650   ret = gst_pad_push (dvdspu->srcpad, buf);
651 
652   return ret;
653 
654 no_ref_frame:
655 
656   DVD_SPU_UNLOCK (dvdspu);
657 
658   return GST_FLOW_OK;
659 }
660 
661 
662 static void
gstspu_render(GstDVDSpu * dvdspu,GstBuffer * buf)663 gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
664 {
665   GstVideoFrame frame;
666 
667   if (!gst_video_frame_map (&frame, &dvdspu->spu_state.info, buf,
668           GST_MAP_READWRITE))
669     return;
670 
671   switch (dvdspu->spu_input_type) {
672     case SPU_INPUT_TYPE_VOBSUB:
673       gstspu_vobsub_render (dvdspu, &frame);
674       break;
675     case SPU_INPUT_TYPE_PGS:
676       gstspu_pgs_render (dvdspu, &frame);
677       break;
678     default:
679       break;
680   }
681   gst_video_frame_unmap (&frame);
682 }
683 
684 /* With SPU LOCK */
685 static void
gst_dvd_spu_redraw_still(GstDVDSpu * dvdspu,gboolean force)686 gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
687 {
688   /* If we have an active SPU command set and a reference frame, copy the
689    * frame, redraw the SPU and store it as the pending frame for output */
690   if (dvdspu->ref_frame) {
691     gboolean redraw = (dvdspu->spu_state.flags & SPU_STATE_FORCED_DSP);
692     redraw |= (dvdspu->spu_state.flags & SPU_STATE_FORCED_ONLY) == 0 &&
693         (dvdspu->spu_state.flags & SPU_STATE_DISPLAY);
694 
695     if (redraw) {
696       GstBuffer *buf = gst_buffer_ref (dvdspu->ref_frame);
697 
698       buf = gst_buffer_make_writable (buf);
699 
700       GST_LOG_OBJECT (dvdspu, "Redraw due to Still Frame with ref %p",
701           dvdspu->ref_frame);
702       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
703       GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
704       GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
705 
706       /* Render the SPU overlay onto the buffer */
707       gstspu_render (dvdspu, buf);
708       gst_buffer_replace (&dvdspu->pending_frame, buf);
709       gst_buffer_unref (buf);
710     } else if (force) {
711       /* Simply output the reference frame */
712       GstBuffer *buf = gst_buffer_ref (dvdspu->ref_frame);
713       buf = gst_buffer_make_writable (buf);
714       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
715       GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
716       GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE;
717 
718       GST_DEBUG_OBJECT (dvdspu, "Pushing reference frame at start of still");
719 
720       gst_buffer_replace (&dvdspu->pending_frame, buf);
721       gst_buffer_unref (buf);
722     } else {
723       GST_LOG_OBJECT (dvdspu, "Redraw due to Still Frame skipped");
724     }
725   } else {
726     GST_LOG_OBJECT (dvdspu, "Not redrawing still frame - no ref frame");
727   }
728 }
729 
730 static void
gst_dvd_spu_handle_dvd_event(GstDVDSpu * dvdspu,GstEvent * event)731 gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
732 {
733   gboolean hl_change = FALSE;
734 
735   GST_INFO_OBJECT (dvdspu, "DVD event of type %s on subp pad OOB=%d",
736       gst_structure_get_string (gst_event_get_structure (event), "event"),
737       (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB));
738 
739   switch (dvdspu->spu_input_type) {
740     case SPU_INPUT_TYPE_VOBSUB:
741       hl_change = gstspu_vobsub_handle_dvd_event (dvdspu, event);
742       break;
743     case SPU_INPUT_TYPE_PGS:
744       hl_change = gstspu_pgs_handle_dvd_event (dvdspu, event);
745       break;
746     default:
747       break;
748   }
749 
750   if (hl_change && (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME)) {
751     gst_dvd_spu_redraw_still (dvdspu, FALSE);
752   }
753 }
754 
755 static gboolean
gstspu_execute_event(GstDVDSpu * dvdspu)756 gstspu_execute_event (GstDVDSpu * dvdspu)
757 {
758   switch (dvdspu->spu_input_type) {
759     case SPU_INPUT_TYPE_VOBSUB:
760       return gstspu_vobsub_execute_event (dvdspu);
761       break;
762     case SPU_INPUT_TYPE_PGS:
763       return gstspu_pgs_execute_event (dvdspu);
764       break;
765     default:
766       g_assert_not_reached ();
767       break;
768   }
769   return FALSE;
770 }
771 
772 /* Advance the SPU packet/command queue to a time. new_ts is in running time */
773 static void
gst_dvd_spu_advance_spu(GstDVDSpu * dvdspu,GstClockTime new_ts)774 gst_dvd_spu_advance_spu (GstDVDSpu * dvdspu, GstClockTime new_ts)
775 {
776   SpuState *state = &dvdspu->spu_state;
777 
778   if (G_UNLIKELY (dvdspu->spu_input_type == SPU_INPUT_TYPE_NONE))
779     return;
780 
781   while (state->next_ts == GST_CLOCK_TIME_NONE || state->next_ts <= new_ts) {
782     GST_DEBUG_OBJECT (dvdspu,
783         "Advancing SPU from TS %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
784         GST_TIME_ARGS (state->next_ts), GST_TIME_ARGS (new_ts));
785 
786     if (!gstspu_execute_event (dvdspu)) {
787       /* No current command buffer, try and get one */
788       SpuPacket *packet = (SpuPacket *) g_queue_pop_head (dvdspu->pending_spus);
789 
790       if (packet == NULL)
791         return;                 /* No SPU packets available */
792 
793       GST_LOG_OBJECT (dvdspu,
794           "Popped new SPU packet with TS %" GST_TIME_FORMAT
795           ". Video position=%" GST_TIME_FORMAT " (%" GST_TIME_FORMAT
796           ") type %s",
797           GST_TIME_ARGS (packet->event_ts),
798           GST_TIME_ARGS (gst_segment_to_running_time (&dvdspu->video_seg,
799                   GST_FORMAT_TIME, dvdspu->video_seg.position)),
800           GST_TIME_ARGS (dvdspu->video_seg.position),
801           packet->buf ? "buffer" : "event");
802 
803       if (packet->buf) {
804         switch (dvdspu->spu_input_type) {
805           case SPU_INPUT_TYPE_VOBSUB:
806             gstspu_vobsub_handle_new_buf (dvdspu, packet->event_ts,
807                 packet->buf);
808             break;
809           case SPU_INPUT_TYPE_PGS:
810             gstspu_pgs_handle_new_buf (dvdspu, packet->event_ts, packet->buf);
811             break;
812           default:
813             g_assert_not_reached ();
814             break;
815         }
816         g_assert (packet->event == NULL);
817       } else if (packet->event)
818         gst_dvd_spu_handle_dvd_event (dvdspu, packet->event);
819 
820       g_free (packet);
821       continue;
822     }
823   }
824 }
825 
826 static void
gst_dvd_spu_check_still_updates(GstDVDSpu * dvdspu)827 gst_dvd_spu_check_still_updates (GstDVDSpu * dvdspu)
828 {
829   GstClockTime sub_ts;
830   GstClockTime vid_ts;
831 
832   if (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME) {
833 
834     if (dvdspu->video_seg.format != GST_FORMAT_TIME)
835       return;                   /* No video segment or frames yet */
836 
837     vid_ts = gst_segment_to_running_time (&dvdspu->video_seg,
838         GST_FORMAT_TIME, dvdspu->video_seg.position);
839     sub_ts = gst_segment_to_running_time (&dvdspu->subp_seg,
840         GST_FORMAT_TIME, dvdspu->subp_seg.position);
841 
842     vid_ts = MAX (vid_ts, sub_ts);
843 
844     GST_DEBUG_OBJECT (dvdspu,
845         "In still frame - advancing TS to %" GST_TIME_FORMAT
846         " to process SPU buffer", GST_TIME_ARGS (vid_ts));
847     gst_dvd_spu_advance_spu (dvdspu, vid_ts);
848   }
849 }
850 
851 static void
submit_new_spu_packet(GstDVDSpu * dvdspu,GstBuffer * buf)852 submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf)
853 {
854   SpuPacket *spu_packet;
855   GstClockTime ts;
856   GstClockTime run_ts = GST_CLOCK_TIME_NONE;
857 
858   GST_DEBUG_OBJECT (dvdspu,
859       "Complete subpicture buffer of %" G_GSIZE_FORMAT " bytes with TS %"
860       GST_TIME_FORMAT, gst_buffer_get_size (buf),
861       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
862 
863   /* Decide whether to pass this buffer through to the rendering code */
864   ts = GST_BUFFER_TIMESTAMP (buf);
865   if (GST_CLOCK_TIME_IS_VALID (ts)) {
866     if (ts < (GstClockTime) dvdspu->subp_seg.start) {
867       GstClockTimeDiff diff = dvdspu->subp_seg.start - ts;
868 
869       /* Buffer starts before segment, see if we can calculate a running time */
870       run_ts =
871           gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME,
872           dvdspu->subp_seg.start);
873       if (run_ts >= (GstClockTime) diff)
874         run_ts -= diff;
875       else
876         run_ts = GST_CLOCK_TIME_NONE;   /* No running time possible for this subpic */
877     } else {
878       /* TS within segment, convert to running time */
879       run_ts =
880           gst_segment_to_running_time (&dvdspu->subp_seg, GST_FORMAT_TIME, ts);
881     }
882   }
883 
884   if (GST_CLOCK_TIME_IS_VALID (run_ts)) {
885     /* Complete SPU packet, push it onto the queue for processing when
886      * video packets come past */
887     spu_packet = g_new0 (SpuPacket, 1);
888     spu_packet->buf = buf;
889 
890     /* Store the activation time of this buffer in running time */
891     spu_packet->event_ts = run_ts;
892     GST_INFO_OBJECT (dvdspu,
893         "Pushing SPU buf with TS %" GST_TIME_FORMAT " running time %"
894         GST_TIME_FORMAT, GST_TIME_ARGS (ts),
895         GST_TIME_ARGS (spu_packet->event_ts));
896 
897     g_queue_push_tail (dvdspu->pending_spus, spu_packet);
898 
899     /* In a still frame condition, advance the SPU to make sure the state is
900      * up to date */
901     gst_dvd_spu_check_still_updates (dvdspu);
902   } else {
903     gst_buffer_unref (buf);
904   }
905 }
906 
907 static GstFlowReturn
gst_dvd_spu_subpic_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)908 gst_dvd_spu_subpic_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
909 {
910   GstDVDSpu *dvdspu = (GstDVDSpu *) parent;
911   GstFlowReturn ret = GST_FLOW_OK;
912   gsize size;
913 
914   g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
915 
916   GST_INFO_OBJECT (dvdspu, "Have subpicture buffer with timestamp %"
917       GST_TIME_FORMAT " and size %" G_GSIZE_FORMAT,
918       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), gst_buffer_get_size (buf));
919 
920   DVD_SPU_LOCK (dvdspu);
921 
922   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
923     dvdspu->subp_seg.position = GST_BUFFER_TIMESTAMP (buf);
924   }
925 
926   if (GST_BUFFER_IS_DISCONT (buf) && dvdspu->partial_spu) {
927     gst_buffer_unref (dvdspu->partial_spu);
928     dvdspu->partial_spu = NULL;
929   }
930 
931   if (dvdspu->partial_spu != NULL) {
932     if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
933       GST_WARNING_OBJECT (dvdspu,
934           "Joining subpicture buffer with timestamp to previous");
935     dvdspu->partial_spu = gst_buffer_append (dvdspu->partial_spu, buf);
936   } else {
937     /* If we don't yet have a buffer, wait for one with a timestamp,
938      * since that will avoid collecting the 2nd half of a partial buf */
939     if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
940       dvdspu->partial_spu = buf;
941     else
942       gst_buffer_unref (buf);
943   }
944 
945   if (dvdspu->partial_spu == NULL)
946     goto done;
947 
948   size = gst_buffer_get_size (dvdspu->partial_spu);
949 
950   switch (dvdspu->spu_input_type) {
951     case SPU_INPUT_TYPE_VOBSUB:
952       if (size >= 2) {
953         guint8 header[2];
954         guint16 packet_size;
955 
956         gst_buffer_extract (dvdspu->partial_spu, 0, header, 2);
957         packet_size = GST_READ_UINT16_BE (header);
958         if (packet_size == size) {
959           submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
960           dvdspu->partial_spu = NULL;
961         } else if (packet_size == 0) {
962           GST_LOG_OBJECT (dvdspu, "Discarding empty SPU buffer");
963           gst_buffer_unref (dvdspu->partial_spu);
964           dvdspu->partial_spu = NULL;
965         } else if (packet_size < size) {
966           /* Somehow we collected too much - something is wrong. Drop the
967            * packet entirely and wait for a new one */
968           GST_DEBUG_OBJECT (dvdspu,
969               "Discarding invalid SPU buffer of size %" G_GSIZE_FORMAT, size);
970 
971           gst_buffer_unref (dvdspu->partial_spu);
972           dvdspu->partial_spu = NULL;
973         } else {
974           GST_LOG_OBJECT (dvdspu,
975               "SPU buffer claims to be of size %u. Collected %" G_GSIZE_FORMAT
976               " so far.", packet_size, size);
977         }
978       }
979       break;
980     case SPU_INPUT_TYPE_PGS:{
981       /* Collect until we have a command buffer that ends exactly at the size
982        * we've collected */
983       guint8 packet_type;
984       guint16 packet_size;
985       GstMapInfo map;
986       guint8 *ptr, *end;
987       gboolean invalid = FALSE;
988 
989       gst_buffer_map (dvdspu->partial_spu, &map, GST_MAP_READ);
990 
991       ptr = map.data;
992       end = ptr + map.size;
993 
994       /* FIXME: There's no need to walk the command set each time. We can set a
995        * marker and resume where we left off next time */
996       /* FIXME: Move the packet parsing and sanity checking into the format-specific modules */
997       while (ptr != end) {
998         if (ptr + 3 > end)
999           break;
1000         packet_type = *ptr++;
1001         packet_size = GST_READ_UINT16_BE (ptr);
1002         ptr += 2;
1003         if (ptr + packet_size > end)
1004           break;
1005         ptr += packet_size;
1006         /* 0x80 is the END command for PGS packets */
1007         if (packet_type == 0x80 && ptr != end) {
1008           /* Extra cruft on the end of the packet -> assume invalid */
1009           invalid = TRUE;
1010           break;
1011         }
1012       }
1013       gst_buffer_unmap (dvdspu->partial_spu, &map);
1014 
1015       if (invalid) {
1016         gst_buffer_unref (dvdspu->partial_spu);
1017         dvdspu->partial_spu = NULL;
1018       } else if (ptr == end) {
1019         GST_DEBUG_OBJECT (dvdspu,
1020             "Have complete PGS packet of size %" G_GSIZE_FORMAT ". Enqueueing.",
1021             map.size);
1022         submit_new_spu_packet (dvdspu, dvdspu->partial_spu);
1023         dvdspu->partial_spu = NULL;
1024       }
1025       break;
1026     }
1027     default:
1028       GST_ERROR_OBJECT (dvdspu, "Input type not configured before SPU passing");
1029       goto caps_not_set;
1030   }
1031 
1032 done:
1033   DVD_SPU_UNLOCK (dvdspu);
1034 
1035   return ret;
1036 
1037   /* ERRORS */
1038 caps_not_set:
1039   {
1040     GST_ELEMENT_ERROR (dvdspu, RESOURCE, NO_SPACE_LEFT,
1041         (_("Subpicture format was not configured before data flow")), (NULL));
1042     ret = GST_FLOW_ERROR;
1043     goto done;
1044   }
1045 }
1046 
1047 static gboolean
gst_dvd_spu_subpic_event(GstPad * pad,GstObject * parent,GstEvent * event)1048 gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent, GstEvent * event)
1049 {
1050   GstDVDSpu *dvdspu = (GstDVDSpu *) parent;
1051   gboolean res = TRUE;
1052 
1053   /* Some events on the subpicture sink pad just get ignored, like
1054    * FLUSH_START */
1055   switch (GST_EVENT_TYPE (event)) {
1056     case GST_EVENT_CAPS:
1057     {
1058       GstCaps *caps;
1059 
1060       gst_event_parse_caps (event, &caps);
1061       res = gst_dvd_spu_subpic_set_caps (dvdspu, pad, caps);
1062       gst_event_unref (event);
1063       break;
1064     }
1065     case GST_EVENT_CUSTOM_DOWNSTREAM:
1066     case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:
1067     case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
1068     {
1069       const GstStructure *structure = gst_event_get_structure (event);
1070       const gchar *name = gst_structure_get_name (structure);
1071       gboolean need_push;
1072 
1073       if (!g_str_has_prefix (name, "application/x-gst-dvd")) {
1074         res = gst_pad_event_default (pad, parent, event);
1075         break;
1076       }
1077 
1078       DVD_SPU_LOCK (dvdspu);
1079       if (GST_EVENT_IS_SERIALIZED (event)) {
1080         SpuPacket *spu_packet = g_new0 (SpuPacket, 1);
1081         GST_DEBUG_OBJECT (dvdspu,
1082             "Enqueueing DVD event on subpicture pad for later");
1083         spu_packet->event = event;
1084         g_queue_push_tail (dvdspu->pending_spus, spu_packet);
1085       } else {
1086         gst_dvd_spu_handle_dvd_event (dvdspu, event);
1087       }
1088 
1089       /* If the handle_dvd_event generated a pending frame, we
1090        * need to synchronise with the video pad's stream lock and push it.
1091        * This requires some dancing to preserve locking order and handle
1092        * flushes correctly */
1093       need_push = (dvdspu->pending_frame != NULL);
1094       DVD_SPU_UNLOCK (dvdspu);
1095       if (need_push) {
1096         GstBuffer *to_push = NULL;
1097         gboolean flushing;
1098 
1099         GST_LOG_OBJECT (dvdspu, "Going for stream lock");
1100         GST_PAD_STREAM_LOCK (dvdspu->videosinkpad);
1101         GST_LOG_OBJECT (dvdspu, "Got stream lock");
1102         GST_OBJECT_LOCK (dvdspu->videosinkpad);
1103         flushing = GST_PAD_IS_FLUSHING (dvdspu->videosinkpad);
1104         GST_OBJECT_UNLOCK (dvdspu->videosinkpad);
1105 
1106         DVD_SPU_LOCK (dvdspu);
1107         if (dvdspu->pending_frame == NULL || flushing) {
1108           /* Got flushed while waiting for the stream lock */
1109           DVD_SPU_UNLOCK (dvdspu);
1110         } else {
1111           to_push = dvdspu->pending_frame;
1112           dvdspu->pending_frame = NULL;
1113 
1114           DVD_SPU_UNLOCK (dvdspu);
1115           gst_pad_push (dvdspu->srcpad, to_push);
1116         }
1117         GST_LOG_OBJECT (dvdspu, "Dropping stream lock");
1118         GST_PAD_STREAM_UNLOCK (dvdspu->videosinkpad);
1119       }
1120 
1121       break;
1122     }
1123     case GST_EVENT_SEGMENT:
1124     {
1125       GstSegment seg;
1126 
1127       gst_event_copy_segment (event, &seg);
1128 
1129       /* Only print updates if they have an end time (don't print start_time
1130        * updates */
1131       GST_DEBUG_OBJECT (dvdspu, "subpic pad Segment: %" GST_SEGMENT_FORMAT,
1132           &seg);
1133 
1134       DVD_SPU_LOCK (dvdspu);
1135 
1136       dvdspu->subp_seg = seg;
1137       GST_LOG_OBJECT (dvdspu, "Subpicture segment now: %" GST_SEGMENT_FORMAT,
1138           &dvdspu->subp_seg);
1139       DVD_SPU_UNLOCK (dvdspu);
1140 
1141       gst_event_unref (event);
1142       break;
1143     }
1144     case GST_EVENT_GAP:
1145     {
1146       GstClockTime timestamp, duration;
1147       gst_event_parse_gap (event, &timestamp, &duration);
1148       if (GST_CLOCK_TIME_IS_VALID (duration))
1149         timestamp += duration;
1150 
1151       DVD_SPU_LOCK (dvdspu);
1152       dvdspu->subp_seg.position = timestamp;
1153       GST_LOG_OBJECT (dvdspu, "Received GAP. Segment now: %" GST_SEGMENT_FORMAT,
1154           &dvdspu->subp_seg);
1155       DVD_SPU_UNLOCK (dvdspu);
1156 
1157       gst_event_unref (event);
1158       break;
1159     }
1160     case GST_EVENT_FLUSH_START:
1161       gst_event_unref (event);
1162       goto done;
1163     case GST_EVENT_FLUSH_STOP:
1164       GST_DEBUG_OBJECT (dvdspu, "Have flush-stop event on SPU pad");
1165       DVD_SPU_LOCK (dvdspu);
1166       gst_segment_init (&dvdspu->subp_seg, GST_FORMAT_UNDEFINED);
1167       gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
1168       DVD_SPU_UNLOCK (dvdspu);
1169 
1170       /* We don't forward flushes on the spu pad */
1171       gst_event_unref (event);
1172       goto done;
1173     case GST_EVENT_EOS:
1174       /* drop EOS on the subtitle pad, it means there are no more subtitles,
1175        * video might still continue, though */
1176       gst_event_unref (event);
1177       goto done;
1178     default:
1179       res = gst_pad_event_default (pad, parent, event);
1180       break;
1181   }
1182 
1183 done:
1184 
1185   return res;
1186 }
1187 
1188 static gboolean
gst_dvd_spu_subpic_set_caps(GstDVDSpu * dvdspu,GstPad * pad,GstCaps * caps)1189 gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps)
1190 {
1191   gboolean res = FALSE;
1192   GstStructure *s;
1193   SpuInputType input_type;
1194 
1195   s = gst_caps_get_structure (caps, 0);
1196 
1197   if (gst_structure_has_name (s, "subpicture/x-dvd")) {
1198     input_type = SPU_INPUT_TYPE_VOBSUB;
1199   } else if (gst_structure_has_name (s, "subpicture/x-pgs")) {
1200     input_type = SPU_INPUT_TYPE_PGS;
1201   } else {
1202     goto done;
1203   }
1204 
1205   DVD_SPU_LOCK (dvdspu);
1206   if (dvdspu->spu_input_type != input_type) {
1207     GST_INFO_OBJECT (dvdspu, "Incoming SPU packet type changed to %u",
1208         input_type);
1209     dvdspu->spu_input_type = input_type;
1210     gst_dvd_spu_flush_spu_info (dvdspu, TRUE);
1211   }
1212 
1213   DVD_SPU_UNLOCK (dvdspu);
1214   res = TRUE;
1215 done:
1216   return res;
1217 }
1218 
1219 static GstStateChangeReturn
gst_dvd_spu_change_state(GstElement * element,GstStateChange transition)1220 gst_dvd_spu_change_state (GstElement * element, GstStateChange transition)
1221 {
1222   GstDVDSpu *dvdspu = (GstDVDSpu *) element;
1223   GstStateChangeReturn ret;
1224 
1225   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1226 
1227   switch (transition) {
1228     case GST_STATE_CHANGE_PAUSED_TO_READY:
1229       DVD_SPU_LOCK (dvdspu);
1230       gst_dvd_spu_clear (dvdspu);
1231       DVD_SPU_UNLOCK (dvdspu);
1232       break;
1233     default:
1234       break;
1235   }
1236 
1237   return ret;
1238 }
1239 
1240 static gboolean
gst_dvd_spu_plugin_init(GstPlugin * plugin)1241 gst_dvd_spu_plugin_init (GstPlugin * plugin)
1242 {
1243   const gchar *env;
1244 
1245   GST_DEBUG_CATEGORY_INIT (dvdspu_debug, "gstspu",
1246       0, "Sub-picture Overlay decoder/renderer");
1247 
1248   env = g_getenv ("GST_DVD_SPU_DEBUG");
1249 
1250   dvdspu_debug_flags = 0;
1251   if (env != NULL) {
1252     if (strstr (env, "render-rectangle") != NULL)
1253       dvdspu_debug_flags |= GST_DVD_SPU_DEBUG_RENDER_RECTANGLE;
1254     if (strstr (env, "highlight-rectangle") != NULL)
1255       dvdspu_debug_flags |= GST_DVD_SPU_DEBUG_HIGHLIGHT_RECTANGLE;
1256   }
1257   GST_INFO ("debug flags : 0x%02x", dvdspu_debug_flags);
1258 
1259   return gst_element_register (plugin, "dvdspu",
1260       GST_RANK_PRIMARY, GST_TYPE_DVD_SPU);
1261 }
1262 
1263 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1264     GST_VERSION_MINOR,
1265     dvdspu,
1266     "DVD Sub-picture Overlay element",
1267     gst_dvd_spu_plugin_init,
1268     VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1269