ducatividdec: add support for outBufsInUseFlag
[gstreamer-omap:gst-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  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "gstducatividdec.h"
25
26 GST_BOILERPLATE (GstDucatiVidDec, gst_ducati_viddec, GstElement,
27     GST_TYPE_ELEMENT);
28
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     );
34
35 enum
36 {
37   PROP_0,
38   PROP_VERSION,
39 };
40
41 /* helper functions */
42
43 static void
44 engine_close (GstDucatiVidDec * self)
45 {
46   if (self->engine) {
47     Engine_close (self->engine);
48     self->engine = NULL;
49   }
50
51   if (self->params) {
52     dce_free (self->params);
53     self->params = NULL;
54   }
55
56   if (self->dynParams) {
57     dce_free (self->dynParams);
58     self->dynParams = NULL;
59   }
60
61   if (self->status) {
62     dce_free (self->status);
63     self->status = NULL;
64   }
65
66   if (self->inBufs) {
67     dce_free (self->inBufs);
68     self->inBufs = NULL;
69   }
70
71   if (self->outBufs) {
72     dce_free (self->outBufs);
73     self->outBufs = NULL;
74   }
75
76   if (self->inArgs) {
77     dce_free (self->inArgs);
78     self->inArgs = NULL;
79   }
80
81   if (self->outArgs) {
82     dce_free (self->outArgs);
83     self->outArgs = NULL;
84   }
85 }
86
87 static gboolean
88 engine_open (GstDucatiVidDec * self)
89 {
90   gboolean ret;
91
92   if (G_UNLIKELY (self->engine)) {
93     return TRUE;
94   }
95
96   GST_DEBUG_OBJECT (self, "opening engine");
97
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   }
103
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));
108
109   return ret;
110 }
111
112 static void
113 codec_delete (GstDucatiVidDec * self)
114 {
115   if (self->pool) {
116     gst_ducati_bufferpool_destroy (self->pool);
117     self->pool = NULL;
118   }
119
120   if (self->codec) {
121     VIDDEC3_delete (self->codec);
122     self->codec = NULL;
123   }
124
125   if (self->input) {
126     MemMgr_Free (self->input);
127     self->input = NULL;
128   }
129 }
130
131 static gboolean
132 codec_create (GstDucatiVidDec * self)
133 {
134   gint err;
135   const gchar *codec_name;
136
137   codec_delete (self);
138
139   if (G_UNLIKELY (!self->engine)) {
140     GST_ERROR_OBJECT (self, "no engine");
141     return FALSE;
142   }
143
144   /* these need to be set before VIDDEC3_create */
145   self->params->maxWidth = self->width;
146   self->params->maxHeight = self->height;
147
148   codec_name = GST_DUCATIVIDDEC_GET_CLASS (self)->codec_name;
149
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);
154
155   if (!self->codec) {
156     return FALSE;
157   }
158
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   }
166
167   self->first_in_buffer = TRUE;
168   self->first_out_buffer = TRUE;
169
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;
175
176   return TRUE;
177 }
178
179 static inline GstBuffer *
180 codec_bufferpool_get (GstDucatiVidDec * self, GstBuffer * buf)
181 {
182   if (G_UNLIKELY (!self->pool)) {
183     guint size;
184
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));
192 }
193
194 static XDAS_Int32
195 codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer * buf)
196 {
197   XDAS_Int16 y_type, uv_type;
198   guint8 *y_vaddr, *uv_vaddr;
199   SSPtr y_paddr, uv_paddr;
200
201   y_vaddr = GST_BUFFER_DATA (buf);
202   uv_vaddr = y_vaddr + self->stride * self->padded_height;
203
204   y_paddr = TilerMem_VirtToPhys (y_vaddr);
205   uv_paddr = TilerMem_VirtToPhys (uv_vaddr);
206
207   y_type = gst_ducati_get_mem_type (y_paddr);
208   uv_type = gst_ducati_get_mem_type (uv_paddr);
209   /* FIXME: workaround for the vc1 codec expecting _RAW when it's actually
210    * _TILEDPAGE... should be removed once the codec is fixed  */
211   if (y_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != y_type)
212     y_type = self->pageMemType;
213   if (uv_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != uv_type)
214     uv_type = self->pageMemType;
215
216   if (y_type < 0 || uv_type < 0) {
217     GST_DEBUG_OBJECT (self, "non TILER buffer, fallback to bufferpool");
218     return codec_prepare_outbuf (self, codec_bufferpool_get (self, buf));
219   }
220
221   if (!self->outBufs->numBufs) {
222     /* initialize output buffer type */
223     self->outBufs->numBufs = 2;
224     self->outBufs->descs[0].memType = y_type;
225     self->outBufs->descs[1].memType = uv_type;
226     if (y_type == XDM_MEMTYPE_RAW || y_type == XDM_MEMTYPE_TILEDPAGE) {
227       self->outBufs->descs[0].bufSize.bytes =
228           self->stride * self->padded_height;
229       self->outBufs->descs[1].bufSize.bytes =
230           self->stride * self->padded_height / 2;
231     } else {
232       self->outBufs->descs[0].bufSize.tileMem.width = self->padded_width;
233       self->outBufs->descs[0].bufSize.tileMem.height = self->padded_height;
234       /* note that UV interleaved width is same a Y: */
235       self->outBufs->descs[1].bufSize.tileMem.width = self->padded_width;
236       self->outBufs->descs[1].bufSize.tileMem.height = self->padded_height / 2;
237     }
238   } else {
239     /* verify output buffer type matches what we've already given
240      * to the codec
241      */
242     if ((self->outBufs->descs[0].memType != y_type) ||
243         (self->outBufs->descs[1].memType != uv_type)) {
244       GST_DEBUG_OBJECT (self, "buffer mismatch, fallback to bufferpool");
245       return codec_prepare_outbuf (self, codec_bufferpool_get (self, buf));
246     }
247   }
248
249   self->outBufs->descs[0].buf = (XDAS_Int8 *) y_paddr;
250   self->outBufs->descs[1].buf = (XDAS_Int8 *) uv_paddr;
251
252   return (XDAS_Int32) buf;      // XXX use lookup table
253 }
254
255 static GstBuffer *
256 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
257 {
258   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
259   if (buf) {
260     gst_buffer_ref (buf);
261   }
262   return buf;
263 }
264
265 static void
266 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
267 {
268   GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
269   if (buf) {
270     GST_DEBUG_OBJECT (self, "free buffer: %d %p", id, buf);
271     gst_buffer_unref (buf);
272   }
273 }
274
275 static gint
276 codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
277 {
278   gint err;
279   GstClockTime t;
280   GstBuffer *outbuf = NULL;
281   gint i;
282
283   memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
284   memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
285
286   t = gst_util_get_timestamp ();
287   err = VIDDEC3_process (self->codec,
288       self->inBufs, self->outBufs, self->inArgs, self->outArgs);
289   GST_DEBUG_OBJECT (self, "%10dns", (gint) (gst_util_get_timestamp () - t));
290
291   if (err) {
292     GST_WARNING_OBJECT (self, "err=%d, extendedError=%08x",
293         err, self->outArgs->extendedError);
294
295     err = VIDDEC3_control (self->codec, XDM_GETSTATUS,
296         self->dynParams, self->status);
297
298     GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
299         err, self->status->extendedError);
300
301     if (XDM_ISFATALERROR (self->outArgs->extendedError) || flush) {
302       /* we are processing for display and it is a non-fatal error, so lets
303        * try to recover.. otherwise return the error
304        */
305       err = XDM_EFAIL;
306     } else {
307       err = XDM_EOK;
308     }
309   }
310
311   for (i = 0; self->outArgs->outputID[i]; i++) {
312     if (G_UNLIKELY (self->first_out_buffer) && send) {
313       gint crop_width, crop_height;
314
315       /* send region of interest to sink on first buffer: */
316       XDM_Rect *r = &(self->outArgs->displayBufs.bufDesc[0].activeFrameRegion);
317
318       crop_width = r->bottomRight.x - r->topLeft.x;
319       crop_height = r->bottomRight.y - r->topLeft.y;
320
321       if (crop_width > self->input_width)
322         crop_width = self->input_width;
323       if (crop_height > self->input_height)
324         crop_height = self->input_height;
325
326       GST_INFO_OBJECT (self, "active frame region %d, %d, %d, %d, crop %dx%d",
327           r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y,
328           crop_width, crop_height);
329
330       gst_pad_push_event (self->srcpad,
331           gst_event_new_crop (r->topLeft.y, r->topLeft.x,
332               crop_width, crop_height));
333
334       self->first_out_buffer = FALSE;
335     }
336
337     outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
338     if (send) {
339       if (GST_IS_DUCATIBUFFER (outbuf)) {
340         outbuf = gst_ducati_buffer_get (GST_DUCATIBUFFER (outbuf));
341       }
342       GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
343           i, outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
344       gst_pad_push (self->srcpad, outbuf);
345     } else {
346       GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
347       gst_buffer_unref (outbuf);
348     }
349   }
350
351   for (i = 0; self->outArgs->freeBufID[i]; i++) {
352     codec_unlock_outbuf (self, self->outArgs->freeBufID[i]);
353   }
354
355   return err;
356 }
357
358 /** call control(FLUSH), and then process() to pop out all buffers */
359 static gboolean
360 codec_flush (GstDucatiVidDec * self, gboolean eos)
361 {
362   gint err;
363
364   GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
365
366   /* note: flush is synchronized against _chain() to avoid calling
367    * the codec from multiple threads
368    */
369   GST_PAD_STREAM_LOCK (self->sinkpad);
370
371   if (G_UNLIKELY (self->first_in_buffer)) {
372     return TRUE;
373   }
374
375   if (G_UNLIKELY (!self->codec)) {
376     GST_WARNING_OBJECT (self, "no codec");
377     return TRUE;
378   }
379
380   err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
381   if (err) {
382     GST_ERROR_OBJECT (self, "failed XDM_FLUSH");
383     goto out;
384   }
385
386   self->inBufs->descs[0].bufSize.bytes = 0;
387   self->inArgs->numBytes = 0;
388   self->inArgs->inputID = 0;
389
390   do {
391     err = codec_process (self, eos, TRUE);
392   } while (err != XDM_EFAIL);
393
394   /* on a flush, it is normal (and not an error) for the last _process() call
395    * to return an error..
396    */
397   err = XDM_EOK;
398
399 out:
400   GST_PAD_STREAM_UNLOCK (self->sinkpad);
401   GST_DEBUG_OBJECT (self, "done");
402
403   return !err;
404 }
405
406 /* GstDucatiVidDec vmethod default implementations */
407
408 static gboolean
409 gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
410 {
411   const GValue *codec_data;
412   gint w, h;
413
414   if (gst_structure_get_int (s, "width", &self->input_width) &&
415       gst_structure_get_int (s, "height", &self->input_height)) {
416
417     h = ALIGN2 (self->input_height, 4); /* round up to MB */
418     w = ALIGN2 (self->input_width, 4);  /* round up to MB */
419
420     /* if we've already created codec, but the resolution has changed, we
421      * need to re-create the codec:
422      */
423     if (G_UNLIKELY (self->codec)) {
424       if ((h != self->height) || (w != self->width)) {
425         codec_delete (self);
426       }
427     }
428
429     self->width = w;
430     self->height = h;
431
432     codec_data = gst_structure_get_value (s, "codec_data");
433
434     if (codec_data) {
435       GstBuffer *buffer = gst_value_get_buffer (codec_data);
436       GST_DEBUG_OBJECT (self, "codec_data: %" GST_PTR_FORMAT, buffer);
437       self->codec_data = gst_buffer_ref (buffer);
438     }
439
440     return TRUE;
441   }
442
443   return FALSE;
444 }
445
446 static gboolean
447 gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
448     gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz)
449 {
450
451   /* allocate params: */
452   self->params = dce_alloc (params_sz);
453   if (G_UNLIKELY (!self->params)) {
454     return FALSE;
455   }
456   self->params->size = params_sz;
457   self->params->maxFrameRate = 30000;
458   self->params->maxBitRate = 10000000;
459
460   self->params->dataEndianness = XDM_BYTE;
461   self->params->forceChromaFormat = XDM_YUV_420SP;
462   self->params->operatingMode = IVIDEO_DECODE_ONLY;
463
464   self->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
465   self->params->inputDataMode = IVIDEO_ENTIREFRAME;
466   self->params->outputDataMode = IVIDEO_ENTIREFRAME;
467   self->params->numInputDataUnits = 0;
468   self->params->numOutputDataUnits = 0;
469
470   self->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
471   self->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
472   self->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
473   self->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
474
475   /* allocate dynParams: */
476   self->dynParams = dce_alloc (dynparams_sz);
477   if (G_UNLIKELY (!self->dynParams)) {
478     return FALSE;
479   }
480   self->dynParams->size = dynparams_sz;
481   self->dynParams->decodeHeader = XDM_DECODE_AU;
482   self->dynParams->displayWidth = 0;
483   self->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
484   self->dynParams->newFrameFlag = XDAS_TRUE;
485
486   /* allocate status: */
487   self->status = dce_alloc (status_sz);
488   if (G_UNLIKELY (!self->status)) {
489     return FALSE;
490   }
491   self->status->size = status_sz;
492
493   /* allocate inBufs/outBufs: */
494   self->inBufs = dce_alloc (sizeof (XDM2_BufDesc));
495   self->outBufs = dce_alloc (sizeof (XDM2_BufDesc));
496   if (G_UNLIKELY (!self->inBufs) || G_UNLIKELY (!self->outBufs)) {
497     return FALSE;
498   }
499
500   /* allocate inArgs/outArgs: */
501   self->inArgs = dce_alloc (inargs_sz);
502   self->outArgs = dce_alloc (outargs_sz);
503   if (G_UNLIKELY (!self->inArgs) || G_UNLIKELY (!self->outArgs)) {
504     return FALSE;
505   }
506   self->inArgs->size = inargs_sz;
507   self->outArgs->size = outargs_sz;
508
509   return TRUE;
510 }
511
512 static GstBuffer *
513 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
514 {
515   if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
516     push_input (self, GST_BUFFER_DATA (self->codec_data),
517         GST_BUFFER_SIZE (self->codec_data));
518   }
519
520   /* just copy entire buffer */
521   push_input (self, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
522   gst_buffer_unref (buf);
523
524   return NULL;
525 }
526
527 /* GstElement vmethod implementations */
528
529 static gboolean
530 gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
531 {
532   gboolean ret = TRUE;
533   GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
534   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
535   GstStructure *s;
536   GstCaps *outcaps = NULL;
537   GstStructure *out_s;
538   gboolean interlaced = FALSE;
539   gint frn = 0, frd = 1;
540   gint par_width, par_height;
541   gboolean par_present;
542
543   s = gst_caps_get_structure (caps, 0);
544   if (!klass->parse_caps (self, s)) {
545     GST_WARNING_OBJECT (self, "missing required fields");
546     ret = FALSE;
547     goto out;
548   }
549
550   /* update output/padded sizes */
551   klass->update_buffer_size (self);
552
553   GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
554
555   gst_structure_get_fraction (s, "framerate", &frn, &frd);
556   gst_structure_get_boolean (s, "interlaced", &interlaced);
557   par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
558       &par_width, &par_height);
559
560   outcaps = gst_pad_get_allowed_caps (self->srcpad);
561   if (outcaps) {
562     outcaps = gst_caps_make_writable (outcaps);
563     gst_caps_truncate (outcaps);
564   } else {
565     outcaps = gst_caps_new_simple ("video/x-raw-yuv-strided",
566         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
567   }
568
569   out_s = gst_caps_get_structure (outcaps, 0);
570   gst_structure_set (out_s,
571       "width", G_TYPE_INT, self->padded_width,
572       "height", G_TYPE_INT, self->padded_height,
573       "framerate", GST_TYPE_FRACTION, frn, frd, NULL);
574   if (par_present)
575     gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
576         par_width, par_height, NULL);
577
578   if (interlaced)
579     gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
580
581   if (!strcmp (gst_structure_get_name (out_s), "video/x-raw-yuv-strided")) {
582     if (!gst_structure_get_int (out_s, "rowstride", &self->stride)) {
583       self->stride = 4096;
584       gst_structure_set (out_s, "rowstride", G_TYPE_INT, self->stride, NULL);
585     }
586   } else {
587     self->stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_NV12,
588         0, self->padded_width);
589   }
590
591   self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
592       self->stride, self->padded_height);
593
594   GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
595       self->outsize, self->stride, outcaps);
596
597   ret = gst_pad_set_caps (self->srcpad, outcaps);
598
599 out:
600   if (outcaps)
601     gst_caps_unref (outcaps);
602   gst_object_unref (self);
603
604   return ret;
605 }
606
607 static gboolean
608 gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
609 {
610   gboolean res = TRUE, forward = TRUE;
611   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
612
613   GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
614
615   switch (GST_QUERY_TYPE (query)) {
616     case GST_QUERY_BUFFERS:
617       GST_DEBUG_OBJECT (self, "min buffers: %d", self->min_buffers);
618       gst_query_set_buffers_count (query, self->min_buffers);
619
620       GST_DEBUG_OBJECT (self, "min dimensions: %dx%d",
621           self->padded_width, self->padded_height);
622       gst_query_set_buffers_dimensions (query,
623           self->padded_width, self->padded_height);
624       forward = FALSE;
625       break;
626     default:
627       break;
628   }
629
630   if (forward)
631     res = gst_pad_query_default (pad, query);
632
633   return res;
634 }
635
636 static gboolean
637 gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
638 {
639   GstClockTime timestamp, qostime;
640   gint64 diff;
641   gboolean is_keyframe;
642
643   timestamp = GST_BUFFER_TIMESTAMP (buf);
644   if (self->segment.format != GST_FORMAT_TIME ||
645       self->qos_earliest_time == GST_CLOCK_TIME_NONE)
646     goto no_qos;
647
648   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
649     goto no_qos;
650
651   qostime = gst_segment_to_running_time (&self->segment,
652       GST_FORMAT_TIME, timestamp);
653   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime)))
654     /* out of segment */
655     goto no_qos;
656
657   is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
658
659   /* see how our next timestamp relates to the latest qos timestamp. negative
660    * values mean we are early, positive values mean we are too late. */
661   diff = GST_CLOCK_DIFF (qostime, self->qos_earliest_time);
662
663   GST_DEBUG_OBJECT (self, "QOS: qostime %" GST_TIME_FORMAT
664       ", earliest %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT " proportion %f",
665       GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
666       self->qos_proportion);
667
668   if (diff >= 0 && !is_keyframe)
669     return FALSE;
670
671 no_qos:
672   return TRUE;
673 }
674
675 static GstFlowReturn
676 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
677 {
678   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
679   GstFlowReturn ret;
680   Int32 err;
681   GstBuffer *outbuf = NULL;
682   GstCaps *outcaps = NULL;
683   gboolean decode;
684
685   if (G_UNLIKELY (!self->engine)) {
686     GST_ERROR_OBJECT (self, "no engine");
687     return GST_FLOW_ERROR;
688   }
689
690   decode = gst_ducati_viddec_do_qos (self, buf);
691   if (!decode) {
692     gst_buffer_unref (buf);
693     return GST_FLOW_OK;
694   }
695
696   if (!self->need_out_buf)
697     goto have_out_buf;
698
699   /* do this before creating codec to ensure reverse caps negotiation
700    * happens first:
701    */
702 allocate_buffer:
703   ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
704       GST_PAD_CAPS (self->srcpad), &outbuf);
705   if (ret != GST_FLOW_OK) {
706     GST_DEBUG_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
707     return ret;
708   }
709
710   outcaps = GST_BUFFER_CAPS (outbuf);
711   if (outcaps && !gst_caps_is_equal (outcaps, GST_PAD_CAPS (self->srcpad))) {
712     GstStructure *s;
713
714     GST_INFO_OBJECT (self, "doing upstream negotiation bufsize %d",
715         GST_BUFFER_SIZE (outbuf));
716
717     s = gst_caps_get_structure (outcaps, 0);
718     gst_structure_get_int (s, "rowstride", &self->stride);
719     self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
720         self->stride, self->padded_height);
721
722     GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
723         self->outsize, self->stride, outcaps);
724
725     gst_pad_set_caps (self->srcpad, outcaps);
726
727     if (GST_BUFFER_SIZE (outbuf) != self->outsize) {
728       GST_INFO_OBJECT (self, "dropping buffer (bufsize %d != outsize %d)",
729           GST_BUFFER_SIZE (outbuf), self->outsize);
730       gst_buffer_unref (outbuf);
731       goto allocate_buffer;
732     }
733   }
734
735   if (G_UNLIKELY (!self->codec)) {
736     if (!codec_create (self)) {
737       GST_ERROR_OBJECT (self, "could not create codec");
738       return GST_FLOW_ERROR;
739     }
740   }
741
742   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
743   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
744
745   /* pass new output buffer as to the decoder to decode into: */
746   self->inArgs->inputID = codec_prepare_outbuf (self, outbuf);
747   if (!self->inArgs->inputID) {
748     GST_ERROR_OBJECT (self, "could not prepare output buffer");
749     return GST_FLOW_ERROR;
750   }
751
752 have_out_buf:
753   self->in_size = 0;
754   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
755
756   if (self->in_size == 0) {
757     GST_DEBUG_OBJECT (self, "no input, skipping process");
758     gst_buffer_unref (outbuf);
759     return GST_FLOW_OK;
760   }
761
762   self->inArgs->numBytes = self->in_size;
763   self->inBufs->descs[0].bufSize.bytes = self->in_size;
764
765   if (buf) {
766     // XXX
767     GST_WARNING_OBJECT (self, "TODO.. can't push more than one.. need loop");
768     gst_buffer_unref (buf);
769     buf = NULL;
770   }
771
772   err = codec_process (self, TRUE, FALSE);
773   if (err) {
774     GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
775         ("process returned error: %d %08x", err, self->outArgs->extendedError));
776     return GST_FLOW_ERROR;
777   }
778
779   self->first_in_buffer = FALSE;
780
781   if (self->outArgs->outBufsInUseFlag) {
782     GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
783     self->need_out_buf = FALSE;
784   } else {
785     self->need_out_buf = TRUE;
786   }
787
788   return GST_FLOW_OK;
789 }
790
791 static gboolean
792 gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
793 {
794   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
795   gboolean ret = TRUE;
796
797   GST_DEBUG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
798
799   switch (GST_EVENT_TYPE (event)) {
800     case GST_EVENT_NEWSEGMENT:
801     {
802       gboolean update;
803       GstFormat fmt;
804       gint64 start, stop, time;
805       gdouble rate, arate;
806
807       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
808           &start, &stop, &time);
809       gst_segment_set_newsegment_full (&self->segment, update,
810           rate, arate, fmt, start, stop, time);
811       break;
812     }
813     case GST_EVENT_EOS:
814       if (!codec_flush (self, TRUE)) {
815         GST_ERROR_OBJECT (self, "could not flush on eos");
816         ret = FALSE;
817       }
818       break;
819     case GST_EVENT_FLUSH_STOP:
820       if (!codec_flush (self, FALSE)) {
821         GST_ERROR_OBJECT (self, "could not flush");
822         ret = FALSE;
823       }
824       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
825       self->qos_earliest_time = GST_CLOCK_TIME_NONE;
826       self->qos_proportion = 1;
827       self->need_out_buf = TRUE;
828       break;
829     default:
830       break;
831   }
832
833   if (ret)
834     ret = gst_pad_push_event (self->srcpad, event);
835   GST_LOG_OBJECT (self, "end");
836
837   return ret;
838 }
839
840 static gboolean
841 gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
842 {
843   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
844   gboolean ret = TRUE;
845
846   GST_LOG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
847
848   switch (GST_EVENT_TYPE (event)) {
849     case GST_EVENT_QOS:
850     {
851       gdouble proportion;
852       GstClockTimeDiff diff;
853       GstClockTime timestamp;
854
855       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
856
857       GST_OBJECT_LOCK (self);
858       self->qos_proportion = proportion;
859       self->qos_earliest_time = timestamp + 2 * diff;
860       GST_OBJECT_UNLOCK (self);
861
862       GST_DEBUG_OBJECT (self,
863           "got QoS proportion %f %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
864           proportion, GST_TIME_ARGS (timestamp), diff);
865
866       ret = gst_pad_push_event (self->sinkpad, event);
867       break;
868     }
869     default:
870       ret = gst_pad_push_event (self->sinkpad, event);
871       break;
872   }
873
874   GST_LOG_OBJECT (self, "end");
875
876   return ret;
877 }
878
879 static GstStateChangeReturn
880 gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
881 {
882   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
883   GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
884
885   GST_INFO_OBJECT (self, "begin: changing state %s -> %s",
886       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
887       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
888
889   switch (transition) {
890     case GST_STATE_CHANGE_NULL_TO_READY:
891       if (!engine_open (self)) {
892         GST_ERROR_OBJECT (self, "could not open");
893         return GST_STATE_CHANGE_FAILURE;
894       }
895       break;
896     default:
897       break;
898   }
899
900   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
901
902   if (ret == GST_STATE_CHANGE_FAILURE)
903     goto leave;
904
905   switch (transition) {
906     case GST_STATE_CHANGE_READY_TO_NULL:
907       codec_delete (self);
908       engine_close (self);
909       break;
910     default:
911       break;
912   }
913
914 leave:
915   GST_LOG_OBJECT (self, "end");
916
917   return ret;
918 }
919
920 /* GObject vmethod implementations */
921
922 #define VERSION_LENGTH 256
923
924 static void
925 gst_ducati_viddec_get_property (GObject * obj,
926     guint prop_id, GValue * value, GParamSpec * pspec)
927 {
928   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
929
930   switch (prop_id) {
931     case PROP_VERSION:{
932       int err;
933       char *version = gst_ducati_alloc_1d (VERSION_LENGTH);
934
935       /* in case something fails: */
936       snprintf (version, VERSION_LENGTH, "unsupported");
937
938       if (!self->engine)
939         engine_open (self);
940
941       if (!self->codec)
942         codec_create (self);
943
944       if (self->codec) {
945         self->status->data.buf = (XDAS_Int8 *) TilerMem_VirtToPhys (version);
946         self->status->data.bufSize = VERSION_LENGTH;
947
948         err = VIDDEC3_control (self->codec, XDM_GETVERSION,
949             self->dynParams, self->status);
950         if (err) {
951           GST_ERROR_OBJECT (self, "failed XDM_GETVERSION");
952         }
953
954         self->status->data.buf = NULL;
955         self->status->data.bufSize = 0;
956       }
957
958       g_value_set_string (value, version);
959
960       MemMgr_Free (version);
961
962       break;
963     }
964     default:{
965       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
966       break;
967     }
968   }
969 }
970
971 static void
972 gst_ducati_viddec_finalize (GObject * obj)
973 {
974   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
975
976   codec_delete (self);
977   engine_close (self);
978
979   if (self->codec_data) {
980     gst_buffer_unref (self->codec_data);
981     self->codec_data = NULL;
982   }
983
984   G_OBJECT_CLASS (parent_class)->finalize (obj);
985 }
986
987 static void
988 gst_ducati_viddec_base_init (gpointer gclass)
989 {
990   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
991
992   gst_element_class_add_pad_template (element_class,
993       gst_static_pad_template_get (&src_factory));
994 }
995
996 static void
997 gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
998 {
999   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1000   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1001
1002   gobject_class->get_property =
1003       GST_DEBUG_FUNCPTR (gst_ducati_viddec_get_property);
1004   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ducati_viddec_finalize);
1005   gstelement_class->change_state =
1006       GST_DEBUG_FUNCPTR (gst_ducati_viddec_change_state);
1007
1008   klass->parse_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
1009   klass->allocate_params =
1010       GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
1011   klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
1012
1013   g_object_class_install_property (gobject_class, PROP_VERSION,
1014       g_param_spec_string ("version", "Version",
1015           "The codec version string", "",
1016           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1017 }
1018
1019 static void
1020 gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
1021 {
1022   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1023
1024   self->sinkpad =
1025       gst_pad_new_from_template (gst_element_class_get_pad_template
1026       (gstelement_class, "sink"), "sink");
1027   gst_pad_set_setcaps_function (self->sinkpad,
1028       GST_DEBUG_FUNCPTR (gst_ducati_viddec_sink_setcaps));
1029   gst_pad_set_chain_function (self->sinkpad,
1030       GST_DEBUG_FUNCPTR (gst_ducati_viddec_chain));
1031   gst_pad_set_event_function (self->sinkpad,
1032       GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
1033
1034   self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
1035   gst_pad_set_event_function (self->srcpad,
1036       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_event));
1037   gst_pad_set_query_function (self->srcpad,
1038       GST_DEBUG_FUNCPTR (gst_ducati_viddec_query));
1039
1040   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1041   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1042
1043   self->input_width = 0;
1044   self->input_height = 0;
1045   /* sane defaults in case we need to create codec without caps negotiation
1046    * (for example, to get 'version' property)
1047    */
1048   self->width = 128;
1049   self->height = 128;
1050
1051   self->first_in_buffer = TRUE;
1052   self->first_out_buffer = TRUE;
1053
1054   self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
1055
1056   gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1057
1058   self->qos_proportion = 1;
1059   self->qos_earliest_time = GST_CLOCK_TIME_NONE;
1060
1061   self->need_out_buf = TRUE;
1062 }