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 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
267 }
269 static GstBuffer *
270 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
271 {
272 GstBuffer *buf = (GstBuffer *) id; // XXX use lookup table
273 if (buf) {
274 gst_buffer_ref (buf);
275 }
276 return buf;
277 }
279 static void
280 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
281 {
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 }
287 }
289 static gint
290 codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
291 {
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 = self->outArgs->decodedBufs.contentType == IVIDEO_PROGRESSIVE ? FALSE : TRUE;
331 /* if send is FALSE, don't try to renegotiate as we could be flushing during
332 * a PAUSED->READY state change
333 */
334 if (send && interlaced != self->interlaced) {
335 GstCaps *caps;
337 GST_WARNING_OBJECT (self, "upstream set interlaced=%d but codec "
338 "thinks interlaced=%d... trusting codec", self->interlaced,
339 interlaced);
341 self->interlaced = interlaced;
343 caps = gst_caps_make_writable (gst_pad_get_negotiated_caps (self->srcpad));
344 GST_INFO_OBJECT (self, "changing interlace field in caps");
345 gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced, NULL);
346 gst_ducati_bufferpool_set_caps (self->pool, caps);
347 if (!gst_pad_set_caps (self->srcpad, caps)) {
348 GST_ERROR_OBJECT (self, "downstream didn't want to change interlace mode");
349 err = XDM_EFAIL;
350 }
351 gst_caps_unref (caps);
353 /* this buffer still has the old caps so we skip it */
354 send = FALSE;
355 }
357 if (G_UNLIKELY (self->first_out_buffer) && send) {
358 gint crop_width, crop_height;
359 GstDucatiBufferPool *pool;
360 GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
362 /* send region of interest to sink on first buffer: */
363 XDM_Rect *r = &(self->outArgs->displayBufs.bufDesc[0].activeFrameRegion);
365 crop_width = r->bottomRight.x - r->topLeft.x;
366 crop_height = r->bottomRight.y - r->topLeft.y;
368 if (crop_width > self->input_width)
369 crop_width = self->input_width;
370 if (crop_height > self->input_height)
371 crop_height = self->input_height;
373 if (self->interlaced && !strcmp (klass->codec_name, "ivahd_mpeg2vdec"))
374 crop_height = crop_height / 2;
376 GST_INFO_OBJECT (self, "active frame region %d, %d, %d, %d, crop %dx%d",
377 r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y,
378 crop_width, crop_height);
380 gst_pad_push_event (self->srcpad,
381 gst_event_new_crop (r->topLeft.y, r->topLeft.x,
382 crop_width, crop_height));
384 self->first_out_buffer = FALSE;
386 /* Destroy the pool so the buffers we used so far are eventually released.
387 * The pool will be recreated if needed.
388 */
389 pool = self->pool;
390 self->pool = NULL;
391 gst_ducati_bufferpool_destroy (pool);
392 }
394 if (send) {
395 GstClockTime ts;
397 if (GST_IS_DUCATIBUFFER (outbuf)) {
398 outbuf = gst_ducati_buffer_get (GST_DUCATIBUFFER (outbuf));
399 }
401 ts = GST_BUFFER_TIMESTAMP (outbuf);
403 GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
404 i, outbuf, GST_TIME_ARGS (ts));
406 if (self->ts_may_be_pts) {
407 if ((self->last_pts != GST_CLOCK_TIME_NONE) &&
408 (self->last_pts > ts)) {
409 GST_DEBUG_OBJECT (self, "detected PTS going backwards, "
410 "enabling ts_is_pts");
411 self->ts_is_pts = TRUE;
412 }
413 }
415 self->last_pts = ts;
417 if (self->dts_ridx != self->dts_widx) {
418 ts = self->dts_queue[self->dts_ridx++ % NDTS];
419 }
421 if (self->ts_is_pts) {
422 /* if we have a queued DTS from demuxer, use that instead: */
423 GST_BUFFER_TIMESTAMP (outbuf) = ts;
424 GST_DEBUG_OBJECT (self, "fixed ts: %d %p (%" GST_TIME_FORMAT ")",
425 i, outbuf, GST_TIME_ARGS (ts));
426 }
429 if (GST_BUFFER_CAPS (outbuf) &&
430 !gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
431 GST_PAD_CAPS (self->srcpad))) {
432 /* this looks a bit scary but it's really just to change the interlace=
433 * field in caps when we start as !interlaced and the codec detects
434 * otherwise */
435 GST_WARNING_OBJECT (self, "overriding buffer caps to fix "
436 "interlace mismatch");
437 outbuf = gst_buffer_make_metadata_writable (outbuf);
438 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
439 }
441 gst_pad_push (self->srcpad, outbuf);
442 } else {
443 GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
444 gst_buffer_unref (outbuf);
445 }
446 }
448 for (i = 0; self->outArgs->freeBufID[i]; i++) {
449 codec_unlock_outbuf (self, self->outArgs->freeBufID[i]);
450 }
452 return err;
453 }
455 /** call control(FLUSH), and then process() to pop out all buffers */
456 static gboolean
457 codec_flush (GstDucatiVidDec * self, gboolean eos)
458 {
459 gint err;
461 GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
463 /* note: flush is synchronized against _chain() to avoid calling
464 * the codec from multiple threads
465 */
466 GST_PAD_STREAM_LOCK (self->sinkpad);
468 self->dts_ridx = self->dts_widx = 0;
469 self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
470 self->ts_may_be_pts = TRUE;
471 self->ts_is_pts = FALSE;
472 self->wait_keyframe = TRUE;
474 if (G_UNLIKELY (self->first_in_buffer)) {
475 return TRUE;
476 }
478 if (G_UNLIKELY (!self->codec)) {
479 GST_WARNING_OBJECT (self, "no codec");
480 return TRUE;
481 }
483 err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
484 if (err) {
485 GST_ERROR_OBJECT (self, "failed XDM_FLUSH");
486 goto out;
487 }
489 self->inBufs->descs[0].bufSize.bytes = 0;
490 self->inArgs->numBytes = 0;
491 self->inArgs->inputID = 0;
493 do {
494 err = codec_process (self, eos, TRUE);
495 } while (err != XDM_EFAIL);
497 /* on a flush, it is normal (and not an error) for the last _process() call
498 * to return an error..
499 */
500 err = XDM_EOK;
502 out:
503 GST_PAD_STREAM_UNLOCK (self->sinkpad);
504 GST_DEBUG_OBJECT (self, "done");
506 return !err;
507 }
509 /* GstDucatiVidDec vmethod default implementations */
511 static gboolean
512 gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
513 {
514 const GValue *codec_data;
515 gint w, h;
517 if (gst_structure_get_int (s, "width", &self->input_width) &&
518 gst_structure_get_int (s, "height", &self->input_height)) {
520 h = ALIGN2 (self->input_height, 4); /* round up to MB */
521 w = ALIGN2 (self->input_width, 4); /* round up to MB */
523 /* if we've already created codec, but the resolution has changed, we
524 * need to re-create the codec:
525 */
526 if (G_UNLIKELY (self->codec)) {
527 if ((h != self->height) || (w != self->width)) {
528 codec_delete (self);
529 }
530 }
532 self->width = w;
533 self->height = h;
535 codec_data = gst_structure_get_value (s, "codec_data");
537 if (codec_data) {
538 GstBuffer *buffer = gst_value_get_buffer (codec_data);
539 GST_DEBUG_OBJECT (self, "codec_data: %" GST_PTR_FORMAT, buffer);
540 self->codec_data = gst_buffer_ref (buffer);
541 }
543 return TRUE;
544 }
546 return FALSE;
547 }
549 static gboolean
550 gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
551 gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz)
552 {
554 /* allocate params: */
555 self->params = dce_alloc (params_sz);
556 if (G_UNLIKELY (!self->params)) {
557 return FALSE;
558 }
559 self->params->size = params_sz;
560 self->params->maxFrameRate = 30000;
561 self->params->maxBitRate = 10000000;
563 self->params->dataEndianness = XDM_BYTE;
564 self->params->forceChromaFormat = XDM_YUV_420SP;
565 self->params->operatingMode = IVIDEO_DECODE_ONLY;
567 self->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
568 self->params->inputDataMode = IVIDEO_ENTIREFRAME;
569 self->params->outputDataMode = IVIDEO_ENTIREFRAME;
570 self->params->numInputDataUnits = 0;
571 self->params->numOutputDataUnits = 0;
573 self->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
574 self->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
575 self->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
576 self->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
578 /* allocate dynParams: */
579 self->dynParams = dce_alloc (dynparams_sz);
580 if (G_UNLIKELY (!self->dynParams)) {
581 return FALSE;
582 }
583 self->dynParams->size = dynparams_sz;
584 self->dynParams->decodeHeader = XDM_DECODE_AU;
585 self->dynParams->displayWidth = 0;
586 self->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
587 self->dynParams->newFrameFlag = XDAS_TRUE;
589 /* allocate status: */
590 self->status = dce_alloc (status_sz);
591 if (G_UNLIKELY (!self->status)) {
592 return FALSE;
593 }
594 self->status->size = status_sz;
596 /* allocate inBufs/outBufs: */
597 self->inBufs = dce_alloc (sizeof (XDM2_BufDesc));
598 self->outBufs = dce_alloc (sizeof (XDM2_BufDesc));
599 if (G_UNLIKELY (!self->inBufs) || G_UNLIKELY (!self->outBufs)) {
600 return FALSE;
601 }
603 /* allocate inArgs/outArgs: */
604 self->inArgs = dce_alloc (inargs_sz);
605 self->outArgs = dce_alloc (outargs_sz);
606 if (G_UNLIKELY (!self->inArgs) || G_UNLIKELY (!self->outArgs)) {
607 return FALSE;
608 }
609 self->inArgs->size = inargs_sz;
610 self->outArgs->size = outargs_sz;
612 return TRUE;
613 }
615 static GstBuffer *
616 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
617 {
618 if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
619 push_input (self, GST_BUFFER_DATA (self->codec_data),
620 GST_BUFFER_SIZE (self->codec_data));
621 }
623 /* just copy entire buffer */
624 push_input (self, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
625 gst_buffer_unref (buf);
627 return NULL;
628 }
630 static gint
631 gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
632 gint extended_error, gint status_extended_error)
633 {
634 if (XDM_ISFATALERROR (extended_error))
635 ret = XDM_EFAIL;
636 else
637 ret = XDM_EOK;
639 return ret;
640 }
642 /* GstElement vmethod implementations */
644 static gboolean
645 gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
646 {
647 gboolean ret = TRUE;
648 GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
649 GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
650 GstStructure *s;
651 GstCaps *outcaps = NULL;
652 GstStructure *out_s;
653 gint frn = 0, frd = 1;
654 gint par_width, par_height;
655 gboolean par_present;
657 GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
659 s = gst_caps_get_structure (caps, 0);
660 if (!klass->parse_caps (self, s)) {
661 GST_WARNING_OBJECT (self, "missing required fields");
662 ret = FALSE;
663 goto out;
664 }
666 /* update output/padded sizes */
667 klass->update_buffer_size (self);
669 gst_structure_get_fraction (s, "framerate", &frn, &frd);
670 gst_structure_get_boolean (s, "interlaced", &self->interlaced);
671 par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
672 &par_width, &par_height);
674 outcaps = gst_pad_get_allowed_caps (self->srcpad);
675 if (outcaps) {
676 outcaps = gst_caps_make_writable (outcaps);
677 gst_caps_truncate (outcaps);
678 if (gst_caps_is_empty (outcaps)) {
679 gst_caps_unref (outcaps);
680 outcaps = NULL;
681 }
682 }
684 if (!outcaps) {
685 /* note: default to non-strided for better compatibility with
686 * other gst elements that don't understand stride:
687 */
688 outcaps = gst_caps_new_simple ("video/x-raw-yuv",
689 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
690 }
692 out_s = gst_caps_get_structure (outcaps, 0);
693 gst_structure_set (out_s,
694 "width", G_TYPE_INT, self->padded_width,
695 "height", G_TYPE_INT, self->padded_height,
696 "framerate", GST_TYPE_FRACTION, frn, frd, NULL);
697 if (par_present)
698 gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
699 par_width, par_height, NULL);
701 if (self->interlaced)
702 gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
704 if (!strcmp (gst_structure_get_name (out_s), "video/x-raw-yuv-strided")) {
705 if (!gst_structure_get_int (out_s, "rowstride", &self->stride)) {
706 self->stride = 4096;
707 gst_structure_set (out_s, "rowstride", G_TYPE_INT, self->stride, NULL);
708 }
709 } else {
710 self->stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_NV12,
711 0, self->padded_width);
712 }
714 self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
715 self->stride, self->padded_height);
717 GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
718 self->outsize, self->stride, outcaps);
720 ret = gst_pad_set_caps (self->srcpad, outcaps);
722 out:
723 if (outcaps)
724 gst_caps_unref (outcaps);
725 gst_object_unref (self);
727 return ret;
728 }
730 static gboolean
731 gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
732 {
733 gboolean res = TRUE, forward = TRUE;
734 GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
736 GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
738 switch (GST_QUERY_TYPE (query)) {
739 case GST_QUERY_BUFFERS:
740 GST_DEBUG_OBJECT (self, "min buffers: %d", self->min_buffers);
741 gst_query_set_buffers_count (query, self->min_buffers);
743 GST_DEBUG_OBJECT (self, "min dimensions: %dx%d",
744 self->padded_width, self->padded_height);
745 gst_query_set_buffers_dimensions (query,
746 self->padded_width, self->padded_height);
747 forward = FALSE;
748 break;
749 default:
750 break;
751 }
753 if (forward)
754 res = gst_pad_query_default (pad, query);
756 return res;
757 }
759 static gboolean
760 gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
761 {
762 GstClockTime timestamp, qostime;
763 GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
764 gint64 diff;
766 if (self->wait_keyframe) {
767 if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
768 GST_INFO_OBJECT (self, "skipping until the next keyframe");
769 return FALSE;
770 }
772 self->wait_keyframe = FALSE;
773 }
775 timestamp = GST_BUFFER_TIMESTAMP (buf);
776 if (self->segment.format != GST_FORMAT_TIME ||
777 self->qos_earliest_time == GST_CLOCK_TIME_NONE)
778 goto no_qos;
780 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
781 goto no_qos;
783 qostime = gst_segment_to_running_time (&self->segment,
784 GST_FORMAT_TIME, timestamp);
785 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime)))
786 /* out of segment */
787 goto no_qos;
789 /* see how our next timestamp relates to the latest qos timestamp. negative
790 * values mean we are early, positive values mean we are too late. */
791 diff = GST_CLOCK_DIFF (qostime, self->qos_earliest_time);
793 GST_DEBUG_OBJECT (self, "QOS: qostime %" GST_TIME_FORMAT
794 ", earliest %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT " proportion %f",
795 GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
796 self->qos_proportion);
798 if (klass->drop_frame (self, buf, diff)) {
799 GST_INFO_OBJECT (self, "dropping frame");
800 return FALSE;
801 }
803 no_qos:
804 return TRUE;
805 }
807 static gboolean
808 gst_ducati_viddec_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
809 gint64 diff)
810 {
811 gboolean is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf,
812 GST_BUFFER_FLAG_DELTA_UNIT);
814 if (diff >= 0 && !is_keyframe)
815 return TRUE;
817 return FALSE;
818 }
820 static GstFlowReturn
821 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
822 {
823 GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
824 GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
825 GstFlowReturn ret;
826 Int32 err;
827 GstBuffer *outbuf = NULL;
828 GstCaps *outcaps = NULL;
829 gboolean decode;
831 if (G_UNLIKELY (!self->engine)) {
832 GST_ERROR_OBJECT (self, "no engine");
833 return GST_FLOW_ERROR;
834 }
836 GST_DEBUG_OBJECT (self, "chain: %" GST_TIME_FORMAT " (%d bytes, flags %d)",
837 GST_TIME_ARGS (ts), GST_BUFFER_SIZE (buf), GST_BUFFER_FLAGS (buf));
839 decode = gst_ducati_viddec_do_qos (self, buf);
840 if (!decode) {
841 gst_buffer_unref (buf);
842 return GST_FLOW_OK;
843 }
845 if (!self->need_out_buf)
846 goto have_out_buf;
848 /* do this before creating codec to ensure reverse caps negotiation
849 * happens first:
850 */
851 allocate_buffer:
852 ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
853 GST_PAD_CAPS (self->srcpad), &outbuf);
854 if (ret != GST_FLOW_OK) {
855 GST_ERROR_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
856 return ret;
857 }
859 outcaps = GST_BUFFER_CAPS (outbuf);
860 if (outcaps && !gst_caps_is_equal (outcaps, GST_PAD_CAPS (self->srcpad))) {
861 GstStructure *s;
863 GST_INFO_OBJECT (self, "doing upstream negotiation bufsize %d",
864 GST_BUFFER_SIZE (outbuf));
866 s = gst_caps_get_structure (outcaps, 0);
867 gst_structure_get_int (s, "rowstride", &self->stride);
868 self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
869 self->stride, self->padded_height);
871 GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
872 self->outsize, self->stride, outcaps);
874 gst_pad_set_caps (self->srcpad, outcaps);
876 if (GST_BUFFER_SIZE (outbuf) != self->outsize) {
877 GST_INFO_OBJECT (self, "dropping buffer (bufsize %d != outsize %d)",
878 GST_BUFFER_SIZE (outbuf), self->outsize);
879 gst_buffer_unref (outbuf);
880 goto allocate_buffer;
881 }
882 }
884 if (G_UNLIKELY (!self->codec)) {
885 if (!codec_create (self)) {
886 GST_ERROR_OBJECT (self, "could not create codec");
887 return GST_FLOW_ERROR;
888 }
889 }
891 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
892 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
894 /* Pass new output buffer to the decoder to decode into. Use buffers from the
895 * internal pool while self->first_out_buffer == TRUE in order to simplify
896 * things in case we need to renegotiate */
897 self->inArgs->inputID = codec_prepare_outbuf (self, &outbuf, self->first_out_buffer);
898 if (!self->inArgs->inputID) {
899 GST_ERROR_OBJECT (self, "could not prepare output buffer");
900 return GST_FLOW_ERROR;
901 }
903 have_out_buf:
904 self->in_size = 0;
905 buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
907 if (ts != GST_CLOCK_TIME_NONE) {
908 self->dts_queue[self->dts_widx++ % NDTS] = ts;
909 /* if next buffer has earlier ts than previous, then the ts
910 * we are getting are definitely decode order (DTS):
911 */
912 if ((self->last_dts != GST_CLOCK_TIME_NONE) &&
913 (self->last_dts > ts)) {
914 GST_DEBUG_OBJECT (self, "input timestamp definitely DTS");
915 self->ts_may_be_pts = FALSE;
916 }
917 self->last_dts = ts;
918 }
920 if (self->in_size == 0 && outbuf) {
921 GST_DEBUG_OBJECT (self, "no input, skipping process");
922 gst_buffer_unref (outbuf);
923 return GST_FLOW_OK;
924 }
926 self->inArgs->numBytes = self->in_size;
927 self->inBufs->descs[0].bufSize.bytes = self->in_size;
929 err = codec_process (self, TRUE, FALSE);
930 if (err) {
931 GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
932 ("process returned error: %d %08x", err, self->outArgs->extendedError));
933 return GST_FLOW_ERROR;
934 }
936 self->first_in_buffer = FALSE;
938 if (self->outArgs->outBufsInUseFlag) {
939 GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
940 self->need_out_buf = FALSE;
941 } else {
942 self->need_out_buf = TRUE;
943 }
945 if (buf) {
946 GST_DEBUG_OBJECT (self, "found remaining data: %d bytes",
947 GST_BUFFER_SIZE (buf));
948 ts = GST_BUFFER_TIMESTAMP (buf);
949 goto allocate_buffer;
950 }
952 return GST_FLOW_OK;
953 }
955 static gboolean
956 gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
957 {
958 GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
959 gboolean ret = TRUE;
961 GST_DEBUG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
963 switch (GST_EVENT_TYPE (event)) {
964 case GST_EVENT_NEWSEGMENT:
965 {
966 gboolean update;
967 GstFormat fmt;
968 gint64 start, stop, time;
969 gdouble rate, arate;
971 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
972 &start, &stop, &time);
973 gst_segment_set_newsegment_full (&self->segment, update,
974 rate, arate, fmt, start, stop, time);
975 break;
976 }
977 case GST_EVENT_EOS:
978 if (!codec_flush (self, TRUE)) {
979 GST_ERROR_OBJECT (self, "could not flush on eos");
980 ret = FALSE;
981 }
982 break;
983 case GST_EVENT_FLUSH_STOP:
984 if (!codec_flush (self, FALSE)) {
985 GST_ERROR_OBJECT (self, "could not flush");
986 ret = FALSE;
987 }
988 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
989 self->qos_earliest_time = GST_CLOCK_TIME_NONE;
990 self->qos_proportion = 1;
991 self->need_out_buf = TRUE;
992 break;
993 default:
994 break;
995 }
997 if (ret)
998 ret = gst_pad_push_event (self->srcpad, event);
999 GST_LOG_OBJECT (self, "end");
1001 return ret;
1002 }
1004 static gboolean
1005 gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
1006 {
1007 GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
1008 gboolean ret = TRUE;
1010 GST_LOG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
1012 switch (GST_EVENT_TYPE (event)) {
1013 case GST_EVENT_QOS:
1014 {
1015 gdouble proportion;
1016 GstClockTimeDiff diff;
1017 GstClockTime timestamp;
1019 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
1021 GST_OBJECT_LOCK (self);
1022 self->qos_proportion = proportion;
1023 self->qos_earliest_time = timestamp + 2 * diff;
1024 GST_OBJECT_UNLOCK (self);
1026 GST_DEBUG_OBJECT (self,
1027 "got QoS proportion %f %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
1028 proportion, GST_TIME_ARGS (timestamp), diff);
1030 ret = gst_pad_push_event (self->sinkpad, event);
1031 break;
1032 }
1033 default:
1034 ret = gst_pad_push_event (self->sinkpad, event);
1035 break;
1036 }
1038 GST_LOG_OBJECT (self, "end");
1040 return ret;
1041 }
1043 static GstStateChangeReturn
1044 gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
1045 {
1046 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1047 GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
1049 GST_DEBUG_OBJECT (self, "begin: changing state %s -> %s",
1050 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1051 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1053 switch (transition) {
1054 case GST_STATE_CHANGE_NULL_TO_READY:
1055 if (!engine_open (self)) {
1056 GST_ERROR_OBJECT (self, "could not open");
1057 return GST_STATE_CHANGE_FAILURE;
1058 }
1059 break;
1060 default:
1061 break;
1062 }
1064 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1066 if (ret == GST_STATE_CHANGE_FAILURE)
1067 goto leave;
1069 switch (transition) {
1070 case GST_STATE_CHANGE_PAUSED_TO_READY:
1071 self->interlaced = FALSE;
1072 codec_flush (self, FALSE);
1073 break;
1074 case GST_STATE_CHANGE_READY_TO_NULL:
1075 codec_delete (self);
1076 engine_close (self);
1077 break;
1078 default:
1079 break;
1080 }
1082 leave:
1083 GST_LOG_OBJECT (self, "end");
1085 return ret;
1086 }
1088 /* GObject vmethod implementations */
1090 #define VERSION_LENGTH 256
1092 static void
1093 gst_ducati_viddec_get_property (GObject * obj,
1094 guint prop_id, GValue * value, GParamSpec * pspec)
1095 {
1096 GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1098 switch (prop_id) {
1099 case PROP_VERSION:{
1100 int err;
1101 char *version = gst_ducati_alloc_1d (VERSION_LENGTH);
1103 /* in case something fails: */
1104 snprintf (version, VERSION_LENGTH, "unsupported");
1106 if (!self->engine)
1107 engine_open (self);
1109 if (!self->codec)
1110 codec_create (self);
1112 if (self->codec) {
1113 self->status->data.buf = (XDAS_Int8 *) TilerMem_VirtToPhys (version);
1114 self->status->data.bufSize = VERSION_LENGTH;
1116 err = VIDDEC3_control (self->codec, XDM_GETVERSION,
1117 self->dynParams, self->status);
1118 if (err) {
1119 GST_ERROR_OBJECT (self, "failed XDM_GETVERSION");
1120 }
1122 self->status->data.buf = NULL;
1123 self->status->data.bufSize = 0;
1124 }
1126 g_value_set_string (value, version);
1128 MemMgr_Free (version);
1130 break;
1131 }
1132 default:{
1133 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
1134 break;
1135 }
1136 }
1137 }
1139 static void
1140 gst_ducati_viddec_finalize (GObject * obj)
1141 {
1142 GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
1144 codec_delete (self);
1145 engine_close (self);
1147 if (self->codec_data) {
1148 gst_buffer_unref (self->codec_data);
1149 self->codec_data = NULL;
1150 }
1152 G_OBJECT_CLASS (parent_class)->finalize (obj);
1153 }
1155 static void
1156 gst_ducati_viddec_base_init (gpointer gclass)
1157 {
1158 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
1160 gst_element_class_add_pad_template (element_class,
1161 gst_static_pad_template_get (&src_factory));
1162 }
1164 static void
1165 gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
1166 {
1167 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1168 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1170 gobject_class->get_property =
1171 GST_DEBUG_FUNCPTR (gst_ducati_viddec_get_property);
1172 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ducati_viddec_finalize);
1173 gstelement_class->change_state =
1174 GST_DEBUG_FUNCPTR (gst_ducati_viddec_change_state);
1176 klass->parse_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
1177 klass->allocate_params =
1178 GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
1179 klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
1180 klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
1181 klass->drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_drop_frame);
1183 g_object_class_install_property (gobject_class, PROP_VERSION,
1184 g_param_spec_string ("version", "Version",
1185 "The codec version string", "",
1186 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1187 }
1189 static void
1190 gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
1191 {
1192 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1194 self->sinkpad =
1195 gst_pad_new_from_template (gst_element_class_get_pad_template
1196 (gstelement_class, "sink"), "sink");
1197 gst_pad_set_setcaps_function (self->sinkpad,
1198 GST_DEBUG_FUNCPTR (gst_ducati_viddec_sink_setcaps));
1199 gst_pad_set_chain_function (self->sinkpad,
1200 GST_DEBUG_FUNCPTR (gst_ducati_viddec_chain));
1201 gst_pad_set_event_function (self->sinkpad,
1202 GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
1204 self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
1205 gst_pad_set_event_function (self->srcpad,
1206 GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_event));
1207 gst_pad_set_query_function (self->srcpad,
1208 GST_DEBUG_FUNCPTR (gst_ducati_viddec_query));
1210 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1211 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1213 self->input_width = 0;
1214 self->input_height = 0;
1215 /* sane defaults in case we need to create codec without caps negotiation
1216 * (for example, to get 'version' property)
1217 */
1218 self->width = 128;
1219 self->height = 128;
1221 self->first_in_buffer = TRUE;
1222 self->first_out_buffer = TRUE;
1223 self->interlaced = FALSE;
1225 self->dts_ridx = self->dts_widx = 0;
1226 self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
1227 self->ts_may_be_pts = TRUE;
1228 self->ts_is_pts = FALSE;
1230 self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
1232 gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1234 self->qos_proportion = 1;
1235 self->qos_earliest_time = GST_CLOCK_TIME_NONE;
1236 self->wait_keyframe = TRUE;
1238 self->need_out_buf = TRUE;
1239 }