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