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