• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params);
562         } else {
563           allocator = NULL;
564           gst_allocation_params_init (&params);
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, &params);
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