• 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-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, &params);
560         } else {
561           allocator = NULL;
562           gst_allocation_params_init (&params);
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, &params);
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