Add more checks to detect certain process call errors and exit
[glsdk/omapdrmtest.git] / viddec3test.c
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  */
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <libdce.h>
21 #include <xf86drm.h>
22 #include <omap_drm.h>
23 #include <omap_drmif.h>
25 #include <pthread.h>
27 #include "util.h"
28 #include "demux.h"
30 /* Padding for width as per Codec Requirement (for h264) */
31 #define PADX  32
32 /* Padding for height as per Codec requirement (for h264)*/
33 #define PADY  24
34 /* omap drm device handle */
35 struct omap_device *dev = NULL;
37 struct decoder {
38         struct display *disp;
39         struct demux *demux;
40         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;
50         char *input;
51         struct omap_bo *input_bo;
52         int input_sz, uv_offset;
53         int padded_width;
54         int padded_height;
55         int num_outBuf;
56         size_t *outBuf_fd;
57         suseconds_t tdisp;
58 };
60 /* When true, do not actually call VIDDEC3_process. For benchmarking. */
61 static int no_process = 0;
62 static int inloop = 0;
64 /* When true, loop at end of playback. */
65 static int loop = 0;
67 static void
68 usage(char *name)
69 {
70         MSG("Usage: %s [OPTIONS] INFILE", name);
71         MSG("Test of viddec3 decoder.");
72         MSG("");
73         MSG("viddec3test options:");
74         MSG("\t-h, --help: Print this help and exit.");
75         MSG("\t--loop\tRestart playback at end of stream.");
76         MSG("\t--inloop\tRestart playback at end of stream along with decoder reinitialization.");
77         MSG("\t--no-process\tDo not actually call VIDDEC3_process method. For benchmarking.");
78         MSG("");
79         disp_usage();
80 }
82 static void
83 decoder_close(struct decoder *decoder)
84 {
85         if(!decoder) return;
86         /* free output buffers allocated by display */
87         if(inloop < 2 && decoder->disp) disp_free_buffers(decoder->disp,decoder->num_outBuf);
89         if (decoder->status)         dce_free(decoder->status);
90         if (decoder->params)         dce_free(decoder->params);
91         if (decoder->dynParams)      dce_free(decoder->dynParams);
92         if (decoder->inBufs) {
93                 dce_buf_unlock(1, &(decoder->inBufs->descs[0].buf));
94                 close(decoder->inBufs->descs[0].buf);
95                 dce_free(decoder->inBufs);
96         }
97         if (decoder->outBufs)        dce_free(decoder->outBufs);
98         if (decoder->inArgs)         dce_free(decoder->inArgs);
99         if (decoder->outArgs)        dce_free(decoder->outArgs);
100         if (decoder->codec)          VIDDEC3_delete(decoder->codec);
101         if (decoder->engine)         Engine_close(decoder->engine);
102     if (decoder->input_bo)       omap_bo_del(decoder->input_bo);
103         if (decoder->outBuf_fd)      free(decoder->outBuf_fd);
104         if(inloop < 2) {
105                 if (dev)                             dce_deinit(dev);
106                 if (decoder->demux)          demux_deinit(decoder->demux);
107                 if (decoder->disp)           disp_close(decoder->disp);
108                 if(decoder) free(decoder);
109         }
112 static struct decoder *
113 decoder_open(int argc, char **argv)
115         static struct decoder *decoder = NULL;
116         char *infile = NULL;
117         int i;
118         static int width, height, padded_width, padded_height;
119         Engine_Error ec;
120         XDAS_Int32 err;
122     if(inloop < 2) {
123                 decoder = calloc(1, sizeof(*decoder));
124                 if (!decoder)
125                         return NULL;
127                 MSG("%p: Opening Display..", decoder);
128                 decoder->disp = disp_open(argc, argv);
129                 if (!decoder->disp)
130                         goto usage;
132             /* loop thru args, find input file.. */
133                 for (i = 1; i < argc; i++) {
134                         int fd;
135                             if (!argv[i]) {
136                                     continue;
137                         }
138                         fd = open(argv[i], 0);
139                         if (fd > 0) {
140                                 infile = argv[i];
141                                 argv[i] = NULL;
142                                 close(fd);
143                                 break;
144                         }
145                         break;
146                 }
147                 if (check_args(argc, argv) || !infile)
148                         goto usage;
149                 MSG("%p: Opening Demuxer..", decoder);
150                 decoder->demux = demux_init(infile, &width, &height);
151                 if (!decoder->demux) {
152                         ERROR("%p: could not open demuxer", decoder);
153                         goto fail;
154                 }
155                 MSG("%p: infile=%s, width=%d, height=%d", decoder, infile, width, height);
157                 /* calculate output buffer parameters: */
158                 width  = ALIGN2 (width, 4);        /* round up to macroblocks */
159                 height = ALIGN2 (height, 4);       /* round up to macroblocks */
160                 if (decoder->demux->cc->codec_id == CODEC_ID_MPEG2VIDEO) {
161                         padded_width = width;
162                         padded_height= height;
163                 }
164                 else {
165                         padded_width  = ALIGN2 (width + (2*PADX), 7);
166                         padded_height = height + 4*PADY;
167                 }
168                 decoder->num_outBuf   = MIN(16, 32768 / ((width/16) * (height/16))) + 3;
169                 decoder->padded_width = padded_width;
170                 decoder->padded_height = padded_height;
171                 MSG("%p: padded_width=%d, padded_height=%d, num_buffers=%d",
172                         decoder, padded_width, padded_height, decoder->num_outBuf);
173                 dce_set_fd(decoder->disp->fd);
174                 dev = dce_init();
175                 if(dev == NULL) {
176                         ERROR("%p: dce init failed", dev);
177                         goto fail;
178                 }
179                 decoder->framebuf = disp_get_fb(decoder->disp);
180                 if (! disp_get_vid_buffers(decoder->disp, decoder->num_outBuf,
181                                 FOURCC_STR("NV12"), decoder->padded_width, decoder->padded_height)) {
182                         ERROR("%p: could not allocate buffers", decoder);
183                         goto fail;
184                 }
185                 if(inloop) inloop = 2; /*Don't bother about looping if not asked to*/
186     }
188         if (!decoder->disp->multiplanar) {
189                 decoder->uv_offset = padded_width * padded_height;
190                 decoder->outBuf_fd = malloc(sizeof(int)*decoder->num_outBuf);
191                 MSG("%p: uv_offset=%d", decoder, decoder->uv_offset);
192         }
193         else{
194                 decoder->outBuf_fd = malloc(sizeof(int)*(decoder->num_outBuf*2));
195         }
197         decoder->input_sz = width * height;
198         decoder->input_bo = omap_bo_new(decoder->disp->dev,
199                         decoder->input_sz, OMAP_BO_WC);
200         decoder->input = omap_bo_map(decoder->input_bo);
204         MSG("%p: Opening Engine..", decoder);
205         decoder->engine = Engine_open("ivahd_vidsvr", NULL, &ec);
206         if (!decoder->engine) {
207                 ERROR("%p: could not open engine", decoder);
208                 goto fail;
209         }
211         decoder->params = dce_alloc(sizeof(IVIDDEC3_Params));
212         decoder->params->size = sizeof(IVIDDEC3_Params);
214         decoder->params->maxWidth         = width;
215         decoder->params->maxHeight        = height;
216         decoder->params->maxFrameRate     = 30000;
217         decoder->params->maxBitRate       = 10000000;
218         decoder->params->dataEndianness   = XDM_BYTE;
219         decoder->params->forceChromaFormat= XDM_YUV_420SP;
220         decoder->params->operatingMode    = IVIDEO_DECODE_ONLY;
221         decoder->params->displayDelay     = IVIDDEC3_DISPLAY_DELAY_AUTO;
222         decoder->params->displayBufsMode  = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
223         MSG("displayBufsMode: %d", decoder->params->displayBufsMode);
224         decoder->params->inputDataMode    = IVIDEO_ENTIREFRAME;
225         decoder->params->metadataType[0]  = IVIDEO_METADATAPLANE_NONE;
226         decoder->params->metadataType[1]  = IVIDEO_METADATAPLANE_NONE;
227         decoder->params->metadataType[2]  = IVIDEO_METADATAPLANE_NONE;
228         decoder->params->numInputDataUnits= 0;
229         decoder->params->outputDataMode   = IVIDEO_ENTIREFRAME;
230         decoder->params->numOutputDataUnits = 0;
231         decoder->params->errorInfoMode    = IVIDEO_ERRORINFO_OFF;
233         if (decoder->demux->cc->codec_id == CODEC_ID_MPEG2VIDEO) {
234             decoder->codec = VIDDEC3_create(decoder->engine,
235                                         "ivahd_mpeg2vdec", decoder->params);
236         }
237         else if (decoder->demux->cc->codec_id == CODEC_ID_H264) {
238             decoder->codec = VIDDEC3_create(decoder->engine,
239                                         "ivahd_h264dec", decoder->params);
240         }
241         else if (decoder->demux->cc->codec_id == CODEC_ID_MPEG4) {
242                 decoder->demux->first_in_buff = 1;
243                 decoder->codec = VIDDEC3_create(decoder->engine,
244                                         "ivahd_mpeg4dec", decoder->params);
245         }
247         if (!decoder->codec) {
248                 ERROR("%p: could not create codec", decoder);
249                 goto fail;
250         }
252         decoder->dynParams = dce_alloc(sizeof(IVIDDEC3_DynamicParams));
253         decoder->dynParams->size = sizeof(IVIDDEC3_DynamicParams);
255         decoder->dynParams->decodeHeader  = XDM_DECODE_AU;
257         /*Not Supported: Set default*/
258         decoder->dynParams->displayWidth  = 0;
259         decoder->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
260         decoder->dynParams->newFrameFlag  = XDAS_TRUE;
262         decoder->status = dce_alloc(sizeof(IVIDDEC3_Status));
263         decoder->status->size = sizeof(IVIDDEC3_Status);
265         err = VIDDEC3_control(decoder->codec, XDM_SETPARAMS,
266                         decoder->dynParams, decoder->status);
267         if (err) {
268                 ERROR("%p: fail: %d", decoder, err);
269                 goto fail;
270         }
272         /* not entirely sure why we need to call this here.. just copying omx.. */
273         err = VIDDEC3_control(decoder->codec, XDM_GETBUFINFO,
274                         decoder->dynParams, decoder->status);
275         if (err) {
276                 ERROR("%p: fail: %d", decoder, err);
277                 goto fail;
278         }
280         decoder->inBufs = dce_alloc(sizeof(XDM2_BufDesc));
281         decoder->inBufs->numBufs = 1;
282         decoder->inBufs->descs[0].buf = (XDAS_Int8 *)omap_bo_dmabuf(decoder->input_bo);
283         dce_buf_lock(1, &(decoder->inBufs->descs[0].buf));
284         decoder->inBufs->descs[0].bufSize.bytes = omap_bo_size(decoder->input_bo);
285         decoder->inBufs->descs[0].memType = XDM_MEMTYPE_RAW;
288         decoder->outBufs = dce_alloc(sizeof(XDM2_BufDesc));
289         decoder->outBufs->numBufs = 2;
290         decoder->outBufs->descs[0].memType = XDM_MEMTYPE_RAW;
291         decoder->outBufs->descs[1].memType = XDM_MEMTYPE_RAW;
293         decoder->inArgs = dce_alloc(sizeof(IVIDDEC3_InArgs));
294         decoder->inArgs->size = sizeof(IVIDDEC3_InArgs);
296         decoder->outArgs = dce_alloc(sizeof(IVIDDEC3_OutArgs));
297         decoder->outArgs->size = sizeof(IVIDDEC3_OutArgs);
299         decoder->tdisp = mark(NULL);
301         return decoder;
303 usage:
304         usage(argv[0]);
305 fail:
306         if(inloop) inloop = 1; /*Error case: delete everything*/
307         if (decoder)
308                 decoder_close(decoder);
309         return NULL;
312 static int
313 decoder_process(struct decoder *decoder)
315         XDM2_BufDesc *inBufs = decoder->inBufs;
316         XDM2_BufDesc *outBufs = decoder->outBufs;
317         VIDDEC3_InArgs *inArgs = decoder->inArgs;
318         VIDDEC3_OutArgs *outArgs = decoder->outArgs;
319         struct buffer *buf;
320         int freeBufCount =0;
321         int i, n;
322         XDAS_Int32 err;
323         int eof = 0; /* end of file flag */
325         /* demux; in loop mode, we can do two tries at the end of the stream. */
326         for (i = 0; i < 2; i++) {
327                 n = demux_read(decoder->demux, decoder->input, decoder->input_sz);
328                 if (n) {
329                         buf = disp_get_vid_buffer(decoder->disp);
330                         if (!buf) {
331                                 ERROR("%p: fail: out of buffers", decoder);
332                                 return -1;
333                         }
334                         inBufs->descs[0].bufSize.bytes = n;
335                         inArgs->numBytes = n;
336                         DBG("%p: push: %d bytes (%p)", decoder, n, buf);
337                 } else {
338                         /* end of input.. do we need to flush? */
339                         MSG("%p: end of input", decoder);
341                         /* In loop mode: rewind and retry once. */
342                         if (loop && i == 0) {
343                                 int err = demux_rewind(decoder->demux);
344                                 if (err < 0) {
345                                         ERROR("%p: demux_rewind returned error: %d", decoder, err);
346                                         return -1;
347                                 }
348                                 MSG("%p: rewound.", decoder);
349                                 if(!inloop) continue;
350                         }
351                         eof = 1; /* set the flag for end of file to 1 */
352                         /* Control call call with XDM_FLUSH command */
353                         err = VIDDEC3_control(decoder->codec, XDM_FLUSH,
354                                         decoder->dynParams, decoder->status);
355                         inBufs->numBufs = 0;
356                         outBufs->numBufs = 0;
357                         inArgs->inputID = 0;
358                 }
359                 break;
360         }
362         /*set the parameters if it is not the end of file */
363         if (!eof) {
364                 inArgs->inputID = (XDAS_Int32)buf;
365                 outBufs->descs[0].buf = buf->fd[0];
366                 outBufs->descs[1].buf = (buf->multiplanar) ?buf->fd[1]:(XDAS_Int8 *)((outBufs->descs[0].buf));
369                 if(buf->multiplanar){
370                         decoder->outBuf_fd[0] = buf->fd[0];
371                         decoder->outBuf_fd[1] = buf->fd[1];
372             dce_buf_lock(2,decoder->outBuf_fd);
373                 }
374                 else{
375                         decoder->outBuf_fd[0] = buf->fd[0];
376                         dce_buf_lock(1,decoder->outBuf_fd);
377                 }
378                 decoder->outBufs->descs[0].bufSize.bytes =decoder->padded_width*decoder->padded_height;
379                 decoder->outBufs->descs[1].bufSize.bytes = decoder->padded_width* (decoder->padded_height/2);
380         }
382         do {
383                 if (no_process) {
384                         /* Do not process. This is for benchmarking. We need to "fake"
385                          * the outArgs. */
386                         outArgs->outputID[0] = 0;
387                         outArgs->freeBufID[0] = 0;
388                         if(!eof) {
389                                 outArgs->outputID[0] = buf;
390                                 outArgs->freeBufID[0] = buf;
391                         }
392                         outArgs->outputID[1] = NULL;
393                         outArgs->freeBufID[1] = NULL;
394                         outArgs->outBufsInUseFlag = 0;
396                 } else {
397                         suseconds_t tproc;
398                         tproc = mark(NULL);
399                         err = VIDDEC3_process(decoder->codec, inBufs, outBufs, inArgs, outArgs);
400                         DBG("%p: processed returned in: %ldus", decoder, (long int)mark(&tproc));
401                         if (err) {
402                                 ERROR("%p: process returned error: %d", decoder, err);
403                                 ERROR("%p: extendedError: %08x", decoder, outArgs->extendedError);
404                                 if (XDM_ISFATALERROR(outArgs->extendedError) || ( err == DCE_EXDM_UNSUPPORTED ) || ( err == DCE_EIPC_CALL_FAIL ) || ( err == DCE_EINVALID_INPUT ))
405                                         return -1;
406                         }
407                 }
409                 for (i = 0; outArgs->outputID[i]; i++) {
410                         /* calculate offset to region of interest */
411                         XDM_Rect *r = &(outArgs->displayBufs.bufDesc[0].activeFrameRegion);
413                         /* get the output buffer and write it to file */
414                         buf = (struct buffer *)outArgs->outputID[i];
415                         if(!no_process)
416                                 disp_post_vid_buffer(decoder->disp, buf,
417                                         r->topLeft.x, r->topLeft.y,
418                                         r->bottomRight.x - r->topLeft.x,
419                                         r->bottomRight.y - r->topLeft.y);
420                 }
422                 for (i = 0; outArgs->freeBufID[i]; i++) {
423                         buf = (struct buffer *)outArgs->freeBufID[i];
424                         disp_put_vid_buffer(decoder->disp, buf);
426                         if(buf->multiplanar){
427                                 decoder->outBuf_fd[freeBufCount++] = buf->fd[0];
428                                 decoder->outBuf_fd[freeBufCount++] = buf->fd[1];
429                         }
430                         else{
431                                 decoder->outBuf_fd[freeBufCount++] = buf->fd[0];
432                                 DBG("FreeBufID: %p fd:%d\n", outArgs->freeBufID[i], buf->fd[0]);
433                         }
434                 }
436                 if(freeBufCount){
437             if(!eof)dce_buf_unlock(freeBufCount,decoder->outBuf_fd);
438                         freeBufCount =0;
439                 }
440                 if (outArgs->outBufsInUseFlag) {
441                         MSG("%p: TODO... outBufsInUseFlag", decoder); // XXX
442                 }
443         } while ((err == 0) && eof && !no_process);
445         return (inBufs->numBufs > 0) ? 0 : -1;
448 void *decode_stream(void *decoderHandle)
450         int ret = 0;
451         struct decoder *decoder = (struct decoder*)decoderHandle;
452     int n = 0;
453     if(!decoder) goto exit;
455     while((ret = decoder_process(decoder)) == 0);
456     if((ret != -1 && ret != 0) && inloop) inloop = 1; /*Assuming Good case. Otherwise logic gets messy*/
457     decoder_close(decoder);
458 exit:
459         return NULL;
462 int
463 main(int argc, char **argv)
465         struct decoder *decoders[8] = {};
466         int i, first = 0, ndecoders = 0;
468         for (i = 1; i < argc; i++) {
469                 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
470                         usage(argv[0]);
471                         exit(0);
473                 } else if (!strcmp(argv[i], "--loop")) {
474                         loop = 1;
475                         argv[i] = NULL;
477                 } else if (!strcmp(argv[i], "--no-process")) {
478                         no_process = 1;
479                         argv[i] = NULL;
481                 } else if (!strcmp(argv[i], "--inloop")) {
482                         inloop = 1; // this means inloop is detected
483                         DBG("detected inloop = %d\n", inloop);
484                         loop = 1; //we want rewind as well
485                         argv[i] = NULL;
486                 } else if (!strcmp(argv[i], "--")) {
487                         argv[first] = argv[0];
488                         decoders[ndecoders++] = decoder_open(i - first, &argv[first]);
489                         first = i;
490                 }
491         }
493         argv[first] = argv[0];
494         argc = i - first;
496         if(ndecoders) decoders[ndecoders++] = decoder_open(argc ,&argv[first]);
498         if (ndecoders > 1) {
499                 pthread_t threadIds[8];
501                 for (i = 0; i < ndecoders; i++) {
502                         if (decoders[i]) {
503                                 int ret = pthread_create(&threadIds[i], NULL,
504                                                 &decode_stream, decoders[i]);
505                                 if (ret != 0)
506                                         ERROR("%p: creation of pthread, error: %d",
507                                                         decoders[i], ret);
508                         }
509                 }
511                 for (i = 0; i < ndecoders; i++) {
512                         pthread_join(threadIds[i], NULL);
513                 }
514         }
515         else {
516                 int itr = 0;
517                 do {
518                         decoders[0] = decoder_open(argc, &argv[first]);
519                         decode_stream(decoders[0]);
520                         if (inloop) {
521                                 MSG("=================Iteration %d complete =============== %d\n", ++itr);
522                         }
523                 }while(inloop);
524         }
526         return 0;