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, ×tamp, &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, ×tamp, &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