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;
110 }
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 }
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 }
129 }
131 static gboolean
132 codec_create (GstDucatiVidDec * self)
133 {
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;
177 }
179 static inline GstBuffer *
180 codec_bufferpool_get (GstDucatiVidDec * self, GstBuffer * buf)
181 {
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));
192 }
194 static XDAS_Int32
195 codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
196 gboolean force_internal)
197 {
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
263 }
265 static GstBuffer *
266 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
267 {
268 GstBuffer *buf = (GstBuffer *) id; // XXX use lookup table
269 if (buf) {
270 gst_buffer_ref (buf);
271 }
272 return buf;
273 }
275 static void
276 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
277 {
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 }
283 }
285 static gint
286 codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
287 {
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 }
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;
433 }
435 /** call control(FLUSH), and then process() to pop out all buffers */
436 static gboolean
437 codec_flush (GstDucatiVidDec * self, gboolean eos)
438 {
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;
485 }
487 /* GstDucatiVidDec vmethod default implementations */
489 static gboolean
490 gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
491 {
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;
525 }
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)
530 {
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;
591 }
593 static GstBuffer *
594 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
595 {
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;
606 }
608 static gint
609 gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
610 gint extended_error, gint status_extended_error)
611 {
612 if (XDM_ISFATALERROR (extended_error))
613 ret = XDM_EFAIL;
614 else
615 ret = XDM_EOK;
617 return ret;
618 }
620 /* GstElement vmethod implementations */
622 static gboolean
623 gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
624 {
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;
706 }
708 static gboolean
709 gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
710 {
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;
735 }
737 static gboolean
738 gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
739 {
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;
774 }
776 static GstFlowReturn
777 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
778 {
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;
907 }
909 static gboolean
910 gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
911 {
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;
956 }
958 static gboolean
959 gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
960 {
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, ×tamp);
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;
995 }
997 static GstStateChangeReturn
998 gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
999 {
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;
1039 }
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)
1048 {
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 }
1090 }
1092 static void
1093 gst_ducati_viddec_finalize (GObject * obj)
1094 {
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);
1106 }
1108 static void
1109 gst_ducati_viddec_base_init (gpointer gclass)
1110 {
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));
1115 }
1117 static void
1118 gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
1119 {
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));
1139 }
1141 static void
1142 gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
1143 {
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;
1190 }