author | Angela Stegmaier <angelabaker@ti.com> | |
Wed, 19 Jun 2019 15:14:57 +0000 (10:14 -0500) | ||
committer | Angela Stegmaier <angelabaker@ti.com> | |
Wed, 19 Jun 2019 15:42:38 +0000 (10:42 -0500) |
Signed-off-by: Angela Stegmaier <angelabaker@ti.com>
Signed-off-by: David Huang <d-huang@ti.com>
Signed-off-by: David Huang <d-huang@ti.com>
.gitignore | [new file with mode: 0644] | patch | blob |
Makefile.am | [new file with mode: 0644] | patch | blob |
README.txt | [new file with mode: 0644] | patch | blob |
autogen.sh | [new file with mode: 0644] | patch | blob |
configure.ac | [new file with mode: 0644] | patch | blob |
demux.c | [new file with mode: 0644] | patch | blob |
demux.h | [new file with mode: 0644] | patch | blob |
main.c | [new file with mode: 0644] | patch | blob |
diff --git a/.gitignore b/.gitignore
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+main.o
+tidec_decode
diff --git a/Makefile.am b/Makefile.am
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2018 Texas Instruments
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+bin_PROGRAMS = tidec_decode
+
+LDADD_COMMON = @DRM_LIBS@
+AM_CFLAGS = @DRM_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
+LDFLAGS = -lm -lavcodec -lavutil -lavformat -lswresample -lbz2 -lz -llzma -ltheoraenc -ltheoradec -logg
+
+tidec_decode_SOURCES = main.c demux.c
+tidec_decode_LDADD = $(LDADD_COMMON)
diff --git a/README.txt b/README.txt
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,57 @@
+=======
+PREREQ:
+=======
+* cross compiler is in PATH, eg:
+ export PATH=$PATH:/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin
+* Modify the Makefile on to point to the proper directory on your machine or pass from command line:
+ DRMHEADERS --> should point to location of the DRM headers, eg:
+ DRMHEADERS = $HOME/buildroot/output/host/usr/aarch64-buildroot-linux-gnu/sysroot/usr
+* Modify the Makefile on to point to location of the Kernel headers (linux source code path), eg:
+ KERNELHEADERS = $HOME/ti-linux-kernel
+======
+BUILD:
+======
+make
+
+=======
+OUTPUT:
+=======
+tidec_decode binary
+
+=============
+SETUP TARGET:
+=============
+* Copy the output binary to the target rootfs
+* The following modules must also be present on the target:
+ - videobuf2-common.ko
+ - videobuf2-memops.ko
+ - videobuf2-v4l2.ko
+ - videobuf2-dma-sg.ko
+ - videobuf2-dma-contig.ko
+ - v4l2-mem2mem.ko
+ - vxd-dec.ko
+
+========
+EXECUTE:
+========
+* On the target:
+ # insmod videobuf2-common.ko
+ # insmod videobuf2-memops.ko
+ # insmod videobuf2-v4l2.ko
+ # insmod videobuf2-dma-sg.ko
+ # insmod videobuf2-dma-contig.ko
+ # insmod v4l2-mem2mem.ko
+ # insmod vxd-dec.ko
+ # ./tidec_decode
+* On successful completion, you will see this trace:
+ "test app completed successfully"
+
+==============
+RETURN VALUES:
+==============
+- 0: test app completed successfully
+- EXIT_FAILURE: test app failed to complete successfully
+
+
+* Reference: credentiality2.blogspot.com/2010/04/v4l2-example.html
+
diff --git a/autogen.sh b/autogen.sh
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+autoreconf -v --install || exit 1
+cd $ORIGDIR || exit $?
+
+test -n "$NOCONFIGURE" && {
+ echo "skipping configure stage as requested."
+ echo "autogen.sh done."
+ exit 0
+}
+
+$srcdir/configure --enable-maintainer-mode "$@"
diff --git a/configure.ac b/configure.ac
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,98 @@
+#
+# Copyright (C) 2018 Texas Instruments
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Initialize Autoconf
+AC_PREREQ([2.60])
+AC_INIT([tidec_decode], [1.0.0], [https://www.ti.com], [tidec_decode])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADERS([config.h])
+
+# Initialize Automake
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+# Enable quiet compiles on automake 1.11.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+# Initialize libtool
+AC_PROG_LIBTOOL
+AC_PROG_CXX
+
+# Obtain compiler/linker options for depedencies
+PKG_CHECK_MODULES(DRM, libdrm libavformat libavcodec)
+
+dnl ===========================================================================
+dnl check compiler flags
+AC_DEFUN([LIBDRM_CC_TRY_FLAG], [
+ AC_MSG_CHECKING([whether $CC supports $1])
+
+ libdrm_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+
+ AC_COMPILE_IFELSE([ ], [libdrm_cc_flag=yes], [libdrm_cc_flag=no])
+ CFLAGS="$libdrm_save_CFLAGS"
+
+ if test "x$libdrm_cc_flag" = "xyes"; then
+ ifelse([$2], , :, [$2])
+ else
+ ifelse([$3], , :, [$3])
+ fi
+ AC_MSG_RESULT([$libdrm_cc_flag])
+])
+
+MAYBE_WARN="-Wall -Wextra \
+-Wsign-compare -Werror-implicit-function-declaration \
+-Wpointer-arith -Wwrite-strings -Wstrict-prototypes \
+-Wnested-externs \
+-Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \
+-Wdeclaration-after-statement -Wold-style-definition \
+-Wno-missing-field-initializers -Wno-unused-parameter \
+-Wno-attributes -Wno-long-long -Winline"
+
+# invalidate cached value if MAYBE_WARN has changed
+if test "x$libdrm_cv_warn_maybe" != "x$MAYBE_WARN"; then
+ unset libdrm_cv_warn_cflags
+fi
+AC_CACHE_CHECK([for supported warning flags], libdrm_cv_warn_cflags, [
+ echo
+ WARN_CFLAGS=""
+
+ # Some warning options are not supported by all versions of
+ # gcc, so test all desired options against the current
+ # compiler.
+ #
+ # Note that there are some order dependencies
+ # here. Specifically, an option that disables a warning will
+ # have no net effect if a later option then enables that
+ # warnings, (perhaps implicitly). So we put some grouped
+ # options (-Wall and -Wextra) up front and the -Wno options
+ # last.
+
+ for W in $MAYBE_WARN; do
+ LIBDRM_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+ done
+
+ libdrm_cv_warn_cflags=$WARN_CFLAGS
+ libdrm_cv_warn_maybe=$MAYBE_WARN
+
+ AC_MSG_CHECKING([which warning flags were supported])])
+WARN_CFLAGS="$libdrm_cv_warn_cflags"
+AC_SUBST(WARN_CFLAGS)
+
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/demux.c b/demux.c
--- /dev/null
+++ b/demux.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2018 Texas Instruments, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "demux.h"
+
+char mpeg4head[45] = { 0, 0, 0, 0 };
+int get_esds_offset(const char *filename, struct demux *demux);
+
+static AVFormatContext *
+open_file(const char *filename)
+{
+ AVFormatContext *afc = NULL;
+ int err = avformat_open_input(&afc, filename, NULL, NULL);
+
+ if (!err)
+ err = avformat_find_stream_info(afc, NULL);
+
+ if (err < 0) {
+ printf("%s: lavf error %d", filename, err);
+ exit(1);
+ }
+
+ av_dump_format(afc, 0, filename, 0);
+
+ return afc;
+}
+
+static AVStream *
+find_stream(AVFormatContext *afc)
+{
+ AVStream *st = NULL;
+ unsigned int i;
+
+ for (i = 0; i < afc->nb_streams; i++) {
+ if (afc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && !st)
+ st = afc->streams[i];
+ else
+ afc->streams[i]->discard = AVDISCARD_ALL;
+ }
+
+ return st;
+}
+
+static struct demux * open_stream(const char * filename, int *width,
+ int *height, int *bitdepth, enum AVPixelFormat *pix_fmt, enum AVCodecID *codec)
+{
+ AVFormatContext *afc = open_file(filename);
+ AVStream *st = find_stream(afc);
+ AVCodecContext *cc = st->codec;
+ AVBitStreamFilterContext *bsf = NULL;
+ struct demux *demux;
+
+ if ((cc->codec_id != AV_CODEC_ID_H264) &&
+ (cc->codec_id != AV_CODEC_ID_MPEG2VIDEO) &&
+ (cc->codec_id != AV_CODEC_ID_MPEG4) &&
+ (cc->codec_id != AV_CODEC_ID_HEVC)) {
+ printf("could not open '%s': unsupported codec %d", filename,
+ cc->codec_id);
+ return NULL;
+ }
+
+ if (cc->extradata && cc->extradata_size > 0 && cc->extradata[0] == 1) {
+ printf("initializing bitstream filter");
+ bsf = av_bitstream_filter_init("h264_mp4toannexb");
+ if (!bsf) {
+ printf("could not open '%s': failed to initialize bitstream filter",
+ filename);
+ return NULL;
+ }
+ }
+
+ *width = cc->width;
+ *height = cc->height;
+ *bitdepth = cc->bits_per_raw_sample;
+ *pix_fmt = cc->pix_fmt;
+ *codec = cc->codec_id;
+
+ demux = calloc(1, sizeof(*demux));
+
+ demux->afc = afc;
+ demux->cc = cc;
+ demux->st = st;
+ demux->bsf = bsf;
+ demux->first_in_buff = 0;
+
+ return demux;
+}
+
+struct demux * demux_init(const char * filename, int *width, int *height,
+ int *bitdepth, enum AVPixelFormat *pix_fmt, enum AVCodecID *codec)
+{
+ struct demux *demux;
+
+ av_register_all();
+ avcodec_register_all();
+ demux = open_stream(filename, width, height, bitdepth, pix_fmt, codec);
+ if ((demux != NULL) && (demux->cc->codec_id == AV_CODEC_ID_MPEG4)) {
+ if (get_esds_offset(filename, demux))
+ return NULL;
+ }
+
+ return demux;
+}
+
+int get_esds_offset(const char *filename, struct demux *demux)
+{
+ FILE *inputStream;
+ int i = 0;
+ unsigned char *buffer = NULL;
+ unsigned long fileLen;
+ int esds_index = 0;
+ inputStream = fopen(filename, "rb");
+ if (!inputStream) {
+ printf("Unable to open %s\n", filename);
+ return -1;
+ }
+
+ // Get the length
+ fseek(inputStream, 0, SEEK_END);
+ fileLen = ftell(inputStream);
+ fseek(inputStream, 0, SEEK_SET);
+ buffer = malloc(fileLen);
+ if (!buffer) {
+ printf("Memory error!\n");
+ fclose(inputStream);
+ return -1;
+ }
+
+ // File to buffer
+ fread(buffer, fileLen, 1, inputStream);
+ fclose(inputStream);
+ printf("Buffer starts: %p\n", &buffer[0]);
+ printf("Buffer ends: %p\n", &buffer[fileLen]);
+
+ //Determines offset of known_string
+ for (i = 0; i < fileLen; i++) {
+ if ((buffer[i] == 0x65) && (buffer[++i] == 0x73))
+ if ((buffer[++i] == 0x64) && (buffer[i + 1] == 0x73)) {
+ printf("data in buffer = %x %x %x %d \n",
+ buffer[i],
+ buffer[i + 1],
+ buffer[i + 2],
+ buffer[i + 27]);
+ esds_index = i + 27;
+ break;
+ }
+ }
+
+ demux->esds.length = buffer[esds_index];
+ demux->esds.data = malloc(demux->esds.length);
+ for (i = 1; i <= demux->esds.length; i++) {
+ demux->esds.data[i - 1] = buffer[esds_index + i];
+ printf(" index= %x \n", demux->esds.data[i - 1]);
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+int demux_read(struct demux *demux, unsigned char *input, int size)
+{
+ AVPacket pk = { };
+
+ while (!av_read_frame(demux->afc, &pk)) {
+ if (pk.stream_index == demux->st->index) {
+ uint8_t *buf;
+ int bufsize;
+
+ if (demux->bsf) {
+ int ret;
+ ret = av_bitstream_filter_filter(demux->bsf,
+ demux->cc,
+ NULL,
+ &buf,
+ &bufsize,
+ pk.data,
+ pk.size,
+ 0);
+ if (ret < 0) {
+ printf("bsf error: %d", ret);
+ return 0;
+ }
+ } else {
+ buf = pk.data;
+ bufsize = pk.size;
+ }
+
+ if (bufsize > size)
+ bufsize = size;
+
+ if (demux->first_in_buff == 1) {
+ memcpy(input, demux->esds.data, demux->esds.length);
+ memcpy(input + demux->esds.length, buf, bufsize);
+ demux->first_in_buff = 0;
+ bufsize = bufsize + demux->esds.length;
+ }
+ else {
+ memcpy(input, buf, bufsize);
+ }
+
+ if (demux->bsf)
+ av_free(buf);
+
+ av_free_packet(&pk);
+
+ return bufsize;
+ }
+ av_free_packet(&pk);
+ }
+
+ return 0;
+}
+
+int demux_rewind(struct demux *demux)
+{
+ return av_seek_frame(demux->afc, demux->st->index, 0, AVSEEK_FLAG_FRAME);
+}
+
+void demux_deinit(struct demux *demux)
+{
+ avformat_close_input(&demux->afc);
+ if (demux->bsf)
+ av_bitstream_filter_close(demux->bsf);
+ free(demux->esds.data);
+ free(demux);
+}
diff --git a/demux.h b/demux.h
--- /dev/null
+++ b/demux.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 Texas Instruments, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DEMUX_H__
+#define __DEMUX_H__
+
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/pixdesc.h>
+
+struct ESdescriptor {
+ int length;
+ unsigned char *data;
+};
+
+struct demux {
+ AVFormatContext *afc;
+ AVStream *st;
+ AVCodecContext *cc;
+ AVBitStreamFilterContext *bsf;
+ struct ESdescriptor esds;
+ /* Used for mpeg4 esds data copy */
+ int first_in_buff;
+};
+struct demux *demux_init(const char * filename, int *width, int *height,
+ int *bitdepth, enum AVPixelFormat *pix_fmt, enum AVCodecID *codec);
+int demux_read(struct demux *demux, unsigned char *input, int size);
+int demux_rewind(struct demux *demux);
+void demux_deinit(struct demux *demux);
+
+#endif /* __DEMUX_H__ */
diff --git a/main.c b/main.c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,1493 @@
+/*
+ * Texas Instruments IMG video driver test application.
+ *
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <poll.h>
+#include <dirent.h>
+
+#include <asm/types.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-controls.h>
+
+#include <drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "demux.h"
+
+/* HEVC aka H.265 */
+#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C')
+/* TI NV12 10-bit, two bytes per channel */
+#define V4L2_PIX_FMT_TI1210 v4l2_fourcc('T', 'I', '1', '2')
+/* TI YUV422 10-bit, two bytes per channel */
+#define V4L2_PIX_FMT_TI1610 v4l2_fourcc('T', 'I', '1', '6')
+
+static const struct AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
+ [AV_PIX_FMT_YUV420P] = {
+ .name = "yuv420p",
+ },
+ [AV_PIX_FMT_YUYV422] = {
+ .name = "yuyv422",
+ },
+ [AV_PIX_FMT_YVYU422] = {
+ .name = "yvyu422",
+ },
+ [AV_PIX_FMT_YUV422P] = {
+ .name = "yuv422p",
+ },
+ [AV_PIX_FMT_YUV444P] = {
+ .name = "yuv444p",
+ },
+ [AV_PIX_FMT_YUV410P] = {
+ .name = "yuv410p",
+ },
+ [AV_PIX_FMT_YUV411P] = {
+ .name = "yuv411p",
+ },
+ [AV_PIX_FMT_YUVJ411P] = {
+ .name = "yuvj411p",
+ },
+ [AV_PIX_FMT_YUVJ420P] = {
+ .name = "yuvj420p",
+ },
+ [AV_PIX_FMT_YUVJ422P] = {
+ .name = "yuvj422p",
+ },
+ [AV_PIX_FMT_YUVJ444P] = {
+ .name = "yuvj444p",
+ },
+ [AV_PIX_FMT_XVMC] = {
+ .name = "xvmc",
+ .flags = AV_PIX_FMT_FLAG_HWACCEL,
+ },
+ [AV_PIX_FMT_UYVY422] = {
+ .name = "uyvy422",
+ },
+ [AV_PIX_FMT_UYYVYY411] = {
+ .name = "uyyvyy411",
+ },
+ [AV_PIX_FMT_NV12] = {
+ .name = "nv12",
+ },
+ [AV_PIX_FMT_NV21] = {
+ .name = "nv21",
+ },
+ [AV_PIX_FMT_YUV440P] = {
+ .name = "yuv440p",
+ },
+ [AV_PIX_FMT_YUVJ440P] = {
+ .name = "yuvj440p",
+ },
+ [AV_PIX_FMT_YUV440P10LE] = {
+ .name = "yuv440p10le",
+ },
+ [AV_PIX_FMT_YUV440P10BE] = {
+ .name = "yuv440p10be",
+ },
+ [AV_PIX_FMT_YUV440P12LE] = {
+ .name = "yuv440p12le",
+ },
+ [AV_PIX_FMT_YUV440P12BE] = {
+ .name = "yuv440p12be",
+ },
+ [AV_PIX_FMT_YUVA420P] = {
+ .name = "yuva420p",
+ },
+ [AV_PIX_FMT_YUVA422P] = {
+ .name = "yuva422p",
+ },
+ [AV_PIX_FMT_YUVA444P] = {
+ .name = "yuva444p",
+ },
+ [AV_PIX_FMT_YUVA420P9BE] = {
+ .name = "yuva420p9be",
+ },
+ [AV_PIX_FMT_YUVA420P9LE] = {
+ .name = "yuva420p9le",
+ },
+ [AV_PIX_FMT_YUVA422P9BE] = {
+ .name = "yuva422p9be",
+ },
+ [AV_PIX_FMT_YUVA422P9LE] = {
+ .name = "yuva422p9le",
+ },
+ [AV_PIX_FMT_YUVA444P9BE] = {
+ .name = "yuva444p9be",
+ },
+ [AV_PIX_FMT_YUVA444P9LE] = {
+ .name = "yuva444p9le",
+ },
+ [AV_PIX_FMT_YUVA420P10BE] = {
+ .name = "yuva420p10be",
+ },
+ [AV_PIX_FMT_YUVA420P10LE] = {
+ .name = "yuva420p10le",
+ },
+ [AV_PIX_FMT_YUVA422P10BE] = {
+ .name = "yuva422p10be",
+ },
+ [AV_PIX_FMT_YUVA422P10LE] = {
+ .name = "yuva422p10le",
+ },
+ [AV_PIX_FMT_YUVA444P10BE] = {
+ .name = "yuva444p10be",
+ },
+ [AV_PIX_FMT_YUVA444P10LE] = {
+ .name = "yuva444p10le",
+ },
+ [AV_PIX_FMT_YUVA420P16BE] = {
+ .name = "yuva420p16be",
+ },
+ [AV_PIX_FMT_YUVA420P16LE] = {
+ .name = "yuva420p16le",
+ },
+ [AV_PIX_FMT_YUVA422P16BE] = {
+ .name = "yuva422p16be",
+ },
+ [AV_PIX_FMT_YUVA422P16LE] = {
+ .name = "yuva422p16le",
+ },
+ [AV_PIX_FMT_YUVA444P16BE] = {
+ .name = "yuva444p16be",
+ },
+ [AV_PIX_FMT_YUVA444P16LE] = {
+ .name = "yuva444p16le",
+ },
+ [AV_PIX_FMT_YUV420P9LE] = {
+ .name = "yuv420p9le",
+ },
+ [AV_PIX_FMT_YUV420P9BE] = {
+ .name = "yuv420p9be",
+ },
+ [AV_PIX_FMT_YUV420P10LE] = {
+ .name = "yuv420p10le",
+ },
+ [AV_PIX_FMT_YUV420P10BE] = {
+ .name = "yuv420p10be",
+ },
+ [AV_PIX_FMT_YUV420P12LE] = {
+ .name = "yuv420p12le",
+ },
+ [AV_PIX_FMT_YUV420P12BE] = {
+ .name = "yuv420p12be",
+ },
+ [AV_PIX_FMT_YUV420P14LE] = {
+ .name = "yuv420p14le",
+ },
+ [AV_PIX_FMT_YUV420P14BE] = {
+ .name = "yuv420p14be",
+ },
+ [AV_PIX_FMT_YUV420P16LE] = {
+ .name = "yuv420p16le",
+ },
+ [AV_PIX_FMT_YUV420P16BE] = {
+ .name = "yuv420p16be",
+ },
+ [AV_PIX_FMT_YUV422P9LE] = {
+ .name = "yuv422p9le",
+ },
+ [AV_PIX_FMT_YUV422P9BE] = {
+ .name = "yuv422p9be",
+ },
+ [AV_PIX_FMT_YUV422P10LE] = {
+ .name = "yuv422p10le",
+ },
+ [AV_PIX_FMT_YUV422P10BE] = {
+ .name = "yuv422p10be",
+ },
+ [AV_PIX_FMT_YUV422P12LE] = {
+ .name = "yuv422p12le",
+ },
+ [AV_PIX_FMT_YUV422P12BE] = {
+ .name = "yuv422p12be",
+ },
+ [AV_PIX_FMT_YUV422P14LE] = {
+ .name = "yuv422p14le",
+ },
+ [AV_PIX_FMT_YUV422P14BE] = {
+ .name = "yuv422p14be",
+ },
+ [AV_PIX_FMT_YUV422P16LE] = {
+ .name = "yuv422p16le",
+ },
+ [AV_PIX_FMT_YUV422P16BE] = {
+ .name = "yuv422p16be",
+ },
+ [AV_PIX_FMT_YUV444P16LE] = {
+ .name = "yuv444p16le",
+ },
+ [AV_PIX_FMT_YUV444P16BE] = {
+ .name = "yuv444p16be",
+ },
+ [AV_PIX_FMT_YUV444P10LE] = {
+ .name = "yuv444p10le",
+ },
+ [AV_PIX_FMT_YUV444P10BE] = {
+ .name = "yuv444p10be",
+ },
+ [AV_PIX_FMT_YUV444P9LE] = {
+ .name = "yuv444p9le",
+ },
+ [AV_PIX_FMT_YUV444P9BE] = {
+ .name = "yuv444p9be",
+ },
+ [AV_PIX_FMT_YUV444P12LE] = {
+ .name = "yuv444p12le",
+ },
+ [AV_PIX_FMT_YUV444P12BE] = {
+ .name = "yuv444p12be",
+ },
+ [AV_PIX_FMT_YUV444P14LE] = {
+ .name = "yuv444p14le",
+ },
+ [AV_PIX_FMT_YUV444P14BE] = {
+ .name = "yuv444p14be",
+ },
+ [AV_PIX_FMT_P010LE] = {
+ .name = "p010le",
+ },
+ [AV_PIX_FMT_P010BE] = {
+ .name = "p010be",
+ },
+ [AV_PIX_FMT_P016LE] = {
+ .name = "p016le",
+ },
+ [AV_PIX_FMT_P016BE] = {
+ .name = "p016be",
+ }
+};
+
+#define memzero(x) memset(&(x), 0, sizeof (x))
+#define MAX_TEST_FDS 10
+#define MAX_FRAMES 1000
+#define ALIGN(x,a) (((x) + (a) - 1L) & ~((a) - 1L))
+#define HW_ALIGN 64
+
+#define MAX_OUTBUFS 2
+
+#define DISPLAY_LAG 3
+
+#define MAX_CAPBUFS_H264 16
+#define MAX_CAPBUFS (MAX_CAPBUFS_H264 + DISPLAY_LAG)
+
+#define MIN(a, b) ((a < b) ? a : b)
+
+#define DEVICE_NAME "cardx"
+
+//#define DEBUG
+#ifdef DEBUG
+#define debug_printf(fmt, arg...) printf(fmt, ##arg);
+#else
+#define debug_printf(fmt, arg...)
+#endif
+
+/* Enable the below option for converting YUV422 decoded output to NV12 format*/
+#define CC_YUV422PLANAR_TO_NV12
+
+#define PROFILE
+#ifdef PROFILE
+#define perf_printf(fmt, arg...) printf(fmt, ##arg);
+#else
+ perf_printf(fmt, arg...)
+#endif
+
+static int drmfd1 = -1;
+#define FMT_NUM_PLANES 1 /* Used when creating V4l2 buffers for capture buffers */
+
+/*
+ * @bo_handle: drm buffer handle
+ * @dbuf_fd: dma buffer handle
+ * @mapped: Pointer to mmap'ed buffer memory
+ * @offset: offset of buffer
+ * @length: Buffer length for munmap
+ */
+struct buffer {
+ unsigned int bo_handle;
+ int dbuf_fd;
+ void *mapped;
+ int offset;
+ int length;
+};
+
+struct stream_context
+{
+ size_t frame_sizes[MAX_FRAMES];
+ int width;
+ int height;
+ int bitdepth;
+ int num_bytes_per_pix;
+ enum AVPixelFormat pix_fmt;
+ enum AVCodecID codec;
+};
+
+/*
+ *
+ * @fourcc: The V4L2 fourcc value
+ * @size_num: The numerator to multiply image size by
+ * @size_den: The denominator to divide image size by
+ */
+struct output_format
+{
+ uint32_t fourcc;
+ int size_num;
+ int size_den;
+};
+
+static void errno_exit(const char *s)
+{
+ printf("%s error %d, %s\n", s, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static int handle_outbuf(int fd, int index, int rdfd, struct buffer buff,
+ struct stream_context *str, int nframes, int sleep_time)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane buf_planes[1];
+ struct v4l2_decoder_cmd cmd = {};
+ int ret = 0;
+ int send_cmd = 0;
+ static int fs_ind = 0;
+
+ memset(buff.mapped, 0, (str->width * str->height));
+ read(rdfd, buff.mapped, str->frame_sizes[fs_ind]);
+
+ memzero(buf);
+ memzero(buf_planes[0]);
+
+ buf_planes[0].bytesused = str->frame_sizes[fs_ind];
+ buf_planes[0].m.mem_offset = buff.offset;
+ buf_planes[0].data_offset = 0;
+
+ fs_ind++;
+
+ /* Stop queueing OUTPUT buffers if no more content */
+ if (fs_ind > nframes)
+ return ret;
+
+ if (fs_ind == nframes) {
+ debug_printf("[fd%d] handle_outbuf sending DEC_CMD_STOP\n", fd);
+ cmd.cmd = V4L2_DEC_CMD_STOP;
+ ret = ioctl(fd, VIDIOC_TRY_DECODER_CMD, &cmd);
+ if (ret < 0) {
+ printf("[fd%d] handle_outbuf TRY_DECODER_CMD failed trying FLAG_LAST ret=%d err=%s\n",
+ fd, ret, strerror(errno));
+ buf.flags |= V4L2_BUF_FLAG_LAST;
+ } else {
+ send_cmd = 1;
+ }
+ }
+
+ buf.index = index;
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.m.planes = buf_planes;
+ buf.length = 1;
+
+ ret = ioctl(fd, VIDIOC_QBUF, &buf);
+ if (ret < 0)
+ printf("[fd%d] handle_outbuf QBUF failed ret=%d err=%s\n",
+ fd, ret, strerror(errno));
+ else
+ debug_printf("[fd%d] handle_outbuf QBUF success\n", fd);
+
+ if (send_cmd) {
+ debug_printf("[fd%d] handle_outbuf sending DEC_CMD_STOP after %d ms delay\n", fd, sleep_time / 1000);
+ usleep(sleep_time);
+ cmd.cmd = V4L2_DEC_CMD_STOP;
+ ret = ioctl(fd, VIDIOC_DECODER_CMD, &cmd);
+ if (ret < 0)
+ printf("[fd%d] handle_outbuf DECODER_CMD failed ret=%d err=%s\n",
+ fd, ret, strerror(errno));
+ }
+
+ return ret;
+}
+
+static int handle_capbuf(int fd, int wrfd, int index, struct buffer buff,
+ int save, struct stream_context *str, struct output_format fmt, int usedrmbuff)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane buf_planes[1];
+ int ret = 0;
+ int i;
+ int h = ALIGN(str->height, HW_ALIGN);
+ int s = ALIGN(str->width, HW_ALIGN) * str->num_bytes_per_pix;
+
+ if(save && (wrfd >= 0))
+ {
+ switch (fmt.fourcc) {
+ case (V4L2_PIX_FMT_NV12):
+ case (V4L2_PIX_FMT_TI1210):
+ /*
+ for(i=0; i<str->height; i++)
+ write(wrfd, buff.mapped + (i * s),
+ str->width);
+
+ for(i=0; i<(str->height/2); i++)
+ write(wrfd, buff.mapped + (h * s) +
+ (i * s), str->width);
+ */
+ for(i=0; i<str->height; i++)
+ write(wrfd, buff.mapped + (i * s),
+ (str->width *
+ str->num_bytes_per_pix));
+
+ for(i=0; i<(str->height/2); i++)
+ write(wrfd, buff.mapped + (h * s) +
+ (i * s),
+ (str->width *
+ str->num_bytes_per_pix));
+ break;
+ case (V4L2_PIX_FMT_NV16):
+ case (V4L2_PIX_FMT_TI1610):
+#ifdef CC_YUV422PLANAR_TO_NV12
+ for (i = 0; i < str->height; i++)
+ write(wrfd, buff.mapped + (i * s),
+ str->width * str->num_bytes_per_pix);
+ for (i = 0; i < str->height; i+=2)
+ write(wrfd, buff.mapped + (h * s) + (i * s), str->width * str->num_bytes_per_pix);
+
+#else
+ for (i = 0; i < str->height; i++)
+ write(wrfd, buff.mapped + (i * s),
+ 2* str->width * str->num_bytes_per_pix);
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+
+ memzero(buf);
+ memzero(buf_planes[0]);
+
+ buf_planes[0].m.fd = buff.dbuf_fd;
+ buf_planes[0].length = buff.length;
+
+ buf.index = index;
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (usedrmbuff == 0) {
+ /* Using v4l2 buffers for capture */
+ buf.memory = V4L2_MEMORY_MMAP;
+ } else {
+ /* Using drm buffers for capture */
+ buf.memory = V4L2_MEMORY_DMABUF;
+ }
+ buf.m.planes = buf_planes;
+ buf.length = 1;
+
+ ret = ioctl(fd, VIDIOC_QBUF, &buf);
+ if (ret < 0)
+ printf("[fd%d] handle_capbuf QBUF failed ret=%d err=%s\n",
+ fd, ret, strerror(errno));
+ else
+ debug_printf("[fd%d] handle_capbuf QBUF success\n", fd);
+
+ return ret;
+}
+
+static int mainloop(int fd, int rdfd, int wrfd,
+ struct stream_context *str, struct buffer outbufs[],
+ struct buffer capbufs[], int n_outbufs,
+ int n_capbufs, int nframes, int enable_prof,
+ struct output_format fmt, int sleep_time, int usedrmbuff)
+{
+ int type, i, ret = 0;
+ uint32_t flags = 0;
+ struct v4l2_buffer buf;
+ struct v4l2_plane buf_planes[2];
+ struct pollfd pfd;
+ struct timeval times;
+ long curr_time = 0;
+ static long prev_time = 0;
+ struct v4l2_event event;
+
+ debug_printf("[fd%d] MAINLOOP\n", fd);
+
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLOUT | POLLPRI;
+ pfd.revents = 0;
+
+ for (i = 0; i < n_outbufs; i++)
+ handle_outbuf(fd, i, rdfd, outbufs[i], str, nframes, 0);
+
+ for (i = 0; i < n_capbufs; i++)
+ handle_capbuf(fd, wrfd, i, capbufs[i], 0, str, fmt, usedrmbuff);
+
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ ret = ioctl(fd, VIDIOC_STREAMON, &type);
+ if (ret) {
+ printf("[fd%d] OUTPUT VIDIOC_STREAMON failed with ret %d\n",
+ fd, ret);
+ return ret;
+ }
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ ret = ioctl(fd, VIDIOC_STREAMON, &type);
+ if (ret)
+ printf("[fd%d] CAPTURE VIDIOC_STREAMON failed with ret %d\n",
+ fd, ret);
+
+ i = 0;
+ while (!(flags & V4L2_BUF_FLAG_LAST)) {
+ pfd.revents = 0;
+
+ /* Poll for any event for 100ms */
+ ret = poll(&pfd, 1, 100);
+ if (ret < 0) {
+ printf("poll had an error %d: %s\n",
+ errno, strerror(errno));
+ } else if (ret > 0) {
+ if (pfd.revents & POLLOUT) {
+ while (1) {
+ /* Check for OUTPUT buffer */
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.m.planes = buf_planes;
+ buf.length = 1;
+ ret = ioctl(fd, VIDIOC_DQBUF, &buf);
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ printf("[fd%d] OUTPUT VIDIOC_DQBUF failed: ret=%d errno=%d: %s\n",
+ fd, ret, errno,
+ strerror(errno));
+ } else {
+ debug_printf("[fd%d] OUTPUT EAGAIN\n", fd);
+ break;
+ }
+ } else {
+ handle_outbuf(fd, buf.index, rdfd,
+ outbufs[buf.index],
+ str, nframes,
+ sleep_time);
+ }
+ }
+ }
+ if (pfd.revents & POLLIN) {
+ while (1) {
+ /* Check for CAPTURE buffer */
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (usedrmbuff == 0) {
+ /* Using v4l2 bufffers for capture */
+ buf.memory = V4L2_MEMORY_MMAP;
+ } else {
+ /* Using drm buffers for capture */
+ buf.memory = V4L2_MEMORY_DMABUF;
+ }
+ buf.m.planes = buf_planes;
+ buf.length = 2;
+ ret = ioctl(fd, VIDIOC_DQBUF, &buf);
+ if (ret < 0) {
+ if (errno != EAGAIN) {
+ printf("[fd%d] CAPTURE VIDIOC_DQBUF failed: ret=%d errno=%d: %s\n",
+ fd, ret, errno,
+ strerror(errno));
+ } else {
+ debug_printf("[fd%d] CAPTURE EAGAIN\n", fd);
+ break;
+ }
+ } else {
+ if (enable_prof) {
+ gettimeofday(×, NULL);
+ curr_time =
+ (times.tv_sec * 1000000 + times.tv_usec);
+
+ perf_printf("Picture buffer dequeue time is %ld us\n",
+ (curr_time - prev_time));
+
+ prev_time = curr_time;
+ }
+ debug_printf("[fd%d] CAPTURE VIDIOC_DQBUF bytesused=%d\n",
+ fd, buf.m.planes[0].bytesused);
+ if (buf.m.planes[0].bytesused)
+ handle_capbuf(fd, wrfd,
+ buf.index,
+ capbufs[buf.index],
+ 1, str,
+ fmt, usedrmbuff);
+ flags = buf.flags;
+ debug_printf("[fd%d] CAPTURE VIDIOC_DQBUF buffer %d flags=%08x FLAG_LAST=%08x\n",
+ fd, buf.index,
+ flags,
+ V4L2_BUF_FLAG_LAST);
+ if (buf.flags & V4L2_BUF_FLAG_LAST)
+ break;
+ }
+ }
+ }
+ if (pfd.revents & POLLPRI) {
+ /* Check for events */
+ memzero(event);
+ ret = ioctl(fd, VIDIOC_DQEVENT, &event);
+ if (ret < 0) {
+ printf("[fd%d] VIDIOC_DQEVENT failed:: ret=%d errno=%d: %s\n",
+ fd, ret, errno,
+ strerror(errno));
+ } else if (event.type == V4L2_EVENT_EOS) {
+ debug_printf("[fd%d] GOT EVENT\n", fd);
+ } else {
+ printf("[fd%d] VIDIOC_DQEVENT got unexpected event %d\n",
+ fd, event.type);
+ }
+ }
+ }
+ }
+
+ debug_printf("Stream ended, calling STREAMOFF\n");
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
+ if (ret) {
+ printf("[fd%d] VIDIOC_STREAMOFF on OUTPUT failed with ret %d\n",
+ fd, ret);
+ return ret;
+ }
+
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
+ if (ret)
+ printf("[fd%d] VIDIOC_STREAMOFF on CAPTURE failed with ret %d\n",
+ fd, ret);
+
+ return ret;
+}
+
+static void uninit_device(int fd, struct buffer outbufs[],
+ struct buffer capbufs[], int *n_outbufs, int *n_capbufs, int usedrmbuff)
+{
+ struct drm_mode_destroy_dumb gem_destroy;
+ int i, ret = 0;
+ struct v4l2_event_subscription sub;
+
+ debug_printf("[fd%d] uninit_device\n", fd);
+
+ for (i = 0; i < *n_outbufs; i++) {
+ debug_printf("[fd%d] munmap outbuf %d mapped=0x%p length=%d\n",
+ fd, i, outbufs[i].mapped, outbufs[i].length);
+ ret = munmap(outbufs[i].mapped, outbufs[i].length);
+ if (ret) {
+ printf("[fd%d] munmap failed for outbuf %d: %d %s\n",
+ fd, i, errno, strerror(errno));
+ }
+ }
+
+/* For unint capbufs, check which was used, drm or v4l2*/
+ if (usedrmbuff == 0) {
+ /* Using v4l2 bufffers for capture */
+ for (i = 0; i < *n_capbufs; i++) {
+ debug_printf("[fd%d] munmap capbuf %d mapped=0x%p length=%d\n", fd, i, capbufs[i].mapped, capbufs[i].length);
+ ret = munmap(capbufs[i].mapped, capbufs[i].length);
+ if (ret) {
+ printf("[fd%d] munmap failed for capbuf %d: %d %s\n", fd, i, errno, strerror(errno));
+ }
+ }
+ } else {
+ /* Using drm bufffers for capture */
+ for (i = 0; i < *n_capbufs; i++) {
+ debug_printf("[fd%d] munmap capbuf %d mapped=0x%p length=%d\n", fd, i, capbufs[i].mapped, capbufs[i].length);
+ ret = munmap(capbufs[i].mapped, capbufs[i].length);
+ if (ret) {
+ printf("[fd%d] munmap failed for %d: %d %s\n", fd, i, errno, strerror(errno));
+ }
+ debug_printf("[fd%d] destroy gem capbuf %d handle=%d\n",
+ fd, i, capbufs[i].bo_handle);
+ memset(&gem_destroy, 0, sizeof(gem_destroy));
+ gem_destroy.handle = capbufs[i].bo_handle;
+ ret = ioctl(drmfd1, DRM_IOCTL_MODE_DESTROY_DUMB, &gem_destroy);
+ if (ret)
+ printf(" DRM_IOCTL_MODE_DESTROY_DUMB failed\n");
+ }
+ }
+
+ memset(&sub, 0, sizeof(sub));
+ sub.type = V4L2_EVENT_ALL;
+
+ debug_printf("[fd%d] Calling V4L2 IOCTL VIDIOC_SUBSCRIBE_EVENT\n", fd);
+ ret = ioctl(fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub);
+ if (ret != 0) {
+ printf("[fd%d] Failed to unsubscribe to events: err: %d %s\n",
+ fd, errno, strerror(errno));
+ }
+}
+
+static int create_drm_buffer(struct buffer *b,
+ unsigned int width, unsigned int height)
+{
+ struct drm_mode_create_dumb gem;
+ struct drm_mode_map_dumb gem_map;
+ struct drm_mode_destroy_dumb gem_destroy;
+ int ret;
+
+ memset(&gem, 0, sizeof gem);
+ gem.width = width;
+ gem.height = height;
+ gem.bpp = 8;
+
+ ret = ioctl(drmfd1, DRM_IOCTL_MODE_CREATE_DUMB, &gem);
+ if (ret) {
+ printf(" DRM_IOCTL_MODE_CREATE_DUMB failed\n");
+ return ret;
+ }
+
+ b->bo_handle = gem.handle;
+
+ struct drm_prime_handle prime;
+ memset(&prime, 0, sizeof prime);
+ prime.handle = b->bo_handle;
+
+ ret = ioctl(drmfd1, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
+ if (ret) {
+ printf(" DRM_IOCTL_PRIME_HANDLE_TO_FD failed\n");
+ goto fail_gem;
+ }
+ b->dbuf_fd = prime.fd;
+
+ memset(&gem_map, 0, sizeof(gem_map));
+ gem_map.handle = gem.handle;
+
+ ret = ioctl(drmfd1, DRM_IOCTL_MODE_MAP_DUMB, &gem_map);
+ if (ret) {
+ printf(" DRM_IOCTL_MODE_MAP_DUMB failed\n");
+ goto fail_gem;
+ }
+
+ b->mapped = mmap(NULL, (size_t)gem.size, PROT_READ,
+ MAP_SHARED, drmfd1, gem_map.offset);
+ if (MAP_FAILED == b->mapped) {
+ printf(" mmap failed %d: %s\n", errno, strerror(errno));
+ goto fail_gem;
+ }
+ b->offset = gem_map.offset;
+ b->length = gem.size;
+
+ return 0;
+fail_gem:
+ memset(&gem_destroy, 0, sizeof gem_destroy);
+ gem_destroy.handle = b->bo_handle;
+ ret = ioctl(drmfd1, DRM_IOCTL_MODE_DESTROY_DUMB, &gem_destroy);
+
+ return ret;
+}
+
+int stream_framelevel_parsing(struct stream_context *str, char *input_file, int max_frames)
+{
+ struct demux *demux;
+ int inp_buf_len = 0;
+ int inp_width, inp_height;
+ int frame_num = 0;
+ int frame_size = 0;
+ int bitdepth = 0;
+ enum AVPixelFormat pix_fmt;
+ enum AVCodecID codec;
+
+ /* Demux initialization */
+ demux = demux_init(input_file, &inp_width, &inp_height, &bitdepth, &pix_fmt, &codec);
+ if (!demux)
+ {
+ printf("%s: could not open demuxer\n", __FUNCTION__);
+ return -1;
+ }
+
+ debug_printf("\n%s: bitdepth: %d\n",__FUNCTION__, bitdepth);
+ debug_printf("%s: pix_fmt: %d\n",__FUNCTION__, pix_fmt);
+ debug_printf("%s: pix_fmt_string: %s\n",__FUNCTION__, pix_fmt != AV_PIX_FMT_NONE ?
+ av_pix_fmt_descriptors[pix_fmt].name : "unknown");
+ debug_printf("\n%s: codec: %d\n",__FUNCTION__, codec);
+ if(codec == AV_CODEC_ID_H264)
+ debug_printf("\n%s: codec: H264",__FUNCTION__);
+ if(codec == AV_CODEC_ID_HEVC)
+ debug_printf("\n%s: codec format: HEVC\n",__FUNCTION__);
+
+ inp_buf_len = (inp_width * inp_height);
+
+ unsigned char *inp_buf = malloc(inp_buf_len);
+ if(inp_buf == NULL)
+ {
+ printf("%s: Memory allocation failed for inp_buf\n",
+ __FUNCTION__);
+ return -1;
+ }
+
+ debug_printf("%s: demuxer is initialized, width=%d, height=%d\n",
+ __FUNCTION__, inp_width, inp_height);
+ str->width = inp_width;
+ str->height = inp_height;
+ str->bitdepth = bitdepth;
+ str->pix_fmt = pix_fmt;
+ str->codec = codec;
+
+ if(bitdepth == 8 || pix_fmt == AV_PIX_FMT_YUV420P ||
+ pix_fmt == AV_PIX_FMT_YUV422P)
+ str->num_bytes_per_pix = 1;
+ if(bitdepth == 10 || pix_fmt == AV_PIX_FMT_YUV420P10LE ||
+ pix_fmt == AV_PIX_FMT_YUV422P10LE)
+ str->num_bytes_per_pix = 2;
+
+ /*read the frame size and store into array*/
+ while(1)
+ {
+ frame_size = demux_read(demux, inp_buf, inp_buf_len);
+ if (frame_size)
+ {
+ debug_printf("frame %d frame size in demux %d\n",
+ frame_num, frame_size);
+ str->frame_sizes[frame_num] = frame_size;
+ frame_num++;
+ }
+ else
+ {
+ /*if the input file contains less number frames*/
+ break;
+ }
+ if (max_frames > 0 && frame_num >= max_frames)
+ break;
+ }
+
+ /* Demux de-initialization */
+ demux_deinit(demux);
+ free(inp_buf);
+
+ debug_printf("H264 frame sizes update is done\n");
+ return frame_num;
+}
+
+static int init_device(int fd, int rdfd,
+ struct stream_context *str, struct buffer outbufs[],
+ struct buffer capbufs[], int *n_outbufs, int *n_capbufs,
+ struct output_format format, int usedrmbuff)
+{
+ struct v4l2_format fmt;
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_buffer buffer;
+ struct v4l2_plane buf_planes[1];
+ int ret = 0;
+ int i, j;
+ /*for v4l2 based capture buffers*/
+ struct v4l2_buffer buffer_cap;
+ struct v4l2_plane buf_planes_cap[FMT_NUM_PLANES];
+
+ debug_printf("[fd%d] init_device\n", fd);
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ fmt.fmt.pix_mp.width = str->width;
+ fmt.fmt.pix_mp.height = str->height;
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = (str->width * str->height);
+ fmt.fmt.pix_mp.num_planes = 1;
+
+ if(str->codec == AV_CODEC_ID_H264)
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ if(str->codec == AV_CODEC_ID_HEVC)
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_HEVC;
+
+ ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
+ if (ret != 0) {
+ printf("[fd%d] VIDIOC_S_FMT errorno %d, %s\n",
+ fd, errno, strerror(errno));
+ return ret;
+ }
+
+ debug_printf("[fd%d] After S_FMT on OUTPUT\n", fd);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.pixelformat = format.fourcc;
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage =
+ ((fmt.fmt.pix_mp.width * fmt.fmt.pix_mp.height) *
+ format.size_num) * (str->num_bytes_per_pix) / format.size_den;
+ fmt.fmt.pix_mp.width = ALIGN(str->width, HW_ALIGN);
+ fmt.fmt.pix_mp.height = ALIGN(str->height, HW_ALIGN);
+ fmt.fmt.pix_mp.num_planes = 1;
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(str->width, HW_ALIGN) * str->num_bytes_per_pix;
+
+ ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
+ if (ret) {
+ printf("[fd%d] VIDIOC_S_FMT errorno %d, %s\n",
+ fd, errno, strerror(errno));
+ return ret;
+ }
+
+ debug_printf("[fd%d] After S_FMT on CAPTURE\n", fd);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
+ if (ret) {
+ printf("[fd%d] VIDIOC_G_FMT errorno %d, %s\n",
+ fd, errno, strerror(errno));
+ return ret;
+ }
+
+ debug_printf("[fd%d] After G_FMT on OUTPUT\n", fd);
+ debug_printf("[fd%d] After G_FMT fmt.fmt.pix_mp.pixelformat = %c%c%c%c numplanes %d\n",
+ fd, fmt.fmt.pix_mp.pixelformat & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >> 8) & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >>16) & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >> 24) & 0xff,
+ fmt.fmt.pix_mp.num_planes);
+
+ debug_printf("[fd%d] fmt.fmt.pix_mp.width %d fmt.fmt.pix_mp.height %d"
+ " sizeimage %d bytesperline %d\n",
+ fd, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
+ if (ret) {
+ printf("[fd%d] VIDIOC_G_FMT errorno %d, %s\n",
+ fd, errno, strerror(errno));
+ return ret;
+ }
+
+ debug_printf("[fd%d] After G_FMT on CAPTURE\n", fd);
+ debug_printf("[fd%d] After G_FMT fmt.fmt.pix_mp.pixelformat = %c%c%c%c numplanes %d\n",
+ fd, fmt.fmt.pix_mp.pixelformat & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >> 8) & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >>16) & 0xff,
+ (fmt.fmt.pix_mp.pixelformat >> 24) & 0xff,
+ fmt.fmt.pix_mp.num_planes);
+
+ debug_printf("[fd%d] fmt.fmt.pix_mp.width %d fmt.fmt.pix_mp.height %d"
+ " sizeimage %d bytesperline %d\n",
+ fd, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ fmt.fmt.pix_mp.plane_fmt[1].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[1].bytesperline);
+
+ /* Setup Decoder OUTPUT (SRC buffer) through VIDIOC_REQBUFS */
+ debug_printf("[fd%d] Setup decoding OUTPUT with VIDIOC_REQBUFS buffer size %u\n",
+ fd, fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+
+ memzero(reqbuf);
+ reqbuf.count = *n_outbufs;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ printf("[fd%d] Err REQBUFS failed on OUTPUT queue ret %d errno %d\n",
+ fd, ret, errno);
+ return ret;
+ }
+ debug_printf("[fd%d] After VIDIOC_REQBUFS getting buf_cnt %d\n",
+ fd, reqbuf.count);
+ *n_outbufs = reqbuf.count;
+
+ /* QUERYBUF on OUTPUT - memory of V4L2_MEMORY_MMAP */
+ for (i = 0; i < *n_outbufs; i++) {
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buffer.index = i;
+ buffer.m.planes = buf_planes;
+ buffer.length = 1;
+
+ ret = ioctl(fd, VIDIOC_QUERYBUF, &buffer);
+ if (ret < 0) {
+ printf("[fd%d] CANNOT QUERY BUFFERS ret = %d\n",
+ fd, ret);
+ return -1;
+ }
+
+ debug_printf("[fd%d] query buf, buffer %p plane 0 = %d buffer.length %d buffer.data_offset %d buffer.mem_offset %d\n",
+ fd, &buffer, buffer.m.planes[0].length,
+ buffer.length, buffer.m.planes[0].data_offset,
+ buffer.m.planes[0].m.mem_offset);
+
+ outbufs[i].mapped = mmap(NULL, buffer.m.planes[0].length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, buffer.m.planes[0].m.mem_offset);
+ outbufs[i].offset = buffer.m.planes[0].m.mem_offset;
+ outbufs[i].length = buffer.m.planes[0].length;
+
+ debug_printf("[fd%d] After mmap -> outbufs[%d].mapped = 0x%p\n",
+ fd, i, outbufs[i].mapped);
+
+ if (MAP_FAILED == outbufs[i].mapped) {
+ while (i >= 0) {
+ /* Unmap all previous buffers */
+ i--;
+ munmap(outbufs[i].mapped,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+ outbufs[i].mapped = NULL;
+ }
+ printf("[fd%d] Cant mmap buffers Y", fd);
+ return -1;
+ }
+ }
+
+ /* Setup Decoder CAPTURE (DST buffer) through VIDIOC_REQBUFS */
+
+ if (usedrmbuff == 0) {
+ /* Setup Decoder CAPTURE (DST buffer) through VIDIOC_REQBUFS */
+ debug_printf("[fd%d] Setup decoding CAPTURE with VIDIOC_REQBUFS\n", fd);
+ debug_printf("[fd%d] buffer(y) size %u buffer(uv) size %u\n",
+ fd, fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[1].sizeimage);
+
+ memzero(reqbuf);
+ reqbuf.count = *n_capbufs;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ printf("[fd%d] Err REQBUFS failed on CAPTURE queue ret %d errno %d\n",
+ fd, ret, errno);
+ return ret;
+ }
+ debug_printf("[fd%d] After VIDIOC_REQBUFS getting buf_cnt %d\n",
+ fd, reqbuf.count);
+ *n_capbufs = reqbuf.count;
+ /*Creating Capbuffers with v4l2 BUFFERS*/
+ /* QUERYBUF on Capture - memory of V4L2_MEMORY_MMAP */
+ for (j = 0; j < *n_capbufs; j++) {
+ memset(&buffer_cap, 0, sizeof(buffer_cap));
+ buffer_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buffer_cap.index = j;
+ buffer_cap.m.planes = buf_planes_cap;
+ buffer_cap.length = FMT_NUM_PLANES;
+ buffer_cap.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(fd, VIDIOC_QUERYBUF, &buffer_cap);
+ if (ret < 0) {
+ printf("[fd%d] CANNOT QUERY BUFFERS for Capture ret = %d\n",
+ fd, ret);
+ return -1;
+ }
+ debug_printf("[fd%d] query buf, buffer %p plane 0 = %d buffer_cap.length %d buffer_cap.data_offset %d buffer_cap.mem_offset %d\n",
+ fd, &buffer_cap,
+ buffer_cap.m.planes[0].length,
+ buffer_cap.length,
+ buffer_cap.m.planes[0].data_offset,
+ buffer_cap.m.planes[0].m.mem_offset);
+
+ capbufs[j].mapped = mmap(NULL, buffer_cap.m.planes[0].length,
+ PROT_READ, MAP_SHARED,
+ fd, buffer_cap.m.planes[0].m.mem_offset);
+ capbufs[j].offset = buffer_cap.m.planes[0].m.mem_offset;
+ capbufs[j].length = buffer_cap.m.planes[0].length;
+
+ debug_printf("[fd%d] After mmap -> capbufs[%d].mapped = 0x%p\n",
+ fd, j, capbufs[j].mapped);
+
+ if (MAP_FAILED == capbufs[j].mapped) {
+ while (j >= 0) {
+ /* Unmap all previous buffers */
+ j--;
+ munmap(capbufs[j].mapped,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage);
+ capbufs[j].mapped = NULL;
+ }
+ printf("[fd%d] Cant mmap capture buffers Y\n", fd);
+ return -1;
+ }
+ }
+ } else {
+ debug_printf("[fd%d] Setup decoding CAPTURE with VIDIOC_REQBUFS\n", fd);
+ debug_printf("[fd%d] buffer(y) size %u buffer(uv) size %u\n",
+ fd, fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[1].sizeimage);
+
+ memzero(reqbuf);
+ reqbuf.count = *n_capbufs;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_DMABUF;
+
+ ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ printf("[fd%d] Err REQBUFS failed on CAPTURE queue ret %d errno %d\n",
+ fd, ret, errno);
+ return ret;
+ }
+ debug_printf("[fd%d] After VIDIOC_REQBUFS getting buf_cnt %d\n",
+ fd, reqbuf.count);
+ *n_capbufs = reqbuf.count;
+
+ /* Create DRM buffers */
+ for (i = 0; i < *n_capbufs; i++) {
+ ret = create_drm_buffer(&capbufs[i],
+ ALIGN(fmt.fmt.pix_mp.width, HW_ALIGN),
+ ((str->num_bytes_per_pix *
+ fmt.fmt.pix_mp.height * format.size_num) /
+ format.size_den));
+ if (ret) {
+ printf("[fd%d] failed to create drm buffers\n", fd);
+ return -1;
+ }
+ debug_printf("[fd%d] Create_DRM_BUFFERS drm_y_buffer[%d].dbuf_fd 0x%x\n",
+ fd, i, capbufs[i].dbuf_fd);
+ }
+ }
+ return ret;
+}
+
+static void close_device(int fd)
+{
+ debug_printf("[fd%d] close_device\n", fd);
+ if (-1 == close(fd))
+ errno_exit ("close");
+ fd = -1;
+}
+
+static int open_device(char *dev_name)
+{
+ struct v4l2_capability cap;
+ struct v4l2_fmtdesc argp;
+ int ret = 0;
+ int fd = -1;
+ struct v4l2_event_subscription sub;
+
+ fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (-1 == fd) {
+ printf("Cannot open '%s': %d, %s\n",
+ dev_name, errno, strerror(errno));
+ return -1;
+ }
+
+ ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+ if (ret != 0) {
+ printf("Failed to verify capabilities\n");
+ return -1;
+ }
+
+ debug_printf("[fd%d] Info (%s): driver\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n",
+ fd, dev_name, cap.driver, cap.bus_info, cap.card, fd);
+
+ debug_printf("[fd%d] Info (%s): capabilities\"0x%x\" device_caps=\"0x%x\" \n",
+ fd, dev_name, cap.capabilities, cap.device_caps);
+
+ memset(&argp, 0, sizeof(argp));
+ argp.index = 0;
+ argp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ debug_printf("[fd%d] Calling V4L2 IOCTL VIDIOC_ENUM_FMT on CAPTURE\n",
+ fd);
+ while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &argp)) == 0) {
+ debug_printf("[fd%d] argp.index = %d, {pixelformat = %c%c%c%c}, description = '%s'\n",
+ fd, argp.index, argp.pixelformat & 0xff,
+ (argp.pixelformat >> 8) & 0xff,
+ (argp.pixelformat >>16) & 0xff,
+ (argp.pixelformat >> 24) & 0xff,
+ argp.description);
+ argp.index ++;
+ }
+
+ argp.index = 0;
+ argp.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ debug_printf("[fd%d] Calling V4L2 IOCTL VIDIOC_ENUM_FMT on OUTPUT\n",
+ fd);
+ while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &argp)) == 0) {
+ debug_printf("[fd%d] argp.index = %d, {pixelformat = %c%c%c%c}, description = '%s'\n",
+ fd, argp.index, argp.pixelformat & 0xff,
+ (argp.pixelformat >> 8) & 0xff,
+ (argp.pixelformat >>16) & 0xff,
+ (argp.pixelformat >> 24) & 0xff,
+ argp.description);
+ argp.index ++;
+ }
+
+ memset(&sub, 0, sizeof(sub));
+ sub.type = V4L2_EVENT_EOS;
+
+ debug_printf("[fd%d] Calling V4L2 IOCTL VIDIOC_SUBSCRIBE_EVENT\n", fd);
+ ret = ioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
+ if (ret != 0) {
+ printf("[fd%d] Failed to subscribe to events: err: %d %s\n",
+ fd, errno, strerror(errno));
+ }
+
+ return fd;
+}
+
+int main(int argc, char **argv)
+{
+ char *dev_name = "/dev/video0";
+ int i = 0;
+ int ret = 0;
+ unsigned int num_devs = 1;
+ int fds[MAX_TEST_FDS];
+ int rdfd = -1;
+ int wrfd = -1;
+ int c;
+ int n_outbufs, n_capbufs, n_frames;
+ struct buffer outbufs[MAX_OUTBUFS];
+ struct buffer capbufs[MAX_CAPBUFS];
+ struct stream_context str_context;
+ char input_file[256];
+ char output_file_base[120];
+ char output_file[128];
+ const char *dir_path = "/dev/dri/";
+ char drm_file_name[20];
+ DIR *d;
+ struct dirent *dir;
+ drmModeResPtr res;
+ int max_frames = -1;
+ int enable_prof = 0;
+ int sleep_time = 0;
+ struct output_format fmt;
+ int use_drm_capbuff = 1; /* Defaulting to use drm dss Capture Buffers */
+
+ printf("TI DEC V4L2 Codec decoding example application\n");
+ printf("Copyright (c) 2018 Texas Instruments, Inc.\n");
+
+ /* Parse the input args */
+ while (1)
+ {
+ c = getopt (argc, argv, "nf:hbi:o:d:te:");
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'n':
+ num_devs = atoi(optarg);
+ break;
+ case 'h':
+ printf("Use:\n");
+ printf("\t./tidec_decode -i <input_file> [OPTIONS]\n");
+ printf("\tThe final output file/s will be '<output_file_base>_xx.out'\n");
+ printf("\twhere xx ranges from 00, 01, 02, ...\n");
+ printf("\tdepending on how many fds are specified to -n\n");
+ printf("\tOPTIONS:\n");
+ printf("\t\t-h help\n");
+ printf("\t\t-b DO NOT use drm dss device capture buffer (instead, use v4l2)\n");
+ printf("\t\t-n <number> number of fds to open\n");
+ printf("\t\t-o <output_file_base> Dump output stream to file\n");
+ printf("\t\t\tThe final output file/s will be '<output_file_base>_xx.out'\n");
+ printf("\t\t\twhere xx ranges from 00, 01, 02, ...\n");
+ printf("\t\t\tdepending on how many fds are specified to -n\n");
+ printf("\t\t-f <number of frames to decode> Maximum number of frames to decode\n");
+ printf("\t\t-d <path> Path to which drm device to use for buffer allocation\n");
+ printf("\t\t-t for enable time profiling\n");
+ printf("\t\t-e <number of ms> Number of milliseconds to sleep between\n");
+ printf("\t\t\tqueueing the last bitstream buffer and sending CMD_STOP\n");
+ printf("\t\t\tUsed to test various timing scenarios for EOS\n");
+ break;
+ case 'b':
+ use_drm_capbuff = 0; /* DO NOT use drm dss device*/
+ break;
+ case 'i':
+ snprintf(input_file, sizeof(input_file),
+ "%s", optarg);
+ break;
+ case 'o':
+ snprintf(output_file_base,
+ sizeof(output_file_base),
+ "%s", optarg);
+ break;
+ case 'f':
+ max_frames = atoi(optarg);
+ break;
+ case 'd':
+ snprintf(drm_file_name, sizeof(drm_file_name),
+ "%s", optarg);
+ break;
+ case 't':
+ enable_prof = 1;
+ break;
+ case 'e':
+ sleep_time = 1000 * atoi(optarg);
+ break;
+ default:
+ printf("Unrecognized argument %c\n", c);
+ }
+ }
+
+ if (0 == strlen(input_file)) {
+ printf("Requires an input value\n");
+ printf("\t./tidec_decode -i <input_file> [OPTIONS]\n");
+ return EXIT_FAILURE;
+ }
+
+ /* validate input args */
+ if (num_devs > MAX_TEST_FDS) {
+ printf("Invalid value passed for number of devices %d\n",
+ num_devs);
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < num_devs; i++) {
+ debug_printf("*** Calling open_device for device %d \n", i);
+ fds[i] = open_device(dev_name);
+ if (fds[i] < 0)
+ return EXIT_FAILURE;
+ debug_printf("*** device %d is fd %d", i, fds[i]);
+ }
+
+ if (use_drm_capbuff == 0) {
+ printf("\nUsing v4l2 for capture buffers\n");
+ } else {
+ if (strlen(drm_file_name) > 0) {
+ drmfd1 = open(drm_file_name, O_CLOEXEC);
+ if (drmfd1 < 0) {
+ printf("Failed to open drm device\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ d = opendir(dir_path);
+ if (!d) {
+ printf("Failed to open drm device directory\n");
+ return EXIT_FAILURE;
+ }
+ while ((dir = readdir(d)) != NULL) {
+ if(strncmp(dir->d_name, DEVICE_NAME, 4) == 0) {
+ strcpy(drm_file_name, dir_path);
+ strncat(drm_file_name, dir->d_name,
+ sizeof(DEVICE_NAME));
+ drmfd1 = open(drm_file_name, O_CLOEXEC);
+ if (drmfd1 < 0) {
+ printf("Failed to open drm device %s\n",
+ drm_file_name);
+ }
+ printf("No drm device specified, testing %s\n",
+ drm_file_name);
+ res = drmModeGetResources(drmfd1);
+ if (res && res->count_crtcs > 0 &&
+ res->count_connectors > 0 &&
+ res->count_encoders > 0) {
+ printf("No drm device specified, using %s\n",
+ drm_file_name);
+ break;
+ }
+ close(drmfd1);
+ drmfd1 = -1;
+ }
+ }
+ }
+ if (drmfd1 < 0) {
+ printf("Failed to open drm device\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Get the stream width, height and framesizes of H264 stream */
+ n_frames = stream_framelevel_parsing(&str_context, input_file, max_frames);
+ if (n_frames < 0) {
+ printf("Failed to init frame sizes\n");
+ return EXIT_FAILURE;
+ }
+
+ switch (str_context.pix_fmt) {
+ case AV_PIX_FMT_YUV420P:
+ fmt.fourcc = V4L2_PIX_FMT_NV12;
+ fmt.size_num = 3;
+ fmt.size_den = 2;
+ break;
+ case AV_PIX_FMT_YUV422P:
+ fmt.fourcc = V4L2_PIX_FMT_NV16;
+ fmt.size_num = 2;
+ fmt.size_den = 1;
+ break;
+ case AV_PIX_FMT_YUV420P10LE:
+ fmt.fourcc = V4L2_PIX_FMT_TI1210;
+ fmt.size_num = 3;
+ fmt.size_den = 2;
+ break;
+ case AV_PIX_FMT_YUV422P10LE:
+ fmt.fourcc = V4L2_PIX_FMT_TI1610;
+ fmt.size_num = 2;
+ fmt.size_den = 1;
+ break;
+ default:
+ printf("Invalid pixel format detected\n");
+ return -1;
+ }
+
+ for (i = 0; i < num_devs; i++) {
+ n_outbufs = MAX_OUTBUFS;
+ /* Request number of output buffers based on h264 spec
+ * + display delay */
+ n_capbufs = MIN(MAX_CAPBUFS_H264, (32768 /
+ ((str_context.width / 16) *
+ (str_context.height / 16)))) +
+ DISPLAY_LAG;
+
+ debug_printf("Opening input file\n");
+ rdfd = open(input_file, O_RDONLY);
+ if (rdfd < 0) {
+ printf("Failed to open input file %s", input_file);
+ }
+
+ if (strcmp(output_file_base, "")) {
+ snprintf(output_file, sizeof(output_file),
+ "%s_%02d.raw", output_file_base, i);
+ wrfd = open(output_file,
+ O_CREAT | O_WRONLY | O_SYNC | O_TRUNC,
+ 0777);
+ }
+
+ if (wrfd < 0) {
+ if (strcmp(output_file_base, "")) {
+ printf("Failed to open output file %s\n",
+ output_file);
+ return EXIT_FAILURE;
+ } else {
+ printf("No output file specified\n");
+ }
+ } else {
+ debug_printf("Opened output file %s\n", output_file);
+ }
+
+ ret = init_device(fds[i], rdfd, &str_context, outbufs,
+ capbufs, &n_outbufs, &n_capbufs, fmt, use_drm_capbuff);
+ if (ret) {
+ printf("init_device failed with ret %d for device %d [fd%d]\n",
+ ret, i, fds[i]);
+ return EXIT_FAILURE;
+ }
+
+ if (mainloop(fds[i], rdfd, wrfd, &str_context, outbufs,
+ capbufs, n_outbufs,
+ n_capbufs, n_frames,
+ enable_prof, fmt,
+ sleep_time, use_drm_capbuff)) {
+ return EXIT_FAILURE;
+ }
+
+ close(rdfd);
+ if (!(wrfd < 0))
+ close(wrfd);
+ rdfd = -1;
+ wrfd = -1;
+
+ uninit_device(fds[i], outbufs, capbufs, &n_outbufs, &n_capbufs, use_drm_capbuff);
+
+ debug_printf("*** Calling close_device for device %d [fd%d]\n",
+ i, fds[i]);
+ close_device(fds[i]);
+ }
+
+ if (use_drm_capbuff && drmfd1)
+ close(drmfd1);
+
+ printf("test app completed successfully\n");
+ return 0;
+}