]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugin-ducati.git/blob - src/gstducatividdec.c
ducatividdec: workaround for interlaced clips that are detected as progressive from...
[glsdk/gst-plugin-ducati.git] / src / gstducatividdec.c
1 /*
2  * GStreamer
3  * Copyright (c) 2010, Texas Instruments Incorporated
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation
8  * version 2.1 of the License.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
24 #include "gstducatividdec.h"
26 GST_BOILERPLATE (GstDucatiVidDec, gst_ducati_viddec, GstElement,
27     GST_TYPE_ELEMENT);
29 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
30     GST_PAD_SRC,
31     GST_PAD_ALWAYS,
32     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV_STRIDED ("NV12", "[ 0, max ]"))
33     );
35 enum
36 {
37   PROP_0,
38   PROP_VERSION,
39 };
41 /* helper functions */
43 static void
44 engine_close (GstDucatiVidDec * self)
45 {
46   if (self->engine) {
47     Engine_close (self->engine);
48     self->engine = NULL;
49   }
51   if (self->params) {
52     dce_free (self->params);
53     self->params = NULL;
54   }
56   if (self->dynParams) {
57     dce_free (self->dynParams);
58     self->dynParams = NULL;
59   }
61   if (self->status) {
62     dce_free (self->status);
63     self->status = NULL;
64   }
66   if (self->inBufs) {
67     dce_free (self->inBufs);
68     self->inBufs = NULL;
69   }
71   if (self->outBufs) {
72     dce_free (self->outBufs);
73     self->outBufs = NULL;
74   }
76   if (self->inArgs) {
77     dce_free (self->inArgs);
78     self->inArgs = NULL;
79   }
81   if (self->outArgs) {
82     dce_free (self->outArgs);
83     self->outArgs = NULL;
84   }
85 }
87 static gboolean
88 engine_open (GstDucatiVidDec * self)
89 {
90   gboolean ret;
92   if (G_UNLIKELY (self->engine)) {
93     return TRUE;
94   }
96   GST_DEBUG_OBJECT (self, "opening engine");
98   self->engine = Engine_open ((String) "ivahd_vidsvr", NULL, NULL);
99   if (G_UNLIKELY (!self->engine)) {
100     GST_ERROR_OBJECT (self, "could not create engine");
101     return FALSE;
102   }
104   ret = GST_DUCATIVIDDEC_GET_CLASS (self)->allocate_params (self,
105       sizeof (IVIDDEC3_Params), sizeof (IVIDDEC3_DynamicParams),
106       sizeof (IVIDDEC3_Status), sizeof (IVIDDEC3_InArgs),
107       sizeof (IVIDDEC3_OutArgs));
109   return ret;
112 static void
113 codec_delete (GstDucatiVidDec * self)
115   if (self->pool) {
116     gst_ducati_bufferpool_destroy (self->pool);
117     self->pool = NULL;
118   }
120   if (self->codec) {
121     VIDDEC3_delete (self->codec);
122     self->codec = NULL;
123   }
125   if (self->input) {
126     MemMgr_Free (self->input);
127     self->input = NULL;
128   }
131 static gboolean
132 codec_create (GstDucatiVidDec * self)
134   gint err;
135   const gchar *codec_name;
137   codec_delete (self);
139   if (G_UNLIKELY (!self->engine)) {
140     GST_ERROR_OBJECT (self, "no engine");
141     return FALSE;
142   }
144   /* these need to be set before VIDDEC3_create */
145   self->params->maxWidth = self->width;
146   self->params->maxHeight = self->height;
148   codec_name = GST_DUCATIVIDDEC_GET_CLASS (self)->codec_name;
150   /* create codec: */
151   GST_DEBUG_OBJECT (self, "creating codec: %s", codec_name);
152   self->codec =
153       VIDDEC3_create (self->engine, (String) codec_name, self->params);
155   if (!self->codec) {
156     return FALSE;
157   }
159   err =
160       VIDDEC3_control (self->codec, XDM_SETPARAMS, self->dynParams,
161       self->status);
162   if (err) {
163     GST_ERROR_OBJECT (self, "failed XDM_SETPARAMS");
164     return FALSE;
165   }
167   self->first_in_buffer = TRUE;
168   self->first_out_buffer = TRUE;
170   /* allocate input buffer and initialize inBufs: */
171   self->inBufs->numBufs = 1;
172   self->input = gst_ducati_alloc_1d (self->width * self->height);
173   self->inBufs->descs[0].buf = (XDAS_Int8 *) TilerMem_VirtToPhys (self->input);
174   self->inBufs->descs[0].memType = XDM_MEMTYPE_RAW;
176   return TRUE;
179 static inline GstBuffer *
180 codec_bufferpool_get (GstDucatiVidDec * self, GstBuffer * buf)
182   if (G_UNLIKELY (!self->pool)) {
183     guint size;
185     size = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
186         self->padded_width, self->padded_height);
187     GST_DEBUG_OBJECT (self, "creating bufferpool");
188     self->pool = gst_ducati_bufferpool_new (GST_ELEMENT (self),
189         GST_PAD_CAPS (self->srcpad), size);
190   }
191   return GST_BUFFER (gst_ducati_bufferpool_get (self->pool, buf));
194 static XDAS_Int32
195 codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
196     gboolean force_internal)
198   XDAS_Int16 y_type, uv_type;
199   guint8 *y_vaddr, *uv_vaddr;
200   SSPtr y_paddr, uv_paddr;
202   if (force_internal) {
203     GST_DEBUG_OBJECT (self, "internal bufferpool forced");
204     gst_buffer_unref (*buf);
205     *buf = codec_bufferpool_get (self, NULL);
206     return codec_prepare_outbuf (self, buf, FALSE);
207   }
209   y_vaddr = GST_BUFFER_DATA (*buf);
210   uv_vaddr = y_vaddr + self->stride * self->padded_height;
212   y_paddr = TilerMem_VirtToPhys (y_vaddr);
213   uv_paddr = TilerMem_VirtToPhys (uv_vaddr);
215   y_type = gst_ducati_get_mem_type (y_paddr);
216   uv_type = gst_ducati_get_mem_type (uv_paddr);
217   /* FIXME: workaround for the vc1 codec expecting _RAW when it's actually
218    * _TILEDPAGE... should be removed once the codec is fixed  */
219   if (y_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != y_type)
220     y_type = self->pageMemType;
221   if (uv_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != uv_type)
222     uv_type = self->pageMemType;
224   if (y_type < 0 || uv_type < 0) {
225     GST_DEBUG_OBJECT (self, "non TILER buffer, fallback to bufferpool");
226     *buf = codec_bufferpool_get (self, *buf);
227     return codec_prepare_outbuf (self, buf, FALSE);
228   }
230   if (!self->outBufs->numBufs) {
231     /* initialize output buffer type */
232     self->outBufs->numBufs = 2;
233     self->outBufs->descs[0].memType = y_type;
234     self->outBufs->descs[1].memType = uv_type;
235     if (y_type == XDM_MEMTYPE_RAW || y_type == XDM_MEMTYPE_TILEDPAGE) {
236       self->outBufs->descs[0].bufSize.bytes =
237           self->stride * self->padded_height;
238       self->outBufs->descs[1].bufSize.bytes =
239           self->stride * self->padded_height / 2;
240     } else {
241       self->outBufs->descs[0].bufSize.tileMem.width = self->padded_width;
242       self->outBufs->descs[0].bufSize.tileMem.height = self->padded_height;
243       /* note that UV interleaved width is same a Y: */
244       self->outBufs->descs[1].bufSize.tileMem.width = self->padded_width;
245       self->outBufs->descs[1].bufSize.tileMem.height = self->padded_height / 2;
246     }
247   } else {
248     /* verify output buffer type matches what we've already given
249      * to the codec
250      */
251     if ((self->outBufs->descs[0].memType != y_type) ||
252         (self->outBufs->descs[1].memType != uv_type)) {
253       GST_DEBUG_OBJECT (self, "buffer mismatch, fallback to bufferpool");
254       *buf = codec_bufferpool_get (self, *buf);
255       return codec_prepare_outbuf (self, buf, FALSE);
256     }
257   }
259   self->outBufs->descs[0].buf = (XDAS_Int8 *) y_paddr;
260   self->outBufs->descs[1].buf = (XDAS_Int8 *) uv_paddr;
262   return (XDAS_Int32) *buf;      // XXX use lookup table
265 static GstBuffer *
266 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
268   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
269   if (buf) {
270     gst_buffer_ref (buf);
271   }
272   return buf;
275 static void
276 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
278   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
279   if (buf) {
280     GST_DEBUG_OBJECT (self, "free buffer: %d %p", id, buf);
281     gst_buffer_unref (buf);
282   }
285 static gint
286 codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
288   gint err;
289   GstClockTime t;
290   GstBuffer *outbuf = NULL;
291   gint i;
292   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
294   memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
295   memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
297   t = gst_util_get_timestamp ();
298   err = VIDDEC3_process (self->codec,
299       self->inBufs, self->outBufs, self->inArgs, self->outArgs);
300   GST_DEBUG_OBJECT (self, "%10dns", (gint) (gst_util_get_timestamp () - t));
302   if (err) {
303     GST_WARNING_OBJECT (self, "err=%d, extendedError=%08x",
304         err, self->outArgs->extendedError);
306     err = VIDDEC3_control (self->codec, XDM_GETSTATUS,
307         self->dynParams, self->status);
308     if (!err) {
309       GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
310           err, self->status->extendedError);
311     }
313     if (flush)
314       err = XDM_EFAIL;
315     else
316       err = klass->handle_error (self, err,
317           self->outArgs->extendedError, self->status->extendedError);
318   }
320   for (i = 0; self->outArgs->outputID[i]; i++) {
321     gboolean interlaced;
323     outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
325     interlaced = self->outArgs->decodedBufs.contentType == IVIDEO_PROGRESSIVE ? FALSE : TRUE;
327     if (interlaced != self->interlaced) {
328       GstCaps *caps;
330       GST_WARNING_OBJECT (self, "upstream set interlaced=%d but codec "
331           "thinks interlaced=%d... trusting codec", self->interlaced,
332           interlaced);
334       self->interlaced = interlaced;
336       caps = gst_caps_make_writable (gst_pad_get_negotiated_caps (self->srcpad));
337       GST_INFO_OBJECT (self, "changing interlace field in caps");
338       gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced, NULL);
339       if (!gst_pad_set_caps (self->srcpad, caps)) {
340         GST_ERROR_OBJECT (self, "downstream didn't want to change interlace mode");
341         err = XDM_EFAIL;
342       }
343       gst_caps_unref (caps);
345       /* this buffer still has the old caps so we skip it */
346       send = FALSE;
347     }
349     if (G_UNLIKELY (self->first_out_buffer) && send) {
350       gint crop_width, crop_height;
352       /* send region of interest to sink on first buffer: */
353       XDM_Rect *r = &(self->outArgs->displayBufs.bufDesc[0].activeFrameRegion);
355       crop_width = r->bottomRight.x - r->topLeft.x;
356       crop_height = r->bottomRight.y - r->topLeft.y;
358       if (crop_width > self->input_width)
359         crop_width = self->input_width;
360       if (crop_height > self->input_height)
361         crop_height = self->input_height;
363       GST_INFO_OBJECT (self, "active frame region %d, %d, %d, %d, crop %dx%d",
364           r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y,
365           crop_width, crop_height);
367       gst_pad_push_event (self->srcpad,
368           gst_event_new_crop (r->topLeft.y, r->topLeft.x,
369               crop_width, crop_height));
371       self->first_out_buffer = FALSE;
372     }
374     if (send) {
375       GstClockTime ts;
377       if (GST_IS_DUCATIBUFFER (outbuf)) {
378         outbuf = gst_ducati_buffer_get (GST_DUCATIBUFFER (outbuf));
379       }
381       ts = GST_BUFFER_TIMESTAMP (outbuf);
383       GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
384           i, outbuf, GST_TIME_ARGS (ts));
386       if (self->ts_may_be_pts) {
387         if ((self->last_pts != GST_CLOCK_TIME_NONE) &&
388             (self->last_pts > ts)) {
389           GST_DEBUG_OBJECT (self, "detected PTS going backwards, "
390               "enabling ts_is_pts");
391           self->ts_is_pts = TRUE;
392         }
393       }
395       self->last_pts = ts;
397       if (self->dts_ridx != self->dts_widx) {
398         ts = self->dts_queue[self->dts_ridx++ % NDTS];
399       }
401       if (self->ts_is_pts) {
402         /* if we have a queued DTS from demuxer, use that instead: */
403         GST_BUFFER_TIMESTAMP (outbuf) = ts;
404         GST_DEBUG_OBJECT (self, "fixed ts: %d %p (%" GST_TIME_FORMAT ")",
405             i, outbuf, GST_TIME_ARGS (ts));
406       }
408     
409       if (GST_BUFFER_CAPS (outbuf) &&
410           !gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
411               GST_PAD_CAPS (self->srcpad))) {
412         /* this looks a bit scary but it's really just to change the interlace=
413          * field in caps when we start as !interlaced and the codec detects
414          * otherwise */
415         GST_WARNING_OBJECT (self, "overriding buffer caps to fix "
416             "interlace mismatch");
417         outbuf = gst_buffer_make_metadata_writable (outbuf);
418         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
419       }
421       gst_pad_push (self->srcpad, outbuf);
422     } else {
423       GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
424       gst_buffer_unref (outbuf);
425     }
426   }
428   for (i = 0; self->outArgs->freeBufID[i]; i++) {
429     codec_unlock_outbuf (self, self->outArgs->freeBufID[i]);
430   }
432   return err;
435 /** call control(FLUSH), and then process() to pop out all buffers */
436 static gboolean
437 codec_flush (GstDucatiVidDec * self, gboolean eos)
439   gint err;
441   GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
443   /* note: flush is synchronized against _chain() to avoid calling
444    * the codec from multiple threads
445    */
446   GST_PAD_STREAM_LOCK (self->sinkpad);
448   self->dts_ridx = self->dts_widx = 0;
449   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
450   self->ts_may_be_pts = TRUE;
452   if (G_UNLIKELY (self->first_in_buffer)) {
453     return TRUE;
454   }
456   if (G_UNLIKELY (!self->codec)) {
457     GST_WARNING_OBJECT (self, "no codec");
458     return TRUE;
459   }
461   err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
462   if (err) {
463     GST_ERROR_OBJECT (self, "failed XDM_FLUSH");
464     goto out;
465   }
467   self->inBufs->descs[0].bufSize.bytes = 0;
468   self->inArgs->numBytes = 0;
469   self->inArgs->inputID = 0;
471   do {
472     err = codec_process (self, eos, TRUE);
473   } while (err != XDM_EFAIL);
475   /* on a flush, it is normal (and not an error) for the last _process() call
476    * to return an error..
477    */
478   err = XDM_EOK;
480 out:
481   GST_PAD_STREAM_UNLOCK (self->sinkpad);
482   GST_DEBUG_OBJECT (self, "done");
484   return !err;
487 /* GstDucatiVidDec vmethod default implementations */
489 static gboolean
490 gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
492   const GValue *codec_data;
493   gint w, h;
495   if (gst_structure_get_int (s, "width", &self->input_width) &&
496       gst_structure_get_int (s, "height", &self->input_height)) {
498     h = ALIGN2 (self->input_height, 4); /* round up to MB */
499     w = ALIGN2 (self->input_width, 4);  /* round up to MB */
501     /* if we've already created codec, but the resolution has changed, we
502      * need to re-create the codec:
503      */
504     if (G_UNLIKELY (self->codec)) {
505       if ((h != self->height) || (w != self->width)) {
506         codec_delete (self);
507       }
508     }
510     self->width = w;
511     self->height = h;
513     codec_data = gst_structure_get_value (s, "codec_data");
515     if (codec_data) {
516       GstBuffer *buffer = gst_value_get_buffer (codec_data);
517       GST_DEBUG_OBJECT (self, "codec_data: %" GST_PTR_FORMAT, buffer);
518       self->codec_data = gst_buffer_ref (buffer);
519     }
521     return TRUE;
522   }
524   return FALSE;
527 static gboolean
528 gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
529     gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz)
532   /* allocate params: */
533   self->params = dce_alloc (params_sz);
534   if (G_UNLIKELY (!self->params)) {
535     return FALSE;
536   }
537   self->params->size = params_sz;
538   self->params->maxFrameRate = 30000;
539   self->params->maxBitRate = 10000000;
541   self->params->dataEndianness = XDM_BYTE;
542   self->params->forceChromaFormat = XDM_YUV_420SP;
543   self->params->operatingMode = IVIDEO_DECODE_ONLY;
545   self->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
546   self->params->inputDataMode = IVIDEO_ENTIREFRAME;
547   self->params->outputDataMode = IVIDEO_ENTIREFRAME;
548   self->params->numInputDataUnits = 0;
549   self->params->numOutputDataUnits = 0;
551   self->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
552   self->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
553   self->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
554   self->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
556   /* allocate dynParams: */
557   self->dynParams = dce_alloc (dynparams_sz);
558   if (G_UNLIKELY (!self->dynParams)) {
559     return FALSE;
560   }
561   self->dynParams->size = dynparams_sz;
562   self->dynParams->decodeHeader = XDM_DECODE_AU;
563   self->dynParams->displayWidth = 0;
564   self->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
565   self->dynParams->newFrameFlag = XDAS_TRUE;
567   /* allocate status: */
568   self->status = dce_alloc (status_sz);
569   if (G_UNLIKELY (!self->status)) {
570     return FALSE;
571   }
572   self->status->size = status_sz;
574   /* allocate inBufs/outBufs: */
575   self->inBufs = dce_alloc (sizeof (XDM2_BufDesc));
576   self->outBufs = dce_alloc (sizeof (XDM2_BufDesc));
577   if (G_UNLIKELY (!self->inBufs) || G_UNLIKELY (!self->outBufs)) {
578     return FALSE;
579   }
581   /* allocate inArgs/outArgs: */
582   self->inArgs = dce_alloc (inargs_sz);
583   self->outArgs = dce_alloc (outargs_sz);
584   if (G_UNLIKELY (!self->inArgs) || G_UNLIKELY (!self->outArgs)) {
585     return FALSE;
586   }
587   self->inArgs->size = inargs_sz;
588   self->outArgs->size = outargs_sz;
590   return TRUE;
593 static GstBuffer *
594 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
596   if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
597     push_input (self, GST_BUFFER_DATA (self->codec_data),
598         GST_BUFFER_SIZE (self->codec_data));
599   }
601   /* just copy entire buffer */
602   push_input (self, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
603   gst_buffer_unref (buf);
605   return NULL;
608 static gint
609 gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
610     gint extended_error, gint status_extended_error)
612   if (XDM_ISFATALERROR (extended_error))
613     ret = XDM_EFAIL;
614   else
615     ret = XDM_EOK;
617   return ret;
620 /* GstElement vmethod implementations */
622 static gboolean
623 gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
625   gboolean ret = TRUE;
626   GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
627   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
628   GstStructure *s;
629   GstCaps *outcaps = NULL;
630   GstStructure *out_s;
631   gint frn = 0, frd = 1;
632   gint par_width, par_height;
633   gboolean par_present;
635   GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
637   s = gst_caps_get_structure (caps, 0);
638   if (!klass->parse_caps (self, s)) {
639     GST_WARNING_OBJECT (self, "missing required fields");
640     ret = FALSE;
641     goto out;
642   }
644   /* update output/padded sizes */
645   klass->update_buffer_size (self);
647   gst_structure_get_fraction (s, "framerate", &frn, &frd);
648   gst_structure_get_boolean (s, "interlaced", &self->interlaced);
649   par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
650       &par_width, &par_height);
652   outcaps = gst_pad_get_allowed_caps (self->srcpad);
653   if (outcaps) {
654     outcaps = gst_caps_make_writable (outcaps);
655     gst_caps_truncate (outcaps);
656     if (gst_caps_is_empty (outcaps)) {
657       gst_caps_unref (outcaps);
658       outcaps = NULL;
659     }
660   }
662   if (!outcaps) {
663     /* note: default to non-strided for better compatibility with
664      * other gst elements that don't understand stride:
665      */
666     outcaps = gst_caps_new_simple ("video/x-raw-yuv",
667         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
668   }
670   out_s = gst_caps_get_structure (outcaps, 0);
671   gst_structure_set (out_s,
672       "width", G_TYPE_INT, self->padded_width,
673       "height", G_TYPE_INT, self->padded_height,
674       "framerate", GST_TYPE_FRACTION, frn, frd, NULL);
675   if (par_present)
676     gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
677         par_width, par_height, NULL);
679   if (self->interlaced)
680     gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
682   if (!strcmp (gst_structure_get_name (out_s), "video/x-raw-yuv-strided")) {
683     if (!gst_structure_get_int (out_s, "rowstride", &self->stride)) {
684       self->stride = 4096;
685       gst_structure_set (out_s, "rowstride", G_TYPE_INT, self->stride, NULL);
686     }
687   } else {
688     self->stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_NV12,
689         0, self->padded_width);
690   }
692   self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
693       self->stride, self->padded_height);
695   GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
696       self->outsize, self->stride, outcaps);
698   ret = gst_pad_set_caps (self->srcpad, outcaps);
700 out:
701   if (outcaps)
702     gst_caps_unref (outcaps);
703   gst_object_unref (self);
705   return ret;
708 static gboolean
709 gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
711   gboolean res = TRUE, forward = TRUE;
712   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
714   GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
716   switch (GST_QUERY_TYPE (query)) {
717     case GST_QUERY_BUFFERS:
718       GST_DEBUG_OBJECT (self, "min buffers: %d", self->min_buffers);
719       gst_query_set_buffers_count (query, self->min_buffers);
721       GST_DEBUG_OBJECT (self, "min dimensions: %dx%d",
722           self->padded_width, self->padded_height);
723       gst_query_set_buffers_dimensions (query,
724           self->padded_width, self->padded_height);
725       forward = FALSE;
726       break;
727     default:
728       break;
729   }
731   if (forward)
732     res = gst_pad_query_default (pad, query);
734   return res;
737 static gboolean
738 gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
740   GstClockTime timestamp, qostime;
741   gint64 diff;
742   gboolean is_keyframe;
744   timestamp = GST_BUFFER_TIMESTAMP (buf);
745   if (self->segment.format != GST_FORMAT_TIME ||
746       self->qos_earliest_time == GST_CLOCK_TIME_NONE)
747     goto no_qos;
749   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
750     goto no_qos;
752   qostime = gst_segment_to_running_time (&self->segment,
753       GST_FORMAT_TIME, timestamp);
754   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime)))
755     /* out of segment */
756     goto no_qos;
758   is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
760   /* see how our next timestamp relates to the latest qos timestamp. negative
761    * values mean we are early, positive values mean we are too late. */
762   diff = GST_CLOCK_DIFF (qostime, self->qos_earliest_time);
764   GST_DEBUG_OBJECT (self, "QOS: qostime %" GST_TIME_FORMAT
765       ", earliest %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT " proportion %f",
766       GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
767       self->qos_proportion);
769   if (diff >= 0 && !is_keyframe)
770     return FALSE;
772 no_qos:
773   return TRUE;
776 static GstFlowReturn
777 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
779   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
780   GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
781   GstFlowReturn ret;
782   Int32 err;
783   GstBuffer *outbuf = NULL;
784   GstCaps *outcaps = NULL;
785   gboolean decode;
787   if (G_UNLIKELY (!self->engine)) {
788     GST_ERROR_OBJECT (self, "no engine");
789     return GST_FLOW_ERROR;
790   }
792   GST_DEBUG_OBJECT (self, "chain: %" GST_TIME_FORMAT " (%d bytes)",
793       GST_TIME_ARGS (ts), GST_BUFFER_SIZE (buf));
795   decode = gst_ducati_viddec_do_qos (self, buf);
796   if (!decode) {
797     gst_buffer_unref (buf);
798     return GST_FLOW_OK;
799   }
801   if (!self->need_out_buf)
802     goto have_out_buf;
804   /* do this before creating codec to ensure reverse caps negotiation
805    * happens first:
806    */
807 allocate_buffer:
808   ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
809       GST_PAD_CAPS (self->srcpad), &outbuf);
810   if (ret != GST_FLOW_OK) {
811     GST_ERROR_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
812     return ret;
813   }
815   outcaps = GST_BUFFER_CAPS (outbuf);
816   if (outcaps && !gst_caps_is_equal (outcaps, GST_PAD_CAPS (self->srcpad))) {
817     GstStructure *s;
819     GST_INFO_OBJECT (self, "doing upstream negotiation bufsize %d",
820         GST_BUFFER_SIZE (outbuf));
822     s = gst_caps_get_structure (outcaps, 0);
823     gst_structure_get_int (s, "rowstride", &self->stride);
824     self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
825         self->stride, self->padded_height);
827     GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
828         self->outsize, self->stride, outcaps);
830     gst_pad_set_caps (self->srcpad, outcaps);
832     if (GST_BUFFER_SIZE (outbuf) != self->outsize) {
833       GST_INFO_OBJECT (self, "dropping buffer (bufsize %d != outsize %d)",
834           GST_BUFFER_SIZE (outbuf), self->outsize);
835       gst_buffer_unref (outbuf);
836       goto allocate_buffer;
837     }
838   }
840   if (G_UNLIKELY (!self->codec)) {
841     if (!codec_create (self)) {
842       GST_ERROR_OBJECT (self, "could not create codec");
843       return GST_FLOW_ERROR;
844     }
845   }
847   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
848   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
850   /* pass new output buffer as to the decoder to decode into: */
851   self->inArgs->inputID = codec_prepare_outbuf (self, &outbuf, self->first_out_buffer);
852   if (!self->inArgs->inputID) {
853     GST_ERROR_OBJECT (self, "could not prepare output buffer");
854     return GST_FLOW_ERROR;
855   }
857 have_out_buf:
858   self->in_size = 0;
859   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
861   if (ts != GST_CLOCK_TIME_NONE) {
862     self->dts_queue[self->dts_widx++ % NDTS] = ts;
863     /* if next buffer has earlier ts than previous, then the ts
864      * we are getting are definitely decode order (DTS):
865      */
866     if ((self->last_dts != GST_CLOCK_TIME_NONE) &&
867         (self->last_dts > ts)) {
868       GST_DEBUG_OBJECT (self, "input timestamp definitely DTS");
869       self->ts_may_be_pts = FALSE;
870     }
871     self->last_dts = ts;
872   }
874   if (self->in_size == 0 && outbuf) {
875     GST_DEBUG_OBJECT (self, "no input, skipping process");
876     gst_buffer_unref (outbuf);
877     return GST_FLOW_OK;
878   }
880   self->inArgs->numBytes = self->in_size;
881   self->inBufs->descs[0].bufSize.bytes = self->in_size;
883   err = codec_process (self, TRUE, FALSE);
884   if (err) {
885     GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
886         ("process returned error: %d %08x", err, self->outArgs->extendedError));
887     return GST_FLOW_ERROR;
888   }
890   self->first_in_buffer = FALSE;
892   if (self->outArgs->outBufsInUseFlag) {
893     GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
894     self->need_out_buf = FALSE;
895   } else {
896     self->need_out_buf = TRUE;
897   }
899   if (buf) {
900     GST_DEBUG_OBJECT (self, "found remaining data: %d bytes",
901         GST_BUFFER_SIZE (buf));
902     ts = GST_BUFFER_TIMESTAMP (buf);
903     goto allocate_buffer;
904   }
906   return GST_FLOW_OK;
909 static gboolean
910 gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
912   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
913   gboolean ret = TRUE;
915   GST_DEBUG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
917   switch (GST_EVENT_TYPE (event)) {
918     case GST_EVENT_NEWSEGMENT:
919     {
920       gboolean update;
921       GstFormat fmt;
922       gint64 start, stop, time;
923       gdouble rate, arate;
925       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
926           &start, &stop, &time);
927       gst_segment_set_newsegment_full (&self->segment, update,
928           rate, arate, fmt, start, stop, time);
929       break;
930     }
931     case GST_EVENT_EOS:
932       if (!codec_flush (self, TRUE)) {
933         GST_ERROR_OBJECT (self, "could not flush on eos");
934         ret = FALSE;
935       }
936       break;
937     case GST_EVENT_FLUSH_STOP:
938       if (!codec_flush (self, FALSE)) {
939         GST_ERROR_OBJECT (self, "could not flush");
940         ret = FALSE;
941       }
942       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
943       self->qos_earliest_time = GST_CLOCK_TIME_NONE;
944       self->qos_proportion = 1;
945       self->need_out_buf = TRUE;
946       break;
947     default:
948       break;
949   }
951   if (ret)
952     ret = gst_pad_push_event (self->srcpad, event);
953   GST_LOG_OBJECT (self, "end");
955   return ret;
958 static gboolean
959 gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
961   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
962   gboolean ret = TRUE;
964   GST_LOG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
966   switch (GST_EVENT_TYPE (event)) {
967     case GST_EVENT_QOS:
968     {
969       gdouble proportion;
970       GstClockTimeDiff diff;
971       GstClockTime timestamp;
973       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
975       GST_OBJECT_LOCK (self);
976       self->qos_proportion = proportion;
977       self->qos_earliest_time = timestamp + 2 * diff;
978       GST_OBJECT_UNLOCK (self);
980       GST_DEBUG_OBJECT (self,
981           "got QoS proportion %f %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
982           proportion, GST_TIME_ARGS (timestamp), diff);
984       ret = gst_pad_push_event (self->sinkpad, event);
985       break;
986     }
987     default:
988       ret = gst_pad_push_event (self->sinkpad, event);
989       break;
990   }
992   GST_LOG_OBJECT (self, "end");
994   return ret;
997 static GstStateChangeReturn
998 gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
1000   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1001   GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
1003   GST_DEBUG_OBJECT (self, "begin: changing state %s -> %s",
1004       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1005       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1007   switch (transition) {
1008     case GST_STATE_CHANGE_NULL_TO_READY:
1009       if (!engine_open (self)) {
1010         GST_ERROR_OBJECT (self, "could not open");
1011         return GST_STATE_CHANGE_FAILURE;
1012       }
1013       break;
1014     default:
1015       break;
1016   }
1018   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1020   if (ret == GST_STATE_CHANGE_FAILURE)
1021     goto leave;
1023   switch (transition) {
1024     case GST_STATE_CHANGE_PAUSED_TO_READY:
1025       self->interlaced = FALSE;
1026       break;
1027     case GST_STATE_CHANGE_READY_TO_NULL:
1028       codec_delete (self);
1029       engine_close (self);
1030       break;
1031     default:
1032       break;
1033   }
1035 leave:
1036   GST_LOG_OBJECT (self, "end");
1038   return ret;
1041 /* GObject vmethod implementations */
1043 #define VERSION_LENGTH 256
1045 static void
1046 gst_ducati_viddec_get_property (GObject * obj,
1047     guint prop_id, GValue * value, GParamSpec * pspec)
1049   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1051   switch (prop_id) {
1052     case PROP_VERSION:{
1053       int err;
1054       char *version = gst_ducati_alloc_1d (VERSION_LENGTH);
1056       /* in case something fails: */
1057       snprintf (version, VERSION_LENGTH, "unsupported");
1059       if (!self->engine)
1060         engine_open (self);
1062       if (!self->codec)
1063         codec_create (self);
1065       if (self->codec) {
1066         self->status->data.buf = (XDAS_Int8 *) TilerMem_VirtToPhys (version);
1067         self->status->data.bufSize = VERSION_LENGTH;
1069         err = VIDDEC3_control (self->codec, XDM_GETVERSION,
1070             self->dynParams, self->status);
1071         if (err) {
1072           GST_ERROR_OBJECT (self, "failed XDM_GETVERSION");
1073         }
1075         self->status->data.buf = NULL;
1076         self->status->data.bufSize = 0;
1077       }
1079       g_value_set_string (value, version);
1081       MemMgr_Free (version);
1083       break;
1084     }
1085     default:{
1086       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
1087       break;
1088     }
1089   }
1092 static void
1093 gst_ducati_viddec_finalize (GObject * obj)
1095   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1097   codec_delete (self);
1098   engine_close (self);
1100   if (self->codec_data) {
1101     gst_buffer_unref (self->codec_data);
1102     self->codec_data = NULL;
1103   }
1105   G_OBJECT_CLASS (parent_class)->finalize (obj);
1108 static void
1109 gst_ducati_viddec_base_init (gpointer gclass)
1111   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
1113   gst_element_class_add_pad_template (element_class,
1114       gst_static_pad_template_get (&src_factory));
1117 static void
1118 gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
1120   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1121   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1123   gobject_class->get_property =
1124       GST_DEBUG_FUNCPTR (gst_ducati_viddec_get_property);
1125   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ducati_viddec_finalize);
1126   gstelement_class->change_state =
1127       GST_DEBUG_FUNCPTR (gst_ducati_viddec_change_state);
1129   klass->parse_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
1130   klass->allocate_params =
1131       GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
1132   klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
1133   klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
1135   g_object_class_install_property (gobject_class, PROP_VERSION,
1136       g_param_spec_string ("version", "Version",
1137           "The codec version string", "",
1138           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1141 static void
1142 gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
1144   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1146   self->sinkpad =
1147       gst_pad_new_from_template (gst_element_class_get_pad_template
1148       (gstelement_class, "sink"), "sink");
1149   gst_pad_set_setcaps_function (self->sinkpad,
1150       GST_DEBUG_FUNCPTR (gst_ducati_viddec_sink_setcaps));
1151   gst_pad_set_chain_function (self->sinkpad,
1152       GST_DEBUG_FUNCPTR (gst_ducati_viddec_chain));
1153   gst_pad_set_event_function (self->sinkpad,
1154       GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
1156   self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
1157   gst_pad_set_event_function (self->srcpad,
1158       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_event));
1159   gst_pad_set_query_function (self->srcpad,
1160       GST_DEBUG_FUNCPTR (gst_ducati_viddec_query));
1162   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1163   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1165   self->input_width = 0;
1166   self->input_height = 0;
1167   /* sane defaults in case we need to create codec without caps negotiation
1168    * (for example, to get 'version' property)
1169    */
1170   self->width = 128;
1171   self->height = 128;
1173   self->first_in_buffer = TRUE;
1174   self->first_out_buffer = TRUE;
1175   self->interlaced = FALSE;
1177   self->dts_ridx = self->dts_widx = 0;
1178   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
1179   self->ts_may_be_pts = TRUE;
1180   self->ts_is_pts = FALSE;
1182   self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
1184   gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1186   self->qos_proportion = 1;
1187   self->qos_earliest_time = GST_CLOCK_TIME_NONE;
1189   self->need_out_buf = TRUE;