1 /* GStreamer
2 * Copyright (C) 2010 David Schleef <ds@schleef.org>
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-gsty4mdec
21 * @title: gsty4mdec
22 *
23 * The gsty4mdec element decodes uncompressed video in YUV4MPEG format.
24 *
25 * ## Example launch line
26 * |[
27 * gst-launch-1.0 -v filesrc location=file.y4m ! y4mdec ! xvimagesink
28 * ]|
29 *
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <gst/gst.h>
37 #include <gst/video/video.h>
38 #include "gsty4mdec.h"
39
40 #include <stdlib.h>
41 #include <string.h>
42
43 #define MAX_SIZE 32768
44
45 GST_DEBUG_CATEGORY (y4mdec_debug);
46 #define GST_CAT_DEFAULT y4mdec_debug
47
48 /* prototypes */
49
50
51 static void gst_y4m_dec_set_property (GObject * object,
52 guint property_id, const GValue * value, GParamSpec * pspec);
53 static void gst_y4m_dec_get_property (GObject * object,
54 guint property_id, GValue * value, GParamSpec * pspec);
55 static void gst_y4m_dec_dispose (GObject * object);
56 static void gst_y4m_dec_finalize (GObject * object);
57
58 static GstFlowReturn gst_y4m_dec_chain (GstPad * pad, GstObject * parent,
59 GstBuffer * buffer);
60 static gboolean gst_y4m_dec_sink_event (GstPad * pad, GstObject * parent,
61 GstEvent * event);
62
63 static gboolean gst_y4m_dec_src_event (GstPad * pad, GstObject * parent,
64 GstEvent * event);
65 static gboolean gst_y4m_dec_src_query (GstPad * pad, GstObject * parent,
66 GstQuery * query);
67
68 static GstStateChangeReturn
69 gst_y4m_dec_change_state (GstElement * element, GstStateChange transition);
70
71 enum
72 {
73 PROP_0
74 };
75
76 /* pad templates */
77
78 static GstStaticPadTemplate gst_y4m_dec_sink_template =
79 GST_STATIC_PAD_TEMPLATE ("sink",
80 GST_PAD_SINK,
81 GST_PAD_ALWAYS,
82 GST_STATIC_CAPS ("application/x-yuv4mpeg, y4mversion=2")
83 );
84
85 static GstStaticPadTemplate gst_y4m_dec_src_template =
86 GST_STATIC_PAD_TEMPLATE ("src",
87 GST_PAD_SRC,
88 GST_PAD_ALWAYS,
89 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{I420,Y42B,Y444}"))
90 );
91
92 /* class initialization */
93 #define gst_y4m_dec_parent_class parent_class
94 G_DEFINE_TYPE (GstY4mDec, gst_y4m_dec, GST_TYPE_ELEMENT);
95
96 static void
gst_y4m_dec_class_init(GstY4mDecClass * klass)97 gst_y4m_dec_class_init (GstY4mDecClass * klass)
98 {
99 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
100 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
101
102 gobject_class->set_property = gst_y4m_dec_set_property;
103 gobject_class->get_property = gst_y4m_dec_get_property;
104 gobject_class->dispose = gst_y4m_dec_dispose;
105 gobject_class->finalize = gst_y4m_dec_finalize;
106
107 element_class->change_state = GST_DEBUG_FUNCPTR (gst_y4m_dec_change_state);
108
109 gst_element_class_add_static_pad_template (element_class,
110 &gst_y4m_dec_src_template);
111 gst_element_class_add_static_pad_template (element_class,
112 &gst_y4m_dec_sink_template);
113
114 gst_element_class_set_static_metadata (element_class,
115 "YUV4MPEG demuxer/decoder", "Codec/Demuxer",
116 "Demuxes/decodes YUV4MPEG streams", "David Schleef <ds@schleef.org>");
117 }
118
119 static void
gst_y4m_dec_init(GstY4mDec * y4mdec)120 gst_y4m_dec_init (GstY4mDec * y4mdec)
121 {
122 y4mdec->adapter = gst_adapter_new ();
123
124 y4mdec->sinkpad =
125 gst_pad_new_from_static_template (&gst_y4m_dec_sink_template, "sink");
126 gst_pad_set_event_function (y4mdec->sinkpad,
127 GST_DEBUG_FUNCPTR (gst_y4m_dec_sink_event));
128 gst_pad_set_chain_function (y4mdec->sinkpad,
129 GST_DEBUG_FUNCPTR (gst_y4m_dec_chain));
130 gst_element_add_pad (GST_ELEMENT (y4mdec), y4mdec->sinkpad);
131
132 y4mdec->srcpad = gst_pad_new_from_static_template (&gst_y4m_dec_src_template,
133 "src");
134 gst_pad_set_event_function (y4mdec->srcpad,
135 GST_DEBUG_FUNCPTR (gst_y4m_dec_src_event));
136 gst_pad_set_query_function (y4mdec->srcpad,
137 GST_DEBUG_FUNCPTR (gst_y4m_dec_src_query));
138 gst_pad_use_fixed_caps (y4mdec->srcpad);
139 gst_element_add_pad (GST_ELEMENT (y4mdec), y4mdec->srcpad);
140
141 }
142
143 void
gst_y4m_dec_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)144 gst_y4m_dec_set_property (GObject * object, guint property_id,
145 const GValue * value, GParamSpec * pspec)
146 {
147 g_return_if_fail (GST_IS_Y4M_DEC (object));
148
149 switch (property_id) {
150 default:
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
152 break;
153 }
154 }
155
156 void
gst_y4m_dec_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)157 gst_y4m_dec_get_property (GObject * object, guint property_id,
158 GValue * value, GParamSpec * pspec)
159 {
160 g_return_if_fail (GST_IS_Y4M_DEC (object));
161
162 switch (property_id) {
163 default:
164 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
165 break;
166 }
167 }
168
169 void
gst_y4m_dec_dispose(GObject * object)170 gst_y4m_dec_dispose (GObject * object)
171 {
172 GstY4mDec *y4mdec;
173
174 g_return_if_fail (GST_IS_Y4M_DEC (object));
175 y4mdec = GST_Y4M_DEC (object);
176
177 /* clean up as possible. may be called multiple times */
178 if (y4mdec->adapter) {
179 g_object_unref (y4mdec->adapter);
180 y4mdec->adapter = NULL;
181 }
182
183 G_OBJECT_CLASS (parent_class)->dispose (object);
184 }
185
186 void
gst_y4m_dec_finalize(GObject * object)187 gst_y4m_dec_finalize (GObject * object)
188 {
189 g_return_if_fail (GST_IS_Y4M_DEC (object));
190
191 /* clean up object here */
192
193 G_OBJECT_CLASS (parent_class)->finalize (object);
194 }
195
196 static GstStateChangeReturn
gst_y4m_dec_change_state(GstElement * element,GstStateChange transition)197 gst_y4m_dec_change_state (GstElement * element, GstStateChange transition)
198 {
199 GstY4mDec *y4mdec;
200 GstStateChangeReturn ret;
201
202 g_return_val_if_fail (GST_IS_Y4M_DEC (element), GST_STATE_CHANGE_FAILURE);
203
204 y4mdec = GST_Y4M_DEC (element);
205
206 switch (transition) {
207 case GST_STATE_CHANGE_NULL_TO_READY:
208 break;
209 case GST_STATE_CHANGE_READY_TO_PAUSED:
210 break;
211 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
212 break;
213 default:
214 break;
215 }
216
217 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
218
219 switch (transition) {
220 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
221 break;
222 case GST_STATE_CHANGE_PAUSED_TO_READY:
223 if (y4mdec->pool) {
224 gst_buffer_pool_set_active (y4mdec->pool, FALSE);
225 gst_object_unref (y4mdec->pool);
226 }
227 y4mdec->pool = NULL;
228 break;
229 case GST_STATE_CHANGE_READY_TO_NULL:
230 break;
231 default:
232 break;
233 }
234
235 return ret;
236 }
237
238 static GstClockTime
gst_y4m_dec_frames_to_timestamp(GstY4mDec * y4mdec,gint64 frame_index)239 gst_y4m_dec_frames_to_timestamp (GstY4mDec * y4mdec, gint64 frame_index)
240 {
241 if (frame_index == -1)
242 return -1;
243
244 return gst_util_uint64_scale (frame_index, GST_SECOND * y4mdec->info.fps_d,
245 y4mdec->info.fps_n);
246 }
247
248 static gint64
gst_y4m_dec_timestamp_to_frames(GstY4mDec * y4mdec,GstClockTime timestamp)249 gst_y4m_dec_timestamp_to_frames (GstY4mDec * y4mdec, GstClockTime timestamp)
250 {
251 if (timestamp == -1)
252 return -1;
253
254 return gst_util_uint64_scale (timestamp, y4mdec->info.fps_n,
255 GST_SECOND * y4mdec->info.fps_d);
256 }
257
258 static gint64
gst_y4m_dec_bytes_to_frames(GstY4mDec * y4mdec,gint64 bytes)259 gst_y4m_dec_bytes_to_frames (GstY4mDec * y4mdec, gint64 bytes)
260 {
261 if (bytes == -1)
262 return -1;
263
264 if (bytes < y4mdec->header_size)
265 return 0;
266 return (bytes - y4mdec->header_size) / (y4mdec->info.size + 6);
267 }
268
269 static guint64
gst_y4m_dec_frames_to_bytes(GstY4mDec * y4mdec,gint64 frame_index)270 gst_y4m_dec_frames_to_bytes (GstY4mDec * y4mdec, gint64 frame_index)
271 {
272 if (frame_index == -1)
273 return -1;
274
275 return y4mdec->header_size + (y4mdec->info.size + 6) * frame_index;
276 }
277
278 static GstClockTime
gst_y4m_dec_bytes_to_timestamp(GstY4mDec * y4mdec,gint64 bytes)279 gst_y4m_dec_bytes_to_timestamp (GstY4mDec * y4mdec, gint64 bytes)
280 {
281 if (bytes == -1)
282 return -1;
283
284 return gst_y4m_dec_frames_to_timestamp (y4mdec,
285 gst_y4m_dec_bytes_to_frames (y4mdec, bytes));
286 }
287
288
289 static gboolean
gst_y4m_dec_parse_header(GstY4mDec * y4mdec,char * header)290 gst_y4m_dec_parse_header (GstY4mDec * y4mdec, char *header)
291 {
292 char *end;
293 int iformat = 420;
294 int interlaced_char = 0;
295 gint fps_n = 0, fps_d = 0;
296 gint par_n = 0, par_d = 0;
297 gint width = 0, height = 0;
298 GstVideoFormat format;
299
300 if (memcmp (header, "YUV4MPEG2 ", 10) != 0) {
301 return FALSE;
302 }
303
304 header += 10;
305 while (*header) {
306 GST_DEBUG_OBJECT (y4mdec, "parsing at '%s'", header);
307 switch (*header) {
308 case ' ':
309 header++;
310 break;
311 case 'C':
312 header++;
313 iformat = strtoul (header, &end, 10);
314 if (end == header)
315 goto error;
316 header = end;
317 break;
318 case 'W':
319 header++;
320 width = strtoul (header, &end, 10);
321 if (end == header)
322 goto error;
323 header = end;
324 break;
325 case 'H':
326 header++;
327 height = strtoul (header, &end, 10);
328 if (end == header)
329 goto error;
330 header = end;
331 break;
332 case 'I':
333 header++;
334 if (header[0] == 0) {
335 GST_WARNING_OBJECT (y4mdec, "Expecting interlaced flag");
336 return FALSE;
337 }
338 interlaced_char = header[0];
339 header++;
340 break;
341 case 'F':
342 header++;
343 fps_n = strtoul (header, &end, 10);
344 if (end == header)
345 goto error;
346 header = end;
347 if (header[0] != ':') {
348 GST_WARNING_OBJECT (y4mdec, "Expecting :");
349 return FALSE;
350 }
351 header++;
352 fps_d = strtoul (header, &end, 10);
353 if (end == header)
354 goto error;
355 header = end;
356 break;
357 case 'A':
358 header++;
359 par_n = strtoul (header, &end, 10);
360 if (end == header)
361 goto error;
362 header = end;
363 if (header[0] != ':') {
364 GST_WARNING_OBJECT (y4mdec, "Expecting :");
365 return FALSE;
366 }
367 header++;
368 par_d = strtoul (header, &end, 10);
369 if (end == header)
370 goto error;
371 header = end;
372 break;
373 default:
374 GST_WARNING_OBJECT (y4mdec, "Unknown y4m header field '%c', ignoring",
375 *header);
376 while (*header && *header != ' ')
377 header++;
378 break;
379 }
380 }
381
382 switch (iformat) {
383 case 420:
384 format = GST_VIDEO_FORMAT_I420;
385 break;
386 case 422:
387 format = GST_VIDEO_FORMAT_Y42B;
388 break;
389 case 444:
390 format = GST_VIDEO_FORMAT_Y444;
391 break;
392 default:
393 GST_WARNING_OBJECT (y4mdec, "unknown y4m format %d", iformat);
394 return FALSE;
395 }
396
397 if (width <= 0 || width > MAX_SIZE || height <= 0 || height > MAX_SIZE) {
398 GST_WARNING_OBJECT (y4mdec, "Dimensions %dx%d out of range", width, height);
399 return FALSE;
400 }
401
402 gst_video_info_init (&y4mdec->info);
403 gst_video_info_set_format (&y4mdec->out_info, format, width, height);
404 y4mdec->info = y4mdec->out_info;
405
406 switch (y4mdec->info.finfo->format) {
407 case GST_VIDEO_FORMAT_I420:
408 y4mdec->info.offset[0] = 0;
409 y4mdec->info.stride[0] = width;
410 y4mdec->info.offset[1] = y4mdec->info.stride[0] * height;
411 y4mdec->info.stride[1] = GST_ROUND_UP_2 (width) / 2;
412 y4mdec->info.offset[2] =
413 y4mdec->info.offset[1] +
414 y4mdec->info.stride[1] * (GST_ROUND_UP_2 (height) / 2);
415 y4mdec->info.stride[2] = GST_ROUND_UP_2 (width) / 2;
416 y4mdec->info.size =
417 y4mdec->info.offset[2] +
418 y4mdec->info.stride[2] * (GST_ROUND_UP_2 (height) / 2);
419 break;
420 case GST_VIDEO_FORMAT_Y42B:
421 y4mdec->info.offset[0] = 0;
422 y4mdec->info.stride[0] = width;
423 y4mdec->info.offset[1] = y4mdec->info.stride[0] * height;
424 y4mdec->info.stride[1] = GST_ROUND_UP_2 (width) / 2;
425 y4mdec->info.offset[2] =
426 y4mdec->info.offset[1] + y4mdec->info.stride[1] * height;
427 y4mdec->info.stride[2] = GST_ROUND_UP_2 (width) / 2;
428 y4mdec->info.size =
429 y4mdec->info.offset[2] + y4mdec->info.stride[2] * height;
430 break;
431 case GST_VIDEO_FORMAT_Y444:
432 y4mdec->info.offset[0] = 0;
433 y4mdec->info.stride[0] = width;
434 y4mdec->info.offset[1] = y4mdec->info.stride[0] * height;
435 y4mdec->info.stride[1] = width;
436 y4mdec->info.offset[2] =
437 y4mdec->info.offset[1] + y4mdec->info.stride[1] * height;
438 y4mdec->info.stride[2] = width;
439 y4mdec->info.size =
440 y4mdec->info.offset[2] + y4mdec->info.stride[2] * height;
441 break;
442 default:
443 g_assert_not_reached ();
444 break;
445 }
446
447 switch (interlaced_char) {
448 case 0:
449 case '?':
450 case 'p':
451 y4mdec->info.interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
452 break;
453 case 't':
454 case 'b':
455 y4mdec->info.interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
456 break;
457 default:
458 GST_WARNING_OBJECT (y4mdec, "Unknown interlaced char '%c'",
459 interlaced_char);
460 return FALSE;
461 break;
462 }
463
464 if (fps_n == 0)
465 fps_n = 1;
466 if (fps_d == 0)
467 fps_d = 1;
468 if (par_n == 0)
469 par_n = 1;
470 if (par_d == 0)
471 par_d = 1;
472
473 y4mdec->info.fps_n = fps_n;
474 y4mdec->info.fps_d = fps_d;
475 y4mdec->info.par_n = par_n;
476 y4mdec->info.par_d = par_d;
477
478 return TRUE;
479 error:
480 GST_WARNING_OBJECT (y4mdec, "Expecting number y4m header at '%s'", header);
481 return FALSE;
482 }
483
484 static GstFlowReturn
gst_y4m_dec_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)485 gst_y4m_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
486 {
487 GstY4mDec *y4mdec;
488 int n_avail;
489 GstFlowReturn flow_ret = GST_FLOW_OK;
490 #define MAX_HEADER_LENGTH 80
491 char header[MAX_HEADER_LENGTH];
492 int i;
493 int len;
494
495 y4mdec = GST_Y4M_DEC (parent);
496
497 GST_DEBUG_OBJECT (y4mdec, "chain");
498
499 if (GST_BUFFER_IS_DISCONT (buffer)) {
500 GST_DEBUG ("got discont");
501 gst_adapter_clear (y4mdec->adapter);
502 }
503
504 gst_adapter_push (y4mdec->adapter, buffer);
505 n_avail = gst_adapter_available (y4mdec->adapter);
506
507 if (!y4mdec->have_header) {
508 gboolean ret;
509 GstCaps *caps;
510 GstQuery *query;
511
512 if (n_avail < MAX_HEADER_LENGTH)
513 return GST_FLOW_OK;
514
515 gst_adapter_copy (y4mdec->adapter, (guint8 *) header, 0, MAX_HEADER_LENGTH);
516
517 header[MAX_HEADER_LENGTH - 1] = 0;
518 for (i = 0; i < MAX_HEADER_LENGTH; i++) {
519 if (header[i] == 0x0a)
520 header[i] = 0;
521 }
522
523 ret = gst_y4m_dec_parse_header (y4mdec, header);
524 if (!ret) {
525 GST_ELEMENT_ERROR (y4mdec, STREAM, DECODE,
526 ("Failed to parse YUV4MPEG header"), (NULL));
527 return GST_FLOW_ERROR;
528 }
529
530 y4mdec->header_size = strlen (header) + 1;
531 gst_adapter_flush (y4mdec->adapter, y4mdec->header_size);
532
533 caps = gst_video_info_to_caps (&y4mdec->info);
534 ret = gst_pad_set_caps (y4mdec->srcpad, caps);
535
536 query = gst_query_new_allocation (caps, FALSE);
537 y4mdec->video_meta = FALSE;
538
539 if (y4mdec->pool) {
540 gst_buffer_pool_set_active (y4mdec->pool, FALSE);
541 gst_object_unref (y4mdec->pool);
542 }
543 y4mdec->pool = NULL;
544
545 if (gst_pad_peer_query (y4mdec->srcpad, query)) {
546 y4mdec->video_meta =
547 gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
548
549 /* We only need a pool if we need to do stride conversion for downstream */
550 if (!y4mdec->video_meta && memcmp (&y4mdec->info, &y4mdec->out_info,
551 sizeof (y4mdec->info)) != 0) {
552 GstBufferPool *pool = NULL;
553 GstAllocator *allocator = NULL;
554 GstAllocationParams params;
555 GstStructure *config;
556 guint size, min, max;
557
558 if (gst_query_get_n_allocation_params (query) > 0) {
559 gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
560 } else {
561 allocator = NULL;
562 gst_allocation_params_init (¶ms);
563 }
564
565 if (gst_query_get_n_allocation_pools (query) > 0) {
566 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min,
567 &max);
568 size = MAX (size, y4mdec->out_info.size);
569 } else {
570 pool = NULL;
571 size = y4mdec->out_info.size;
572 min = max = 0;
573 }
574
575 if (pool == NULL) {
576 pool = gst_video_buffer_pool_new ();
577 }
578
579 config = gst_buffer_pool_get_config (pool);
580 gst_buffer_pool_config_set_params (config, caps, size, min, max);
581 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
582 gst_buffer_pool_set_config (pool, config);
583
584 if (allocator)
585 gst_object_unref (allocator);
586
587 y4mdec->pool = pool;
588 }
589 } else if (memcmp (&y4mdec->info, &y4mdec->out_info,
590 sizeof (y4mdec->info)) != 0) {
591 GstBufferPool *pool;
592 GstStructure *config;
593
594 /* No pool, create our own if we need to do stride conversion */
595 pool = gst_video_buffer_pool_new ();
596 config = gst_buffer_pool_get_config (pool);
597 gst_buffer_pool_config_set_params (config, caps, y4mdec->out_info.size, 0,
598 0);
599 gst_buffer_pool_set_config (pool, config);
600 y4mdec->pool = pool;
601 }
602 if (y4mdec->pool) {
603 gst_buffer_pool_set_active (y4mdec->pool, TRUE);
604 }
605 gst_query_unref (query);
606 gst_caps_unref (caps);
607 if (!ret) {
608 GST_DEBUG_OBJECT (y4mdec, "Couldn't set caps on src pad");
609 return GST_FLOW_ERROR;
610 }
611
612 y4mdec->have_header = TRUE;
613 }
614
615 if (y4mdec->have_new_segment) {
616 GstEvent *event;
617 GstClockTime start = gst_y4m_dec_bytes_to_timestamp (y4mdec,
618 y4mdec->segment.start);
619 GstClockTime stop = gst_y4m_dec_bytes_to_timestamp (y4mdec,
620 y4mdec->segment.stop);
621 GstClockTime time = gst_y4m_dec_bytes_to_timestamp (y4mdec,
622 y4mdec->segment.time);
623 GstSegment seg;
624
625 gst_segment_init (&seg, GST_FORMAT_TIME);
626 seg.start = start;
627 seg.stop = stop;
628 seg.time = time;
629 event = gst_event_new_segment (&seg);
630
631 gst_pad_push_event (y4mdec->srcpad, event);
632 //gst_event_unref (event);
633
634 y4mdec->have_new_segment = FALSE;
635 y4mdec->frame_index = gst_y4m_dec_bytes_to_frames (y4mdec,
636 y4mdec->segment.time);
637 GST_DEBUG ("new frame_index %d", y4mdec->frame_index);
638
639 }
640
641 while (1) {
642 n_avail = gst_adapter_available (y4mdec->adapter);
643 if (n_avail < MAX_HEADER_LENGTH)
644 break;
645
646 gst_adapter_copy (y4mdec->adapter, (guint8 *) header, 0, MAX_HEADER_LENGTH);
647 header[MAX_HEADER_LENGTH - 1] = 0;
648 for (i = 0; i < MAX_HEADER_LENGTH; i++) {
649 if (header[i] == 0x0a)
650 header[i] = 0;
651 }
652 if (memcmp (header, "FRAME", 5) != 0) {
653 GST_ELEMENT_ERROR (y4mdec, STREAM, DECODE,
654 ("Failed to parse YUV4MPEG frame"), (NULL));
655 flow_ret = GST_FLOW_ERROR;
656 break;
657 }
658
659 len = strlen (header);
660 if (n_avail < y4mdec->info.size + len + 1) {
661 /* not enough data */
662 GST_DEBUG ("not enough data for frame %d < %" G_GSIZE_FORMAT,
663 n_avail, y4mdec->info.size + len + 1);
664 break;
665 }
666
667 gst_adapter_flush (y4mdec->adapter, len + 1);
668
669 buffer = gst_adapter_take_buffer (y4mdec->adapter, y4mdec->info.size);
670
671 GST_BUFFER_TIMESTAMP (buffer) =
672 gst_y4m_dec_frames_to_timestamp (y4mdec, y4mdec->frame_index);
673 GST_BUFFER_DURATION (buffer) =
674 gst_y4m_dec_frames_to_timestamp (y4mdec, y4mdec->frame_index + 1) -
675 GST_BUFFER_TIMESTAMP (buffer);
676
677 y4mdec->frame_index++;
678
679 if (y4mdec->video_meta) {
680 gst_buffer_add_video_meta_full (buffer, 0, y4mdec->info.finfo->format,
681 y4mdec->info.width, y4mdec->info.height, y4mdec->info.finfo->n_planes,
682 y4mdec->info.offset, y4mdec->info.stride);
683 } else if (memcmp (&y4mdec->info, &y4mdec->out_info,
684 sizeof (y4mdec->info)) != 0) {
685 GstBuffer *outbuf;
686 GstVideoFrame iframe, oframe;
687 gint i, j;
688 gint w, h, istride, ostride;
689 guint8 *src, *dest;
690
691 /* Allocate a new buffer and do stride conversion */
692 g_assert (y4mdec->pool != NULL);
693
694 flow_ret = gst_buffer_pool_acquire_buffer (y4mdec->pool, &outbuf, NULL);
695 if (flow_ret != GST_FLOW_OK) {
696 gst_buffer_unref (buffer);
697 break;
698 }
699
700 gst_video_frame_map (&iframe, &y4mdec->info, buffer, GST_MAP_READ);
701 gst_video_frame_map (&oframe, &y4mdec->out_info, outbuf, GST_MAP_WRITE);
702
703 for (i = 0; i < 3; i++) {
704 w = GST_VIDEO_FRAME_COMP_WIDTH (&iframe, i);
705 h = GST_VIDEO_FRAME_COMP_HEIGHT (&iframe, i);
706 istride = GST_VIDEO_FRAME_COMP_STRIDE (&iframe, i);
707 ostride = GST_VIDEO_FRAME_COMP_STRIDE (&oframe, i);
708 src = GST_VIDEO_FRAME_COMP_DATA (&iframe, i);
709 dest = GST_VIDEO_FRAME_COMP_DATA (&oframe, i);
710
711 for (j = 0; j < h; j++) {
712 memcpy (dest, src, w);
713
714 dest += ostride;
715 src += istride;
716 }
717 }
718
719 gst_video_frame_unmap (&iframe);
720 gst_video_frame_unmap (&oframe);
721 gst_buffer_copy_into (outbuf, buffer, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
722 gst_buffer_unref (buffer);
723 buffer = outbuf;
724 }
725
726 flow_ret = gst_pad_push (y4mdec->srcpad, buffer);
727 if (flow_ret != GST_FLOW_OK)
728 break;
729 }
730
731 GST_DEBUG ("returning %d", flow_ret);
732
733 return flow_ret;
734 }
735
736 static gboolean
gst_y4m_dec_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)737 gst_y4m_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
738 {
739 gboolean res;
740 GstY4mDec *y4mdec;
741
742 y4mdec = GST_Y4M_DEC (parent);
743
744 GST_DEBUG_OBJECT (y4mdec, "event");
745
746 switch (GST_EVENT_TYPE (event)) {
747 case GST_EVENT_FLUSH_START:
748 res = gst_pad_push_event (y4mdec->srcpad, event);
749 break;
750 case GST_EVENT_FLUSH_STOP:
751 res = gst_pad_push_event (y4mdec->srcpad, event);
752 break;
753 case GST_EVENT_SEGMENT:
754 {
755 GstSegment seg;
756
757 gst_event_copy_segment (event, &seg);
758
759 GST_DEBUG ("segment: %" GST_SEGMENT_FORMAT, &seg);
760
761 if (seg.format == GST_FORMAT_BYTES) {
762 y4mdec->segment = seg;
763 y4mdec->have_new_segment = TRUE;
764 }
765
766 res = TRUE;
767 /* not sure why it's not forwarded, but let's unref it so it
768 doesn't leak, remove the unref if it gets forwarded again */
769 gst_event_unref (event);
770 //res = gst_pad_push_event (y4mdec->srcpad, event);
771 }
772 break;
773 case GST_EVENT_EOS:
774 default:
775 res = gst_pad_event_default (pad, parent, event);
776 break;
777 }
778
779 return res;
780 }
781
782 static gboolean
gst_y4m_dec_src_event(GstPad * pad,GstObject * parent,GstEvent * event)783 gst_y4m_dec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
784 {
785 gboolean res;
786 GstY4mDec *y4mdec;
787
788 y4mdec = GST_Y4M_DEC (parent);
789
790 GST_DEBUG_OBJECT (y4mdec, "event");
791
792 switch (GST_EVENT_TYPE (event)) {
793 case GST_EVENT_SEEK:
794 {
795 gdouble rate;
796 GstFormat format;
797 GstSeekFlags flags;
798 GstSeekType start_type, stop_type;
799 gint64 start, stop;
800 gint64 framenum;
801 guint64 byte;
802
803 gst_event_parse_seek (event, &rate, &format, &flags, &start_type,
804 &start, &stop_type, &stop);
805
806 if (format != GST_FORMAT_TIME) {
807 res = FALSE;
808 break;
809 }
810
811 framenum = gst_y4m_dec_timestamp_to_frames (y4mdec, start);
812 GST_DEBUG ("seeking to frame %" G_GINT64_FORMAT, framenum);
813 if (framenum == -1) {
814 res = FALSE;
815 break;
816 }
817
818 byte = gst_y4m_dec_frames_to_bytes (y4mdec, framenum);
819 GST_DEBUG ("offset %" G_GUINT64_FORMAT, (guint64) byte);
820 if (byte == -1) {
821 res = FALSE;
822 break;
823 }
824
825 gst_event_unref (event);
826 event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
827 start_type, byte, stop_type, -1);
828
829 res = gst_pad_push_event (y4mdec->sinkpad, event);
830 }
831 break;
832 default:
833 res = gst_pad_event_default (pad, parent, event);
834 break;
835 }
836
837 return res;
838 }
839
840 static gboolean
gst_y4m_dec_src_query(GstPad * pad,GstObject * parent,GstQuery * query)841 gst_y4m_dec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
842 {
843 GstY4mDec *y4mdec = GST_Y4M_DEC (parent);
844 gboolean res = FALSE;
845
846 switch (GST_QUERY_TYPE (query)) {
847 case GST_QUERY_DURATION:
848 {
849 GstFormat format;
850 GstQuery *peer_query;
851
852 GST_DEBUG ("duration query");
853
854 gst_query_parse_duration (query, &format, NULL);
855
856 if (format != GST_FORMAT_TIME) {
857 res = FALSE;
858 GST_DEBUG_OBJECT (y4mdec, "not handling duration query in format %d",
859 format);
860 break;
861 }
862
863 peer_query = gst_query_new_duration (GST_FORMAT_BYTES);
864
865 res = gst_pad_peer_query (y4mdec->sinkpad, peer_query);
866 if (res) {
867 gint64 duration;
868 int n_frames;
869
870 gst_query_parse_duration (peer_query, &format, &duration);
871
872 n_frames = gst_y4m_dec_bytes_to_frames (y4mdec, duration);
873 GST_DEBUG ("duration in frames %d", n_frames);
874
875 duration = gst_y4m_dec_frames_to_timestamp (y4mdec, n_frames);
876 GST_DEBUG ("duration in time %" GST_TIME_FORMAT,
877 GST_TIME_ARGS (duration));
878
879 gst_query_set_duration (query, GST_FORMAT_TIME, duration);
880 res = TRUE;
881 }
882 gst_query_unref (peer_query);
883 break;
884 }
885 default:
886 res = gst_pad_query_default (pad, parent, query);
887 break;
888 }
889
890 return res;
891 }
892
893
894 static gboolean
plugin_init(GstPlugin * plugin)895 plugin_init (GstPlugin * plugin)
896 {
897
898 gst_element_register (plugin, "y4mdec", GST_RANK_SECONDARY,
899 gst_y4m_dec_get_type ());
900
901 GST_DEBUG_CATEGORY_INIT (y4mdec_debug, "y4mdec", 0, "y4mdec element");
902
903 return TRUE;
904 }
905
906
907 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
908 GST_VERSION_MINOR,
909 y4mdec,
910 "Demuxes/decodes YUV4MPEG streams",
911 plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
912