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>
24 #include <signal.h>
25 #include <unistd.h>
27 #include <pthread.h>
29 #include "util.h"
30 #include "demux.h"
32 /* Padding for width as per Codec Requirement (for h264) */
33 #define PADX 32
34 /* Padding for height as per Codec requirement (for h264)*/
35 #define PADY 24
36 /* Padding for height as per Codec requirement (for MPEG4)*/
37 #define PADY_MPEG4 32
38 /* omap drm device handle */
39 struct omap_device *dev = NULL;
41 struct decoder {
42 struct display *disp;
43 struct demux *demux;
44 struct buffer *framebuf;
45 Engine_Handle engine;
46 VIDDEC3_Handle codec;
47 VIDDEC3_Params *params;
48 VIDDEC3_DynamicParams *dynParams;
49 VIDDEC3_Status *status;
50 XDM2_BufDesc *inBufs;
51 XDM2_BufDesc *outBufs;
52 VIDDEC3_InArgs *inArgs;
53 VIDDEC3_OutArgs *outArgs;
54 char *input;
55 struct omap_bo *input_bo;
56 int input_sz, uv_offset;
57 int padded_width;
58 int padded_height;
59 int num_outBuf;
60 size_t *outBuf_fd;
61 suseconds_t tdisp;
62 int id;
63 struct buffer *lastOutBuf;
64 int outBufsInUseFlag;
65 };
68 struct decoder *decoders[8] = {};
69 int ndecoders = 0;
71 /* When true, do not actually call VIDDEC3_process. For benchmarking. */
72 static int no_process = 0;
73 static int inloop = 0;
75 /* When true, loop at end of playback. */
76 static int loop = 0;
78 static void
79 usage(char *name)
80 {
81 MSG("Usage: %s [OPTIONS] INFILE", name);
82 MSG("Test of viddec3 decoder.");
83 MSG("");
84 MSG("viddec3test options:");
85 MSG("\t-h, --help: Print this help and exit.");
86 MSG("\t--loop\tRestart playback at end of stream.");
87 MSG("\t--inloop\tRestart playback at end of stream along with decoder reinitialization.");
88 MSG("\t--no-process\tDo not actually call VIDDEC3_process method. For benchmarking.");
89 MSG("");
90 disp_usage();
91 }
93 static void
94 decoder_close(struct decoder *decoder)
95 {
96 if(!decoder) return;
97 /* free output buffers allocated by display */
98 if(inloop < 2 && decoder->disp) disp_free_buffers(decoder->disp,decoder->num_outBuf);
100 if (decoder->status) dce_free(decoder->status);
101 if (decoder->params) dce_free(decoder->params);
102 if (decoder->dynParams) dce_free(decoder->dynParams);
103 if (decoder->inBufs) {
104 dce_buf_unlock(1, &(decoder->inBufs->descs[0].buf));
105 close(decoder->inBufs->descs[0].buf);
106 dce_free(decoder->inBufs);
107 }
108 if (decoder->outBufs) dce_free(decoder->outBufs);
109 if (decoder->inArgs) dce_free(decoder->inArgs);
110 if (decoder->outArgs) dce_free(decoder->outArgs);
111 if (decoder->codec) VIDDEC3_delete(decoder->codec);
112 if (decoder->engine) Engine_close(decoder->engine);
113 if (decoder->input_bo) omap_bo_del(decoder->input_bo);
114 if (decoder->outBuf_fd) free(decoder->outBuf_fd);
115 if(inloop < 2) {
116 if (dev) dce_deinit(dev);
117 if (decoder->demux) demux_deinit(decoder->demux);
118 if (decoder->disp) disp_close(decoder->disp);
119 if(decoder) {
120 free(decoder);
121 }
122 }
123 }
125 static struct decoder *
126 decoder_open(int argc, char **argv)
127 {
128 static struct decoder *decoder = NULL;
129 char *infile = NULL;
130 int i;
131 static int width, height, padded_width, padded_height;
132 Engine_Error ec;
133 XDAS_Int32 err;
135 if(inloop < 2) {
136 decoder = calloc(1, sizeof(*decoder));
137 if (!decoder)
138 return NULL;
140 MSG("%p: Opening Display..", decoder);
141 decoder->disp = disp_open(argc, argv);
142 if (!decoder->disp)
143 goto usage;
145 /* loop thru args, find input file.. */
146 for (i = 1; i < argc; i++) {
147 int fd;
148 if (!argv[i]) {
149 continue;
150 }
151 fd = open(argv[i], 0);
152 if (fd > 0) {
153 infile = argv[i];
154 argv[i] = NULL;
155 close(fd);
156 break;
157 }
158 break;
159 }
160 if (check_args(argc, argv) || !infile)
161 goto usage;
162 MSG("%p: Opening Demuxer..", decoder);
163 decoder->demux = demux_init(infile, &width, &height);
164 if (!decoder->demux) {
165 ERROR("%p: could not open demuxer", decoder);
166 goto fail;
167 }
168 MSG("%p: infile=%s, width=%d, height=%d", decoder, infile, width, height);
170 /* calculate output buffer parameters: */
171 width = ALIGN2 (width, 4); /* round up to macroblocks */
172 height = ALIGN2 (height, 4); /* round up to macroblocks */
173 if (decoder->demux->cc->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
174 padded_width = width;
175 padded_height= height;
176 } else if (decoder->demux->cc->codec_id == AV_CODEC_ID_MPEG4) {
177 padded_width = ALIGN2 (width + PADX, 7);
178 padded_height = height + PADY_MPEG4;
179 } else {
180 padded_width = ALIGN2 (width + (2*PADX), 7);
181 padded_height = height + 4*PADY;
182 }
183 decoder->num_outBuf = MIN(16, 32768 / ((width/16) * (height/16))) + 3;
184 decoder->padded_width = padded_width;
185 decoder->padded_height = padded_height;
186 MSG("%p: padded_width=%d, padded_height=%d, num_buffers=%d",
187 decoder, padded_width, padded_height, decoder->num_outBuf);
188 dce_set_fd(decoder->disp->fd);
189 dev = dce_init();
190 if(dev == NULL) {
191 ERROR("%p: dce init failed", dev);
192 goto fail;
193 }
194 decoder->framebuf = disp_get_fb(decoder->disp);
195 if (! disp_get_vid_buffers(decoder->disp, decoder->num_outBuf,
196 FOURCC_STR("NV12"), decoder->padded_width, decoder->padded_height)) {
197 ERROR("%p: could not allocate buffers", decoder);
198 goto fail;
199 }
200 if(inloop) inloop = 2; /*Don't bother about looping if not asked to*/
201 }
203 if (!decoder->disp->multiplanar) {
204 decoder->uv_offset = padded_width * padded_height;
205 decoder->outBuf_fd = malloc(sizeof(int)*decoder->num_outBuf);
206 MSG("%p: uv_offset=%d", decoder, decoder->uv_offset);
207 }
208 else{
209 decoder->outBuf_fd = malloc(sizeof(int)*(decoder->num_outBuf*2));
210 }
212 decoder->input_sz = width * height;
213 decoder->input_bo = omap_bo_new(decoder->disp->dev,
214 decoder->input_sz, OMAP_BO_WC);
215 decoder->input = omap_bo_map(decoder->input_bo);
219 MSG("%p: Opening Engine..", decoder);
220 decoder->engine = Engine_open("ivahd_vidsvr", NULL, &ec);
221 if (!decoder->engine) {
222 ERROR("%p: could not open engine", decoder);
223 goto fail;
224 }
226 decoder->params = dce_alloc(sizeof(IVIDDEC3_Params));
227 decoder->params->size = sizeof(IVIDDEC3_Params);
229 decoder->params->maxWidth = width;
230 decoder->params->maxHeight = height;
231 decoder->params->maxFrameRate = 30000;
232 decoder->params->maxBitRate = 10000000;
233 decoder->params->dataEndianness = XDM_BYTE;
234 decoder->params->forceChromaFormat= XDM_YUV_420SP;
235 decoder->params->operatingMode = IVIDEO_DECODE_ONLY;
236 decoder->params->displayDelay = IVIDDEC3_DISPLAY_DELAY_AUTO;
237 decoder->params->displayBufsMode = IVIDDEC3_DISPLAYBUFS_EMBEDDED;
238 MSG("displayBufsMode: %d", decoder->params->displayBufsMode);
239 decoder->params->inputDataMode = IVIDEO_ENTIREFRAME;
240 decoder->params->metadataType[0] = IVIDEO_METADATAPLANE_NONE;
241 decoder->params->metadataType[1] = IVIDEO_METADATAPLANE_NONE;
242 decoder->params->metadataType[2] = IVIDEO_METADATAPLANE_NONE;
243 decoder->params->numInputDataUnits= 0;
244 decoder->params->outputDataMode = IVIDEO_ENTIREFRAME;
245 decoder->params->numOutputDataUnits = 0;
246 decoder->params->errorInfoMode = IVIDEO_ERRORINFO_OFF;
248 if (decoder->demux->cc->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
249 decoder->codec = VIDDEC3_create(decoder->engine,
250 "ivahd_mpeg2vdec", decoder->params);
251 }
252 else if (decoder->demux->cc->codec_id == AV_CODEC_ID_H264) {
253 decoder->codec = VIDDEC3_create(decoder->engine,
254 "ivahd_h264dec", decoder->params);
255 }
256 else if (decoder->demux->cc->codec_id == AV_CODEC_ID_MPEG4) {
257 decoder->demux->first_in_buff = 1;
258 decoder->codec = VIDDEC3_create(decoder->engine,
259 "ivahd_mpeg4dec", decoder->params);
260 }
262 if (!decoder->codec) {
263 ERROR("%p: could not create codec", decoder);
264 goto fail;
265 }
267 decoder->dynParams = dce_alloc(sizeof(IVIDDEC3_DynamicParams));
268 decoder->dynParams->size = sizeof(IVIDDEC3_DynamicParams);
270 decoder->dynParams->decodeHeader = XDM_DECODE_AU;
272 /*Not Supported: Set default*/
273 decoder->dynParams->displayWidth = 0;
274 decoder->dynParams->frameSkipMode = IVIDEO_NO_SKIP;
275 decoder->dynParams->newFrameFlag = XDAS_TRUE;
277 decoder->status = dce_alloc(sizeof(IVIDDEC3_Status));
278 decoder->status->size = sizeof(IVIDDEC3_Status);
280 err = VIDDEC3_control(decoder->codec, XDM_SETPARAMS,
281 decoder->dynParams, decoder->status);
282 if (err) {
283 ERROR("%p: fail: %d", decoder, err);
284 goto fail;
285 }
287 /* not entirely sure why we need to call this here.. just copying omx.. */
288 err = VIDDEC3_control(decoder->codec, XDM_GETBUFINFO,
289 decoder->dynParams, decoder->status);
290 if (err) {
291 ERROR("%p: fail: %d", decoder, err);
292 goto fail;
293 }
295 decoder->inBufs = dce_alloc(sizeof(XDM2_BufDesc));
296 decoder->inBufs->numBufs = 1;
297 decoder->inBufs->descs[0].buf = (XDAS_Int8 *)omap_bo_dmabuf(decoder->input_bo);
298 dce_buf_lock(1, &(decoder->inBufs->descs[0].buf));
299 decoder->inBufs->descs[0].bufSize.bytes = omap_bo_size(decoder->input_bo);
300 decoder->inBufs->descs[0].memType = XDM_MEMTYPE_RAW;
303 decoder->outBufs = dce_alloc(sizeof(XDM2_BufDesc));
304 decoder->outBufs->numBufs = 2;
305 decoder->outBufs->descs[0].memType = XDM_MEMTYPE_RAW;
306 decoder->outBufs->descs[1].memType = XDM_MEMTYPE_RAW;
308 decoder->inArgs = dce_alloc(sizeof(IVIDDEC3_InArgs));
309 decoder->inArgs->size = sizeof(IVIDDEC3_InArgs);
311 decoder->outArgs = dce_alloc(sizeof(IVIDDEC3_OutArgs));
312 decoder->outArgs->size = sizeof(IVIDDEC3_OutArgs);
314 decoder->outBufsInUseFlag = XDAS_FALSE;
316 decoder->tdisp = mark(NULL);
318 return decoder;
320 usage:
321 usage(argv[0]);
322 fail:
323 if(inloop) inloop = 1; /*Error case: delete everything*/
324 if (decoder)
325 decoder_close(decoder);
326 return NULL;
327 }
329 static int
330 decoder_process(struct decoder *decoder)
331 {
332 XDM2_BufDesc *inBufs = decoder->inBufs;
333 XDM2_BufDesc *outBufs = decoder->outBufs;
334 VIDDEC3_InArgs *inArgs = decoder->inArgs;
335 VIDDEC3_OutArgs *outArgs = decoder->outArgs;
336 struct buffer *buf;
337 int freeBufCount =0;
338 int i, n;
339 XDAS_Int32 err;
340 int eof = 0; /* end of file flag */
342 /* demux; in loop mode, we can do two tries at the end of the stream. */
343 for (i = 0; i < 2; i++) {
344 n = demux_read(decoder->demux, decoder->input, decoder->input_sz);
345 if (n) {
346 if(decoder->outBufsInUseFlag == XDAS_FALSE){
347 buf = disp_get_vid_buffer(decoder->disp);
348 if (!buf) {
349 ERROR("%p: fail: out of buffers", decoder);
350 return -1;
351 }
352 decoder->lastOutBuf = buf;
354 inArgs->inputID = (XDAS_Int32)buf;
355 outBufs->descs[0].buf = buf->fd[0];
356 outBufs->descs[1].buf = (buf->multiplanar) ?buf->fd[1]:(XDAS_Int8 *)((outBufs->descs[0].buf));
358 if(buf->multiplanar){
359 decoder->outBuf_fd[0] = buf->fd[0];
360 decoder->outBuf_fd[1] = buf->fd[1];
361 dce_buf_lock(2,decoder->outBuf_fd);
362 }
363 else{
364 decoder->outBuf_fd[0] = buf->fd[0];
365 dce_buf_lock(1,decoder->outBuf_fd);
366 }
367 decoder->outBufs->descs[0].bufSize.bytes =decoder->padded_width*decoder->padded_height;
368 decoder->outBufs->descs[1].bufSize.bytes = decoder->padded_width* (decoder->padded_height/2);
369 }
370 else{
371 /*For second field decoding, send last output buffer
372 decoder uses buffer which was sent for first field
373 decoding. No need to pass the buffer. This decision is
374 taken based on outArgs->outBufsInUseFlag
375 */
376 buf = decoder->lastOutBuf;
377 }
378 inBufs->descs[0].bufSize.bytes = n;
379 inArgs->numBytes = n;
380 DBG("%p: push: %d bytes (%p)", decoder, n, buf);
381 } else {
382 /* end of input.. do we need to flush? */
383 MSG("%p: end of input", decoder);
385 /* In loop mode: rewind and retry once. */
386 if (loop && i == 0) {
387 int err = demux_rewind(decoder->demux);
388 if (err < 0) {
389 ERROR("%p: demux_rewind returned error: %d", decoder, err);
390 return -1;
391 }
392 MSG("%p: rewound.", decoder);
393 if(!inloop) continue;
394 }
395 eof = 1; /* set the flag for end of file to 1 */
396 /* Control call call with XDM_FLUSH command */
397 err = VIDDEC3_control(decoder->codec, XDM_FLUSH,
398 decoder->dynParams, decoder->status);
399 inBufs->numBufs = 0;
400 outBufs->numBufs = 0;
401 inArgs->inputID = 0;
402 }
403 break;
404 }
407 do {
408 if (no_process) {
409 /* Do not process. This is for benchmarking. We need to "fake"
410 * the outArgs. */
411 outArgs->outputID[0] = 0;
412 outArgs->freeBufID[0] = 0;
413 if(!eof) {
414 outArgs->outputID[0] = buf;
415 outArgs->freeBufID[0] = buf;
416 }
417 outArgs->outputID[1] = NULL;
418 outArgs->freeBufID[1] = NULL;
419 outArgs->outBufsInUseFlag = 0;
421 } else {
422 suseconds_t tproc;
423 tproc = mark(NULL);
424 err = VIDDEC3_process(decoder->codec, inBufs, outBufs, inArgs, outArgs);
425 DBG("%p: processed returned in: %ldus", decoder, (long int)mark(&tproc));
426 if (err) {
427 ERROR("%p: process returned error: %d", decoder, err);
428 ERROR("%p: extendedError: %08x", decoder, outArgs->extendedError);
429 if (XDM_ISFATALERROR(outArgs->extendedError) || ( err == DCE_EXDM_UNSUPPORTED ) || ( err == DCE_EIPC_CALL_FAIL ) || ( err == DCE_EINVALID_INPUT ))
430 return -1;
431 }
432 }
434 for (i = 0; outArgs->outputID[i]; i++) {
435 /* calculate offset to region of interest */
436 XDM_Rect *r = &(outArgs->displayBufs.bufDesc[0].activeFrameRegion);
438 /* get the output buffer and write it to file */
439 buf = (struct buffer *)outArgs->outputID[i];
440 if(!no_process)
441 disp_post_vid_buffer(decoder->disp, buf,
442 r->topLeft.x, r->topLeft.y,
443 r->bottomRight.x - r->topLeft.x,
444 r->bottomRight.y - r->topLeft.y);
445 }
447 for (i = 0; outArgs->freeBufID[i]; i++) {
448 buf = (struct buffer *)outArgs->freeBufID[i];
449 disp_put_vid_buffer(decoder->disp, buf);
451 if(buf->multiplanar){
452 decoder->outBuf_fd[freeBufCount++] = buf->fd[0];
453 decoder->outBuf_fd[freeBufCount++] = buf->fd[1];
454 }
455 else{
456 decoder->outBuf_fd[freeBufCount++] = buf->fd[0];
457 DBG("FreeBufID: %p fd:%d\n", outArgs->freeBufID[i], buf->fd[0]);
458 }
459 }
461 if(freeBufCount){
462 dce_buf_unlock(freeBufCount,decoder->outBuf_fd);
463 freeBufCount =0;
464 }
466 decoder->outBufsInUseFlag = outArgs->outBufsInUseFlag;
468 } while ((err == 0) && eof && !no_process);
470 return (inBufs->numBufs > 0) ? 0 : -1;
471 }
473 /* Returns 1 (true) if the mutex is unlocked, which is the
474 * thread's signal to terminate.
475 */
477 pthread_mutex_t mtx;
479 int needQuit()
480 {
481 switch(pthread_mutex_trylock(&mtx)) {
482 case 0: /* if we got the lock, unlock and return 1 (true) */
483 pthread_mutex_unlock(&mtx);
484 return 1;
485 case EBUSY: /* return 0 (false) if the mutex was locked */
486 return 0;
487 }
488 return 1;
489 }
491 void *decode_stream(void *decoderHandle)
492 {
493 int ret = 0;
494 struct decoder *decoder = (struct decoder*)decoderHandle;
495 int n = 0;
496 if(!decoder) goto exit;
498 while((ret = decoder_process(decoder)) == 0) {
499 if(needQuit()){
500 inloop = 1;
501 break;
502 }
503 }
504 if((ret != -1 && ret != 0) && inloop) inloop = 1; /*Assuming Good case. Otherwise logic gets messy*/
505 int i = decoder->id;
506 decoder_close(decoder);
507 decoders[i]=NULL;
508 exit:
509 return NULL;
510 }
513 static void sig_handler(int signo)
514 {
515 if (signo == SIGINT) {
516 int i=0;
517 pthread_mutex_unlock(&mtx);
518 sleep(1);
519 exit(0);
520 }
521 }
524 int
525 main(int argc, char **argv)
526 {
528 int i, first = 0;
530 struct sigaction sa;
532 sa.sa_handler = sig_handler;
533 sigemptyset(&sa.sa_mask);
534 sa.sa_flags = 0;
536 if (sigaction(SIGINT, &sa, NULL) == -1) {
537 ERROR ("\nDid not catch SIGINT\n");
538 }
539 signal(SIGINT,sig_handler);
541 pthread_mutex_init(&mtx,NULL);
542 pthread_mutex_lock(&mtx);
544 for (i = 1; i < argc; i++) {
545 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
546 usage(argv[0]);
547 exit(0);
549 } else if (!strcmp(argv[i], "--loop")) {
550 loop = 1;
551 argv[i] = NULL;
553 } else if (!strcmp(argv[i], "--no-process")) {
554 no_process = 1;
555 argv[i] = NULL;
557 } else if (!strcmp(argv[i], "--inloop")) {
558 inloop = 1; // this means inloop is detected
559 DBG("detected inloop = %d\n", inloop);
560 loop = 1; //we want rewind as well
561 argv[i] = NULL;
562 } else if (!strcmp(argv[i], "--")) {
563 argv[first] = argv[0];
564 decoders[ndecoders] = decoder_open(i - first, &argv[first]);
565 decoders[ndecoders]->id = ndecoders;
566 ndecoders++;
567 first = i;
568 }
569 }
571 argv[first] = argv[0];
572 argc = i - first;
574 if(ndecoders) {
575 decoders[ndecoders] = decoder_open(argc ,&argv[first]);
576 decoders[ndecoders]->id = ndecoders;
577 ndecoders++;
578 }
580 if (ndecoders > 1) {
581 pthread_t threadIds[8];
583 for (i = 0; i < ndecoders; i++) {
584 if (decoders[i]) {
585 int ret = pthread_create(&threadIds[i], NULL,
586 &decode_stream, decoders[i]);
587 if (ret != 0)
588 ERROR("%p: creation of pthread, error: %d",
589 decoders[i], ret);
590 }
591 }
593 for (i = 0; i < ndecoders; i++) {
594 pthread_join(threadIds[i], NULL);
595 }
596 }
597 else {
598 int itr = 0;
599 do {
600 decoders[0] = decoder_open(argc, &argv[first]);
601 decoders[0]->id = 0;
602 ndecoders++;
603 decode_stream(decoders[0]);
604 if (inloop) {
605 MSG("=================Iteration %d complete =============== %d\n", ++itr);
606 }
607 }while(inloop);
608 }
610 return 0;
611 }