1 /*
2 * Copyright (C) 2012 Texas Instruments
3 * Author: Rob Clark <rob.clark@linaro.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
22 #include <dce.h>
23 #include <xdc/std.h>
24 #include <ti/xdais/xdas.h>
25 #include <ti/sdo/ce/Engine.h>
26 #include <ti/sdo/ce/video3/viddec3.h>
28 #include "util.h"
29 #include "demux.h"
31 /* Padding for width as per Codec Requirement (for h264) */
32 #define PADX 32
33 /* Padding for height as per Codec requirement (for h264)*/
34 #define PADY 24
36 struct decoder {
37 struct display *disp;
38 struct demux *demux;
39 struct buffer *framebuf;
41 Engine_Handle engine;
42 VIDDEC3_Handle codec;
43 VIDDEC3_Params *params;
44 VIDDEC3_DynamicParams *dynParams;
45 VIDDEC3_Status *status;
46 XDM2_BufDesc *inBufs;
47 XDM2_BufDesc *outBufs;
48 VIDDEC3_InArgs *inArgs;
49 VIDDEC3_OutArgs *outArgs;
51 char *input;
52 struct omap_bo *input_bo;
53 int input_sz, uv_offset;
55 suseconds_t tdisp;
57 };
59 /* When true, do not actually call VIDDEC3_process. For benchmarking. */
60 static int no_process = 0;
62 /* When true, loop at end of playback. */
63 static int loop = 0;
65 static void
66 usage(char *name)
67 {
68 MSG("Usage: %s [OPTIONS] INFILE", name);
69 MSG("Test of viddec3 decoder.");
70 MSG("");
71 MSG("viddec3test options:");
72 MSG("\t-h, --help: Print this help and exit.");
73 MSG("\t--loop\tRestart playback at end of stream.");
74 MSG("\t--no-process\tDo not actually call VIDDEC3_process method. For benchmarking.");
75 MSG("");
76 disp_usage();
77 }
79 static void
80 decoder_close(struct decoder *decoder)
81 {
82 if (decoder->codec) VIDDEC3_delete(decoder->codec);
83 if (decoder->engine) Engine_close(decoder->engine);
84 if (decoder->params) dce_free(decoder->params);
85 if (decoder->dynParams) dce_free(decoder->dynParams);
86 if (decoder->status) dce_free(decoder->status);
87 if (decoder->inBufs) free(decoder->inBufs);
88 if (decoder->outBufs) free(decoder->outBufs);
89 if (decoder->inArgs) dce_free(decoder->inArgs);
90 if (decoder->outArgs) dce_free(decoder->outArgs);
91 if (decoder->input_bo) omap_bo_del(decoder->input_bo);
92 if (decoder->demux) demux_deinit(decoder->demux);
93 if (decoder->disp) disp_close(decoder->disp);
95 free(decoder);
96 }
98 static struct decoder *
99 decoder_open(int argc, char **argv)
100 {
101 struct decoder *decoder;
102 char *infile = NULL;
103 int i, num_buffers;
104 int width, height, padded_width, padded_height;
105 Engine_Error ec;
106 XDAS_Int32 err;
108 decoder = calloc(1, sizeof(*decoder));
109 if (!decoder)
110 return NULL;
112 MSG("%p: Opening Display..", decoder);
113 decoder->disp = disp_open(argc, argv);
114 if (!decoder->disp)
115 goto usage;
117 /* loop thru args, find input file.. */
118 for (i = 1; i < argc; i++) {
119 int fd;
120 if (!argv[i]) {
121 continue;
122 }
123 fd = open(argv[i], 0);
124 if (fd > 0) {
125 infile = argv[i];
126 argv[i] = NULL;
127 close(fd);
128 break;
129 }
130 break;
131 }
133 if (check_args(argc, argv) || !infile)
134 goto usage;
136 MSG("%p: Opening Demuxer..", decoder);
137 decoder->demux = demux_init(infile, &width, &height);
138 if (!decoder->demux) {
139 ERROR("%p: could not open demuxer", decoder);
140 goto fail;
141 }
143 MSG("%p: infile=%s, width=%d, height=%d", decoder, infile, width, height);
145 /* calculate output buffer parameters: */
146 width = ALIGN2 (width, 4); /* round up to macroblocks */
147 height = ALIGN2 (height, 4); /* round up to macroblocks */
148 padded_width = ALIGN2 (width + (2*PADX), 7);
149 padded_height = height + 4*PADY;
150 num_buffers = MIN(16, 32768 / ((width/16) * (height/16))) + 3;
152 MSG("%p: padded_width=%d, padded_height=%d, num_buffers=%d",
153 decoder, padded_width, padded_height, num_buffers);
155 if (!decoder->disp->multiplanar) {
156 decoder->uv_offset = padded_width * padded_height;
157 MSG("%p: uv_offset=%d", decoder, decoder->uv_offset);
158 }
160 decoder->input_sz = width * height;
161 decoder->input_bo = omap_bo_new(decoder->disp->dev,
162 decoder->input_sz, OMAP_BO_WC);
163 decoder->input = omap_bo_map(decoder->input_bo);
165 decoder->framebuf = disp_get_fb(decoder->disp);
167 if (! disp_get_vid_buffers(decoder->disp, num_buffers,
168 FOURCC_STR("NV12"), padded_width, padded_height)) {
169 ERROR("%p: could not allocate buffers", decoder);
170 goto fail;
171 }
173 MSG("%p: Opening Engine..", decoder);
174 dce_set_fd(decoder->disp->fd);
175 decoder->engine = Engine_open("ivahd_vidsvr", NULL, &ec);
176 if (!decoder->engine) {
177 ERROR("%p: could not open engine", decoder);
178 goto fail;
179 }
181 decoder->params = dce_alloc(sizeof(IVIDDEC3_Params));
182 decoder->params->size = sizeof(IVIDDEC3_Params);
184 decoder->params->maxWidth = width;
185 decoder->params->maxHeight = height;
186 decoder->params->maxFrameRate = 30000;
187 decoder->params->maxBitRate = 10000000;
188 decoder->params->dataEndianness = XDM_BYTE;
189 decoder->params->forceChromaFormat= XDM_YUV_420SP;
190 decoder->params->operatingMode = IVIDEO_DECODE_ONLY;
191 decoder->params->displayDelay = IVIDDEC3_DISPLAY_DELAY_AUTO;
192 decoder->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
193 MSG("displayBufsMode: %d", decoder->params->displayBufsMode);
194 decoder->params->inputDataMode = IVIDEO_ENTIREFRAME;
195 decoder->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
196 decoder->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
197 decoder->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
198 decoder->params->numInputDataUnits= 0;
199 decoder->params->outputDataMode = IVIDEO_ENTIREFRAME;
200 decoder->params->numOutputDataUnits = 0;
201 decoder->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
203 decoder->codec = VIDDEC3_create(decoder->engine,
204 "ivahd_h264dec", decoder->params);
205 if (!decoder->codec) {
206 ERROR("%p: could not create codec", decoder);
207 goto fail;
208 }
210 decoder->dynParams = dce_alloc(sizeof(IVIDDEC3_DynamicParams));
211 decoder->dynParams->size = sizeof(IVIDDEC3_DynamicParams);
213 decoder->dynParams->decodeHeader = XDM_DECODE_AU;
215 /*Not Supported: Set default*/
216 decoder->dynParams->displayWidth = 0;
217 decoder->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
218 decoder->dynParams->newFrameFlag = XDAS_TRUE;
220 decoder->status = dce_alloc(sizeof(IVIDDEC3_Status));
221 decoder->status->size = sizeof(IVIDDEC3_Status);
223 err = VIDDEC3_control(decoder->codec, XDM_SETPARAMS,
224 decoder->dynParams, decoder->status);
225 if (err) {
226 ERROR("%p: fail: %d", decoder, err);
227 goto fail;
228 }
230 /* not entirely sure why we need to call this here.. just copying omx.. */
231 err = VIDDEC3_control(decoder->codec, XDM_GETBUFINFO,
232 decoder->dynParams, decoder->status);
233 if (err) {
234 ERROR("%p: fail: %d", decoder, err);
235 goto fail;
236 }
238 decoder->inBufs = calloc(1, sizeof(XDM2_BufDesc));
239 decoder->inBufs->numBufs = 1;
240 decoder->inBufs->descs[0].buf =
241 (XDAS_Int8 *)omap_bo_handle(decoder->input_bo);
242 decoder->inBufs->descs[0].bufSize.bytes = omap_bo_size(decoder->input_bo);
243 decoder->inBufs->descs[0].memType = XDM_MEMTYPE_BO;
245 decoder->outBufs = calloc(1, sizeof(XDM2_BufDesc));
246 decoder->outBufs->numBufs = 2;
247 decoder->outBufs->descs[0].memType = XDM_MEMTYPE_BO;
249 if (decoder->disp->multiplanar) {
250 decoder->outBufs->descs[1].memType = XDM_MEMTYPE_BO;
251 } else {
252 decoder->outBufs->descs[1].memType = XDM_MEMTYPE_BO_OFFSET;
253 }
255 decoder->inArgs = dce_alloc(sizeof(IVIDDEC3_InArgs));
256 decoder->inArgs->size = sizeof(IVIDDEC3_InArgs);
258 decoder->outArgs = dce_alloc(sizeof(IVIDDEC3_OutArgs));
259 decoder->outArgs->size = sizeof(IVIDDEC3_OutArgs);
261 decoder->tdisp = mark(NULL);
263 return decoder;
265 usage:
266 usage(argv[0]);
267 fail:
268 if (decoder)
269 decoder_close(decoder);
270 return NULL;
271 }
273 static int
274 decoder_process(struct decoder *decoder)
275 {
276 XDM2_BufDesc *inBufs = decoder->inBufs;
277 XDM2_BufDesc *outBufs = decoder->outBufs;
278 VIDDEC3_InArgs *inArgs = decoder->inArgs;
279 VIDDEC3_OutArgs *outArgs = decoder->outArgs;
280 struct buffer *buf;
281 int i, n;
283 buf = disp_get_vid_buffer(decoder->disp);
284 if (!buf) {
285 ERROR("%p: fail: out of buffers", decoder);
286 return -1;
287 }
289 /* demux; in loop mode, we can do two tries at the end of the stream. */
290 for (i = 0; i < 2; i++) {
291 n = demux_read(decoder->demux, decoder->input, decoder->input_sz);
292 if (n) {
293 inBufs->descs[0].bufSize.bytes = n;
294 inArgs->numBytes = n;
295 DBG("%p: push: %d bytes (%p)", decoder, n, buf);
296 } else {
297 /* end of input.. do we need to flush? */
298 MSG("%p: end of input", decoder);
300 /* In loop mode: rewind and retry once. */
301 if (loop && i == 0) {
302 int err = demux_rewind(decoder->demux);
303 if (err < 0) {
304 ERROR("%p: demux_rewind returned error: %d", decoder, err);
305 return -1;
306 }
307 MSG("%p: rewound.", decoder);
308 continue;
309 }
311 /* Not in loop or second try: end. */
312 inBufs->numBufs = 0;
313 inArgs->inputID = 0;
314 }
315 break;
316 }
318 inArgs->inputID = (XDAS_Int32)buf;
319 outBufs->descs[0].buf = (XDAS_Int8 *)omap_bo_handle(buf->bo[0]);
321 if (buf->multiplanar) {
322 outBufs->descs[0].bufSize.bytes = omap_bo_size(buf->bo[0]);
323 outBufs->descs[1].buf = (XDAS_Int8 *)omap_bo_handle(buf->bo[1]);
324 outBufs->descs[1].bufSize.bytes = omap_bo_size(buf->bo[1]);
325 } else {
326 outBufs->descs[0].bufSize.bytes = decoder->uv_offset;
327 outBufs->descs[1].buf = (XDAS_Int8 *)decoder->uv_offset;
328 outBufs->descs[1].bufSize.bytes = omap_bo_size(buf->bo[0]) - decoder->uv_offset;
329 }
331 if (no_process) {
332 /* Do not process. This is for benchmarking. We need to "fake"
333 * the outArgs. */
334 outArgs->outputID[0] = buf;
335 outArgs->outputID[1] = NULL;
336 outArgs->freeBufID[0] = buf;
337 outArgs->freeBufID[1] = NULL;
338 outArgs->outBufsInUseFlag = 0;
340 } else {
341 XDAS_Int32 err;
342 suseconds_t tproc;
343 tproc = mark(NULL);
344 err = VIDDEC3_process(decoder->codec, inBufs, outBufs, inArgs, outArgs);
345 DBG("%p: processed returned in: %ldus", decoder, (long int)mark(&tproc));
346 if (err) {
347 ERROR("%p: process returned error: %d", decoder, err);
348 ERROR("%p: extendedError: %08x", decoder, outArgs->extendedError);
349 if (XDM_ISFATALERROR(outArgs->extendedError))
350 return -1;
351 }
352 }
354 for (i = 0; outArgs->outputID[i]; i++) {
355 /* calculate offset to region of interest */
356 XDM_Rect *r = &(outArgs->displayBufs.bufDesc[0].activeFrameRegion);
358 /* get the output buffer and write it to file */
359 buf = (struct buffer *)outArgs->outputID[i];
360 DBG("%p: post buffer: %p %d,%d %d,%d", decoder, buf,
361 r->topLeft.x, r->topLeft.y,
362 r->bottomRight.x, r->bottomRight.y);
363 disp_post_vid_buffer(decoder->disp, buf,
364 r->topLeft.x, r->topLeft.y,
365 r->bottomRight.x - r->topLeft.x,
366 r->bottomRight.y - r->topLeft.y);
367 DBG("%p: display in: %ldus", decoder, (long int)mark(&decoder->tdisp));
368 }
370 for (i = 0; outArgs->freeBufID[i]; i++) {
371 buf = (struct buffer *)outArgs->freeBufID[i];
372 disp_put_vid_buffer(decoder->disp, buf);
373 }
375 if (outArgs->outBufsInUseFlag) {
376 MSG("%p: TODO... outBufsInUseFlag", decoder); // XXX
377 }
379 return (inBufs->numBufs > 0) ? 0 : -1;
380 }
382 int
383 main(int argc, char **argv)
384 {
385 struct decoder *decoders[8] = {};
386 int i, n, first = 0, ndecoders = 0;
388 for (i = 1; i < argc; i++) {
389 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
390 usage(argv[0]);
391 exit(0);
393 } else if (!strcmp(argv[i], "--loop")) {
394 loop = 1;
395 argv[i] = NULL;
397 } else if (!strcmp(argv[i], "--no-process")) {
398 no_process = 1;
399 argv[i] = NULL;
401 } else if (!strcmp(argv[i], "--")) {
402 argv[first] = argv[0];
403 decoders[ndecoders++] = decoder_open(i - first, &argv[first]);
404 first = i;
405 }
406 }
408 argv[first] = argv[0];
409 decoders[ndecoders++] = decoder_open(i - first, &argv[first]);
411 do {
412 for (i = 0, n = 0; i < ndecoders; i++) {
413 if (decoders[i]) {
414 int ret = decoder_process(decoders[i]);
415 if (ret) {
416 decoder_close(decoders[i]);
417 decoders[i] = NULL;
418 continue;
419 }
420 n++;
421 }
422 }
423 } while(n > 0);
425 return 0;
426 }