ducatividdec: Implement src pad getcaps function
[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     GstBuffer *orig = *buf;
205     GST_DEBUG_OBJECT (self, "internal bufferpool forced");
206     *buf = codec_bufferpool_get (self, NULL);
207     GST_BUFFER_TIMESTAMP (*buf) = GST_BUFFER_TIMESTAMP (orig);
208     GST_BUFFER_DURATION (*buf) = GST_BUFFER_DURATION (orig);
209     gst_buffer_unref (orig);
210     return codec_prepare_outbuf (self, buf, FALSE);
211   }
213   y_vaddr = GST_BUFFER_DATA (*buf);
214   uv_vaddr = y_vaddr + self->stride * self->padded_height;
216   y_paddr = TilerMem_VirtToPhys (y_vaddr);
217   uv_paddr = TilerMem_VirtToPhys (uv_vaddr);
219   y_type = gst_ducati_get_mem_type (y_paddr);
220   uv_type = gst_ducati_get_mem_type (uv_paddr);
221   /* FIXME: workaround for the vc1 codec expecting _RAW when it's actually
222    * _TILEDPAGE... should be removed once the codec is fixed  */
223   if (y_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != y_type)
224     y_type = self->pageMemType;
225   if (uv_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != uv_type)
226     uv_type = self->pageMemType;
228   if (y_type < 0 || uv_type < 0) {
229     GST_DEBUG_OBJECT (self, "non TILER buffer, fallback to bufferpool");
230     *buf = codec_bufferpool_get (self, *buf);
231     return codec_prepare_outbuf (self, buf, FALSE);
232   }
234   if (!self->outBufs->numBufs) {
235     /* initialize output buffer type */
236     self->outBufs->numBufs = 2;
237     self->outBufs->descs[0].memType = y_type;
238     self->outBufs->descs[1].memType = uv_type;
239     if (y_type == XDM_MEMTYPE_RAW || y_type == XDM_MEMTYPE_TILEDPAGE) {
240       self->outBufs->descs[0].bufSize.bytes =
241           self->stride * self->padded_height;
242       self->outBufs->descs[1].bufSize.bytes =
243           self->stride * self->padded_height / 2;
244     } else {
245       self->outBufs->descs[0].bufSize.tileMem.width = self->padded_width;
246       self->outBufs->descs[0].bufSize.tileMem.height = self->padded_height;
247       /* note that UV interleaved width is same a Y: */
248       self->outBufs->descs[1].bufSize.tileMem.width = self->padded_width;
249       self->outBufs->descs[1].bufSize.tileMem.height = self->padded_height / 2;
250     }
251   } else {
252     /* verify output buffer type matches what we've already given
253      * to the codec
254      */
255     if ((self->outBufs->descs[0].memType != y_type) ||
256         (self->outBufs->descs[1].memType != uv_type)) {
257       GST_DEBUG_OBJECT (self, "buffer mismatch, fallback to bufferpool");
258       *buf = codec_bufferpool_get (self, *buf);
259       return codec_prepare_outbuf (self, buf, FALSE);
260     }
261   }
263   self->outBufs->descs[0].buf = (XDAS_Int8 *) y_paddr;
264   self->outBufs->descs[1].buf = (XDAS_Int8 *) uv_paddr;
266   return (XDAS_Int32) * buf;    // XXX use lookup table
269 static GstBuffer *
270 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
272   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
273   if (buf) {
274     gst_buffer_ref (buf);
275   }
276   return buf;
279 static void
280 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
282   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
283   if (buf) {
284     GST_DEBUG_OBJECT (self, "free buffer: %d %p", id, buf);
285     gst_buffer_unref (buf);
286   }
289 static gint
290 codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
292   gint err;
293   GstClockTime t;
294   GstBuffer *outbuf = NULL;
295   gint i;
296   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
298   memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
299   memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
301   t = gst_util_get_timestamp ();
302   err = VIDDEC3_process (self->codec,
303       self->inBufs, self->outBufs, self->inArgs, self->outArgs);
304   GST_DEBUG_OBJECT (self, "%10dns", (gint) (gst_util_get_timestamp () - t));
306   if (err) {
307     GST_WARNING_OBJECT (self, "err=%d, extendedError=%08x",
308         err, self->outArgs->extendedError);
310     err = VIDDEC3_control (self->codec, XDM_GETSTATUS,
311         self->dynParams, self->status);
312     if (!err) {
313       GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
314           err, self->status->extendedError);
315     }
317     if (flush)
318       err = XDM_EFAIL;
319     else
320       err = klass->handle_error (self, err,
321           self->outArgs->extendedError, self->status->extendedError);
322   }
324   for (i = 0; self->outArgs->outputID[i]; i++) {
325     gboolean interlaced;
327     outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
329     interlaced =
330         self->outArgs->decodedBufs.contentType ==
331         IVIDEO_PROGRESSIVE ? FALSE : TRUE;
333     /* if send is FALSE, don't try to renegotiate as we could be flushing during
334      * a PAUSED->READY state change
335      */
336     if (send && interlaced != self->interlaced) {
337       GstCaps *caps;
339       GST_WARNING_OBJECT (self, "upstream set interlaced=%d but codec "
340           "thinks interlaced=%d... trusting codec", self->interlaced,
341           interlaced);
343       self->interlaced = interlaced;
345       caps =
346           gst_caps_make_writable (gst_pad_get_negotiated_caps (self->srcpad));
347       GST_INFO_OBJECT (self, "changing interlace field in caps");
348       gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
349           NULL);
350       gst_ducati_bufferpool_set_caps (self->pool, caps);
351       if (!gst_pad_set_caps (self->srcpad, caps)) {
352         GST_ERROR_OBJECT (self,
353             "downstream didn't want to change interlace mode");
354         err = XDM_EFAIL;
355       }
356       gst_caps_unref (caps);
358       /* this buffer still has the old caps so we skip it */
359       send = FALSE;
360     }
362     if (G_UNLIKELY (self->send_crop_event) && send) {
363       gint crop_width, crop_height;
364       GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
366       /* send region of interest to sink on first buffer: */
367       XDM_Rect *r = &(self->outArgs->displayBufs.bufDesc[0].activeFrameRegion);
369       crop_width = r->bottomRight.x - r->topLeft.x;
370       crop_height = r->bottomRight.y - r->topLeft.y;
372       if (crop_width > self->input_width)
373         crop_width = self->input_width;
374       if (crop_height > self->input_height)
375         crop_height = self->input_height;
377       if (self->interlaced && !strcmp (klass->codec_name, "ivahd_mpeg2vdec"))
378         crop_height = crop_height / 2;
380       GST_INFO_OBJECT (self, "active frame region %d, %d, %d, %d, crop %dx%d",
381           r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y,
382           crop_width, crop_height);
384       gst_pad_push_event (self->srcpad,
385           gst_event_new_crop (r->topLeft.y, r->topLeft.x,
386               crop_width, crop_height));
388       self->send_crop_event = FALSE;
389     }
391     if (G_UNLIKELY (self->first_out_buffer) && send) {
392       GstDucatiBufferPool *pool;
393       self->first_out_buffer = FALSE;
395       /* Destroy the pool so the buffers we used so far are eventually released.
396        * The pool will be recreated if needed.
397        */
398       pool = self->pool;
399       self->pool = NULL;
400       gst_ducati_bufferpool_destroy (pool);
401     }
403     if (send) {
404       GstClockTime ts;
406       if (GST_IS_DUCATIBUFFER (outbuf)) {
407         outbuf = gst_ducati_buffer_get (GST_DUCATIBUFFER (outbuf));
408       }
410       ts = GST_BUFFER_TIMESTAMP (outbuf);
412       GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
413           i, outbuf, GST_TIME_ARGS (ts));
415       if (self->ts_may_be_pts) {
416         if ((self->last_pts != GST_CLOCK_TIME_NONE) && (self->last_pts > ts)) {
417           GST_DEBUG_OBJECT (self, "detected PTS going backwards, "
418               "enabling ts_is_pts");
419           self->ts_is_pts = TRUE;
420         }
421       }
423       self->last_pts = ts;
425       if (self->dts_ridx != self->dts_widx) {
426         ts = self->dts_queue[self->dts_ridx++ % NDTS];
427       }
429       if (self->ts_is_pts) {
430         /* if we have a queued DTS from demuxer, use that instead: */
431         GST_BUFFER_TIMESTAMP (outbuf) = ts;
432         GST_DEBUG_OBJECT (self, "fixed ts: %d %p (%" GST_TIME_FORMAT ")",
433             i, outbuf, GST_TIME_ARGS (ts));
434       }
436       if (GST_BUFFER_CAPS (outbuf) &&
437           !gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
438               GST_PAD_CAPS (self->srcpad))) {
439         /* this looks a bit scary but it's really just to change the interlace=
440          * field in caps when we start as !interlaced and the codec detects
441          * otherwise */
442         GST_WARNING_OBJECT (self, "overriding buffer caps to fix "
443             "interlace mismatch");
444         outbuf = gst_buffer_make_metadata_writable (outbuf);
445         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
446       }
448       gst_pad_push (self->srcpad, outbuf);
449     } else {
450       GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
451       gst_buffer_unref (outbuf);
452     }
453   }
455   for (i = 0; self->outArgs->freeBufID[i]; i++) {
456     codec_unlock_outbuf (self, self->outArgs->freeBufID[i]);
457   }
459   return err;
462 /** call control(FLUSH), and then process() to pop out all buffers */
463 gboolean
464 gst_ducati_viddec_codec_flush (GstDucatiVidDec * self, gboolean eos)
466   gint err;
468   GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
470   /* note: flush is synchronized against _chain() to avoid calling
471    * the codec from multiple threads
472    */
473   GST_PAD_STREAM_LOCK (self->sinkpad);
475   self->dts_ridx = self->dts_widx = 0;
476   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
477   self->ts_may_be_pts = TRUE;
478   self->ts_is_pts = FALSE;
479   self->wait_keyframe = TRUE;
481   if (G_UNLIKELY (self->first_in_buffer)) {
482     return TRUE;
483   }
485   if (G_UNLIKELY (!self->codec)) {
486     GST_WARNING_OBJECT (self, "no codec");
487     return TRUE;
488   }
490   err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
491   if (err) {
492     GST_ERROR_OBJECT (self, "failed XDM_FLUSH");
493     goto out;
494   }
496   self->inBufs->descs[0].bufSize.bytes = 0;
497   self->inArgs->numBytes = 0;
498   self->inArgs->inputID = 0;
500   do {
501     err = codec_process (self, eos, TRUE);
502   } while (err != XDM_EFAIL);
504   /* on a flush, it is normal (and not an error) for the last _process() call
505    * to return an error..
506    */
507   err = XDM_EOK;
509 out:
510   GST_PAD_STREAM_UNLOCK (self->sinkpad);
511   GST_DEBUG_OBJECT (self, "done");
513   return !err;
516 /* GstDucatiVidDec vmethod default implementations */
518 static gboolean
519 gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
521   const GValue *codec_data;
522   gint w, h;
524   if (gst_structure_get_int (s, "width", &self->input_width) &&
525       gst_structure_get_int (s, "height", &self->input_height)) {
527     h = ALIGN2 (self->input_height, 4); /* round up to MB */
528     w = ALIGN2 (self->input_width, 4);  /* round up to MB */
530     /* if we've already created codec, but the resolution has changed, we
531      * need to re-create the codec:
532      */
533     if (G_UNLIKELY (self->codec)) {
534       if ((h != self->height) || (w != self->width)) {
535         codec_delete (self);
536       }
537     }
539     self->width = w;
540     self->height = h;
542     codec_data = gst_structure_get_value (s, "codec_data");
544     if (codec_data) {
545       GstBuffer *buffer = gst_value_get_buffer (codec_data);
546       GST_DEBUG_OBJECT (self, "codec_data: %" GST_PTR_FORMAT, buffer);
547       self->codec_data = gst_buffer_ref (buffer);
548     }
550     return TRUE;
551   }
553   return FALSE;
556 static gboolean
557 gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
558     gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz)
561   /* allocate params: */
562   self->params = dce_alloc (params_sz);
563   if (G_UNLIKELY (!self->params)) {
564     return FALSE;
565   }
566   self->params->size = params_sz;
567   self->params->maxFrameRate = 30000;
568   self->params->maxBitRate = 10000000;
570   self->params->dataEndianness = XDM_BYTE;
571   self->params->forceChromaFormat = XDM_YUV_420SP;
572   self->params->operatingMode = IVIDEO_DECODE_ONLY;
574   self->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
575   self->params->inputDataMode = IVIDEO_ENTIREFRAME;
576   self->params->outputDataMode = IVIDEO_ENTIREFRAME;
577   self->params->numInputDataUnits = 0;
578   self->params->numOutputDataUnits = 0;
580   self->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
581   self->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
582   self->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
583   self->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
585   /* allocate dynParams: */
586   self->dynParams = dce_alloc (dynparams_sz);
587   if (G_UNLIKELY (!self->dynParams)) {
588     return FALSE;
589   }
590   self->dynParams->size = dynparams_sz;
591   self->dynParams->decodeHeader = XDM_DECODE_AU;
592   self->dynParams->displayWidth = 0;
593   self->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
594   self->dynParams->newFrameFlag = XDAS_TRUE;
596   /* allocate status: */
597   self->status = dce_alloc (status_sz);
598   if (G_UNLIKELY (!self->status)) {
599     return FALSE;
600   }
601   self->status->size = status_sz;
603   /* allocate inBufs/outBufs: */
604   self->inBufs = dce_alloc (sizeof (XDM2_BufDesc));
605   self->outBufs = dce_alloc (sizeof (XDM2_BufDesc));
606   if (G_UNLIKELY (!self->inBufs) || G_UNLIKELY (!self->outBufs)) {
607     return FALSE;
608   }
610   /* allocate inArgs/outArgs: */
611   self->inArgs = dce_alloc (inargs_sz);
612   self->outArgs = dce_alloc (outargs_sz);
613   if (G_UNLIKELY (!self->inArgs) || G_UNLIKELY (!self->outArgs)) {
614     return FALSE;
615   }
616   self->inArgs->size = inargs_sz;
617   self->outArgs->size = outargs_sz;
619   return TRUE;
622 static GstBuffer *
623 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
625   if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
626     push_input (self, GST_BUFFER_DATA (self->codec_data),
627         GST_BUFFER_SIZE (self->codec_data));
628   }
630   /* just copy entire buffer */
631   push_input (self, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
632   gst_buffer_unref (buf);
634   return NULL;
637 static gint
638 gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
639     gint extended_error, gint status_extended_error)
641   if (XDM_ISFATALERROR (extended_error))
642     ret = XDM_EFAIL;
643   else
644     ret = XDM_EOK;
646   return ret;
649 /* GstElement vmethod implementations */
651 static gboolean
652 gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
654   gboolean ret = TRUE;
655   GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
656   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
657   GstStructure *s;
658   GstCaps *outcaps = NULL;
659   GstStructure *out_s;
660   gint par_width, par_height;
661   gboolean par_present;
663   GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
665   s = gst_caps_get_structure (caps, 0);
666   if (!klass->parse_caps (self, s)) {
667     GST_WARNING_OBJECT (self, "missing required fields");
668     ret = FALSE;
669     goto out;
670   }
672   /* update output/padded sizes */
673   klass->update_buffer_size (self);
675   if (!gst_structure_get_fraction (s, "framerate", &self->fps_n, &self->fps_d)) {
676     self->fps_n = 0;
677     self->fps_d = 1;
678   }
679   gst_structure_get_boolean (s, "interlaced", &self->interlaced);
680   par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
681       &par_width, &par_height);
683   outcaps = gst_pad_get_allowed_caps (self->srcpad);
684   if (outcaps) {
685     outcaps = gst_caps_make_writable (outcaps);
686     gst_caps_truncate (outcaps);
687     if (gst_caps_is_empty (outcaps)) {
688       gst_caps_unref (outcaps);
689       outcaps = NULL;
690     }
691   }
693   if (!outcaps) {
694     /* note: default to non-strided for better compatibility with
695      * other gst elements that don't understand stride:
696      */
697     outcaps = gst_caps_new_simple ("video/x-raw-yuv",
698         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
699   }
701   out_s = gst_caps_get_structure (outcaps, 0);
702   gst_structure_set (out_s,
703       "width", G_TYPE_INT, self->padded_width,
704       "height", G_TYPE_INT, self->padded_height,
705       "framerate", GST_TYPE_FRACTION, self->fps_n, self->fps_d, NULL);
706   if (par_present)
707     gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
708         par_width, par_height, NULL);
710   if (self->interlaced)
711     gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
713   if (!strcmp (gst_structure_get_name (out_s), "video/x-raw-yuv-strided")) {
714     if (!gst_structure_get_int (out_s, "rowstride", &self->stride)) {
715       self->stride = 4096;
716       gst_structure_set (out_s, "rowstride", G_TYPE_INT, self->stride, NULL);
717     }
718   } else {
719     self->stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_NV12,
720         0, self->padded_width);
721   }
723   self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
724       self->stride, self->padded_height);
726   GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
727       self->outsize, self->stride, outcaps);
729   if (!self->first_in_buffer) {
730     /* Caps changed mid stream. We flush the codec to unlock all the potentially
731      * locked buffers. This is needed for downstream sinks that provide a
732      * buffer pool and need to destroy all the outstanding buffers before they
733      * can negotiate new caps (hello v4l2sink).
734      */
735     gst_ducati_viddec_codec_flush (self, FALSE);
736   }
738   /* (re)send a crop event when caps change */
739   self->send_crop_event = TRUE;
741   ret = gst_pad_set_caps (self->srcpad, outcaps);
743   GST_INFO_OBJECT (self, "set caps done %d, %"GST_PTR_FORMAT,
744       ret, outcaps);
746 out:
747   if (outcaps)
748     gst_caps_unref (outcaps);
749   gst_object_unref (self);
751   return ret;
754 static GstCaps *
755 gst_ducati_viddec_src_getcaps (GstPad * pad)
757   GstCaps *caps = NULL;
758   GstCaps *outcaps = NULL;
759   int i;
761   caps = GST_PAD_CAPS (pad);
762   if (caps == NULL) {
763     outcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
764     return outcaps;
765   }
767   outcaps = gst_caps_new_empty ();
769   /* allow -rowstrided and regular yuv */
770   for (i = 0; i < gst_caps_get_size (caps); i++) {
771     GstStructure *structure;
772     GstStructure *modified_structure;
774     structure = gst_caps_get_structure (caps, i);
775     gst_caps_append_structure (outcaps, gst_structure_copy (structure));
776     modified_structure = gst_structure_copy (structure);
778     if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
779       gst_structure_set_name (modified_structure, "video/x-raw-yuv-strided");
780       gst_caps_append_structure (outcaps, modified_structure);
781     } else {
782       gst_structure_set_name (modified_structure, "video/x-raw-yuv");
783       gst_structure_remove_field (modified_structure, "rowstride");
784       gst_caps_append_structure (outcaps, modified_structure);
785     }
786   }
787   return outcaps;
790 static gboolean
791 gst_ducati_viddec_query (GstDucatiVidDec * self, GstPad * pad,
792     GstQuery * query, gboolean * forward)
794   gboolean res = TRUE;
796   switch (GST_QUERY_TYPE (query)) {
797     case GST_QUERY_BUFFERS:
798       GST_DEBUG_OBJECT (self, "min buffers: %d", self->min_buffers);
799       gst_query_set_buffers_count (query, self->min_buffers);
801       GST_DEBUG_OBJECT (self, "min dimensions: %dx%d",
802           self->padded_width, self->padded_height);
803       gst_query_set_buffers_dimensions (query,
804           self->padded_width, self->padded_height);
805       *forward = FALSE;
806       break;
807     default:
808       break;
809   }
812   return res;
815 static gboolean
816 gst_ducati_viddec_src_query (GstPad * pad, GstQuery * query)
818   gboolean res = TRUE, forward = TRUE;
819   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
820   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
822   GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
823   res = klass->query(self, pad, query, &forward);
824   if (res && forward)
825     res = gst_pad_query_default (pad, query);
827   return res;
830 static gboolean
831 gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
833   GstClockTime timestamp, qostime;
834   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
835   gint64 diff;
837   if (self->wait_keyframe) {
838     if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
839       GST_INFO_OBJECT (self, "skipping until the next keyframe");
840       return FALSE;
841     }
843     self->wait_keyframe = FALSE;
844   }
846   timestamp = GST_BUFFER_TIMESTAMP (buf);
847   if (self->segment.format != GST_FORMAT_TIME ||
848       self->qos_earliest_time == GST_CLOCK_TIME_NONE)
849     goto no_qos;
851   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
852     goto no_qos;
854   qostime = gst_segment_to_running_time (&self->segment,
855       GST_FORMAT_TIME, timestamp);
856   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime)))
857     /* out of segment */
858     goto no_qos;
860   /* see how our next timestamp relates to the latest qos timestamp. negative
861    * values mean we are early, positive values mean we are too late. */
862   diff = GST_CLOCK_DIFF (qostime, self->qos_earliest_time);
864   GST_DEBUG_OBJECT (self, "QOS: qostime %" GST_TIME_FORMAT
865       ", earliest %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT " proportion %f",
866       GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
867       self->qos_proportion);
869   if (klass->drop_frame (self, buf, diff)) {
870     GST_INFO_OBJECT (self, "dropping frame");
871     return FALSE;
872   }
874 no_qos:
875   return TRUE;
878 static gboolean
879 gst_ducati_viddec_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
880     gint64 diff)
882   gboolean is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf,
883       GST_BUFFER_FLAG_DELTA_UNIT);
885   if (diff >= 0 && !is_keyframe)
886     return TRUE;
888   return FALSE;
891 static GstFlowReturn
892 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
894   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
895   GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
896   GstFlowReturn ret;
897   Int32 err;
898   GstBuffer *outbuf = NULL;
899   GstCaps *outcaps = NULL;
900   gboolean decode;
902   if (G_UNLIKELY (!self->engine)) {
903     GST_ERROR_OBJECT (self, "no engine");
904     return GST_FLOW_ERROR;
905   }
907   GST_DEBUG_OBJECT (self, "chain: %" GST_TIME_FORMAT " (%d bytes, flags %d)",
908       GST_TIME_ARGS (ts), GST_BUFFER_SIZE (buf), GST_BUFFER_FLAGS (buf));
910   decode = gst_ducati_viddec_do_qos (self, buf);
911   if (!decode) {
912     gst_buffer_unref (buf);
913     return GST_FLOW_OK;
914   }
916   if (!self->need_out_buf)
917     goto have_out_buf;
919   /* do this before creating codec to ensure reverse caps negotiation
920    * happens first:
921    */
922 allocate_buffer:
923   ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
924       GST_PAD_CAPS (self->srcpad), &outbuf);
925   if (ret != GST_FLOW_OK) {
926     GST_ERROR_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
927     return ret;
928   }
930   outcaps = GST_BUFFER_CAPS (outbuf);
931   if (outcaps && !gst_caps_is_equal (outcaps, GST_PAD_CAPS (self->srcpad))) {
932     GstStructure *s;
934     GST_INFO_OBJECT (self, "doing upstream negotiation bufsize %d",
935         GST_BUFFER_SIZE (outbuf));
937     s = gst_caps_get_structure (outcaps, 0);
938     gst_structure_get_int (s, "rowstride", &self->stride);
939     self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
940         self->stride, self->padded_height);
942     GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
943         self->outsize, self->stride, outcaps);
945     gst_pad_set_caps (self->srcpad, outcaps);
947     if (GST_BUFFER_SIZE (outbuf) != self->outsize) {
948       GST_INFO_OBJECT (self, "dropping buffer (bufsize %d != outsize %d)",
949           GST_BUFFER_SIZE (outbuf), self->outsize);
950       gst_buffer_unref (outbuf);
951       goto allocate_buffer;
952     }
953   }
955   if (G_UNLIKELY (!self->codec)) {
956     if (!codec_create (self)) {
957       GST_ERROR_OBJECT (self, "could not create codec");
958       return GST_FLOW_ERROR;
959     }
960   }
962   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
963   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
965   /* Pass new output buffer to the decoder to decode into. Use buffers from the
966    * internal pool while self->first_out_buffer == TRUE in order to simplify
967    * things in case we need to renegotiate */
968   self->inArgs->inputID =
969       codec_prepare_outbuf (self, &outbuf, self->first_out_buffer);
970   if (!self->inArgs->inputID) {
971     GST_ERROR_OBJECT (self, "could not prepare output buffer");
972     return GST_FLOW_ERROR;
973   }
975 have_out_buf:
976   self->in_size = 0;
977   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
979   if (ts != GST_CLOCK_TIME_NONE) {
980     self->dts_queue[self->dts_widx++ % NDTS] = ts;
981     /* if next buffer has earlier ts than previous, then the ts
982      * we are getting are definitely decode order (DTS):
983      */
984     if ((self->last_dts != GST_CLOCK_TIME_NONE) && (self->last_dts > ts)) {
985       GST_DEBUG_OBJECT (self, "input timestamp definitely DTS");
986       self->ts_may_be_pts = FALSE;
987     }
988     self->last_dts = ts;
989   }
991   if (self->in_size == 0 && outbuf) {
992     GST_DEBUG_OBJECT (self, "no input, skipping process");
993     gst_buffer_unref (outbuf);
994     return GST_FLOW_OK;
995   }
997   self->inArgs->numBytes = self->in_size;
998   self->inBufs->descs[0].bufSize.bytes = self->in_size;
1000   err = codec_process (self, TRUE, FALSE);
1001   if (err) {
1002     GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
1003         ("process returned error: %d %08x", err, self->outArgs->extendedError));
1004     return GST_FLOW_ERROR;
1005   }
1007   self->first_in_buffer = FALSE;
1009   if (self->outArgs->outBufsInUseFlag) {
1010     GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
1011     self->need_out_buf = FALSE;
1012   } else {
1013     self->need_out_buf = TRUE;
1014   }
1016   if (buf) {
1017     GST_DEBUG_OBJECT (self, "found remaining data: %d bytes",
1018         GST_BUFFER_SIZE (buf));
1019     ts = GST_BUFFER_TIMESTAMP (buf);
1020     goto allocate_buffer;
1021   }
1023   return GST_FLOW_OK;
1026 static gboolean
1027 gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
1029   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
1030   gboolean ret = TRUE;
1032   GST_DEBUG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
1034   switch (GST_EVENT_TYPE (event)) {
1035     case GST_EVENT_NEWSEGMENT:
1036     {
1037       gboolean update;
1038       GstFormat fmt;
1039       gint64 start, stop, time;
1040       gdouble rate, arate;
1042       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
1043           &start, &stop, &time);
1044       gst_segment_set_newsegment_full (&self->segment, update,
1045           rate, arate, fmt, start, stop, time);
1046       break;
1047     }
1048     case GST_EVENT_EOS:
1049       if (!gst_ducati_viddec_codec_flush (self, TRUE)) {
1050         GST_ERROR_OBJECT (self, "could not flush on eos");
1051         ret = FALSE;
1052       }
1053       break;
1054     case GST_EVENT_FLUSH_STOP:
1055       if (!gst_ducati_viddec_codec_flush (self, FALSE)) {
1056         GST_ERROR_OBJECT (self, "could not flush");
1057         ret = FALSE;
1058       }
1059       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1060       self->qos_earliest_time = GST_CLOCK_TIME_NONE;
1061       self->qos_proportion = 1;
1062       self->need_out_buf = TRUE;
1063       break;
1064     default:
1065       break;
1066   }
1068   if (ret)
1069     ret = gst_pad_push_event (self->srcpad, event);
1070   GST_LOG_OBJECT (self, "end");
1072   return ret;
1075 static gboolean
1076 gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
1078   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
1079   gboolean ret = TRUE;
1081   GST_LOG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
1083   switch (GST_EVENT_TYPE (event)) {
1084     case GST_EVENT_QOS:
1085     {
1086       gdouble proportion;
1087       GstClockTimeDiff diff;
1088       GstClockTime timestamp;
1090       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
1092       GST_OBJECT_LOCK (self);
1093       self->qos_proportion = proportion;
1094       self->qos_earliest_time = timestamp + 2 * diff;
1095       GST_OBJECT_UNLOCK (self);
1097       GST_DEBUG_OBJECT (self,
1098           "got QoS proportion %f %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
1099           proportion, GST_TIME_ARGS (timestamp), diff);
1101       ret = gst_pad_push_event (self->sinkpad, event);
1102       break;
1103     }
1104     default:
1105       ret = gst_pad_push_event (self->sinkpad, event);
1106       break;
1107   }
1109   GST_LOG_OBJECT (self, "end");
1111   return ret;
1114 static GstStateChangeReturn
1115 gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
1117   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1118   GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
1120   GST_DEBUG_OBJECT (self, "begin: changing state %s -> %s",
1121       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1122       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1124   switch (transition) {
1125     case GST_STATE_CHANGE_NULL_TO_READY:
1126       if (!engine_open (self)) {
1127         GST_ERROR_OBJECT (self, "could not open");
1128         return GST_STATE_CHANGE_FAILURE;
1129       }
1130       break;
1131     default:
1132       break;
1133   }
1135   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1137   if (ret == GST_STATE_CHANGE_FAILURE)
1138     goto leave;
1140   switch (transition) {
1141     case GST_STATE_CHANGE_PAUSED_TO_READY:
1142       self->interlaced = FALSE;
1143       self->send_crop_event = TRUE;
1144       gst_ducati_viddec_codec_flush (self, FALSE);
1145       break;
1146     case GST_STATE_CHANGE_READY_TO_NULL:
1147       codec_delete (self);
1148       engine_close (self);
1149       break;
1150     default:
1151       break;
1152   }
1154 leave:
1155   GST_LOG_OBJECT (self, "end");
1157   return ret;
1160 /* GObject vmethod implementations */
1162 #define VERSION_LENGTH 256
1164 static void
1165 gst_ducati_viddec_get_property (GObject * obj,
1166     guint prop_id, GValue * value, GParamSpec * pspec)
1168   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1170   switch (prop_id) {
1171     case PROP_VERSION:{
1172       int err;
1173       char *version = gst_ducati_alloc_1d (VERSION_LENGTH);
1175       /* in case something fails: */
1176       snprintf (version, VERSION_LENGTH, "unsupported");
1178       if (!self->engine)
1179         engine_open (self);
1181       if (!self->codec)
1182         codec_create (self);
1184       if (self->codec) {
1185         self->status->data.buf = (XDAS_Int8 *) TilerMem_VirtToPhys (version);
1186         self->status->data.bufSize = VERSION_LENGTH;
1188         err = VIDDEC3_control (self->codec, XDM_GETVERSION,
1189             self->dynParams, self->status);
1190         if (err) {
1191           GST_ERROR_OBJECT (self, "failed XDM_GETVERSION");
1192         }
1194         self->status->data.buf = NULL;
1195         self->status->data.bufSize = 0;
1196       }
1198       g_value_set_string (value, version);
1200       MemMgr_Free (version);
1202       break;
1203     }
1204     default:{
1205       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
1206       break;
1207     }
1208   }
1211 static void
1212 gst_ducati_viddec_finalize (GObject * obj)
1214   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1216   codec_delete (self);
1217   engine_close (self);
1219   if (self->codec_data) {
1220     gst_buffer_unref (self->codec_data);
1221     self->codec_data = NULL;
1222   }
1224   G_OBJECT_CLASS (parent_class)->finalize (obj);
1227 static void
1228 gst_ducati_viddec_base_init (gpointer gclass)
1230   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
1232   gst_element_class_add_pad_template (element_class,
1233       gst_static_pad_template_get (&src_factory));
1236 static void
1237 gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
1239   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1240   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1242   gobject_class->get_property =
1243       GST_DEBUG_FUNCPTR (gst_ducati_viddec_get_property);
1244   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ducati_viddec_finalize);
1245   gstelement_class->change_state =
1246       GST_DEBUG_FUNCPTR (gst_ducati_viddec_change_state);
1248   klass->parse_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
1249   klass->allocate_params =
1250       GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
1251   klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
1252   klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
1253   klass->drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_drop_frame);
1254   klass->query = GST_DEBUG_FUNCPTR (gst_ducati_viddec_query);
1256   g_object_class_install_property (gobject_class, PROP_VERSION,
1257       g_param_spec_string ("version", "Version",
1258           "The codec version string", "",
1259           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1262 static void
1263 gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
1265   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1267   self->sinkpad =
1268       gst_pad_new_from_template (gst_element_class_get_pad_template
1269       (gstelement_class, "sink"), "sink");
1270   gst_pad_set_setcaps_function (self->sinkpad,
1271       GST_DEBUG_FUNCPTR (gst_ducati_viddec_sink_setcaps));
1272   gst_pad_set_chain_function (self->sinkpad,
1273       GST_DEBUG_FUNCPTR (gst_ducati_viddec_chain));
1274   gst_pad_set_event_function (self->sinkpad,
1275       GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
1277   self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
1278   gst_pad_set_event_function (self->srcpad,
1279       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_event));
1280   gst_pad_set_query_function (self->srcpad,
1281       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_query));
1282   gst_pad_set_getcaps_function (self->srcpad,
1283       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_getcaps));
1285   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1286   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1288   self->input_width = 0;
1289   self->input_height = 0;
1290   /* sane defaults in case we need to create codec without caps negotiation
1291    * (for example, to get 'version' property)
1292    */
1293   self->width = 128;
1294   self->height = 128;
1295   self->fps_n = -1;
1296   self->fps_d = -1;
1298   self->first_in_buffer = TRUE;
1299   self->first_out_buffer = TRUE;
1300   self->interlaced = FALSE;
1301   self->send_crop_event = TRUE;
1303   self->dts_ridx = self->dts_widx = 0;
1304   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
1305   self->ts_may_be_pts = TRUE;
1306   self->ts_is_pts = FALSE;
1308   self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
1310   gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1312   self->qos_proportion = 1;
1313   self->qos_earliest_time = GST_CLOCK_TIME_NONE;
1314   self->wait_keyframe = TRUE;
1316   self->need_out_buf = TRUE;