mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
Merge remote-tracking branch 'martiis-libstb-hal/master'
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/m4/
|
||||
/autom4te.cache/
|
||||
/aclocal.m4
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.h.in~
|
||||
/config.sub
|
||||
/configure
|
||||
/depcomp
|
||||
/install-sh
|
||||
/common/Makefile.in
|
||||
/libeplayer3/Makefile.in
|
||||
/libspark/Makefile.in
|
||||
/libtriple/Makefile.in
|
||||
/azbox/Makefile.in
|
||||
/generic-pc/Makefile.in
|
||||
/raspi/Makefile.in
|
||||
/ltmain.sh
|
||||
/missing
|
||||
/Makefile.in
|
||||
/tools/Makefile.in
|
42
Makefile.am
Normal file
42
Makefile.am
Normal file
@@ -0,0 +1,42 @@
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
lib_LTLIBRARIES = libstb-hal.la
|
||||
libstb_hal_la_SOURCES =
|
||||
SUBDIRS = common tools
|
||||
bin_PROGRAMS = libstb-hal-test
|
||||
|
||||
libstb_hal_la_LIBADD = \
|
||||
common/libcommon.la
|
||||
|
||||
libstb_hal_test_SOURCES = libtest.cpp
|
||||
libstb_hal_test_LDADD = libstb-hal.la
|
||||
|
||||
# there has to be a better way to do this...
|
||||
if BOXTYPE_TRIPLE
|
||||
SUBDIRS += libtriple
|
||||
libstb_hal_la_LIBADD += \
|
||||
libtriple/libtriple.la
|
||||
endif
|
||||
if BOXTYPE_AZBOX
|
||||
SUBDIRS += azbox
|
||||
libstb_hal_la_LIBADD += \
|
||||
azbox/libazbox.la
|
||||
endif
|
||||
if BOXTYPE_GENERIC
|
||||
if BOXMODEL_RASPI
|
||||
SUBDIRS += raspi
|
||||
libstb_hal_la_LIBADD += \
|
||||
raspi/libraspi.la
|
||||
else
|
||||
SUBDIRS += generic-pc
|
||||
libstb_hal_la_LIBADD += \
|
||||
generic-pc/libgeneric.la
|
||||
endif
|
||||
endif
|
||||
if BOXTYPE_SPARK
|
||||
libstb_hal_test_LDADD += -lasound
|
||||
SUBDIRS += libspark libeplayer3
|
||||
libstb_hal_la_LIBADD += \
|
||||
libspark/libspark.la \
|
||||
libeplayer3/libeplayer3.la
|
||||
endif
|
407
acinclude.m4
Normal file
407
acinclude.m4
Normal file
@@ -0,0 +1,407 @@
|
||||
AC_DEFUN([TUXBOX_APPS],[
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_ARG_WITH(target,
|
||||
[ --with-target=TARGET target for compilation [[native,cdk]]],
|
||||
[TARGET="$withval"],[TARGET="native"])
|
||||
|
||||
AC_ARG_WITH(targetprefix,
|
||||
[ --with-targetprefix=PATH prefix relative to target root (only applicable in cdk mode)],
|
||||
[targetprefix="$withval"],[targetprefix="NONE"])
|
||||
|
||||
AC_ARG_WITH(debug,
|
||||
[ --without-debug disable debugging code],
|
||||
[DEBUG="$withval"],[DEBUG="yes"])
|
||||
|
||||
if test "$DEBUG" = "yes"; then
|
||||
DEBUG_CFLAGS="-g3 -ggdb"
|
||||
AC_DEFINE(DEBUG,1,[Enable debug messages])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(target)
|
||||
|
||||
if test "$TARGET" = "native"; then
|
||||
AC_MSG_RESULT(native)
|
||||
|
||||
if test "$CFLAGS" = "" -a "$CXXFLAGS" = ""; then
|
||||
CFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS"
|
||||
CXXFLAGS="-Wall -O2 -pipe $DEBUG_CFLAGS"
|
||||
fi
|
||||
if test "$prefix" = "NONE"; then
|
||||
prefix=/usr/local
|
||||
fi
|
||||
targetprefix=$prefix
|
||||
elif test "$TARGET" = "cdk"; then
|
||||
AC_MSG_RESULT(cdk)
|
||||
|
||||
if test "$prefix" = "NONE"; then
|
||||
AC_MSG_ERROR(invalid prefix, you need to specify one in cdk mode)
|
||||
fi
|
||||
if test "$targetprefix" = "NONE"; then
|
||||
targetprefix=""
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(none)
|
||||
AC_MSG_ERROR([invalid target $TARGET, choose on from native,cdk]);
|
||||
fi
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
check_path () {
|
||||
return $(perl -e "if(\"$1\"=~m#^/usr/(local/)?bin#){print \"0\"}else{print \"1\";}")
|
||||
}
|
||||
|
||||
])
|
||||
|
||||
dnl expand nested ${foo}/bar
|
||||
AC_DEFUN([TUXBOX_EXPAND_VARIABLE],[__$1="$2"
|
||||
for __CNT in false false false false true; do dnl max 5 levels of indirection
|
||||
|
||||
$1=`eval echo "$__$1"`
|
||||
echo ${$1} | grep -q '\$' || break # 'grep -q' is POSIX, exit if no $ in variable
|
||||
__$1="${$1}"
|
||||
done
|
||||
$__CNT && AC_MSG_ERROR([can't expand variable $1=$2]) dnl bail out if we did not expand
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_DIRECTORY_ONE],[
|
||||
AC_ARG_WITH($1,[ $6$7 [[PREFIX$4$5]]],[
|
||||
_$2=$withval
|
||||
if test "$TARGET" = "cdk"; then
|
||||
$2=`eval echo "${targetprefix}$withval"` # no indirection possible IMNSHO
|
||||
else
|
||||
$2=$withval
|
||||
fi
|
||||
TARGET_$2=${$2}
|
||||
],[
|
||||
# RFC 1925: "you can always add another level of indirection..."
|
||||
TUXBOX_EXPAND_VARIABLE($2,"${$3}$5")
|
||||
if test "$TARGET" = "cdk"; then
|
||||
TUXBOX_EXPAND_VARIABLE(_$2,"${target$3}$5")
|
||||
else
|
||||
_$2=${$2}
|
||||
fi
|
||||
TARGET_$2=$_$2
|
||||
])
|
||||
|
||||
dnl automake <= 1.6 don't support this
|
||||
dnl AC_SUBST($2)
|
||||
AC_DEFINE_UNQUOTED($2,"$_$2",$7)
|
||||
AC_SUBST(TARGET_$2)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_DIRECTORY],[
|
||||
AC_REQUIRE([TUXBOX_APPS])
|
||||
|
||||
if test "$TARGET" = "cdk"; then
|
||||
datadir="\${prefix}/share"
|
||||
sysconfdir="\${prefix}/etc"
|
||||
localstatedir="\${prefix}/var"
|
||||
libdir="\${prefix}/lib"
|
||||
targetdatadir="\${targetprefix}/share"
|
||||
targetsysconfdir="\${targetprefix}/etc"
|
||||
targetlocalstatedir="\${targetprefix}/var"
|
||||
targetlibdir="\${targetprefix}/lib"
|
||||
fi
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(configdir,CONFIGDIR,localstatedir,/var,/tuxbox/config,
|
||||
[--with-configdir=PATH ],[where to find the config files])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(datadir,DATADIR,datadir,/share,/tuxbox,
|
||||
[--with-datadir=PATH ],[where to find data])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(fontdir,FONTDIR,datadir,/share,/fonts,
|
||||
[--with-fontdir=PATH ],[where to find the fonts])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(gamesdir,GAMESDIR,localstatedir,/var,/tuxbox/games,
|
||||
[--with-gamesdir=PATH ],[where games data is stored])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(libdir,LIBDIR,libdir,/lib,/tuxbox,
|
||||
[--with-libdir=PATH ],[where to find the internal libs])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(plugindir,PLUGINDIR,libdir,/lib,/tuxbox/plugins,
|
||||
[--with-plugindir=PATH ],[where to find the plugins])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(ucodedir,UCODEDIR,localstatedir,/var,/tuxbox/ucodes,
|
||||
[--with-ucodedir=PATH ],[where to find the ucodes])
|
||||
|
||||
TUXBOX_APPS_DIRECTORY_ONE(themesdir,THEMESDIR,datadir,/share,/tuxbox/neutrino/themes,
|
||||
[--with-themesdir=PATH ],[where to find the themes (don't change)])
|
||||
])
|
||||
|
||||
dnl automake <= 1.6 needs this specifications
|
||||
AC_SUBST(CONFIGDIR)
|
||||
AC_SUBST(DATADIR)
|
||||
AC_SUBST(FONTDIR)
|
||||
AC_SUBST(GAMESDIR)
|
||||
AC_SUBST(LIBDIR)
|
||||
AC_SUBST(PLUGINDIR)
|
||||
AC_SUBST(UCODEDIR)
|
||||
AC_SUBST(THEMESDIR)
|
||||
dnl end workaround
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_ENDIAN],[
|
||||
AC_CHECK_HEADERS(endian.h)
|
||||
AC_C_BIGENDIAN
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_DVB],[
|
||||
AC_ARG_WITH(dvbincludes,
|
||||
[ --with-dvbincludes=PATH path for dvb includes [[NONE]]],
|
||||
[DVBINCLUDES="$withval"],[DVBINCLUDES=""])
|
||||
|
||||
if test "$DVBINCLUDES"; then
|
||||
CPPFLAGS="$CPPFLAGS -I$DVBINCLUDES"
|
||||
fi
|
||||
|
||||
if test -z "$DVB_API_VERSION"; then
|
||||
AC_CHECK_HEADERS(linux/dvb/version.h,[
|
||||
AC_LANG_PREPROC_REQUIRE()
|
||||
AC_REQUIRE([AC_PROG_EGREP])
|
||||
AC_LANG_CONFTEST([AC_LANG_SOURCE([[
|
||||
#include <linux/dvb/version.h>
|
||||
version DVB_API_VERSION
|
||||
]])])
|
||||
DVB_API_VERSION=`(eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | $EGREP "^version" | sed "s,version\ ,,"`
|
||||
rm -f conftest*
|
||||
|
||||
AC_MSG_NOTICE([found dvb version $DVB_API_VERSION])
|
||||
])
|
||||
fi
|
||||
|
||||
if test "$DVB_API_VERSION"; then
|
||||
AC_DEFINE(HAVE_DVB,1,[Define to 1 if you have the dvb includes])
|
||||
AC_DEFINE_UNQUOTED(HAVE_DVB_API_VERSION,$DVB_API_VERSION,[Define to the version of the dvb api])
|
||||
else
|
||||
AC_MSG_ERROR([can't find dvb headers])
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_TUXBOX_APPS_LIB_CONFIG],[
|
||||
AC_PATH_PROG($1_CONFIG,$2,no)
|
||||
if test "$$1_CONFIG" != "no"; then
|
||||
if test "$TARGET" = "cdk" && check_path "$$1_CONFIG"; then
|
||||
AC_MSG_$3([could not find a suitable version of $2]);
|
||||
else
|
||||
if test "$1" = "CURL"; then
|
||||
$1_CFLAGS=$($$1_CONFIG --cflags)
|
||||
$1_LIBS=$($$1_CONFIG --libs)
|
||||
else
|
||||
if test "$1" = "FREETYPE"; then
|
||||
$1_CFLAGS=$($$1_CONFIG --cflags)
|
||||
$1_LIBS=$($$1_CONFIG --libs)
|
||||
else
|
||||
$1_CFLAGS=$($$1_CONFIG --prefix=$targetprefix --cflags)
|
||||
$1_LIBS=$($$1_CONFIG --prefix=$targetprefix --libs)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST($1_CFLAGS)
|
||||
AC_SUBST($1_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_CONFIG],[
|
||||
_TUXBOX_APPS_LIB_CONFIG($1,$2,ERROR)
|
||||
if test "$$1_CONFIG" = "no"; then
|
||||
AC_MSG_ERROR([could not find $2]);
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_CHECK],[
|
||||
_TUXBOX_APPS_LIB_CONFIG($1,$2,WARN)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_PKGCONFIG],[
|
||||
m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test x"$PKG_CONFIG" = x"" ; then
|
||||
AC_MSG_ERROR([could not find pkg-config]);
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_TUXBOX_APPS_LIB_PKGCONFIG],[
|
||||
AC_REQUIRE([TUXBOX_APPS_PKGCONFIG])
|
||||
AC_MSG_CHECKING(for package $2)
|
||||
if $PKG_CONFIG --exists "$2" ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
$1_CFLAGS=$($PKG_CONFIG --cflags "$2")
|
||||
$1_LIBS=$($PKG_CONFIG --libs "$2")
|
||||
$1_EXISTS=yes
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_SUBST($1_CFLAGS)
|
||||
AC_SUBST($1_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG],[
|
||||
_TUXBOX_APPS_LIB_PKGCONFIG($1,$2)
|
||||
if test x"$$1_EXISTS" != xyes; then
|
||||
AC_MSG_ERROR([could not find package $2]);
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_PKGCONFIG_CHECK],[
|
||||
_TUXBOX_APPS_LIB_PKGCONFIG($1,$2)
|
||||
])
|
||||
|
||||
AC_DEFUN([_TUXBOX_APPS_LIB_SYMBOL],[
|
||||
AC_CHECK_LIB($2,$3,HAVE_$1="yes",HAVE_$1="no")
|
||||
if test "$HAVE_$1" = "yes"; then
|
||||
$1_LIBS=-l$2
|
||||
fi
|
||||
|
||||
AC_SUBST($1_LIBS)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_SYMBOL],[
|
||||
_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,ERROR)
|
||||
if test "$HAVE_$1" = "no"; then
|
||||
AC_MSG_ERROR([could not find $2]);
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_APPS_LIB_CONFIG_SYMBOL],[
|
||||
_TUXBOX_APPS_LIB_SYMBOL($1,$2,$3,WARN)
|
||||
])
|
||||
|
||||
AC_DEFUN([TUXBOX_BOXTYPE],[
|
||||
AC_ARG_WITH(boxtype,
|
||||
[ --with-boxtype valid values: dbox2,tripledragon,dreambox,ipbox,coolstream,spark,azbox,generic],
|
||||
[case "${withval}" in
|
||||
dbox2|dreambox|ipbox|tripledragon|coolstream|spark|azbox|generic)
|
||||
BOXTYPE="$withval"
|
||||
;;
|
||||
dm*)
|
||||
BOXTYPE="dreambox"
|
||||
BOXMODEL="$withval"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([bad value $withval for --with-boxtype]) ;;
|
||||
esac], [BOXTYPE="generic"])
|
||||
|
||||
AC_ARG_WITH(boxmodel,
|
||||
[ --with-boxmodel valid for dreambox: dm500, dm500plus, dm600pvr, dm56x0, dm7000, dm7020, dm7025
|
||||
valid for ipbox: ip200, ip250, ip350, ip400],
|
||||
[case "${withval}" in
|
||||
dm500|dm500plus|dm600pvr|dm56x0|dm7000|dm7020|dm7025)
|
||||
if test "$BOXTYPE" = "dreambox"; then
|
||||
BOXMODEL="$withval"
|
||||
else
|
||||
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
||||
fi
|
||||
;;
|
||||
ip200|ip250|ip350|ip400)
|
||||
if test "$BOXTYPE" = "ipbox"; then
|
||||
BOXMODEL="$withval"
|
||||
else
|
||||
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
||||
fi
|
||||
;;
|
||||
raspi)
|
||||
if test "$BOXTYPE" = "generic"; then
|
||||
BOXMODEL="$withval"
|
||||
else
|
||||
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([unsupported value $withval for --with-boxmodel])
|
||||
;;
|
||||
esac],
|
||||
[if test "$BOXTYPE" = "dreambox" -o "$BOXTYPE" = "ipbox" && test -z "$BOXMODEL"; then
|
||||
AC_MSG_ERROR([Dreambox/IPBox needs --with-boxmodel])
|
||||
fi])
|
||||
|
||||
AC_SUBST(BOXTYPE)
|
||||
AC_SUBST(BOXMODEL)
|
||||
|
||||
AM_CONDITIONAL(BOXTYPE_AZBOX, test "$BOXTYPE" = "azbox")
|
||||
AM_CONDITIONAL(BOXTYPE_DBOX2, test "$BOXTYPE" = "dbox2")
|
||||
AM_CONDITIONAL(BOXTYPE_TRIPLE, test "$BOXTYPE" = "tripledragon")
|
||||
AM_CONDITIONAL(BOXTYPE_SPARK, test "$BOXTYPE" = "spark")
|
||||
AM_CONDITIONAL(BOXTYPE_DREAMBOX, test "$BOXTYPE" = "dreambox")
|
||||
AM_CONDITIONAL(BOXTYPE_IPBOX, test "$BOXTYPE" = "ipbox")
|
||||
AM_CONDITIONAL(BOXTYPE_COOL, test "$BOXTYPE" = "coolstream")
|
||||
AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic")
|
||||
|
||||
AM_CONDITIONAL(BOXMODEL_DM500,test "$BOXMODEL" = "dm500")
|
||||
AM_CONDITIONAL(BOXMODEL_DM500PLUS,test "$BOXMODEL" = "dm500plus")
|
||||
AM_CONDITIONAL(BOXMODEL_DM600PVR,test "$BOXMODEL" = "dm600pvr")
|
||||
AM_CONDITIONAL(BOXMODEL_DM56x0,test "$BOXMODEL" = "dm56x0")
|
||||
AM_CONDITIONAL(BOXMODEL_DM7000,test "$BOXMODEL" = "dm7000" -o "$BOXMODEL" = "dm7020" -o "$BOXMODEL" = "dm7025")
|
||||
|
||||
AM_CONDITIONAL(BOXMODEL_IP200,test "$BOXMODEL" = "ip200")
|
||||
AM_CONDITIONAL(BOXMODEL_IP250,test "$BOXMODEL" = "ip250")
|
||||
AM_CONDITIONAL(BOXMODEL_IP350,test "$BOXMODEL" = "ip350")
|
||||
AM_CONDITIONAL(BOXMODEL_IP400,test "$BOXMODEL" = "ip400")
|
||||
|
||||
AM_CONDITIONAL(BOXMODEL_RASPI,test "$BOXMODEL" = "raspi")
|
||||
|
||||
if test "$BOXTYPE" = "dbox2"; then
|
||||
AC_DEFINE(HAVE_DBOX_HARDWARE, 1, [building for a dbox2])
|
||||
elif test "$BOXTYPE" = "azbox"; then
|
||||
AC_DEFINE(HAVE_AZBOX_HARDWARE, 1, [building for an azbox])
|
||||
elif test "$BOXTYPE" = "tripledragon"; then
|
||||
AC_DEFINE(HAVE_TRIPLEDRAGON, 1, [building for a tripledragon])
|
||||
elif test "$BOXTYPE" = "spark"; then
|
||||
AC_DEFINE(HAVE_SPARK_HARDWARE, 1, [building for a spark st7111 box])
|
||||
elif test "$BOXTYPE" = "dreambox"; then
|
||||
AC_DEFINE(HAVE_DREAMBOX_HARDWARE, 1, [building for a dreambox])
|
||||
elif test "$BOXTYPE" = "ipbox"; then
|
||||
AC_DEFINE(HAVE_IPBOX_HARDWARE, 1, [building for an ipbox])
|
||||
elif test "$BOXTYPE" = "coolstream"; then
|
||||
AC_DEFINE(HAVE_COOL_HARDWARE, 1, [building for a coolstream])
|
||||
elif test "$BOXTYPE" = "generic"; then
|
||||
AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC])
|
||||
fi
|
||||
|
||||
# TODO: do we need more defines?
|
||||
if test "$BOXMODEL" = "dm500"; then
|
||||
AC_DEFINE(BOXMODEL_DM500, 1, [dreambox 500])
|
||||
elif test "$BOXMODEL" = "ip200"; then
|
||||
AC_DEFINE(BOXMODEL_IP200, 1, [ipbox 200])
|
||||
elif test "$BOXMODEL" = "ip250"; then
|
||||
AC_DEFINE(BOXMODEL_IP250, 1, [ipbox 250])
|
||||
elif test "$BOXMODEL" = "ip350"; then
|
||||
AC_DEFINE(BOXMODEL_IP350, 1, [ipbox 350])
|
||||
elif test "$BOXMODEL" = "ip400"; then
|
||||
AC_DEFINE(BOXMODEL_IP400, 1, [ipbox 400])
|
||||
elif test "$BOXMODEL" = "raspi"; then
|
||||
AC_DEFINE(BOXMODEL_RASPI, 1, [Raspberry pi])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl backward compatiblity
|
||||
AC_DEFUN([AC_GNU_SOURCE],
|
||||
[AH_VERBATIM([_GNU_SOURCE],
|
||||
[/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif])dnl
|
||||
AC_BEFORE([$0], [AC_COMPILE_IFELSE])dnl
|
||||
AC_BEFORE([$0], [AC_RUN_IFELSE])dnl
|
||||
AC_DEFINE([_GNU_SOURCE])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_PROG_EGREP],
|
||||
[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep],
|
||||
[if echo a | (grep -E '(a|b)') >/dev/null 2>&1
|
||||
then ac_cv_prog_egrep='grep -E'
|
||||
else ac_cv_prog_egrep='egrep'
|
||||
fi])
|
||||
EGREP=$ac_cv_prog_egrep
|
||||
AC_SUBST([EGREP])
|
||||
])
|
||||
|
8
autogen.sh
Executable file
8
autogen.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd $(dirname $0)
|
||||
aclocal --force
|
||||
libtoolize --force
|
||||
autoconf --force
|
||||
autoheader --force
|
||||
automake --add-missing --force-missing --foreign
|
18
azbox/Makefile.am
Normal file
18
azbox/Makefile.am
Normal file
@@ -0,0 +1,18 @@
|
||||
noinst_LTLIBRARIES = libazbox.la
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/common
|
||||
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
AM_LDFLAGS = -lpthread
|
||||
|
||||
libazbox_la_SOURCES = \
|
||||
hardware_caps.c \
|
||||
dmx.cpp \
|
||||
video.cpp \
|
||||
audio.cpp \
|
||||
init.cpp \
|
||||
playback.cpp \
|
||||
pwrmngr.cpp \
|
||||
record.cpp
|
||||
|
398
azbox/audio.cpp
Normal file
398
azbox/audio.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/dvb/audio.h>
|
||||
|
||||
#include <proc_tools.h>
|
||||
|
||||
#include "audio_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args)
|
||||
|
||||
#include <linux/soundcard.h>
|
||||
|
||||
cAudio * audioDecoder = NULL;
|
||||
|
||||
cAudio::cAudio(void *, void *, void *)
|
||||
{
|
||||
fd = -1;
|
||||
clipfd = -1;
|
||||
mixer_fd = -1;
|
||||
openDevice();
|
||||
Muted = false;
|
||||
}
|
||||
|
||||
cAudio::~cAudio(void)
|
||||
{
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
void cAudio::openDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
if (fd < 0)
|
||||
{
|
||||
if ((fd = open(AUDIO_DEVICE, O_RDONLY|O_CLOEXEC)) < 0)
|
||||
lt_info("openDevice: open failed (%m)\n");
|
||||
do_mute(true, false);
|
||||
}
|
||||
else
|
||||
lt_info("openDevice: already open (fd = %d)\n", fd);
|
||||
}
|
||||
|
||||
void cAudio::closeDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
ioctl(fd, AUDIO_CONTINUE); /* enigma2 also does CONTINUE before close... */
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
if (clipfd >= 0)
|
||||
close(clipfd);
|
||||
clipfd = -1;
|
||||
if (mixer_fd >= 0)
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
}
|
||||
|
||||
int cAudio::do_mute(bool enable, bool remember)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, enable, remember);
|
||||
|
||||
if (remember)
|
||||
Muted = enable;
|
||||
#if 0
|
||||
/* does not work? */
|
||||
if (ioctl(fd, AUDIO_SET_MUTE, enable) < 0 )
|
||||
lt_info("%s: AUDIO_SET_MUTE failed (%m)\n", __func__);
|
||||
#else
|
||||
char s[2] = { 0, 0 };
|
||||
s[0] = '0' + (int)enable;
|
||||
proc_put("/proc/stb/audio/j1_mute", s, 2);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int map_volume(const int volume)
|
||||
{
|
||||
unsigned char vol = volume;
|
||||
if (vol > 100)
|
||||
vol = 100;
|
||||
|
||||
vol = 63 - vol * 63 / 100;
|
||||
return vol;
|
||||
}
|
||||
|
||||
|
||||
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
||||
|
||||
volume = (left + right) / 2;
|
||||
if (clipfd != -1 && mixer_fd != -1) {
|
||||
int tmp = 0;
|
||||
/* not sure if left / right is correct here, but it is always the same anyways ;-) */
|
||||
if (! Muted)
|
||||
tmp = left << 8 | right;
|
||||
int ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp);
|
||||
if (ret == -1)
|
||||
lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
audio_mixer_t mixer;
|
||||
mixer.volume_left = map_volume(left);
|
||||
mixer.volume_right = map_volume(right);
|
||||
|
||||
if (ioctl(fd, AUDIO_SET_MIXER, &mixer) < 0)
|
||||
lt_info("%s: AUDIO_SET_MIXER failed (%m)\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::Start(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
int ret;
|
||||
ioctl(fd, AUDIO_CONTINUE);
|
||||
ret = ioctl(fd, AUDIO_PLAY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cAudio::Stop(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
ioctl(fd, AUDIO_STOP);
|
||||
ioctl(fd, AUDIO_CONTINUE); /* no idea why we have to stop and then continue => enigma2 does it, too */
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cAudio::Pause(bool /*Pcm*/)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, Mode);
|
||||
ioctl(fd, AUDIO_SET_AV_SYNC, Mode);
|
||||
};
|
||||
|
||||
//AUDIO_ENCODING_AC3
|
||||
#define AUDIO_STREAMTYPE_AC3 0
|
||||
//AUDIO_ENCODING_MPEG2
|
||||
#define AUDIO_STREAMTYPE_MPEG 1
|
||||
//AUDIO_ENCODING_DTS
|
||||
#define AUDIO_STREAMTYPE_DTS 2
|
||||
|
||||
#define AUDIO_ENCODING_LPCM 2
|
||||
#define AUDIO_ENCODING_LPCMA 11
|
||||
void cAudio::SetStreamType(AUDIO_FORMAT type)
|
||||
{
|
||||
int bypass = AUDIO_STREAMTYPE_MPEG;
|
||||
lt_debug("%s %d\n", __func__, type);
|
||||
StreamType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AUDIO_FMT_DOLBY_DIGITAL:
|
||||
bypass = AUDIO_STREAMTYPE_AC3;
|
||||
break;
|
||||
case AUDIO_FMT_DTS:
|
||||
bypass = AUDIO_STREAMTYPE_DTS;
|
||||
break;
|
||||
case AUDIO_FMT_MPEG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Normaly the encoding should be set using AUDIO_SET_ENCODING
|
||||
// But as we implemented the behavior to bypass (cause of e2) this is correct here
|
||||
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
|
||||
lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__);
|
||||
};
|
||||
|
||||
int cAudio::setChannel(int channel)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian)
|
||||
{
|
||||
int fmt;
|
||||
unsigned int devmask, stereo, usable;
|
||||
const char *dsp_dev = getenv("DSP_DEVICE");
|
||||
const char *mix_dev = getenv("MIX_DEVICE");
|
||||
lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian);
|
||||
if (clipfd >= 0) {
|
||||
lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd);
|
||||
return -1;
|
||||
}
|
||||
mixer_num = -1;
|
||||
mixer_fd = -1;
|
||||
/* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE
|
||||
* if this device cannot be opened, we fall back to the internal OSS device
|
||||
* Example:
|
||||
* modprobe snd-usb-audio
|
||||
* export DSP_DEVICE=/dev/sound/dsp2
|
||||
* export MIX_DEVICE=/dev/sound/mixer2
|
||||
* neutrino
|
||||
*/
|
||||
if ((!dsp_dev) || (access(dsp_dev, W_OK))) {
|
||||
if (dsp_dev)
|
||||
lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened,"
|
||||
" fall back to /dev/dsp1\n", __func__, dsp_dev);
|
||||
dsp_dev = "/dev/dsp1";
|
||||
}
|
||||
lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */
|
||||
/* the tdoss dsp driver seems to work only on the second open(). really. */
|
||||
clipfd = open(dsp_dev, O_WRONLY|O_CLOEXEC);
|
||||
if (clipfd < 0) {
|
||||
lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
/* no idea if we ever get little_endian == 0 */
|
||||
if (little_endian)
|
||||
fmt = AFMT_S16_BE;
|
||||
else
|
||||
fmt = AFMT_S16_LE;
|
||||
if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt))
|
||||
perror("SNDCTL_DSP_SETFMT");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch))
|
||||
perror("SNDCTL_DSP_CHANNELS");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate))
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_RESET))
|
||||
perror("SNDCTL_DSP_RESET");
|
||||
|
||||
if (!mix_dev)
|
||||
return 0;
|
||||
|
||||
mixer_fd = open(mix_dev, O_RDWR|O_CLOEXEC);
|
||||
if (mixer_fd < 0) {
|
||||
lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev);
|
||||
/* not a real error */
|
||||
return 0;
|
||||
}
|
||||
if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
|
||||
lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__);
|
||||
devmask = 0;
|
||||
}
|
||||
if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) {
|
||||
lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__);
|
||||
stereo = 0;
|
||||
}
|
||||
usable = devmask & stereo;
|
||||
if (usable == 0) {
|
||||
lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n",
|
||||
__func__, devmask, stereo);
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
return 0; /* TODO: should we treat this as error? */
|
||||
}
|
||||
/* __builtin_popcount needs GCC, it counts the set bits... */
|
||||
if (__builtin_popcount (usable) != 1) {
|
||||
/* TODO: this code is not yet tested as I have only single-mixer devices... */
|
||||
lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n"
|
||||
"%s: querying MIX_NUMBER environment variable...\n",
|
||||
__func__, devmask, stereo, __func__);
|
||||
const char *tmp = getenv("MIX_NUMBER");
|
||||
if (tmp)
|
||||
mixer_num = atoi(tmp);
|
||||
lt_info("%s: mixer_num is %d -> device %08x\n",
|
||||
__func__, mixer_num, (mixer_num >= 0) ? (1 << mixer_num) : 0);
|
||||
/* no error checking, you'd better know what you are doing... */
|
||||
} else {
|
||||
mixer_num = 0;
|
||||
while (!(usable & 0x01)) {
|
||||
mixer_num++;
|
||||
usable >>= 1;
|
||||
}
|
||||
}
|
||||
setVolume(volume, volume);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int cAudio::WriteClip(unsigned char *buffer, int size)
|
||||
{
|
||||
int ret;
|
||||
// lt_debug("cAudio::%s\n", __FUNCTION__);
|
||||
if (clipfd <= 0) {
|
||||
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
ret = write(clipfd, buffer, size);
|
||||
if (ret < 0)
|
||||
lt_info("%s: write error (%m)\n", __FUNCTION__);
|
||||
return ret;
|
||||
};
|
||||
|
||||
int cAudio::StopClip()
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
if (clipfd <= 0) {
|
||||
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
close(clipfd);
|
||||
clipfd = -1;
|
||||
if (mixer_fd >= 0)
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
setVolume(volume, volume);
|
||||
return 0;
|
||||
};
|
||||
|
||||
void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
type = 0;
|
||||
layer = 0;
|
||||
freq = 0;
|
||||
bitrate = 0;
|
||||
mode = 0;
|
||||
#if 0
|
||||
unsigned int atype;
|
||||
static const int freq_mpg[] = {44100, 48000, 32000, 0};
|
||||
static const int freq_ac3[] = {48000, 44100, 32000, 0};
|
||||
scratchl2 i;
|
||||
if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0)
|
||||
perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP");
|
||||
if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0)
|
||||
perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS");
|
||||
|
||||
type = atype;
|
||||
#if 0
|
||||
/* this does not work, some of the values are negative?? */
|
||||
AMPEGStatus A;
|
||||
memcpy(&A, &i.word00, sizeof(i.word00));
|
||||
layer = A.audio_mpeg_layer;
|
||||
mode = A.audio_mpeg_mode;
|
||||
bitrate = A.audio_mpeg_bitrate;
|
||||
switch(A.audio_mpeg_frequency)
|
||||
#endif
|
||||
/* layer and bitrate are not used anyway... */
|
||||
layer = 0; //(i.word00 >> 17) & 3;
|
||||
bitrate = 0; //(i.word00 >> 12) & 3;
|
||||
switch (type)
|
||||
{
|
||||
case 0: /* MPEG */
|
||||
mode = (i.word00 >> 6) & 3;
|
||||
freq = freq_mpg[(i.word00 >> 10) & 3];
|
||||
break;
|
||||
case 1: /* AC3 */
|
||||
mode = (i.word00 >> 28) & 7;
|
||||
freq = freq_ac3[(i.word00 >> 16) & 3];
|
||||
break;
|
||||
default:
|
||||
mode = 0;
|
||||
freq = 0;
|
||||
}
|
||||
//fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode);
|
||||
#endif
|
||||
};
|
||||
|
||||
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
};
|
||||
|
||||
void cAudio::SetHdmiDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
};
|
||||
|
||||
void cAudio::SetSpdifDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
setBypassMode(!enable);
|
||||
};
|
||||
|
||||
void cAudio::ScheduleMute(bool On)
|
||||
{
|
||||
lt_debug("%s %d\n", __FUNCTION__, On);
|
||||
};
|
||||
|
||||
void cAudio::EnableAnalogOut(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __FUNCTION__, enable);
|
||||
};
|
||||
|
||||
#define AUDIO_BYPASS_ON 0
|
||||
#define AUDIO_BYPASS_OFF 1
|
||||
void cAudio::setBypassMode(bool disable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, disable);
|
||||
int mode = disable ? AUDIO_BYPASS_OFF : AUDIO_BYPASS_ON;
|
||||
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, mode) < 0)
|
||||
lt_info("%s AUDIO_SET_BYPASS_MODE %d: %m\n", __func__, mode);
|
||||
return;
|
||||
}
|
98
azbox/audio_lib.h
Normal file
98
azbox/audio_lib.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* public header file */
|
||||
|
||||
#ifndef _AUDIO_LIB_H_
|
||||
#define _AUDIO_LIB_H_
|
||||
|
||||
#include "../common/cs_types.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SYNC_WITH_PTS,
|
||||
AUDIO_NO_SYNC,
|
||||
AUDIO_SYNC_AUDIO_MASTER
|
||||
} AUDIO_SYNC_MODE;
|
||||
|
||||
typedef enum {
|
||||
HDMI_ENCODED_OFF,
|
||||
HDMI_ENCODED_AUTO,
|
||||
HDMI_ENCODED_FORCED
|
||||
} HDMI_ENCODED_MODE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FMT_AUTO = 0,
|
||||
AUDIO_FMT_MPEG,
|
||||
AUDIO_FMT_MP3,
|
||||
AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_AAC,
|
||||
AUDIO_FMT_AAC_PLUS,
|
||||
AUDIO_FMT_DD_PLUS,
|
||||
AUDIO_FMT_DTS,
|
||||
AUDIO_FMT_AVS,
|
||||
AUDIO_FMT_MLP,
|
||||
AUDIO_FMT_WMA,
|
||||
AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback
|
||||
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
||||
} AUDIO_FORMAT;
|
||||
|
||||
class cAudio
|
||||
{
|
||||
friend class cPlayback;
|
||||
private:
|
||||
int fd;
|
||||
bool Muted;
|
||||
|
||||
int clipfd; /* for pcm playback */
|
||||
int mixer_fd; /* if we are using the OSS mixer */
|
||||
int mixer_num; /* oss mixer to use, if any */
|
||||
|
||||
AUDIO_FORMAT StreamType;
|
||||
AUDIO_SYNC_MODE SyncMode;
|
||||
bool started;
|
||||
|
||||
int volume;
|
||||
|
||||
void openDevice(void);
|
||||
void closeDevice(void);
|
||||
|
||||
int do_mute(bool enable, bool remember);
|
||||
void setBypassMode(bool disable);
|
||||
public:
|
||||
/* construct & destruct */
|
||||
cAudio(void *, void *, void *);
|
||||
~cAudio(void);
|
||||
|
||||
void *GetHandle() { return NULL; };
|
||||
/* shut up */
|
||||
int mute(bool remember = true) { return do_mute(true, remember); };
|
||||
int unmute(bool remember = true) { return do_mute(false, remember); };
|
||||
|
||||
/* volume, min = 0, max = 255 */
|
||||
int setVolume(unsigned int left, unsigned int right);
|
||||
int getVolume(void) { return volume;}
|
||||
bool getMuteStatus(void) { return Muted; };
|
||||
|
||||
/* start and stop audio */
|
||||
int Start(void);
|
||||
int Stop(void);
|
||||
bool Pause(bool Pcm = true);
|
||||
void SetStreamType(AUDIO_FORMAT type);
|
||||
void SetSyncMode(AVSYNC_TYPE Mode);
|
||||
|
||||
/* select channels */
|
||||
int setChannel(int channel);
|
||||
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
|
||||
int WriteClip(unsigned char * buffer, int size);
|
||||
int StopClip();
|
||||
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
|
||||
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
|
||||
bool IsHdmiDDSupported();
|
||||
void SetHdmiDD(bool enable);
|
||||
void SetSpdifDD(bool enable);
|
||||
void ScheduleMute(bool On);
|
||||
void EnableAnalogOut(bool enable);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1
azbox/cs_api.h
Symbolic link
1
azbox/cs_api.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/cs_api.h
|
523
azbox/dmx.cpp
Normal file
523
azbox/dmx.cpp
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* cDemux implementation for azbox receivers (tested on azbox me and minime)
|
||||
*
|
||||
* derived from libtriple/dmx_td.cpp
|
||||
*
|
||||
* (C) 2010-2013 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "dmx_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
/* Ugh... see comment in destructor for details... */
|
||||
#include "video_lib.h"
|
||||
extern cVideo *videoDecoder;
|
||||
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, this, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_DEMUX, NULL, args)
|
||||
|
||||
#define dmx_err(_errfmt, _errstr, _revents) do { \
|
||||
uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\
|
||||
if (dmx_type == DMX_PSI_CHANNEL) { \
|
||||
_pid = s_flt.pid; _f = s_flt.filter.filter[0]; \
|
||||
} else { \
|
||||
_pid = p_flt.pid; \
|
||||
}; \
|
||||
lt_info("%s " _errfmt " fd:%d, ev:0x%x %s pid:0x%04hx flt:0x%02hx\n", \
|
||||
__func__, _errstr, fd, _revents, DMX_T[dmx_type], _pid, _f); \
|
||||
} while(0);
|
||||
|
||||
cDemux *videoDemux = NULL;
|
||||
cDemux *audioDemux = NULL;
|
||||
//cDemux *pcrDemux = NULL;
|
||||
|
||||
static const char *DMX_T[] = {
|
||||
"DMX_INVALID",
|
||||
"DMX_VIDEO",
|
||||
"DMX_AUDIO",
|
||||
"DMX_PES",
|
||||
"DMX_PSI",
|
||||
"DMX_PIP",
|
||||
"DMX_TP",
|
||||
"DMX_PCR"
|
||||
};
|
||||
|
||||
/* map the device numbers. for now only demux0 is used */
|
||||
static const char *devname[] = {
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0"
|
||||
};
|
||||
|
||||
/* uuuugly */
|
||||
static int dmx_tp_count = 0;
|
||||
#define MAX_TS_COUNT 8
|
||||
|
||||
cDemux::cDemux(int n)
|
||||
{
|
||||
if (n < 0 || n > 2)
|
||||
{
|
||||
lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n);
|
||||
num = 0;
|
||||
}
|
||||
else
|
||||
num = n;
|
||||
fd = -1;
|
||||
measure = false;
|
||||
last_measure = 0;
|
||||
last_data = 0;
|
||||
}
|
||||
|
||||
cDemux::~cDemux()
|
||||
{
|
||||
lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd);
|
||||
Close();
|
||||
/* in zapit.cpp, videoDemux is deleted after videoDecoder
|
||||
* in the video watchdog, we access videoDecoder
|
||||
* the thread still runs after videoDecoder has been deleted
|
||||
* => set videoDecoder to NULL here to make the check in the
|
||||
* watchdog thread pick this up.
|
||||
* This is ugly, but it saves me from changing neutrino
|
||||
*
|
||||
* if the delete order in neutrino will ever be changed, this
|
||||
* will blow up badly :-(
|
||||
*/
|
||||
if (dmx_type == DMX_VIDEO_CHANNEL)
|
||||
videoDecoder = NULL;
|
||||
}
|
||||
|
||||
bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize)
|
||||
{
|
||||
int devnum = num;
|
||||
int flags = O_RDWR|O_CLOEXEC;
|
||||
if (fd > -1)
|
||||
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
|
||||
|
||||
if (pes_type != DMX_PSI_CHANNEL)
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
fd = open(devname[devnum], flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]);
|
||||
return false;
|
||||
}
|
||||
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
|
||||
num, DMX_T[pes_type], pes_type, uBufferSize, fd);
|
||||
|
||||
dmx_type = pes_type;
|
||||
#if 0
|
||||
if (!pesfds.empty())
|
||||
{
|
||||
lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */
|
||||
return false;
|
||||
}
|
||||
int n = DMX_SOURCE_FRONT0;
|
||||
if (ioctl(fd, DMX_SET_SOURCE, &n) < 0)
|
||||
lt_info("%s DMX_SET_SOURCE failed!\n", __func__);
|
||||
#endif
|
||||
if (uBufferSize > 0)
|
||||
{
|
||||
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
|
||||
if (ioctl(fd, DMX_SET_BUFFER_SIZE, uBufferSize) < 0)
|
||||
lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__);
|
||||
}
|
||||
buffersize = uBufferSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cDemux::Close(void)
|
||||
{
|
||||
lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return;
|
||||
}
|
||||
|
||||
pesfds.clear();
|
||||
ioctl(fd, DMX_STOP);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
if (measure)
|
||||
return;
|
||||
if (dmx_type == DMX_TP_CHANNEL)
|
||||
{
|
||||
dmx_tp_count--;
|
||||
if (dmx_tp_count < 0)
|
||||
{
|
||||
lt_info("%s dmx_tp_count < 0!!\n", __func__);
|
||||
dmx_tp_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cDemux::Start(bool)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return false;
|
||||
}
|
||||
ioctl(fd, DMX_START);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::Stop(void)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return false;
|
||||
}
|
||||
ioctl(fd, DMX_STOP);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDemux::Read(unsigned char *buff, int len, int timeout)
|
||||
{
|
||||
#if 0
|
||||
if (len != 4095 && timeout != 100)
|
||||
fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n",
|
||||
__FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout);
|
||||
#endif
|
||||
int rc;
|
||||
int to = timeout;
|
||||
/* using a one-dimensional array seems to avoid strange segfaults / memory corruption?? */
|
||||
struct pollfd ufds[1];
|
||||
ufds[0].fd = fd;
|
||||
ufds[0].events = POLLIN|POLLPRI|POLLERR;
|
||||
ufds[0].revents = 0;
|
||||
|
||||
/* hack: if the frontend loses and regains lock, the demuxer often will not
|
||||
* return from read(), so as a "emergency exit" for e.g. NIT scan, set a (long)
|
||||
* timeout here */
|
||||
if (dmx_type == DMX_PSI_CHANNEL && timeout <= 0)
|
||||
to = 60 * 1000;
|
||||
|
||||
if (to > 0)
|
||||
{
|
||||
retry:
|
||||
rc = ::poll(ufds, 1, to);
|
||||
if (!rc)
|
||||
{
|
||||
if (timeout == 0) /* we took the emergency exit */
|
||||
{
|
||||
dmx_err("timed out for timeout=0!, %s", "", 0);
|
||||
return -1; /* this timeout is an error */
|
||||
}
|
||||
return 0; // timeout
|
||||
}
|
||||
else if (rc < 0)
|
||||
{
|
||||
dmx_err("poll: %s,", strerror(errno), 0)
|
||||
//lt_info("%s poll: %m\n", __FUNCTION__);
|
||||
/* happens, when running under gdb... */
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */
|
||||
{
|
||||
dmx_err("received %s,", "POLLERR", ufds.revents);
|
||||
/* this seems to happen sometimes at recording start, without bad effects */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (ufds[0].revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */
|
||||
{
|
||||
dmx_err("received %s,", "POLLHUP", ufds[0].revents);
|
||||
return -1;
|
||||
}
|
||||
if (!(ufds[0].revents & POLLIN)) /* we requested POLLIN but did not get it? */
|
||||
{
|
||||
dmx_err("received %s, please report!", "POLLIN", ufds[0].revents);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ::read(fd, buff, len);
|
||||
//fprintf(stderr, "fd %d ret: %d\n", fd, rc);
|
||||
if (rc < 0)
|
||||
dmx_err("read: %s", strerror(errno), 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter,
|
||||
const unsigned char * const mask, int len, int timeout,
|
||||
const unsigned char * const negmask)
|
||||
{
|
||||
memset(&s_flt, 0, sizeof(s_flt));
|
||||
|
||||
if (len > DMX_FILTER_SIZE)
|
||||
{
|
||||
lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE);
|
||||
len = DMX_FILTER_SIZE;
|
||||
}
|
||||
s_flt.pid = pid;
|
||||
s_flt.timeout = timeout;
|
||||
memcpy(s_flt.filter.filter, filter, len);
|
||||
memcpy(s_flt.filter.mask, mask, len);
|
||||
if (negmask != NULL)
|
||||
memcpy(s_flt.filter.mode, negmask, len);
|
||||
|
||||
s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC;
|
||||
|
||||
int to = 0;
|
||||
switch (filter[0]) {
|
||||
case 0x00: /* program_association_section */
|
||||
to = 2000;
|
||||
break;
|
||||
case 0x01: /* conditional_access_section */
|
||||
to = 6000;
|
||||
break;
|
||||
case 0x02: /* program_map_section */
|
||||
to = 1500;
|
||||
break;
|
||||
case 0x03: /* transport_stream_description_section */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x04 - 0x3F: reserved */
|
||||
case 0x40: /* network_information_section - actual_network */
|
||||
to = 10000;
|
||||
break;
|
||||
case 0x41: /* network_information_section - other_network */
|
||||
to = 15000;
|
||||
break;
|
||||
case 0x42: /* service_description_section - actual_transport_stream */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x43 - 0x45: reserved for future use */
|
||||
case 0x46: /* service_description_section - other_transport_stream */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x47 - 0x49: reserved for future use */
|
||||
case 0x4A: /* bouquet_association_section */
|
||||
to = 11000;
|
||||
break;
|
||||
/* 0x4B - 0x4D: reserved for future use */
|
||||
case 0x4E: /* event_information_section - actual_transport_stream, present/following */
|
||||
to = 2000;
|
||||
break;
|
||||
case 0x4F: /* event_information_section - other_transport_stream, present/following */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */
|
||||
/* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */
|
||||
case 0x70: /* time_date_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
s_flt.flags |= DMX_ONESHOT;
|
||||
//s_flt.pid = 0x0014;
|
||||
to = 30000;
|
||||
break;
|
||||
case 0x71: /* running_status_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x72: /* stuffing_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x73: /* time_offset_section */
|
||||
s_flt.flags |= DMX_ONESHOT;
|
||||
//s_flt.pid = 0x0014;
|
||||
to = 30000;
|
||||
break;
|
||||
/* 0x74 - 0x7D: reserved for future use */
|
||||
case 0x7E: /* discontinuity_information_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x7F: /* selection_information_section */
|
||||
to = 0;
|
||||
break;
|
||||
/* 0x80 - 0x8F: ca_message_section */
|
||||
/* 0x90 - 0xFE: user defined */
|
||||
/* 0xFF: reserved */
|
||||
default:
|
||||
break;
|
||||
// return -1;
|
||||
}
|
||||
/* the negmask == NULL is a hack: the users of negmask are PMT-update
|
||||
* and sectionsd EIT-Version change. And they really want no timeout
|
||||
* if timeout == 0 instead of "default timeout" */
|
||||
if (timeout == 0 && negmask == NULL)
|
||||
s_flt.timeout = to;
|
||||
|
||||
lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num,
|
||||
pid, fd, DMX_T[dmx_type], len, s_flt.timeout,s_flt.flags, s_flt.filter.filter[0]);
|
||||
#if 0
|
||||
fprintf(stderr,"filt: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.filter[i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mask: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mask [i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mode: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mode [i]);fprintf(stderr,"\n");
|
||||
#endif
|
||||
ioctl (fd, DMX_STOP);
|
||||
if (ioctl(fd, DMX_SET_FILTER, &s_flt) < 0)
|
||||
return false;
|
||||
ioctl(fd, DMX_START);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::pesFilter(const unsigned short pid)
|
||||
{
|
||||
/* allow PID 0 for web streaming e.g.
|
||||
* this check originally is from tuxbox cvs but I'm not sure
|
||||
* what it is good for...
|
||||
if (pid <= 0x0001 && dmx_type != DMX_PCR_ONLY_CHANNEL)
|
||||
return false;
|
||||
*/
|
||||
if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff)
|
||||
return false;
|
||||
|
||||
lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]);
|
||||
|
||||
memset(&p_flt, 0, sizeof(p_flt));
|
||||
p_flt.pid = pid;
|
||||
p_flt.output = DMX_OUT_DECODER;
|
||||
p_flt.input = DMX_IN_FRONTEND;
|
||||
|
||||
switch (dmx_type) {
|
||||
case DMX_PCR_ONLY_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_PCR;
|
||||
break;
|
||||
case DMX_AUDIO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_AUDIO;
|
||||
break;
|
||||
case DMX_VIDEO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_VIDEO;
|
||||
break;
|
||||
case DMX_PES_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TAP;
|
||||
break;
|
||||
case DMX_TP_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||
break;
|
||||
default:
|
||||
lt_info("%s #%d invalid dmx_type %d!\n", __func__, num, dmx_type);
|
||||
return false;
|
||||
}
|
||||
return (ioctl(fd, DMX_SET_PES_FILTER, &p_flt) >= 0);
|
||||
}
|
||||
|
||||
void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/)
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
}
|
||||
|
||||
void *cDemux::getBuffer()
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *cDemux::getChannel()
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDemux::addPid(unsigned short Pid)
|
||||
{
|
||||
lt_debug("%s: pid 0x%04hx\n", __func__, Pid);
|
||||
pes_pids pfd;
|
||||
int ret;
|
||||
if (dmx_type != DMX_TP_CHANNEL)
|
||||
{
|
||||
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||
return false;
|
||||
}
|
||||
if (fd == -1)
|
||||
lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
|
||||
pfd.fd = fd; /* dummy */
|
||||
pfd.pid = Pid;
|
||||
pesfds.push_back(pfd);
|
||||
ret = (ioctl(fd, DMX_ADD_PID, &Pid));
|
||||
if (ret < 0)
|
||||
lt_info("%s: DMX_ADD_PID (%m)\n", __func__);
|
||||
return (ret != -1);
|
||||
}
|
||||
|
||||
void cDemux::removePid(unsigned short Pid)
|
||||
{
|
||||
if (dmx_type != DMX_TP_CHANNEL)
|
||||
{
|
||||
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||
return;
|
||||
}
|
||||
for (std::vector<pes_pids>::iterator i = pesfds.begin(); i != pesfds.end(); ++i)
|
||||
{
|
||||
if ((*i).pid == Pid) {
|
||||
lt_debug("removePid: removing demux fd %d pid 0x%04x\n", fd, Pid);
|
||||
if (ioctl(fd, DMX_REMOVE_PID, Pid) < 0)
|
||||
lt_info("%s: (DMX_REMOVE_PID, 0x%04hx): %m\n", __func__, Pid);
|
||||
pesfds.erase(i);
|
||||
return; /* TODO: what if the same PID is there multiple times */
|
||||
}
|
||||
}
|
||||
lt_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid);
|
||||
}
|
||||
|
||||
void cDemux::getSTC(int64_t * STC)
|
||||
{
|
||||
/* apparently I can only get the PTS of the video decoder,
|
||||
* but that's good enough for dvbsub */
|
||||
lt_debug("%s #%d\n", __func__, num);
|
||||
int64_t pts = 0;
|
||||
if (videoDecoder)
|
||||
pts = videoDecoder->GetPTS();
|
||||
*STC = pts;
|
||||
}
|
||||
|
||||
int cDemux::getUnit(void)
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
/* just guessed that this is the right thing to do.
|
||||
right now this is only used by the CA code which is stubbed out
|
||||
anyway */
|
||||
return num;
|
||||
}
|
||||
|
||||
bool cDemux::SetSource(int unit, int source)
|
||||
{
|
||||
lt_info_c("%s(%d, %d): not implemented yet\n", __func__, unit, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDemux::GetSource(int unit)
|
||||
{
|
||||
lt_info_c("%s(%d): not implemented yet\n", __func__, unit);
|
||||
return 0;
|
||||
}
|
1
azbox/dmx_cs.h
Normal file
1
azbox/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "dmx_lib.h"
|
70
azbox/dmx_lib.h
Normal file
70
azbox/dmx_lib.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef __DEMUX_TD_H
|
||||
#define __DEMUX_TD_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
#include "../common/cs_types.h"
|
||||
|
||||
#define MAX_DMX_UNITS 4
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DMX_INVALID = 0,
|
||||
DMX_VIDEO_CHANNEL = 1,
|
||||
DMX_AUDIO_CHANNEL,
|
||||
DMX_PES_CHANNEL,
|
||||
DMX_PSI_CHANNEL,
|
||||
DMX_PIP_CHANNEL,
|
||||
DMX_TP_CHANNEL,
|
||||
DMX_PCR_ONLY_CHANNEL
|
||||
} DMX_CHANNEL_TYPE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
unsigned short pid;
|
||||
} pes_pids;
|
||||
|
||||
class cDemux
|
||||
{
|
||||
private:
|
||||
int num;
|
||||
int fd;
|
||||
int buffersize;
|
||||
bool measure;
|
||||
uint64_t last_measure, last_data;
|
||||
DMX_CHANNEL_TYPE dmx_type;
|
||||
std::vector<pes_pids> pesfds;
|
||||
struct dmx_sct_filter_params s_flt;
|
||||
struct dmx_pes_filter_params p_flt;
|
||||
public:
|
||||
|
||||
bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0);
|
||||
void Close(void);
|
||||
bool Start(bool record = false);
|
||||
bool Stop(void);
|
||||
int Read(unsigned char *buff, int len, int Timeout = 0);
|
||||
bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL);
|
||||
bool pesFilter(const unsigned short pid);
|
||||
void SetSyncMode(AVSYNC_TYPE mode);
|
||||
void * getBuffer();
|
||||
void * getChannel();
|
||||
DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; };
|
||||
bool addPid(unsigned short pid);
|
||||
void getSTC(int64_t * STC);
|
||||
int getUnit(void);
|
||||
static bool SetSource(int unit, int source);
|
||||
static int GetSource(int unit);
|
||||
// TD only functions
|
||||
int getFD(void) { return fd; }; /* needed by cPlayback class */
|
||||
void removePid(unsigned short Pid); /* needed by cRecord class */
|
||||
std::vector<pes_pids> getPesPids(void) { return pesfds; };
|
||||
//
|
||||
cDemux(int num = 0);
|
||||
~cDemux();
|
||||
};
|
||||
|
||||
#endif //__DEMUX_H
|
83
azbox/e2mruainclude.h
Normal file
83
azbox/e2mruainclude.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//Additional Azbox
|
||||
enum key_command {
|
||||
KEY_COMMAND_QUIT_ALL = 100,
|
||||
KEY_COMMAND_QUIT,
|
||||
KEY_COMMAND_PLAY,
|
||||
KEY_COMMAND_PAUSE,
|
||||
KEY_COMMAND_RESUME,
|
||||
KEY_COMMAND_STOP,
|
||||
KEY_COMMAND_SEEK_TO_TIME,
|
||||
KEY_COMMAND_SEEK_TO_PERCENT,
|
||||
KEY_COMMAND_NEXT_PICT,
|
||||
KEY_COMMAND_FAST_FWD_ALL_FRAMES,
|
||||
KEY_COMMAND_SLOW_FWD_ALL_FRAMES,
|
||||
KEY_COMMAND_IFRAMES_FWD,
|
||||
KEY_COMMAND_IFRAMES_BWD,
|
||||
KEY_COMMAND_SILENT_FWD,
|
||||
KEY_COMMAND_SILENT_BWD,
|
||||
KEY_COMMAND_SWITCH_VIDEO,
|
||||
KEY_COMMAND_SWITCH_AUDIO,
|
||||
KEY_COMMAND_SWITCH_PROGRAM,
|
||||
KEY_COMMAND_SWITCH_SUBS,
|
||||
KEY_COMMAND_SWITCH_MULTICAST,
|
||||
KEY_COMMAND_APPLY_AV_DELAY,
|
||||
KEY_COMMAND_SUBS_CHANGE_DELAY,
|
||||
KEY_COMMAND_SUBS_INCREASE_FONT_SIZE,
|
||||
KEY_COMMAND_SUBS_DECREASE_FONT_SIZE,
|
||||
KEY_COMMAND_SUBS_INCREASE_POS_Y,
|
||||
KEY_COMMAND_SUBS_DECREASE_POS_Y,
|
||||
KEY_COMMAND_SUBS_SWITCH_ENCODING,
|
||||
KEY_COMMAND_SUBS_RESET_ALL,
|
||||
KEY_COMMAND_SUBS_CHANGE_COLOR,
|
||||
KEY_COMMAND_DEBUG,
|
||||
KEY_COMMAND_PRINT_INFO,
|
||||
KEY_COMMAND_FULL_SCREEN,
|
||||
KEY_COMMAND_HALF_SCREEN,
|
||||
KEY_COMMAND_INCREASE_SIZE,
|
||||
KEY_COMMAND_DECREASE_SIZE,
|
||||
KEY_COMMAND_MOVE_LEFT,
|
||||
KEY_COMMAND_MOVE_RIGHT,
|
||||
KEY_COMMAND_MOVE_TOP,
|
||||
KEY_COMMAND_MOVE_BOTTOM,
|
||||
KEY_COMMAND_NONLINEAR_WIDTH,
|
||||
KEY_COMMAND_NONLINEAR_LEVEL,
|
||||
KEY_COMMAND_SWITCH_SCALER,
|
||||
KEY_COMMAND_HELP,
|
||||
KEY_COMMAND_FAST_FWD_WITH_AUDIO,
|
||||
KEY_COMMAND_SLOW_FWD_WITH_AUDIO,
|
||||
KEY_COMMAND_PRINT_TXT,
|
||||
SPECIAL_KEY_COMMAND_IFRAMES_FWD,
|
||||
SPECIAL_KEY_COMMAND_IFRAMES_BWD,
|
||||
SPECIAL_KEY_COMMAND_NEXT_AUDIO,
|
||||
SPECIAL_KEY_COMMAND_NEXT_SUBS,
|
||||
};
|
||||
|
||||
enum custom_command {
|
||||
CUSTOM_COMMAND_GETLENGTH = 200,
|
||||
CUSTOM_COMMAND_GETPOSITION,
|
||||
CUSTOM_COMMAND_AUDIOGETPOSITION,
|
||||
CUSTOM_COMMAND_SEEK_RELATIVE_FWD,
|
||||
CUSTOM_COMMAND_SEEK_RELATIVE_BWD,
|
||||
CUSTOM_COMMAND_SUBS_COUNT,
|
||||
CUSTOM_COMMAND_GET_SUB_BY_ID,
|
||||
CUSTOM_COMMAND_AUDIO_COUNT,
|
||||
CUSTOM_COMMAND_GET_AUDIO_BY_ID,
|
||||
CUSTOM_COMMAND_AUDIO_CUR_STREAM,
|
||||
CUSTOM_COMMAND_SUBS_CUR_STREAM,
|
||||
CUSTOM_COMMAND_TRICK_SEEK,
|
||||
CUSTOM_COMMAND_SET_SUB_SIZE,
|
||||
CUSTOM_COMMAND_SET_SUB_ENCODING,
|
||||
CUSTOM_COMMAND_SET_SUB_POS,
|
||||
};
|
||||
|
||||
enum event_msg {
|
||||
EVENT_MSG_FDOPEN = 300,
|
||||
EVENT_MSG_PLAYBACK_STARTED,
|
||||
EVENT_MSG_STOPPED,
|
||||
EVENT_MSG_PAUSED,
|
||||
EVENT_MSG_BUFFERING,
|
||||
EVENT_MSG_EOS,
|
||||
EVENT_MSG_SUB_CHANGED,
|
||||
};
|
||||
|
||||
//int fd_cmd, fd_in, fd_out, fd_event, msg;
|
50
azbox/hardware_caps.c
Normal file
50
azbox/hardware_caps.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* determine the capabilities of the hardware.
|
||||
* part of libstb-hal
|
||||
*
|
||||
* (C) 2010-2012 Stefan Seyfried
|
||||
*
|
||||
* License: GPL v2 or later
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <hardware_caps.h>
|
||||
|
||||
static int initialized = 0;
|
||||
static hw_caps_t caps;
|
||||
|
||||
hw_caps_t *get_hwcaps(void)
|
||||
{
|
||||
if (initialized)
|
||||
return ∩︀
|
||||
|
||||
memset(&caps, 0, sizeof(hw_caps_t));
|
||||
|
||||
initialized = 1;
|
||||
caps.can_shutdown = 1;
|
||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
||||
caps.has_HDMI = 1;
|
||||
caps.display_xres = 8;
|
||||
strcpy(caps.boxvendor, "AZBox");
|
||||
const char *tmp;
|
||||
char buf[64];
|
||||
int len = -1;
|
||||
int fd = open("/proc/stb/info/model", O_RDONLY);
|
||||
if (fd != -1) {
|
||||
len = read(fd, buf, sizeof(buf) - 1);
|
||||
close(fd);
|
||||
}
|
||||
if (len > 0) {
|
||||
buf[len] = 0;
|
||||
strcpy(caps.boxname, buf);
|
||||
}
|
||||
else
|
||||
strcpy(caps.boxname, "(unknown model)");
|
||||
|
||||
return ∩︀
|
||||
}
|
21
azbox/init.cpp
Normal file
21
azbox/init.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <unistd.h>
|
||||
#include "init_lib.h"
|
||||
#include "lt_debug.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
||||
|
||||
static bool initialized = false;
|
||||
|
||||
void init_td_api()
|
||||
{
|
||||
if (!initialized)
|
||||
lt_debug_init();
|
||||
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void shutdown_td_api()
|
||||
{
|
||||
lt_info("%s, initialized = %d\n", __func__, (int)initialized);
|
||||
initialized = false;
|
||||
}
|
5
azbox/init_lib.h
Normal file
5
azbox/init_lib.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef __INIT_TD_H
|
||||
#define __INIT_TD_H
|
||||
void init_td_api();
|
||||
void shutdown_td_api();
|
||||
#endif
|
521
azbox/playback.cpp
Normal file
521
azbox/playback.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* cPlayback implementation for azbox
|
||||
* this is actually just a wrapper around rmfp_player which does
|
||||
* all the heavy listing
|
||||
*
|
||||
* based on the original aztrino implementation, but almost
|
||||
* completely rewritten since then
|
||||
*
|
||||
* some of the methods and constants were found by stracing the
|
||||
* AZPlay enigma2 plugin...
|
||||
*
|
||||
* (C) 2012 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <pty.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "lt_debug.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_PLAYBACK, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, this, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_PLAYBACK, NULL, args)
|
||||
|
||||
#include <proc_tools.h>
|
||||
|
||||
/* the file-based commands work better than the FIFOs... */
|
||||
#define CMD_FILE "/tmp/rmfp.cmd2"
|
||||
#define IN_FILE "/tmp/rmfp.in2"
|
||||
#define OUT_FILE "/tmp/rmfp.out2"
|
||||
|
||||
#include "playback.h"
|
||||
|
||||
extern "C"{
|
||||
#include "e2mruainclude.h"
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* useful for debugging */
|
||||
static time_t monotonic_ms(void)
|
||||
{
|
||||
struct timespec t;
|
||||
time_t ret;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &t))
|
||||
{
|
||||
perror("monotonic_ms clock_gettime");
|
||||
return -1;
|
||||
}
|
||||
ret = ((t.tv_sec + 604800)& 0x01FFFFF) * 1000; /* avoid overflow */
|
||||
ret += t.tv_nsec / 1000000;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* the mutex makes sure that commands are not interspersed */
|
||||
bool cPlayback::rmfp_command(int cmd, int param, bool has_param, char *buf, int buflen)
|
||||
{
|
||||
lt_info("%s: %d %d %d %d\n", __func__, cmd, param, has_param, buflen);
|
||||
bool ret = true;
|
||||
int fd;
|
||||
if (cmd == 222)
|
||||
{
|
||||
if (pthread_mutex_trylock(&rmfp_cmd_mutex))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
pthread_mutex_lock(&rmfp_cmd_mutex);
|
||||
unlink(OUT_FILE);
|
||||
if (has_param)
|
||||
{
|
||||
fd = open(IN_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
||||
dprintf(fd, "%i", param);
|
||||
close(fd);
|
||||
}
|
||||
fd = open(CMD_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
||||
dprintf(fd, "%i", cmd);
|
||||
close(fd);
|
||||
int n = 0, m = 0;
|
||||
if (buflen > 0)
|
||||
{
|
||||
while ((fd = open(OUT_FILE, O_RDONLY)) == -1) {
|
||||
if (++m > 500) { /* don't wait more than 5 seconds */
|
||||
lt_info("%s: timed out waiting for %s (cmd %d par %d buflen %d\n",
|
||||
__func__, OUT_FILE, cmd, param, buflen);
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
unlink(OUT_FILE);
|
||||
n = read(fd, buf, buflen);
|
||||
close(fd);
|
||||
/* some commands (CUSTOM_COMMAND_GET_AUDIO_BY_ID for example) actually
|
||||
* return the answer in two successive writes, as we are not using the
|
||||
* FIFO, we need to make sure that the file is deleted immediately, because
|
||||
* rmfp_player will not overwrite it if it exists */
|
||||
while ((fd = open(OUT_FILE, O_RDONLY)) == -1) {
|
||||
if (++m > 10)
|
||||
break;
|
||||
usleep(1000);
|
||||
}
|
||||
if (fd > -1)
|
||||
{
|
||||
read(fd, buf + n, buflen - n);
|
||||
unlink(OUT_FILE);
|
||||
close(fd);
|
||||
}
|
||||
buf[buflen - 1] = '0';
|
||||
}
|
||||
out:
|
||||
pthread_mutex_unlock(&rmfp_cmd_mutex);
|
||||
if (cmd != 222) /* called tooo often :-) */
|
||||
lt_info("%s: reply: '%s' ret: %d m:%d\n", __func__, buf?buf:"(none)", ret, m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* runs the rmfp_player in a terminal
|
||||
* just doing popen() or similar does not work because then
|
||||
* the output will be buffered after starting up and we will only
|
||||
* see "Playback has started..." after the player exits
|
||||
*/
|
||||
void cPlayback::run_rmfp()
|
||||
{
|
||||
lt_debug("%s: starting\n", __func__);
|
||||
thread_started = true;
|
||||
//Watch for the space at the end
|
||||
std::string base = "rmfp_player -dram 1 -ve 1 -waitexit ";
|
||||
std::string filename(mfilename);
|
||||
std::string file = '"' + filename + '"';
|
||||
std::string final = base + file;
|
||||
|
||||
if (playMode == PLAYMODE_TS && mduration != 0)
|
||||
{
|
||||
std::stringstream duration;
|
||||
duration << (mduration /** 60000LL*/);
|
||||
final = base + "-duration " + duration.str() + " " + file;
|
||||
}
|
||||
|
||||
pid_t pid = 0;
|
||||
int master;
|
||||
pid = forkpty(&master, NULL, NULL, NULL);
|
||||
if (! pid) {
|
||||
execl("/bin/sh", "sh", "-c", final.c_str(), (char *)0);
|
||||
lt_info("%s: execl returned: %m\n", __func__);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
char *output=NULL;
|
||||
size_t n = 0;
|
||||
ssize_t len;
|
||||
FILE *f = fdopen(master, "r");
|
||||
while ((len = getline(&output, &n, f)) != -1)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
len--;
|
||||
if (!isspace(output[len]))
|
||||
break;
|
||||
output[len] = '\0';
|
||||
}
|
||||
lt_info("%s out: '%s'\n", __func__, output);
|
||||
if (strstr(output, "Playback has started..."))
|
||||
{
|
||||
playing = 1;
|
||||
lt_info("%s: ===================> playing = true\n", __func__);
|
||||
}
|
||||
else if (strstr(output, "End of file..."))
|
||||
{
|
||||
playing = 1; /* this can happen without "Playback has started..." */
|
||||
eof_reached = true;
|
||||
lt_info("%s: ===================> eof_reached = true\n", __func__);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
int s;
|
||||
while (waitpid(pid, &s, WNOHANG) > 0) {};
|
||||
if (output)
|
||||
free(output);
|
||||
}
|
||||
|
||||
lt_info("%s: terminating\n", __func__);
|
||||
if (playing == 0) /* playback did not start */
|
||||
playing = 2;
|
||||
else
|
||||
playing = 0;
|
||||
eof_reached = true;
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* helper function to call the cpp thread loop */
|
||||
void *execute_rua_thread(void *c)
|
||||
{
|
||||
cPlayback *obj = (cPlayback *)c;
|
||||
lt_info_c("%s\n", __func__);
|
||||
obj->run_rmfp();
|
||||
/* free(obj); // this is most likely wrong */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
bool cPlayback::Open(playmode_t PlayMode)
|
||||
{
|
||||
static const char *aPLAYMODE[] = {
|
||||
"PLAYMODE_TS",
|
||||
"PLAYMODE_FILE"
|
||||
};
|
||||
playMode = PlayMode;
|
||||
if (playMode > 1)
|
||||
{
|
||||
lt_info("%s: PlayMode %d out of range!\n", __func__, PlayMode);
|
||||
playMode = PLAYMODE_FILE;
|
||||
}
|
||||
|
||||
lt_info("%s: mode %d (%s)\n", __func__, PlayMode, aPLAYMODE[PlayMode]);
|
||||
#if 0
|
||||
while (access("/tmp/continue", R_OK))
|
||||
sleep(1);
|
||||
#endif
|
||||
|
||||
char c[2] = "0";
|
||||
int i = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
proc_get("/proc/player", c, 2);
|
||||
if (c[0] != '0')
|
||||
break;
|
||||
i++;
|
||||
if (i > 10)
|
||||
{
|
||||
lt_info("%s: ERROR - player is not idle after 10 seconds!\n", __func__);
|
||||
open_success = false;
|
||||
return false;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
proc_put("/proc/player", "2", 2);
|
||||
lt_info("%s: /proc/player switched to '2'\n", __func__);
|
||||
|
||||
unlink(CMD_FILE);
|
||||
unlink(IN_FILE);
|
||||
unlink(OUT_FILE);
|
||||
|
||||
open_success = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Used by Fileplay
|
||||
bool cPlayback::Start(char *filename, unsigned short vpid, int vtype, unsigned short _apid,
|
||||
int ac3, unsigned int duration)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
lt_info("%s: filename=%s\n", __func__, filename);
|
||||
lt_info("%s: vpid=%u vtype=%d apid=%u ac3=%d duration=%i open_success=%d\n",
|
||||
__func__, vpid, vtype, _apid, ac3, duration, open_success);
|
||||
|
||||
if (!open_success)
|
||||
return false;
|
||||
|
||||
eof_reached = false;
|
||||
//create playback path
|
||||
apid = 0;
|
||||
subpid = 0;
|
||||
mfilename = filename;
|
||||
mduration = duration;
|
||||
if (pthread_create(&thread, 0, execute_rua_thread, this) != 0)
|
||||
{
|
||||
lt_info("%s: error creating rmfp_player thread! (out of memory?)\n", __func__);
|
||||
ret = false;
|
||||
}
|
||||
while (! playing)
|
||||
sleep(1);
|
||||
if (playing == 2)
|
||||
playing = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cPlayback::Close(void)
|
||||
{
|
||||
lt_info("%s: playing %d thread_started %d\n", __func__, playing, thread_started);
|
||||
|
||||
if (thread_started)
|
||||
{
|
||||
rmfp_command(KEY_COMMAND_QUIT_ALL, 0, false, NULL, 0);
|
||||
|
||||
if (pthread_join(thread, NULL))
|
||||
lt_info("%s: error joining rmfp thread (%m)\n", __func__);
|
||||
playing = 0;
|
||||
thread_started = false;
|
||||
}
|
||||
else
|
||||
lt_info("%s: Warning: thread_started == false!\n", __func__);
|
||||
|
||||
if (open_success)
|
||||
{
|
||||
proc_put("/proc/player", "1", 2);
|
||||
open_success = false;
|
||||
lt_info("%s: /proc/player switched to '1'\n", __func__);
|
||||
usleep(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
bool cPlayback::SetAPid(unsigned short pid, int /*ac3*/)
|
||||
{
|
||||
lt_info("%s: pid %i\n", __func__, pid);
|
||||
if (pid != apid) {
|
||||
rmfp_command(KEY_COMMAND_SWITCH_AUDIO, pid, true, NULL, 0);
|
||||
apid = pid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SelectSubtitles(int pid)
|
||||
{
|
||||
lt_info("%s: pid %i\n", __func__, pid);
|
||||
if (pid != subpid)
|
||||
{
|
||||
rmfp_command(KEY_COMMAND_SWITCH_SUBS, pid, true, NULL, 0);
|
||||
subpid = pid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetSpeed(int speed)
|
||||
{
|
||||
lt_info("%s: playing %d speed %d\n", __func__, playing, speed);
|
||||
|
||||
if (!playing)
|
||||
return false;
|
||||
|
||||
playback_speed = speed;
|
||||
|
||||
if (speed > 1 || speed < 0)
|
||||
rmfp_command(CUSTOM_COMMAND_TRICK_SEEK, speed, true, NULL, 0);
|
||||
else if (speed == 0)
|
||||
rmfp_command(KEY_COMMAND_PAUSE, 0, false, NULL, 0);
|
||||
else
|
||||
rmfp_command(KEY_COMMAND_PLAY, 0, false, NULL, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::GetSpeed(int &/*speed*/) const
|
||||
{
|
||||
#if 0
|
||||
lt_info("%s:\n", __func__);
|
||||
speed = playback_speed;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// in milliseconds
|
||||
bool cPlayback::GetPosition(int &position, int &duration)
|
||||
{
|
||||
lt_debug("%s: playing %d\n", __func__, playing);
|
||||
|
||||
if (eof_reached)
|
||||
{
|
||||
position = mduration;
|
||||
duration = mduration;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!playing)
|
||||
return false;
|
||||
|
||||
char buf[32];
|
||||
/* custom command 222 returns "12345\n1234\n",
|
||||
* first line is duration, second line is position */
|
||||
if (! rmfp_command(222, 0, false, buf, 32))
|
||||
return false;
|
||||
duration = atoi(buf);
|
||||
char *p = strchr(buf, '\n');
|
||||
if (!p)
|
||||
return false;
|
||||
position = atoi(++p);
|
||||
/* some mpegs return length 0... which would lead to "eof" after 10 seconds */
|
||||
if (duration == 0)
|
||||
duration = position + 1000;
|
||||
|
||||
if (playMode == PLAYMODE_TS)
|
||||
{
|
||||
if (position > mduration)
|
||||
mduration = position + 1000;
|
||||
duration = mduration;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetPosition(int position, bool absolute)
|
||||
{
|
||||
lt_info("%s: pos %d abs %d playing %d\n", __func__, position, absolute, playing);
|
||||
|
||||
if (!playing)
|
||||
return false;
|
||||
|
||||
int seconds = position / 1000;;
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
rmfp_command(KEY_COMMAND_SEEK_TO_TIME, seconds, true, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (position > 0)
|
||||
rmfp_command(CUSTOM_COMMAND_SEEK_RELATIVE_FWD, seconds, true, NULL, 0);
|
||||
else if (position < 0)
|
||||
rmfp_command(CUSTOM_COMMAND_SEEK_RELATIVE_BWD, seconds, true, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language)
|
||||
{
|
||||
lt_info("%s\n", __func__);
|
||||
char buf[32];
|
||||
rmfp_command(CUSTOM_COMMAND_AUDIO_COUNT, 0, false, buf, 3);
|
||||
unsigned int audio_count = atoi(buf);
|
||||
|
||||
*numpida = audio_count;
|
||||
if (audio_count > 0)
|
||||
{
|
||||
for (unsigned int aid = 0; aid < audio_count; aid++)
|
||||
{
|
||||
char streamidstring[11];
|
||||
char audio_lang[21];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
rmfp_command(CUSTOM_COMMAND_GET_AUDIO_BY_ID, aid, true, buf, 32);
|
||||
memcpy(streamidstring, buf, 10);
|
||||
streamidstring[10] = '\0';
|
||||
memcpy(audio_lang, buf + 10, 20);
|
||||
audio_lang[20] = '\0';
|
||||
apids[aid] = atoi(streamidstring);
|
||||
ac3flags[aid] = 0;
|
||||
language[aid] = audio_lang;
|
||||
lt_info("%s: #%d apid:%d lang: %s\n", __func__, aid, apids[aid], audio_lang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cPlayback::FindAllSubs(uint16_t *spids, unsigned short *supported, uint16_t *numpids, std::string *language)
|
||||
{
|
||||
lt_info("%s\n", __func__);
|
||||
char buf[32];
|
||||
|
||||
rmfp_command(CUSTOM_COMMAND_SUBS_COUNT, 0, false, buf, 3);
|
||||
unsigned int spu_count = atoi(buf);
|
||||
*numpids = spu_count;
|
||||
|
||||
if (spu_count > 0)
|
||||
{
|
||||
for (unsigned int sid = 0; sid < spu_count; sid++)
|
||||
{
|
||||
char streamidstring[11];
|
||||
char spu_lang[21];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
rmfp_command(CUSTOM_COMMAND_GET_SUB_BY_ID, sid, true, buf, 32);
|
||||
memcpy(streamidstring, buf, 10);
|
||||
streamidstring[10] = '\0';
|
||||
memcpy(spu_lang, buf + 10, 20);
|
||||
spu_lang[20] = '\0';
|
||||
spids[sid] = atoi(streamidstring);
|
||||
language[sid] = spu_lang;
|
||||
supported[sid] = 1;
|
||||
lt_info("%s: #%d apid:%d lang: %s\n", __func__, sid, spids[sid], spu_lang);
|
||||
}
|
||||
}
|
||||
//Add streamid -1 to be able to disable subtitles
|
||||
*numpids = spu_count + 1;
|
||||
spids[spu_count] = -1;
|
||||
language[spu_count] = "Disable";
|
||||
}
|
||||
|
||||
/* DVD support is not yet ready... */
|
||||
void cPlayback::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
|
||||
{
|
||||
positions.clear();
|
||||
titles.clear();
|
||||
}
|
||||
|
||||
cPlayback::cPlayback(int /*num*/)
|
||||
{
|
||||
lt_info("%s: constructor\n", __func__);
|
||||
playing = 0;
|
||||
thread_started = false;
|
||||
eof_reached = false;
|
||||
open_success = false;
|
||||
pthread_mutex_init(&rmfp_cmd_mutex, NULL);
|
||||
}
|
||||
|
||||
cPlayback::~cPlayback()
|
||||
{
|
||||
lt_info("%s\n", __func__);
|
||||
pthread_mutex_destroy(&rmfp_cmd_mutex);
|
||||
}
|
62
azbox/playback.h
Normal file
62
azbox/playback.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef __PLAYBACK_H
|
||||
#define __PLAYBACK_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
typedef enum {
|
||||
PLAYMODE_TS = 0,
|
||||
PLAYMODE_FILE,
|
||||
} playmode_t;
|
||||
|
||||
class cPlayback
|
||||
{
|
||||
private:
|
||||
pthread_mutex_t rmfp_cmd_mutex;
|
||||
int playing;
|
||||
bool eof_reached;
|
||||
int playback_speed;
|
||||
playmode_t playMode;
|
||||
bool open_success;
|
||||
uint16_t apid;
|
||||
uint16_t subpid;
|
||||
char *mfilename;
|
||||
int mduration;
|
||||
pthread_t thread;
|
||||
bool thread_started;
|
||||
/* private functions */
|
||||
bool rmfp_command(int cmd, int param, bool has_param, char *buf, int buflen);
|
||||
public:
|
||||
cPlayback(int num = 0);
|
||||
~cPlayback();
|
||||
|
||||
void run_rmfp();
|
||||
|
||||
bool Open(playmode_t PlayMode);
|
||||
void Close(void);
|
||||
bool Start(char *filename, unsigned short vpid, int vtype, unsigned short apid,
|
||||
int ac3, unsigned int duration);
|
||||
bool SetAPid(unsigned short pid, int ac3);
|
||||
bool SetSpeed(int speed);
|
||||
bool GetSpeed(int &speed) const;
|
||||
bool GetPosition(int &position, int &duration); /* pos: current time in ms, dur: file length in ms */
|
||||
bool SetPosition(int position, bool absolute = false); /* position: jump in ms */
|
||||
void FindAllPids(uint16_t *apids, unsigned short *ac3flags, uint16_t *numpida, std::string *language);
|
||||
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
|
||||
bool SelectSubtitles(int pid);
|
||||
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
||||
#if 0
|
||||
// Functions that are not used by movieplayer.cpp:
|
||||
bool Stop(void);
|
||||
bool GetOffset(off64_t &offset);
|
||||
bool IsPlaying(void) const { return playing; }
|
||||
bool IsEnabled(void) const { return enabled; }
|
||||
void * GetHandle(void);
|
||||
void * GetDmHandle(void);
|
||||
int GetCurrPlaybackSpeed(void) const { return nPlaybackSpeed; }
|
||||
void PlaybackNotify (int Event, void *pData, void *pTag);
|
||||
void DMNotify(int Event, void *pTsBuf, void *Tag);
|
||||
#endif
|
||||
};
|
||||
#endif
|
1
azbox/pwrmngr.cpp
Symbolic link
1
azbox/pwrmngr.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.cpp
|
1
azbox/pwrmngr.h
Symbolic link
1
azbox/pwrmngr.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.h
|
1
azbox/record.cpp
Symbolic link
1
azbox/record.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record.cpp
|
1
azbox/record_lib.h
Symbolic link
1
azbox/record_lib.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record_lib.h
|
635
azbox/video.cpp
Normal file
635
azbox/video.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* (C) 2002-2003 Andreas Oberritter <obi@tuxbox.org>
|
||||
* (C) 2010-2012 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/dvb/video.h>
|
||||
|
||||
#include <proc_tools.h>
|
||||
|
||||
#include "video_lib.h"
|
||||
#define VIDEO_DEVICE "/dev/dvb/adapter0/video0"
|
||||
#include "lt_debug.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args)
|
||||
#define lt_debug_c(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, NULL, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args)
|
||||
|
||||
#define fop(cmd, args...) ({ \
|
||||
int _r; \
|
||||
if (fd >= 0) { \
|
||||
if ((_r = ::cmd(fd, args)) < 0) \
|
||||
lt_info(#cmd"(fd, "#args")\n"); \
|
||||
else \
|
||||
lt_debug(#cmd"(fd, "#args")\n");\
|
||||
} \
|
||||
else { _r = fd; } \
|
||||
_r; \
|
||||
})
|
||||
|
||||
cVideo * videoDecoder = NULL;
|
||||
int system_rev = 0;
|
||||
|
||||
static bool stillpicture = false;
|
||||
static unsigned char *blank_data;
|
||||
static ssize_t blank_size;
|
||||
static pthread_mutex_t stillp_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
/* prototype */
|
||||
static void show_iframe(int fd, unsigned char *iframe, size_t st_size);
|
||||
|
||||
#define VIDEO_STREAMTYPE_MPEG2 0
|
||||
#define VIDEO_STREAMTYPE_MPEG4_H264 1
|
||||
#define VIDEO_STREAMTYPE_VC1 3
|
||||
#define VIDEO_STREAMTYPE_MPEG4_Part2 4
|
||||
#define VIDEO_STREAMTYPE_VC1_SM 5
|
||||
#define VIDEO_STREAMTYPE_MPEG1 6
|
||||
|
||||
cVideo::cVideo(int, void *, void *, unsigned int)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
|
||||
//croppingMode = VID_DISPMODE_NORM;
|
||||
//outputformat = VID_OUTFMT_RGBC_SVIDEO;
|
||||
scartvoltage = -1;
|
||||
video_standby = 0;
|
||||
fd = -1;
|
||||
|
||||
const char *blankname = "/share/tuxbox/blank_576.mpg";
|
||||
int blankfd;
|
||||
struct stat st;
|
||||
|
||||
blank_data = NULL; /* initialize */
|
||||
blank_size = 0;
|
||||
blankfd = open(blankname, O_RDONLY|O_CLOEXEC);
|
||||
if (blankfd < 0)
|
||||
lt_info("%s cannot open %s: %m", __func__, blankname);
|
||||
else
|
||||
{
|
||||
if (fstat(blankfd, &st) != -1 && st.st_size > 0)
|
||||
{
|
||||
blank_size = st.st_size;
|
||||
blank_data = (unsigned char *)malloc(blank_size);
|
||||
if (! blank_data)
|
||||
lt_info("%s malloc failed (%m)\n", __func__);
|
||||
else if (read(blankfd, blank_data, blank_size) != blank_size)
|
||||
{
|
||||
lt_info("%s short read (%m)\n", __func__);
|
||||
free(blank_data); /* don't leak... */
|
||||
blank_data = NULL;
|
||||
}
|
||||
}
|
||||
close(blankfd);
|
||||
}
|
||||
openDevice();
|
||||
Pig(-1, -1, -1, -1);
|
||||
}
|
||||
|
||||
cVideo::~cVideo(void)
|
||||
{
|
||||
closeDevice();
|
||||
if (blank_data)
|
||||
free(blank_data);
|
||||
}
|
||||
|
||||
void cVideo::openDevice(void)
|
||||
{
|
||||
int n = 0;
|
||||
lt_debug("%s\n", __func__);
|
||||
/* todo: this fd checking is racy, should be protected by a lock */
|
||||
if (fd != -1) /* already open */
|
||||
return;
|
||||
retry:
|
||||
if ((fd = open(VIDEO_DEVICE, O_RDWR|O_CLOEXEC)) < 0)
|
||||
{
|
||||
if (errno == EBUSY)
|
||||
{
|
||||
/* sometimes we get busy quickly after close() */
|
||||
usleep(50000);
|
||||
if (++n < 10)
|
||||
goto retry;
|
||||
}
|
||||
lt_info("%s cannot open %s: %m, retries %d\n", __func__, VIDEO_DEVICE, n);
|
||||
}
|
||||
playstate = VIDEO_STOPPED;
|
||||
}
|
||||
|
||||
void cVideo::closeDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
playstate = VIDEO_STOPPED;
|
||||
}
|
||||
|
||||
int cVideo::setAspectRatio(int aspect, int mode)
|
||||
{
|
||||
static const char *a[] = { "n/a", "4:3", "14:9", "16:9" };
|
||||
/* { "panscan", "letterbox", "fullscreen", "14:9", "(unset)" } */
|
||||
static const char *m[] = { "1", "2", "0", "1", "(unset)" };
|
||||
int n;
|
||||
lt_debug("%s: a:%d m:%d %s\n", __func__, aspect, mode, m[(mode < 0||mode > 3) ? 4 : mode]);
|
||||
|
||||
if (aspect > 3 || aspect == 0)
|
||||
lt_info("%s: invalid aspect: %d\n", __func__, aspect);
|
||||
else if (aspect > 0) /* -1 == don't set */
|
||||
{
|
||||
lt_debug("%s: /proc/stb/video/aspect -> %s\n", __func__, a[aspect]);
|
||||
n = proc_put("/proc/stb/video/aspect", a[aspect], strlen(a[aspect]));
|
||||
if (n < 0)
|
||||
lt_info("%s: proc_put /proc/stb/video/aspect (%m)\n", __func__);
|
||||
}
|
||||
|
||||
if (mode == -1)
|
||||
return 0;
|
||||
|
||||
lt_debug("%s: /proc/scalingmode -> %s\n", __func__, m[mode]);
|
||||
n = proc_put("/proc/scalingmode", m[mode], strlen(m[mode]));
|
||||
if (n < 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::getAspectRatio(void)
|
||||
{
|
||||
video_size_t s;
|
||||
if (fd == -1)
|
||||
{
|
||||
/* in movieplayer mode, fd is not opened -> fall back to procfs */
|
||||
int n = proc_get_hex("/proc/stb/vmpeg/0/aspect");
|
||||
return n * 2 + 1;
|
||||
}
|
||||
if (fop(ioctl, VIDEO_GET_SIZE, &s) < 0)
|
||||
{
|
||||
lt_info("%s: VIDEO_GET_SIZE %m\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
lt_debug("%s: %d\n", __func__, s.aspect_ratio);
|
||||
return s.aspect_ratio * 2 + 1;
|
||||
}
|
||||
|
||||
int cVideo::setCroppingMode(int /*vidDispMode_t format*/)
|
||||
{
|
||||
return 0;
|
||||
#if 0
|
||||
croppingMode = format;
|
||||
const char *format_string[] = { "norm", "letterbox", "unknown", "mode_1_2", "mode_1_4", "mode_2x", "scale", "disexp" };
|
||||
const char *f;
|
||||
if (format >= VID_DISPMODE_NORM && format <= VID_DISPMODE_DISEXP)
|
||||
f = format_string[format];
|
||||
else
|
||||
f = "ILLEGAL format!";
|
||||
lt_debug("%s(%d) => %s\n", __FUNCTION__, format, f);
|
||||
return fop(ioctl, MPEG_VID_SET_DISPMODE, format);
|
||||
#endif
|
||||
}
|
||||
|
||||
int cVideo::Start(void * /*PcrChannel*/, unsigned short /*PcrPid*/, unsigned short /*VideoPid*/, void * /*hChannel*/)
|
||||
{
|
||||
lt_debug("%s playstate=%d\n", __FUNCTION__, playstate);
|
||||
#if 0
|
||||
if (playstate == VIDEO_PLAYING)
|
||||
return 0;
|
||||
if (playstate == VIDEO_FREEZED) /* in theory better, but not in practice :-) */
|
||||
fop(ioctl, MPEG_VID_CONTINUE);
|
||||
#endif
|
||||
playstate = VIDEO_PLAYING;
|
||||
return fop(ioctl, VIDEO_PLAY);
|
||||
}
|
||||
|
||||
int cVideo::Stop(bool blank)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __FUNCTION__, blank);
|
||||
if (stillpicture)
|
||||
{
|
||||
lt_debug("%s: stillpicture == true\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
/* blank parameter seems to not work on VIDEO_STOP */
|
||||
if (blank)
|
||||
setBlank(1);
|
||||
playstate = blank ? VIDEO_STOPPED : VIDEO_FREEZED;
|
||||
return fop(ioctl, VIDEO_STOP, blank ? 1 : 0);
|
||||
}
|
||||
|
||||
int cVideo::setBlank(int)
|
||||
{
|
||||
pthread_mutex_lock(&stillp_mutex);
|
||||
if (blank_data)
|
||||
show_iframe(fd, blank_data, blank_size);
|
||||
pthread_mutex_unlock(&stillp_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cVideo::SetVideoSystem(int video_system, bool remember)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, video_system, remember);
|
||||
char current[32];
|
||||
static const char *modes[] = {
|
||||
"480i", // VIDEO_STD_NTSC
|
||||
"576i", // VIDEO_STD_SECAM
|
||||
"576i", // VIDEO_STD_PAL
|
||||
"480p", // VIDEO_STD_480P
|
||||
"576p", // VIDEO_STD_576P
|
||||
"720p60", // VIDEO_STD_720P60
|
||||
"1080i60", // VIDEO_STD_1080I60
|
||||
"720p50", // VIDEO_STD_720P50
|
||||
"1080i50", // VIDEO_STD_1080I50
|
||||
"1080p30", // VIDEO_STD_1080P30
|
||||
"1080p24", // VIDEO_STD_1080P24
|
||||
"1080p25", // VIDEO_STD_1080P25
|
||||
"720p50", // VIDEO_STD_AUTO -> not implemented
|
||||
"1080p50" // VIDEO_STD_1080P50 -> SPARK only
|
||||
};
|
||||
|
||||
if (video_system > VIDEO_STD_MAX)
|
||||
{
|
||||
lt_info("%s: video_system (%d) > VIDEO_STD_MAX (%d)\n", __func__, video_system, VIDEO_STD_MAX);
|
||||
return -1;
|
||||
}
|
||||
int ret = proc_get("/proc/stb/video/videomode", current, 32);
|
||||
if (strcmp(current, modes[video_system]) == 0)
|
||||
{
|
||||
lt_info("%s: video_system %d (%s) already set, skipping\n", __func__, video_system, current);
|
||||
return 0;
|
||||
}
|
||||
lt_info("%s: old: '%s' new: '%s'\n", __func__, current, modes[video_system]);
|
||||
ret = proc_put("/proc/stb/video/videomode", modes[video_system],strlen(modes[video_system]));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cVideo::getPlayState(void)
|
||||
{
|
||||
return playstate;
|
||||
}
|
||||
|
||||
void cVideo::SetVideoMode(analog_mode_t mode)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __func__, mode);
|
||||
if (!(mode & ANALOG_SCART_MASK))
|
||||
{
|
||||
lt_debug("%s: non-SCART mode ignored\n", __func__);
|
||||
return;
|
||||
}
|
||||
const char *m;
|
||||
switch(mode)
|
||||
{
|
||||
case ANALOG_SD_YPRPB_SCART:
|
||||
m = "yuv";
|
||||
break;
|
||||
case ANALOG_SD_RGB_SCART:
|
||||
m = "rgb";
|
||||
break;
|
||||
default:
|
||||
lt_info("%s unknown mode %d\n", __func__, mode);
|
||||
m = "rgb";
|
||||
break; /* default to rgb */
|
||||
}
|
||||
proc_put("/proc/stb/avs/0/colorformat", m, strlen(m));
|
||||
}
|
||||
|
||||
void cVideo::ShowPicture(const char * fname)
|
||||
{
|
||||
lt_debug("%s(%s)\n", __func__, fname);
|
||||
char destname[512];
|
||||
char cmd[512];
|
||||
char *p;
|
||||
int mfd;
|
||||
unsigned char *iframe;
|
||||
struct stat st, st2;
|
||||
if (video_standby)
|
||||
{
|
||||
/* does not work and the driver does not seem to like it */
|
||||
lt_info("%s: video_standby == true\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s: decoder not opened?\n", __func__);
|
||||
return;
|
||||
}
|
||||
strcpy(destname, "/var/cache");
|
||||
if (stat(fname, &st2))
|
||||
{
|
||||
lt_info("%s: could not stat %s (%m)\n", __func__, fname);
|
||||
return;
|
||||
}
|
||||
mkdir(destname, 0755);
|
||||
/* the cache filename is (example for /share/tuxbox/neutrino/icons/radiomode.jpg):
|
||||
/var/cache/share.tuxbox.neutrino.icons.radiomode.jpg.m2v
|
||||
build that filename first...
|
||||
TODO: this could cause name clashes, use a hashing function instead... */
|
||||
strcat(destname, fname);
|
||||
p = &destname[strlen("/var/cache/")];
|
||||
while ((p = strchr(p, '/')) != NULL)
|
||||
*p = '.';
|
||||
strcat(destname, ".m2v");
|
||||
/* ...then check if it exists already... */
|
||||
if (stat(destname, &st) || (st.st_mtime != st2.st_mtime) || (st.st_size == 0))
|
||||
{
|
||||
struct utimbuf u;
|
||||
u.actime = time(NULL);
|
||||
u.modtime = st2.st_mtime;
|
||||
/* it does not exist or has a different date, so call ffmpeg... */
|
||||
sprintf(cmd, "ffmpeg -y -f mjpeg -i '%s' -s 1280x720 '%s' </dev/null",
|
||||
fname, destname);
|
||||
system(cmd); /* TODO: use libavcodec to directly convert it */
|
||||
utime(destname, &u);
|
||||
}
|
||||
|
||||
/* the mutex is a workaround: setBlank is apparently called from
|
||||
a differnt thread and takes slightly longer, so that the decoder
|
||||
was blanked immediately after displaying the image, which is not
|
||||
what we want. the mutex ensures proper ordering. */
|
||||
pthread_mutex_lock(&stillp_mutex);
|
||||
|
||||
mfd = open(destname, O_RDONLY|O_CLOEXEC);
|
||||
if (mfd < 0)
|
||||
{
|
||||
lt_info("%s cannot open %s: %m", __func__, destname);
|
||||
goto out;
|
||||
}
|
||||
fstat(mfd, &st);
|
||||
|
||||
stillpicture = true;
|
||||
|
||||
iframe = (unsigned char *)malloc(st.st_size);
|
||||
if (! iframe)
|
||||
{
|
||||
lt_info("%s: malloc failed (%m)\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
read(mfd, iframe, st.st_size);
|
||||
show_iframe(fd, iframe, st.st_size);
|
||||
free(iframe);
|
||||
out:
|
||||
close(mfd);
|
||||
pthread_mutex_unlock(&stillp_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
void cVideo::StopPicture()
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
stillpicture = false;
|
||||
}
|
||||
|
||||
void cVideo::Standby(unsigned int bOn)
|
||||
{
|
||||
lt_debug("%s(%d)\n", __func__, bOn);
|
||||
if (bOn)
|
||||
{
|
||||
closeDevice();
|
||||
proc_put("/proc/stb/avs/0/input", "aux", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_put("/proc/stb/avs/0/input", "encoder", 8);
|
||||
openDevice();
|
||||
}
|
||||
video_standby = bOn;
|
||||
}
|
||||
|
||||
int cVideo::getBlank(void)
|
||||
{
|
||||
int ret = proc_get_hex("/proc/stb/vmpeg/0/xres");
|
||||
lt_debug("%s => %d\n", __func__, !ret);
|
||||
return !ret;
|
||||
}
|
||||
|
||||
/* this function is regularly called, checks if video parameters
|
||||
changed and triggers appropriate actions */
|
||||
void cVideo::VideoParamWatchdog(void)
|
||||
{
|
||||
#if 0
|
||||
static unsigned int _v_info = (unsigned int) -1;
|
||||
unsigned int v_info;
|
||||
if (fd == -1)
|
||||
return;
|
||||
ioctl(fd, MPEG_VID_GET_V_INFO_RAW, &v_info);
|
||||
if (_v_info != v_info)
|
||||
{
|
||||
lt_debug("%s params changed. old: %08x new: %08x\n", __FUNCTION__, _v_info, v_info);
|
||||
setAspectRatio(-1, -1);
|
||||
}
|
||||
_v_info = v_info;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cVideo::Pig(int x, int y, int w, int h, int osd_w, int osd_h)
|
||||
{
|
||||
char buffer[64];
|
||||
int _x, _y, _w, _h;
|
||||
/* the target "coordinates" seem to be in a PAL sized plane
|
||||
* TODO: check this in the driver sources */
|
||||
int xres = 720; /* proc_get_hex("/proc/stb/vmpeg/0/xres") */
|
||||
int yres = 576; /* proc_get_hex("/proc/stb/vmpeg/0/yres") */
|
||||
lt_debug("%s: x:%d y:%d w:%d h:%d ow:%d oh:%d\n", __func__, x, y, w, h, osd_w, osd_h);
|
||||
if (x == -1 && y == -1 && w == -1 && h == -1)
|
||||
{
|
||||
_w = xres;
|
||||
_h = yres;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_x = x * xres / osd_w;
|
||||
_w = w * xres / osd_w;
|
||||
_y = y * yres / osd_h;
|
||||
_h = h * yres / osd_h;
|
||||
}
|
||||
lt_debug("%s: x:%d y:%d w:%d h:%d xr:%d yr:%d\n", __func__, _x, _y, _w, _h, xres, yres);
|
||||
sprintf(buffer, "%x", _x);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_left", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _y);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_top", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _w);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_width", buffer, strlen(buffer));
|
||||
sprintf(buffer, "%x", _h);
|
||||
proc_put("/proc/stb/vmpeg/0/dst_height", buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
static inline int rate2csapi(int rate)
|
||||
{
|
||||
switch (rate)
|
||||
{
|
||||
/* no idea how the float values are represented by the driver */
|
||||
case 23976:
|
||||
return 0;
|
||||
case 24:
|
||||
return 1;
|
||||
case 25:
|
||||
return 2;
|
||||
case 29976:
|
||||
return 3;
|
||||
case 30:
|
||||
return 4;
|
||||
case 50:
|
||||
return 5;
|
||||
case 50940:
|
||||
return 6;
|
||||
case 60:
|
||||
return 7;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cVideo::getPictureInfo(int &width, int &height, int &rate)
|
||||
{
|
||||
video_size_t s;
|
||||
int r;
|
||||
if (fd == -1)
|
||||
{
|
||||
/* in movieplayer mode, fd is not opened -> fall back to procfs */
|
||||
r = proc_get_hex("/proc/stb/vmpeg/0/framerate");
|
||||
width = proc_get_hex("/proc/stb/vmpeg/0/xres");
|
||||
height = proc_get_hex("/proc/stb/vmpeg/0/yres");
|
||||
rate = rate2csapi(r);
|
||||
return;
|
||||
}
|
||||
ioctl(fd, VIDEO_GET_SIZE, &s);
|
||||
ioctl(fd, VIDEO_GET_FRAME_RATE, &r);
|
||||
rate = rate2csapi(r);
|
||||
height = s.h;
|
||||
width = s.w;
|
||||
lt_debug("%s: rate: %d, width: %d height: %d\n", __func__, rate, width, height);
|
||||
}
|
||||
|
||||
void cVideo::SetSyncMode(AVSYNC_TYPE mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, mode);
|
||||
/*
|
||||
* { 0, LOCALE_OPTIONS_OFF },
|
||||
* { 1, LOCALE_OPTIONS_ON },
|
||||
* { 2, LOCALE_AUDIOMENU_AVSYNC_AM }
|
||||
*/
|
||||
#if 0
|
||||
switch(Mode)
|
||||
{
|
||||
case 0:
|
||||
ioctl(fd, MPEG_VID_SYNC_OFF);
|
||||
break;
|
||||
case 1:
|
||||
ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_VID);
|
||||
break;
|
||||
default:
|
||||
ioctl(fd, MPEG_VID_SYNC_ON, VID_SYNC_AUD);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
int cVideo::SetStreamType(VIDEO_FORMAT type)
|
||||
{
|
||||
static const char *VF[] = {
|
||||
"VIDEO_FORMAT_MPEG2",
|
||||
"VIDEO_FORMAT_MPEG4",
|
||||
"VIDEO_FORMAT_VC1",
|
||||
"VIDEO_FORMAT_JPEG",
|
||||
"VIDEO_FORMAT_GIF",
|
||||
"VIDEO_FORMAT_PNG"
|
||||
};
|
||||
int t;
|
||||
lt_debug("%s type=%s\n", __FUNCTION__, VF[type]);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VIDEO_FORMAT_MPEG4:
|
||||
t = VIDEO_STREAMTYPE_MPEG4_H264;
|
||||
break;
|
||||
case VIDEO_FORMAT_VC1:
|
||||
t = VIDEO_STREAMTYPE_VC1;
|
||||
break;
|
||||
case VIDEO_FORMAT_MPEG2:
|
||||
default:
|
||||
t = VIDEO_STREAMTYPE_MPEG2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ioctl(fd, VIDEO_SET_STREAMTYPE, t) < 0)
|
||||
lt_info("%s VIDEO_SET_STREAMTYPE(%d) failed: %m\n", __func__, t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t cVideo::GetPTS(void)
|
||||
{
|
||||
int64_t pts = 0;
|
||||
if (ioctl(fd, VIDEO_GET_PTS, &pts) < 0)
|
||||
lt_info("%s: GET_PTS failed (%m)\n", __func__);
|
||||
return pts;
|
||||
}
|
||||
|
||||
static void show_iframe(int fd, unsigned char *iframe, size_t st_size)
|
||||
{
|
||||
static const unsigned char pes_header[] = { 0, 0, 1, 0xE0, 0, 0, 0x80, 0x80, 5, 0x21, 0, 1, 0, 1};
|
||||
static const unsigned char seq_end[] = { 0x00, 0x00, 0x01, 0xB7 };
|
||||
unsigned char stuffing[128];
|
||||
bool seq_end_avail = false;
|
||||
size_t pos = 0;
|
||||
int count = 7;
|
||||
if (ioctl(fd, VIDEO_SET_FORMAT, VIDEO_FORMAT_16_9) < 0)
|
||||
lt_info_c("%s: VIDEO_SET_FORMAT failed (%m)\n", __func__);
|
||||
ioctl(fd, VIDEO_SET_STREAMTYPE, VIDEO_FORMAT_MPEG2);
|
||||
ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
|
||||
ioctl(fd, VIDEO_PLAY);
|
||||
ioctl(fd, VIDEO_CONTINUE);
|
||||
ioctl(fd, VIDEO_CLEAR_BUFFER);
|
||||
while (pos <= (st_size-4) && !(seq_end_avail = (!iframe[pos] && !iframe[pos+1] && iframe[pos+2] == 1 && iframe[pos+3] == 0xB7)))
|
||||
++pos;
|
||||
while (count--)
|
||||
{
|
||||
if ((iframe[3] >> 4) != 0xE) // no pes header
|
||||
write(fd, pes_header, sizeof(pes_header));
|
||||
else
|
||||
iframe[4] = iframe[5] = 0x00;
|
||||
write(fd, iframe, st_size);
|
||||
usleep(8000);
|
||||
}
|
||||
if (!seq_end_avail)
|
||||
write(fd, seq_end, sizeof(seq_end));
|
||||
|
||||
memset(stuffing, 0, sizeof(stuffing));
|
||||
for (count = 0; count < 8192 / (int)sizeof(stuffing); count++)
|
||||
write(fd, stuffing, sizeof(stuffing));
|
||||
ioctl(fd, VIDEO_STOP, 0);
|
||||
ioctl(fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
|
||||
}
|
||||
|
||||
void cVideo::SetDemux(cDemux *)
|
||||
{
|
||||
lt_debug("%s: not implemented yet\n", __func__);
|
||||
}
|
196
azbox/video_lib.h
Normal file
196
azbox/video_lib.h
Normal file
@@ -0,0 +1,196 @@
|
||||
#ifndef _VIDEO_TD_H
|
||||
#define _VIDEO_TD_H
|
||||
|
||||
#include <linux/dvb/video.h>
|
||||
#include "../common/cs_types.h"
|
||||
#include "dmx_lib.h"
|
||||
|
||||
typedef enum {
|
||||
ANALOG_SD_RGB_CINCH = 0x00,
|
||||
ANALOG_SD_YPRPB_CINCH,
|
||||
ANALOG_HD_RGB_CINCH,
|
||||
ANALOG_HD_YPRPB_CINCH,
|
||||
ANALOG_SD_RGB_SCART = 0x10,
|
||||
ANALOG_SD_YPRPB_SCART,
|
||||
ANALOG_HD_RGB_SCART,
|
||||
ANALOG_HD_YPRPB_SCART,
|
||||
ANALOG_SCART_MASK = 0x10
|
||||
} analog_mode_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
VIDEO_FORMAT_MPEG2 = 0,
|
||||
VIDEO_FORMAT_MPEG4,
|
||||
VIDEO_FORMAT_VC1,
|
||||
VIDEO_FORMAT_JPEG,
|
||||
VIDEO_FORMAT_GIF,
|
||||
VIDEO_FORMAT_PNG
|
||||
} VIDEO_FORMAT;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_SD = 0,
|
||||
VIDEO_HD,
|
||||
VIDEO_120x60i,
|
||||
VIDEO_320x240i,
|
||||
VIDEO_1440x800i,
|
||||
VIDEO_360x288i
|
||||
} VIDEO_DEFINITION;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_FRAME_RATE_23_976 = 0,
|
||||
VIDEO_FRAME_RATE_24,
|
||||
VIDEO_FRAME_RATE_25,
|
||||
VIDEO_FRAME_RATE_29_97,
|
||||
VIDEO_FRAME_RATE_30,
|
||||
VIDEO_FRAME_RATE_50,
|
||||
VIDEO_FRAME_RATE_59_94,
|
||||
VIDEO_FRAME_RATE_60
|
||||
} VIDEO_FRAME_RATE;
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_AR_1_1,
|
||||
DISPLAY_AR_4_3,
|
||||
DISPLAY_AR_14_9,
|
||||
DISPLAY_AR_16_9,
|
||||
DISPLAY_AR_20_9,
|
||||
DISPLAY_AR_RAW,
|
||||
} DISPLAY_AR;
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_AR_MODE_PANSCAN = 0,
|
||||
DISPLAY_AR_MODE_LETTERBOX,
|
||||
DISPLAY_AR_MODE_NONE,
|
||||
DISPLAY_AR_MODE_PANSCAN2
|
||||
} DISPLAY_AR_MODE;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_DB_DR_NEITHER = 0,
|
||||
VIDEO_DB_ON,
|
||||
VIDEO_DB_DR_BOTH
|
||||
} VIDEO_DB_DR;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_PLAY_STILL = 0,
|
||||
VIDEO_PLAY_CLIP,
|
||||
VIDEO_PLAY_TRICK,
|
||||
VIDEO_PLAY_MOTION,
|
||||
VIDEO_PLAY_MOTION_NO_SYNC
|
||||
} VIDEO_PLAY_MODE;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_STD_NTSC,
|
||||
VIDEO_STD_SECAM,
|
||||
VIDEO_STD_PAL,
|
||||
VIDEO_STD_480P,
|
||||
VIDEO_STD_576P,
|
||||
VIDEO_STD_720P60,
|
||||
VIDEO_STD_1080I60,
|
||||
VIDEO_STD_720P50,
|
||||
VIDEO_STD_1080I50,
|
||||
VIDEO_STD_1080P30,
|
||||
VIDEO_STD_1080P24,
|
||||
VIDEO_STD_1080P25,
|
||||
VIDEO_STD_AUTO,
|
||||
VIDEO_STD_1080P50, /* SPARK only */
|
||||
VIDEO_STD_MAX
|
||||
} VIDEO_STD;
|
||||
|
||||
/* not used, for dummy functions */
|
||||
typedef enum {
|
||||
VIDEO_HDMI_CEC_MODE_OFF = 0,
|
||||
VIDEO_HDMI_CEC_MODE_TUNER,
|
||||
VIDEO_HDMI_CEC_MODE_RECORDER
|
||||
} VIDEO_HDMI_CEC_MODE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VIDEO_CONTROL_BRIGHTNESS = 0,
|
||||
VIDEO_CONTROL_CONTRAST,
|
||||
VIDEO_CONTROL_SATURATION,
|
||||
VIDEO_CONTROL_HUE,
|
||||
VIDEO_CONTROL_SHARPNESS,
|
||||
VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS
|
||||
} VIDEO_CONTROL;
|
||||
|
||||
|
||||
class cVideo
|
||||
{
|
||||
friend class cDemux;
|
||||
friend class cPlayback;
|
||||
private:
|
||||
/* video device */
|
||||
int fd;
|
||||
/* apparently we cannot query the driver's state
|
||||
=> remember it */
|
||||
video_play_state_t playstate;
|
||||
int /*vidDispMode_t*/ croppingMode;
|
||||
int /*vidOutFmt_t*/ outputformat;
|
||||
int scartvoltage;
|
||||
|
||||
VIDEO_FORMAT StreamType;
|
||||
VIDEO_DEFINITION VideoDefinition;
|
||||
DISPLAY_AR DisplayAR;
|
||||
VIDEO_PLAY_MODE SyncMode;
|
||||
DISPLAY_AR_MODE ARMode;
|
||||
VIDEO_DB_DR eDbDr;
|
||||
DISPLAY_AR PictureAR;
|
||||
VIDEO_FRAME_RATE FrameRate;
|
||||
int video_standby;
|
||||
int64_t GetPTS(void);
|
||||
|
||||
void openDevice(void);
|
||||
void closeDevice(void);
|
||||
public:
|
||||
/* constructor & destructor */
|
||||
cVideo(int mode, void *, void *, unsigned int unit = 0);
|
||||
~cVideo(void);
|
||||
|
||||
void * GetTVEnc() { return NULL; };
|
||||
void * GetTVEncSD() { return NULL; };
|
||||
|
||||
/* aspect ratio */
|
||||
int getAspectRatio(void);
|
||||
void getPictureInfo(int &width, int &height, int &rate);
|
||||
int setAspectRatio(int aspect, int mode);
|
||||
|
||||
/* cropping mode */
|
||||
int setCroppingMode(int x = 0 /*vidDispMode_t x = VID_DISPMODE_NORM*/);
|
||||
|
||||
/* get play state */
|
||||
int getPlayState(void);
|
||||
|
||||
/* blank on freeze */
|
||||
int getBlank(void);
|
||||
int setBlank(int enable);
|
||||
|
||||
/* change video play state. Parameters are all unused. */
|
||||
int Start(void *PcrChannel = NULL, unsigned short PcrPid = 0, unsigned short VideoPid = 0, void *x = NULL);
|
||||
int Stop(bool blank = true);
|
||||
bool Pause(void);
|
||||
|
||||
/* set video_system */
|
||||
int SetVideoSystem(int video_system, bool remember = true);
|
||||
int SetStreamType(VIDEO_FORMAT type);
|
||||
void SetSyncMode(AVSYNC_TYPE mode);
|
||||
bool SetCECMode(VIDEO_HDMI_CEC_MODE) { return true; };
|
||||
void SetCECAutoView(bool) { return; };
|
||||
void SetCECAutoStandby(bool) { return; };
|
||||
void ShowPicture(const char * fname);
|
||||
void StopPicture();
|
||||
void Standby(unsigned int bOn);
|
||||
void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600);
|
||||
void SetControl(int, int) { return; };
|
||||
void VideoParamWatchdog(void);
|
||||
void setContrast(int val);
|
||||
void SetVideoMode(analog_mode_t mode);
|
||||
void SetDBDR(int) { return; };
|
||||
void SetAudioHandle(void *) { return; };
|
||||
void SetAutoModes(int [VIDEO_STD_MAX]) { return; };
|
||||
int OpenVBI(int) { return 0; };
|
||||
int CloseVBI(void) { return 0; };
|
||||
int StartVBI(unsigned short) { return 0; };
|
||||
int StopVBI(void) { return 0; };
|
||||
void SetDemux(cDemux *dmx);
|
||||
};
|
||||
|
||||
#endif
|
8
common/Makefile.am
Normal file
8
common/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
noinst_LTLIBRARIES = libcommon.la
|
||||
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
|
||||
libcommon_la_SOURCES = \
|
||||
ca.cpp \
|
||||
lt_debug.cpp \
|
||||
proc_tools.c
|
110
common/ca.cpp
Normal file
110
common/ca.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ca.h"
|
||||
#include "lt_debug.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_CA, this, args)
|
||||
|
||||
|
||||
static cCA *inst = NULL;
|
||||
|
||||
/* those are all dummies for now.. */
|
||||
cCA::cCA(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
cCA::~cCA()
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
cCA *cCA::GetInstance()
|
||||
{
|
||||
_lt_debug(TRIPLE_DEBUG_CA, NULL, "%s\n", __FUNCTION__);
|
||||
if (inst == NULL)
|
||||
inst = new cCA();
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void cCA::MenuEnter(enum CA_SLOT_TYPE, uint32_t p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
void cCA::MenuAnswer(enum CA_SLOT_TYPE, uint32_t p, uint32_t /*choice*/)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
void cCA::InputAnswer(enum CA_SLOT_TYPE, uint32_t p, uint8_t * /*Data*/, int /*Len*/)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
void cCA::MenuClose(enum CA_SLOT_TYPE, uint32_t p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
uint32_t cCA::GetNumberCISlots(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t cCA::GetNumberSmartCardSlots(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cCA::ModuleName(enum CA_SLOT_TYPE, uint32_t p, char * /*Name*/)
|
||||
{
|
||||
/* TODO: waht to do with *Name? */
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
bool cCA::ModulePresent(enum CA_SLOT_TYPE, uint32_t p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
bool cCA::SendPMT(int, unsigned char *, int, CA_SLOT_TYPE)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCA::SendMessage(const CA_MESSAGE *)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cCA::Start(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cCA::Stop(void)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void cCA::Ready(bool p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
||||
|
||||
void cCA::SetInitMask(enum CA_INIT_MASK p)
|
||||
{
|
||||
lt_debug("%s param:%d\n", __FUNCTION__, (int)p);
|
||||
}
|
112
common/ca.h
Normal file
112
common/ca.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* dummy functions to implement ca_cs.h interface
|
||||
*/
|
||||
#ifndef __CA_LIBTRIPLE_H_
|
||||
#define __CA_LIBTRIPLE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "cs_types.h"
|
||||
#include <vector>
|
||||
typedef std::vector<u16> CaIdVector;
|
||||
typedef std::vector<u16>::iterator CaIdVectorIterator;
|
||||
typedef std::vector<u16>::const_iterator CaIdVectorConstIterator;
|
||||
|
||||
enum CA_INIT_MASK {
|
||||
CA_INIT_SC = 1,
|
||||
CA_INIT_CI,
|
||||
CA_INIT_BOTH
|
||||
};
|
||||
|
||||
enum CA_SLOT_TYPE {
|
||||
CA_SLOT_TYPE_SMARTCARD,
|
||||
CA_SLOT_TYPE_CI,
|
||||
CA_SLOT_TYPE_ALL,
|
||||
};
|
||||
|
||||
enum CA_MESSAGE_FLAGS {
|
||||
CA_MESSAGE_EMPTY = (1 << 0),
|
||||
CA_MESSAGE_HAS_PARAM1_DATA = (1 << 1), // Free after use!
|
||||
CA_MESSAGE_HAS_PARAM1_INT = (1 << 2),
|
||||
CA_MESSAGE_HAS_PARAM1_PTR = (1 << 3),
|
||||
CA_MESSAGE_HAS_PARAM2_INT = (1 << 4),
|
||||
CA_MESSAGE_HAS_PARAM2_PTR = (1 << 5),
|
||||
CA_MESSAGE_HAS_PARAM2_DATA = (1 << 6),
|
||||
CA_MESSAGE_HAS_PARAM3_DATA = (1 << 7), // Free after use!
|
||||
CA_MESSAGE_HAS_PARAM3_INT = (1 << 8),
|
||||
CA_MESSAGE_HAS_PARAM3_PTR = (1 << 9),
|
||||
CA_MESSAGE_HAS_PARAM4_INT = (1 << 10),
|
||||
CA_MESSAGE_HAS_PARAM4_PTR = (1 << 11),
|
||||
CA_MESSAGE_HAS_PARAM4_DATA = (1 << 12),
|
||||
CA_MESSAGE_HAS_PARAM5_INT = (1 << 13),
|
||||
CA_MESSAGE_HAS_PARAM5_PTR = (1 << 14),
|
||||
CA_MESSAGE_HAS_PARAM5_DATA = (1 << 15),
|
||||
CA_MESSAGE_HAS_PARAM6_INT = (1 << 16),
|
||||
CA_MESSAGE_HAS_PARAM6_PTR = (1 << 17),
|
||||
CA_MESSAGE_HAS_PARAM6_DATA = (1 << 18),
|
||||
CA_MESSAGE_HAS_PARAM1_LONG = (1 << 19),
|
||||
CA_MESSAGE_HAS_PARAM2_LONG = (1 << 20),
|
||||
CA_MESSAGE_HAS_PARAM3_LONG = (1 << 21),
|
||||
CA_MESSAGE_HAS_PARAM4_LONG = (1 << 22)
|
||||
};
|
||||
|
||||
enum CA_MESSAGE_MSGID {
|
||||
CA_MESSAGE_MSG_INSERTED,
|
||||
CA_MESSAGE_MSG_REMOVED,
|
||||
CA_MESSAGE_MSG_INIT_OK,
|
||||
CA_MESSAGE_MSG_INIT_FAILED,
|
||||
CA_MESSAGE_MSG_MMI_MENU,
|
||||
CA_MESSAGE_MSG_MMI_MENU_ENTER,
|
||||
CA_MESSAGE_MSG_MMI_MENU_ANSWER,
|
||||
CA_MESSAGE_MSG_MMI_LIST,
|
||||
CA_MESSAGE_MSG_MMI_TEXT,
|
||||
CA_MESSAGE_MSG_MMI_REQ_INPUT,
|
||||
CA_MESSAGE_MSG_MMI_CLOSE,
|
||||
CA_MESSAGE_MSG_INTERNAL,
|
||||
CA_MESSAGE_MSG_PMT_ARRIVED,
|
||||
CA_MESSAGE_MSG_CAT_ARRIVED,
|
||||
CA_MESSAGE_MSG_ECM_ARRIVED,
|
||||
CA_MESSAGE_MSG_EMM_ARRIVED,
|
||||
CA_MESSAGE_MSG_CHANNEL_CHANGE,
|
||||
CA_MESSAGE_MSG_EXIT,
|
||||
};
|
||||
|
||||
typedef struct CA_MESSAGE {
|
||||
uint32_t MsgId;
|
||||
enum CA_SLOT_TYPE SlotType;
|
||||
int Slot;
|
||||
uint32_t Flags;
|
||||
union {
|
||||
uint8_t *Data[4];
|
||||
uint32_t Param[4];
|
||||
void *Ptr[4];
|
||||
uint64_t ParamLong[4];
|
||||
} Msg;
|
||||
} CA_MESSAGE;
|
||||
|
||||
class cCA {
|
||||
private:
|
||||
cCA(void);
|
||||
public:
|
||||
uint32_t GetNumberCISlots(void);
|
||||
uint32_t GetNumberSmartCardSlots(void);
|
||||
static cCA *GetInstance(void);
|
||||
bool SendPMT(int Unit, unsigned char *Data, int Len, CA_SLOT_TYPE SlotType = CA_SLOT_TYPE_ALL);
|
||||
bool SendCAPMT(u64 /*Source*/, u8 /*DemuxSource*/, u8 /*DemuxMask*/, const unsigned char * /*CAPMT*/, u32 /*CAPMTLen*/, const unsigned char * /*RawPMT*/, u32 /*RawPMTLen*/) { return true; };
|
||||
bool SendMessage(const CA_MESSAGE *Msg);
|
||||
void SetInitMask(enum CA_INIT_MASK InitMask);
|
||||
int GetCAIDS(CaIdVector & /*Caids*/) { return 0; };
|
||||
bool Start(void);
|
||||
void Stop(void);
|
||||
void Ready(bool Set);
|
||||
void ModuleReset(enum CA_SLOT_TYPE, uint32_t Slot);
|
||||
bool ModulePresent(enum CA_SLOT_TYPE, uint32_t Slot);
|
||||
void ModuleName(enum CA_SLOT_TYPE, uint32_t Slot, char *Name);
|
||||
void MenuEnter(enum CA_SLOT_TYPE, uint32_t Slot);
|
||||
void MenuAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint32_t choice);
|
||||
void InputAnswer(enum CA_SLOT_TYPE, uint32_t Slot, uint8_t * Data, int Len);
|
||||
void MenuClose(enum CA_SLOT_TYPE, uint32_t Slot);
|
||||
void SetTSClock(u32 /*Speed*/) { return; };
|
||||
virtual ~cCA();
|
||||
};
|
||||
|
||||
#endif // __CA_LIBTRIPLE_H_
|
23
common/cs_types.h
Normal file
23
common/cs_types.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* pretty useless, but we don't need to work around this if
|
||||
* we just copy it over... */
|
||||
|
||||
#ifndef __CS_TYPES_H_
|
||||
#define __CS_TYPES_H_
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AVSYNC_DISABLED,
|
||||
AVSYNC_ENABLED,
|
||||
AVSYNC_AUDIO_IS_MASTER
|
||||
} AVSYNC_TYPE;
|
||||
|
||||
typedef unsigned long long u64;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned char u8;
|
||||
typedef signed long long s64;
|
||||
typedef signed int s32;
|
||||
typedef signed short s16;
|
||||
typedef signed char s8;
|
||||
|
||||
#endif // __CS_TYPES_H_
|
1
common/hardware_caps.h
Symbolic link
1
common/hardware_caps.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../include/hardware_caps.h
|
90
common/lt_debug.cpp
Normal file
90
common/lt_debug.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/* libtriple debug functions */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int cnxt_debug = 0; /* compat, unused */
|
||||
|
||||
int debuglevel = -1;
|
||||
|
||||
static const char* lt_facility[] = {
|
||||
"audio ",
|
||||
"video ",
|
||||
"demux ",
|
||||
"play ",
|
||||
"power ",
|
||||
"init ",
|
||||
"ca ",
|
||||
"record",
|
||||
NULL
|
||||
};
|
||||
|
||||
void _lt_info(int facility, const void *func, const char *fmt, ...)
|
||||
{
|
||||
/* %p does print "(nil)" instead of 0x00000000 for NULL */
|
||||
fprintf(stderr, "[LT:%08lx:%s] ", (long) func, lt_facility[facility]);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void _lt_debug(int facility, const void *func, const char *fmt, ...)
|
||||
{
|
||||
if (debuglevel < 0)
|
||||
fprintf(stderr, "lt_debug: debuglevel not initialized!\n");
|
||||
|
||||
if (! ((1 << facility) & debuglevel))
|
||||
return;
|
||||
|
||||
fprintf(stderr, "[LT:%08lx:%s] ", (long)func, lt_facility[facility]);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void lt_debug_init(void)
|
||||
{
|
||||
int i = 0;
|
||||
char *tmp = getenv("HAL_DEBUG");
|
||||
if (! tmp)
|
||||
tmp = getenv("TRIPLE_DEBUG"); /* backwards compatibility... */
|
||||
if (! tmp)
|
||||
debuglevel = 0;
|
||||
else
|
||||
debuglevel = (int) strtol(tmp, NULL, 0);
|
||||
|
||||
if (debuglevel == 0)
|
||||
{
|
||||
fprintf(stderr, "libstb-hal debug options can be set by exporting HAL_DEBUG.\n");
|
||||
fprintf(stderr, "The following values (or bitwise OR combinations) are valid:\n");
|
||||
while (lt_facility[i]) {
|
||||
fprintf(stderr, "\tcomponent: %s 0x%02x\n", lt_facility[i], 1 << i);
|
||||
i++;
|
||||
}
|
||||
fprintf(stderr, "\tall components: 0x%02x\n", (1 << i) - 1);
|
||||
} else {
|
||||
fprintf(stderr, "libstb-hal debug is active for the following components:\n");
|
||||
while (lt_facility[i]) {
|
||||
if (debuglevel & (1 << i))
|
||||
fprintf(stderr, "%s ", lt_facility[i]);
|
||||
i++;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void hal_set_threadname(const char *name)
|
||||
{
|
||||
char threadname[17];
|
||||
strncpy(threadname, name, sizeof(threadname));
|
||||
threadname[16] = 0;
|
||||
prctl (PR_SET_NAME, (unsigned long)&threadname);
|
||||
}
|
30
common/lt_debug.h
Normal file
30
common/lt_debug.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef __LT_DEBUG_H
|
||||
#define __LT_DEBUG_H
|
||||
|
||||
#define TRIPLE_DEBUG_AUDIO 0
|
||||
#define TRIPLE_DEBUG_VIDEO 1
|
||||
#define TRIPLE_DEBUG_DEMUX 2
|
||||
#define TRIPLE_DEBUG_PLAYBACK 3
|
||||
#define TRIPLE_DEBUG_PWRMNGR 4
|
||||
#define TRIPLE_DEBUG_INIT 5
|
||||
#define TRIPLE_DEBUG_CA 6
|
||||
#define TRIPLE_DEBUG_RECORD 7
|
||||
#define TRIPLE_DEBUG_ALL ((1<<8)-1)
|
||||
|
||||
#define HAL_DEBUG_AUDIO 0
|
||||
#define HAL_DEBUG_VIDEO 1
|
||||
#define HAL_DEBUG_DEMUX 2
|
||||
#define HAL_DEBUG_PLAYBACK 3
|
||||
#define HAL_DEBUG_PWRMNGR 4
|
||||
#define HAL_DEBUG_INIT 5
|
||||
#define HAL_DEBUG_CA 6
|
||||
#define HAL_DEBUG_RECORD 7
|
||||
#define HAL_DEBUG_ALL ((1<<8)-1)
|
||||
|
||||
extern int debuglevel;
|
||||
|
||||
void hal_set_threadname(const char *name);
|
||||
void _lt_debug(int facility, const void *, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
void _lt_info(int facility, const void *, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
void lt_debug_init(void);
|
||||
#endif
|
59
common/proc_tools.c
Normal file
59
common/proc_tools.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* helper functions to interact with files, usually in the proc filesystem
|
||||
*
|
||||
* (C) 2012 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||
*
|
||||
* License: GPLv2 or later
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h> /* isspace */
|
||||
#include <stdio.h> /* sscanf */
|
||||
|
||||
#include "proc_tools.h"
|
||||
|
||||
int proc_put(const char *path, const char *value, const int len)
|
||||
{
|
||||
int ret, ret2;
|
||||
int pfd = open(path, O_WRONLY);
|
||||
if (pfd < 0)
|
||||
return pfd;
|
||||
ret = write(pfd, value, len);
|
||||
ret2 = close(pfd);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int proc_get(const char *path, char *value, const int len)
|
||||
{
|
||||
int ret, ret2;
|
||||
int pfd = open(path, O_RDONLY);
|
||||
if (pfd < 0)
|
||||
return pfd;
|
||||
ret = read(pfd, value, len);
|
||||
value[len-1] = '\0'; /* make sure string is terminated */
|
||||
if (ret >= 0)
|
||||
{
|
||||
while (ret > 0 && isspace(value[ret-1]))
|
||||
ret--; /* remove trailing whitespace */
|
||||
value[ret] = '\0'; /* terminate, even if ret = 0 */
|
||||
}
|
||||
ret2 = close(pfd);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int proc_get_hex(const char *path)
|
||||
{
|
||||
unsigned int n, ret = 0;
|
||||
char buf[16];
|
||||
n = proc_get(path, buf, 16);
|
||||
if (n > 0)
|
||||
sscanf(buf, "%x", &ret);
|
||||
return ret;
|
||||
}
|
20
common/proc_tools.h
Normal file
20
common/proc_tools.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* helper functions to interact with files, usually in the proc filesystem
|
||||
*
|
||||
* (C) 2012 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||
*
|
||||
* License: GPLv2 or later
|
||||
*
|
||||
*/
|
||||
#ifndef __PROC_TOOLS_H__
|
||||
#define __PROC_TOOLS_H__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int proc_put(const char *path, const char *value, const int len);
|
||||
int proc_get(const char *path, char *value, const int len);
|
||||
unsigned int proc_get_hex(const char *path);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
45
configure.ac
Normal file
45
configure.ac
Normal file
@@ -0,0 +1,45 @@
|
||||
AC_INIT([libstb-hal], [0.1.1])
|
||||
AM_INIT_AUTOMAKE
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_GNU_SOURCE
|
||||
LT_INIT
|
||||
|
||||
## ugly, disables shared library build (not wanted yet)
|
||||
enable_shared=no
|
||||
|
||||
TUXBOX_APPS
|
||||
TUXBOX_APPS_DIRECTORY
|
||||
TUXBOX_APPS_PKGCONFIG
|
||||
TUXBOX_BOXTYPE
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_DISABLE_STATIC
|
||||
AC_SYS_LARGEFILE
|
||||
AM_PROG_LIBTOOL
|
||||
|
||||
if test x"$BOXTYPE" = x"tripledragon"; then
|
||||
TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb)
|
||||
fi
|
||||
|
||||
if test x$BOXTYPE = xgeneric -a x$BOXMODEL != xraspi; then
|
||||
PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 53.21.1])
|
||||
PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 54.28.0])
|
||||
# don't know which version is exactly needed here...
|
||||
PKG_CHECK_MODULES([AVUTIL], [libavutil])
|
||||
PKG_CHECK_MODULES([SWSCALE], [libswscale])
|
||||
PKG_CHECK_MODULES([SWRESAMPLE], [libswresample])
|
||||
fi
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
common/Makefile
|
||||
libeplayer3/Makefile
|
||||
azbox/Makefile
|
||||
generic-pc/Makefile
|
||||
libtriple/Makefile
|
||||
libspark/Makefile
|
||||
raspi/Makefile
|
||||
tools/Makefile
|
||||
])
|
||||
|
28
generic-pc/Makefile.am
Normal file
28
generic-pc/Makefile.am
Normal file
@@ -0,0 +1,28 @@
|
||||
noinst_LTLIBRARIES = libgeneric.la
|
||||
|
||||
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
|
||||
AM_CPPFLAGS += \
|
||||
-I$(top_srcdir)/common
|
||||
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
|
||||
AM_LDFLAGS = \
|
||||
-lglut -lGL -lGLU -lGLEW -lao \
|
||||
-lOpenThreads \
|
||||
@AVFORMAT_LIBS@ \
|
||||
@AVUTIL_LIBS@ \
|
||||
@AVCODEC_LIBS@ \
|
||||
@SWRESAMPLE_LIBS@ \
|
||||
@SWSCALE_LIBS@
|
||||
|
||||
libgeneric_la_SOURCES = \
|
||||
hardware_caps.c \
|
||||
dmx.cpp \
|
||||
video.cpp \
|
||||
audio.cpp \
|
||||
glfb.cpp \
|
||||
init.cpp \
|
||||
playback.cpp \
|
||||
pwrmngr.cpp \
|
||||
record.cpp
|
||||
|
461
generic-pc/audio.cpp
Normal file
461
generic-pc/audio.cpp
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* (C) 2010-2013 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* cAudio implementation with decoder.
|
||||
* uses libao <http://www.xiph.org/ao/> for output
|
||||
* ffmpeg <http://ffmpeg.org> for demuxing / decoding / format conversion
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "audio_lib.h"
|
||||
#include "dmx_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
#define lt_debug(args...) _lt_debug(HAL_DEBUG_AUDIO, this, args)
|
||||
#define lt_info(args...) _lt_info(HAL_DEBUG_AUDIO, this, args)
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <ao/ao.h>
|
||||
}
|
||||
/* ffmpeg buf 2k */
|
||||
#define INBUF_SIZE 0x0800
|
||||
/* my own buf 16k */
|
||||
#define DMX_BUF_SZ 0x4000
|
||||
|
||||
cAudio * audioDecoder = NULL;
|
||||
extern cDemux *audioDemux;
|
||||
static uint8_t *dmxbuf = NULL;
|
||||
static int bufpos;
|
||||
|
||||
extern bool HAL_nodec;
|
||||
|
||||
static cAudio *gThiz = NULL;
|
||||
|
||||
static ao_device *adevice = NULL;
|
||||
static ao_sample_format sformat;
|
||||
|
||||
static AVCodecContext *c= NULL;
|
||||
|
||||
cAudio::cAudio(void *, void *, void *)
|
||||
{
|
||||
thread_started = false;
|
||||
if (!HAL_nodec)
|
||||
dmxbuf = (uint8_t *)malloc(DMX_BUF_SZ);
|
||||
bufpos = 0;
|
||||
curr_pts = 0;
|
||||
gThiz = this;
|
||||
ao_initialize();
|
||||
}
|
||||
|
||||
cAudio::~cAudio(void)
|
||||
{
|
||||
closeDevice();
|
||||
free(dmxbuf);
|
||||
if (adevice)
|
||||
ao_close(adevice);
|
||||
adevice = NULL;
|
||||
ao_shutdown();
|
||||
}
|
||||
|
||||
void cAudio::openDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
}
|
||||
|
||||
void cAudio::closeDevice(void)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
}
|
||||
|
||||
int cAudio::do_mute(bool enable, bool remember)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, enable, remember);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::Start(void)
|
||||
{
|
||||
lt_debug("%s >\n", __func__);
|
||||
if (! HAL_nodec)
|
||||
OpenThreads::Thread::start();
|
||||
lt_debug("%s <\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::Stop(void)
|
||||
{
|
||||
lt_debug("%s >\n", __func__);
|
||||
if (thread_started)
|
||||
{
|
||||
thread_started = false;
|
||||
OpenThreads::Thread::join();
|
||||
}
|
||||
lt_debug("%s <\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cAudio::Pause(bool /*Pcm*/)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, Mode);
|
||||
};
|
||||
|
||||
void cAudio::SetStreamType(AUDIO_FORMAT type)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, type);
|
||||
};
|
||||
|
||||
int cAudio::setChannel(int /*channel*/)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int le)
|
||||
{
|
||||
lt_debug("%s ch %d srate %d bits %d le %d adevice %p\n", __func__, ch, srate, bits, le, adevice);;
|
||||
int driver;
|
||||
int byte_format = le ? AO_FMT_LITTLE : AO_FMT_BIG;
|
||||
if (sformat.bits != bits || sformat.channels != ch || sformat.rate != srate ||
|
||||
sformat.byte_format != byte_format || adevice == NULL)
|
||||
{
|
||||
driver = ao_default_driver_id();
|
||||
sformat.bits = bits;
|
||||
sformat.channels = ch;
|
||||
sformat.rate = srate;
|
||||
sformat.byte_format = byte_format;
|
||||
sformat.matrix = 0;
|
||||
if (adevice)
|
||||
ao_close(adevice);
|
||||
adevice = ao_open_live(driver, &sformat, NULL);
|
||||
ao_info *ai = ao_driver_info(driver);
|
||||
lt_info("%s: changed params ch %d srate %d bits %d le %d adevice %p\n",
|
||||
__func__, ch, srate, bits, le, adevice);;
|
||||
lt_info("libao driver: %d name '%s' short '%s' author '%s'\n",
|
||||
driver, ai->name, ai->short_name, ai->author);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
int cAudio::WriteClip(unsigned char *buffer, int size)
|
||||
{
|
||||
lt_debug("cAudio::%s buf 0x%p size %d\n", __func__, buffer, size);
|
||||
if (!adevice) {
|
||||
lt_info("%s: adevice not opened?\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
ao_play(adevice, (char *)buffer, size);
|
||||
return size;
|
||||
};
|
||||
|
||||
int cAudio::StopClip()
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
#if 0
|
||||
/* don't do anything - closing / reopening ao all the time makes for long delays
|
||||
* reinit on-demand (e.g. for changed parameters) instead */
|
||||
if (!adevice) {
|
||||
lt_info("%s: adevice not opened?\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
ao_close(adevice);
|
||||
adevice = NULL;
|
||||
#endif
|
||||
return 0;
|
||||
};
|
||||
|
||||
void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode)
|
||||
{
|
||||
type = 0;
|
||||
layer = 0; /* not used */
|
||||
freq = 0;
|
||||
bitrate = 0; /* not used, but easy to get :-) */
|
||||
mode = 0; /* default: stereo */
|
||||
if (c) {
|
||||
type = (c->codec_id != AV_CODEC_ID_MP2); /* only mpeg / not mpeg is indicated */
|
||||
freq = c->sample_rate;
|
||||
bitrate = c->bit_rate;
|
||||
if (c->channels == 1)
|
||||
mode = 3; /* for AV_CODEC_ID_MP2, only stereo / mono is detected for now */
|
||||
if (c->codec_id != AV_CODEC_ID_MP2) {
|
||||
switch (c->channel_layout) {
|
||||
case AV_CH_LAYOUT_MONO:
|
||||
mode = 1; // "C"
|
||||
break;
|
||||
case AV_CH_LAYOUT_STEREO:
|
||||
mode = 2; // "L/R"
|
||||
break;
|
||||
case AV_CH_LAYOUT_2_1:
|
||||
case AV_CH_LAYOUT_SURROUND:
|
||||
mode = 3; // "L/C/R"
|
||||
break;
|
||||
case AV_CH_LAYOUT_2POINT1:
|
||||
mode = 4; // "L/R/S"
|
||||
break;
|
||||
case AV_CH_LAYOUT_3POINT1:
|
||||
mode = 5; // "L/C/R/S"
|
||||
break;
|
||||
case AV_CH_LAYOUT_2_2:
|
||||
case AV_CH_LAYOUT_QUAD:
|
||||
mode = 6; // "L/R/SL/SR"
|
||||
break;
|
||||
case AV_CH_LAYOUT_5POINT0:
|
||||
case AV_CH_LAYOUT_5POINT1:
|
||||
mode = 7; // "L/C/R/SL/SR"
|
||||
break;
|
||||
default:
|
||||
lt_info("%s: unknown ch_layout 0x%" PRIx64 "\n",
|
||||
__func__, c->channel_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
lt_debug("%s t: %d l: %d f: %d b: %d m: %d codec_id: %x\n",
|
||||
__func__, type, layer, freq, bitrate, mode, c->codec_id);
|
||||
};
|
||||
|
||||
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
};
|
||||
|
||||
void cAudio::SetHdmiDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
};
|
||||
|
||||
void cAudio::SetSpdifDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
};
|
||||
|
||||
void cAudio::ScheduleMute(bool On)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, On);
|
||||
};
|
||||
|
||||
void cAudio::EnableAnalogOut(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
};
|
||||
|
||||
void cAudio::setBypassMode(bool disable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, disable);
|
||||
}
|
||||
|
||||
static int _my_read(void *, uint8_t *buf, int buf_size)
|
||||
{
|
||||
return gThiz->my_read(buf, buf_size);
|
||||
}
|
||||
|
||||
int cAudio::my_read(uint8_t *buf, int buf_size)
|
||||
{
|
||||
int tmp = 0;
|
||||
if (audioDecoder && bufpos < DMX_BUF_SZ - 4096) {
|
||||
while (bufpos < buf_size && ++tmp < 20) { /* retry max 20 times */
|
||||
int ret = audioDemux->Read(dmxbuf + bufpos, DMX_BUF_SZ - bufpos, 10);
|
||||
if (ret > 0)
|
||||
bufpos += ret;
|
||||
if (! thread_started)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bufpos == 0)
|
||||
return 0;
|
||||
//lt_info("%s buf_size %d bufpos %d th %d tmp %d\n", __func__, buf_size, bufpos, thread_started, tmp);
|
||||
if (bufpos > buf_size) {
|
||||
memcpy(buf, dmxbuf, buf_size);
|
||||
memmove(dmxbuf, dmxbuf + buf_size, bufpos - buf_size);
|
||||
bufpos -= buf_size;
|
||||
return buf_size;
|
||||
}
|
||||
memcpy(buf, dmxbuf, bufpos);
|
||||
tmp = bufpos;
|
||||
bufpos = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void cAudio::run()
|
||||
{
|
||||
lt_info("====================== start decoder thread ================================\n");
|
||||
/* libavcodec & friends */
|
||||
av_register_all();
|
||||
|
||||
AVCodec *codec;
|
||||
AVFormatContext *avfc = NULL;
|
||||
AVInputFormat *inp;
|
||||
AVFrame *frame;
|
||||
uint8_t *inbuf = (uint8_t *)av_malloc(INBUF_SIZE);
|
||||
AVPacket avpkt;
|
||||
int ret, driver;
|
||||
/* libao */
|
||||
ao_info *ai;
|
||||
// ao_device *adevice;
|
||||
// ao_sample_format sformat;
|
||||
/* resample */
|
||||
SwrContext *swr = NULL;
|
||||
uint8_t *obuf = NULL;
|
||||
int obuf_sz = 0; /* in samples */
|
||||
int obuf_sz_max = 0;
|
||||
int o_ch, o_sr; /* output channels and sample rate */
|
||||
uint64_t o_layout; /* output channels layout */
|
||||
char tmp[64] = "unknown";
|
||||
|
||||
curr_pts = 0;
|
||||
av_init_packet(&avpkt);
|
||||
inp = av_find_input_format("mpegts");
|
||||
AVIOContext *pIOCtx = avio_alloc_context(inbuf, INBUF_SIZE, // internal Buffer and its size
|
||||
0, // bWriteable (1=true,0=false)
|
||||
NULL, // user data; will be passed to our callback functions
|
||||
_my_read, // read callback
|
||||
NULL, // write callback
|
||||
NULL); // seek callback
|
||||
avfc = avformat_alloc_context();
|
||||
avfc->pb = pIOCtx;
|
||||
avfc->iformat = inp;
|
||||
avfc->probesize = 188*5;
|
||||
thread_started = true;
|
||||
|
||||
if (avformat_open_input(&avfc, NULL, inp, NULL) < 0) {
|
||||
lt_info("%s: avformat_open_input() failed.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
ret = avformat_find_stream_info(avfc, NULL);
|
||||
lt_debug("%s: avformat_find_stream_info: %d\n", __func__, ret);
|
||||
if (avfc->nb_streams != 1)
|
||||
{
|
||||
lt_info("%s: nb_streams: %d, should be 1!\n", __func__, avfc->nb_streams);
|
||||
goto out;
|
||||
}
|
||||
if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
|
||||
lt_info("%s: stream 0 no audio codec? 0x%x\n", __func__, avfc->streams[0]->codec->codec_type);
|
||||
|
||||
c = avfc->streams[0]->codec;
|
||||
codec = avcodec_find_decoder(c->codec_id);
|
||||
if (!codec) {
|
||||
lt_info("%s: Codec for %s not found\n", __func__, avcodec_get_name(c->codec_id));
|
||||
goto out;
|
||||
}
|
||||
if (avcodec_open2(c, codec, NULL) < 0) {
|
||||
lt_info("%s: avcodec_open2() failed\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
frame = avcodec_alloc_frame();
|
||||
if (!frame) {
|
||||
lt_info("%s: avcodec_alloc_frame failed\n", __func__);
|
||||
goto out2;
|
||||
}
|
||||
/* output sample rate, channels, layout could be set here if necessary */
|
||||
o_ch = c->channels; /* 2 */
|
||||
o_sr = c->sample_rate; /* 48000 */
|
||||
o_layout = c->channel_layout; /* AV_CH_LAYOUT_STEREO */
|
||||
if (sformat.channels != o_ch || sformat.rate != o_sr ||
|
||||
sformat.byte_format != AO_FMT_NATIVE || sformat.bits != 16 || adevice == NULL)
|
||||
{
|
||||
driver = ao_default_driver_id();
|
||||
sformat.bits = 16;
|
||||
sformat.channels = o_ch;
|
||||
sformat.rate = o_sr;
|
||||
sformat.byte_format = AO_FMT_NATIVE;
|
||||
sformat.matrix = 0;
|
||||
if (adevice)
|
||||
ao_close(adevice);
|
||||
adevice = ao_open_live(driver, &sformat, NULL);
|
||||
ai = ao_driver_info(driver);
|
||||
lt_info("%s: changed params ch %d srate %d bits %d adevice %p\n",
|
||||
__func__, o_ch, o_sr, 16, adevice);;
|
||||
lt_info("libao driver: %d name '%s' short '%s' author '%s'\n",
|
||||
driver, ai->name, ai->short_name, ai->author);
|
||||
}
|
||||
#if 0
|
||||
lt_info(" driver options:");
|
||||
for (int i = 0; i < ai->option_count; ++i)
|
||||
fprintf(stderr, " %s", ai->options[i]);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
av_get_sample_fmt_string(tmp, sizeof(tmp), c->sample_fmt);
|
||||
lt_info("decoding %s, sample_fmt %d (%s) sample_rate %d channels %d\n",
|
||||
avcodec_get_name(c->codec_id), c->sample_fmt, tmp, c->sample_rate, c->channels);
|
||||
swr = swr_alloc_set_opts(swr,
|
||||
o_layout, AV_SAMPLE_FMT_S16, o_sr, /* output */
|
||||
c->channel_layout, c->sample_fmt, c->sample_rate, /* input */
|
||||
0, NULL);
|
||||
if (! swr) {
|
||||
lt_info("could not alloc resample context\n");
|
||||
goto out3;
|
||||
}
|
||||
swr_init(swr);
|
||||
while (thread_started) {
|
||||
int gotframe = 0;
|
||||
if (av_read_frame(avfc, &avpkt) < 0)
|
||||
break;
|
||||
avcodec_decode_audio4(c, frame, &gotframe, &avpkt);
|
||||
if (gotframe && thread_started) {
|
||||
int out_linesize;
|
||||
obuf_sz = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) +
|
||||
frame->nb_samples, o_sr, c->sample_rate, AV_ROUND_UP);
|
||||
if (obuf_sz > obuf_sz_max) {
|
||||
lt_info("obuf_sz: %d old: %d\n", obuf_sz, obuf_sz_max);
|
||||
av_free(obuf);
|
||||
if (av_samples_alloc(&obuf, &out_linesize, o_ch,
|
||||
frame->nb_samples, AV_SAMPLE_FMT_S16, 1) < 0) {
|
||||
lt_info("av_samples_alloc failed\n");
|
||||
av_free_packet(&avpkt);
|
||||
break; /* while (thread_started) */
|
||||
}
|
||||
obuf_sz_max = obuf_sz;
|
||||
}
|
||||
obuf_sz = swr_convert(swr, &obuf, obuf_sz,
|
||||
(const uint8_t **)frame->extended_data, frame->nb_samples);
|
||||
curr_pts = av_frame_get_best_effort_timestamp(frame);
|
||||
lt_debug("%s: pts 0x%" PRIx64 " %3f\n", __func__, curr_pts, curr_pts/90000.0);
|
||||
int o_buf_sz = av_samples_get_buffer_size(&out_linesize, o_ch,
|
||||
obuf_sz, AV_SAMPLE_FMT_S16, 1);
|
||||
ao_play(adevice, (char *)obuf, o_buf_sz);
|
||||
}
|
||||
av_free_packet(&avpkt);
|
||||
}
|
||||
// ao_close(adevice); /* can take long :-( */
|
||||
av_free(obuf);
|
||||
swr_free(&swr);
|
||||
out3:
|
||||
avcodec_free_frame(&frame);
|
||||
out2:
|
||||
avcodec_close(c);
|
||||
c = NULL;
|
||||
out:
|
||||
avformat_close_input(&avfc);
|
||||
av_free(pIOCtx->buffer);
|
||||
av_free(pIOCtx);
|
||||
lt_info("======================== end decoder thread ================================\n");
|
||||
}
|
105
generic-pc/audio_lib.h
Normal file
105
generic-pc/audio_lib.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* public header file */
|
||||
|
||||
#ifndef _AUDIO_LIB_H_
|
||||
#define _AUDIO_LIB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <OpenThreads/Thread>
|
||||
#include "../common/cs_types.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SYNC_WITH_PTS,
|
||||
AUDIO_NO_SYNC,
|
||||
AUDIO_SYNC_AUDIO_MASTER
|
||||
} AUDIO_SYNC_MODE;
|
||||
|
||||
typedef enum {
|
||||
HDMI_ENCODED_OFF,
|
||||
HDMI_ENCODED_AUTO,
|
||||
HDMI_ENCODED_FORCED
|
||||
} HDMI_ENCODED_MODE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FMT_AUTO = 0,
|
||||
AUDIO_FMT_MPEG,
|
||||
AUDIO_FMT_MP3,
|
||||
AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_AAC,
|
||||
AUDIO_FMT_AAC_PLUS,
|
||||
AUDIO_FMT_DD_PLUS,
|
||||
AUDIO_FMT_DTS,
|
||||
AUDIO_FMT_AVS,
|
||||
AUDIO_FMT_MLP,
|
||||
AUDIO_FMT_WMA,
|
||||
AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback
|
||||
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
||||
} AUDIO_FORMAT;
|
||||
|
||||
class cAudio : public OpenThreads::Thread
|
||||
{
|
||||
friend class cPlayback;
|
||||
private:
|
||||
int fd;
|
||||
bool Muted;
|
||||
|
||||
int clipfd; /* for pcm playback */
|
||||
int mixer_fd; /* if we are using the OSS mixer */
|
||||
int mixer_num; /* oss mixer to use, if any */
|
||||
|
||||
AUDIO_FORMAT StreamType;
|
||||
AUDIO_SYNC_MODE SyncMode;
|
||||
bool started;
|
||||
bool thread_started;
|
||||
|
||||
int volume;
|
||||
int64_t curr_pts;
|
||||
|
||||
void openDevice(void);
|
||||
void closeDevice(void);
|
||||
|
||||
int do_mute(bool enable, bool remember);
|
||||
void setBypassMode(bool disable);
|
||||
void run();
|
||||
public:
|
||||
/* construct & destruct */
|
||||
cAudio(void *, void *, void *);
|
||||
~cAudio(void);
|
||||
int64_t getPts() { return curr_pts; }
|
||||
|
||||
void *GetHandle() { return NULL; };
|
||||
/* shut up */
|
||||
int mute(bool remember = true) { return do_mute(true, remember); };
|
||||
int unmute(bool remember = true) { return do_mute(false, remember); };
|
||||
|
||||
/* volume, min = 0, max = 255 */
|
||||
int setVolume(unsigned int left, unsigned int right);
|
||||
int getVolume(void) { return volume;}
|
||||
bool getMuteStatus(void) { return Muted; };
|
||||
|
||||
/* start and stop audio */
|
||||
int Start(void);
|
||||
int Stop(void);
|
||||
bool Pause(bool Pcm = true);
|
||||
void SetStreamType(AUDIO_FORMAT type);
|
||||
void SetSyncMode(AVSYNC_TYPE Mode);
|
||||
|
||||
/* select channels */
|
||||
int setChannel(int channel);
|
||||
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
|
||||
int WriteClip(unsigned char * buffer, int size);
|
||||
int StopClip();
|
||||
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
|
||||
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
|
||||
bool IsHdmiDDSupported();
|
||||
void SetHdmiDD(bool enable);
|
||||
void SetSpdifDD(bool enable);
|
||||
void ScheduleMute(bool On);
|
||||
void EnableAnalogOut(bool enable);
|
||||
int my_read(uint8_t *buf, int buf_size);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1
generic-pc/cs_api.h
Symbolic link
1
generic-pc/cs_api.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/cs_api.h
|
505
generic-pc/dmx.cpp
Normal file
505
generic-pc/dmx.cpp
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* cDemux implementation for generic dvbapi
|
||||
*
|
||||
* derived from libtriple/dmx_td.cpp
|
||||
*
|
||||
* (C) 2010-2013 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include "dmx_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
/* needed for getSTC :-( */
|
||||
#include "video_lib.h"
|
||||
extern cVideo *videoDecoder;
|
||||
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_DEMUX, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_DEMUX, this, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_DEMUX, NULL, args)
|
||||
|
||||
#define dmx_err(_errfmt, _errstr, _revents) do { \
|
||||
uint16_t _pid = (uint16_t)-1; uint16_t _f = 0;\
|
||||
if (dmx_type == DMX_PSI_CHANNEL) { \
|
||||
_pid = s_flt.pid; _f = s_flt.filter.filter[0]; \
|
||||
} else { \
|
||||
_pid = p_flt.pid; \
|
||||
}; \
|
||||
lt_info("%s " _errfmt " fd:%d, ev:0x%x %s pid:0x%04hx flt:0x%02hx\n", \
|
||||
__func__, _errstr, fd, _revents, DMX_T[dmx_type], _pid, _f); \
|
||||
} while(0);
|
||||
|
||||
cDemux *videoDemux = NULL;
|
||||
cDemux *audioDemux = NULL;
|
||||
//cDemux *pcrDemux = NULL;
|
||||
|
||||
static const char *DMX_T[] = {
|
||||
"DMX_INVALID",
|
||||
"DMX_VIDEO",
|
||||
"DMX_AUDIO",
|
||||
"DMX_PES",
|
||||
"DMX_PSI",
|
||||
"DMX_PIP",
|
||||
"DMX_TP",
|
||||
"DMX_PCR"
|
||||
};
|
||||
|
||||
/* map the device numbers. for now only demux0 is used */
|
||||
static const char *devname[] = {
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0",
|
||||
"/dev/dvb/adapter0/demux0"
|
||||
};
|
||||
|
||||
/* uuuugly */
|
||||
static int dmx_tp_count = 0;
|
||||
#define MAX_TS_COUNT 8
|
||||
|
||||
extern bool HAL_nodec;
|
||||
|
||||
cDemux::cDemux(int n)
|
||||
{
|
||||
if (n < 0 || n > 2)
|
||||
{
|
||||
lt_info("%s ERROR: n invalid (%d)\n", __FUNCTION__, n);
|
||||
num = 0;
|
||||
}
|
||||
else
|
||||
num = n;
|
||||
fd = -1;
|
||||
measure = false;
|
||||
last_measure = 0;
|
||||
last_data = 0;
|
||||
}
|
||||
|
||||
cDemux::~cDemux()
|
||||
{
|
||||
lt_debug("%s #%d fd: %d\n", __FUNCTION__, num, fd);
|
||||
Close();
|
||||
}
|
||||
|
||||
bool cDemux::Open(DMX_CHANNEL_TYPE pes_type, void * /*hVideoBuffer*/, int uBufferSize)
|
||||
{
|
||||
int devnum = num;
|
||||
int flags = O_RDWR|O_CLOEXEC;
|
||||
if (fd > -1)
|
||||
lt_info("%s FD ALREADY OPENED? fd = %d\n", __FUNCTION__, fd);
|
||||
|
||||
dmx_type = pes_type;
|
||||
if (pes_type != DMX_PSI_CHANNEL)
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
fd = open(devname[devnum], flags);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s %s: %m\n", __FUNCTION__, devname[devnum]);
|
||||
return false;
|
||||
}
|
||||
lt_debug("%s #%d pes_type: %s(%d), uBufferSize: %d fd: %d\n", __func__,
|
||||
num, DMX_T[pes_type], pes_type, uBufferSize, fd);
|
||||
|
||||
if (dmx_type == DMX_VIDEO_CHANNEL)
|
||||
uBufferSize = 0x100000; /* 1MB */
|
||||
if (dmx_type == DMX_AUDIO_CHANNEL)
|
||||
uBufferSize = 0x10000; /* 64k */
|
||||
#if 0
|
||||
if (!pesfds.empty())
|
||||
{
|
||||
lt_info("%s ERROR! pesfds not empty!\n", __FUNCTION__); /* TODO: error handling */
|
||||
return false;
|
||||
}
|
||||
int n = DMX_SOURCE_FRONT0;
|
||||
if (ioctl(fd, DMX_SET_SOURCE, &n) < 0)
|
||||
lt_info("%s DMX_SET_SOURCE %d failed! (%m)\n", __func__, n);
|
||||
#endif
|
||||
if (uBufferSize > 0)
|
||||
{
|
||||
/* probably uBufferSize == 0 means "use default size". TODO: find a reasonable default */
|
||||
if (ioctl(fd, DMX_SET_BUFFER_SIZE, uBufferSize) < 0)
|
||||
lt_info("%s DMX_SET_BUFFER_SIZE failed (%m)\n", __func__);
|
||||
}
|
||||
buffersize = uBufferSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cDemux::Close(void)
|
||||
{
|
||||
lt_debug("%s #%d, fd = %d\n", __FUNCTION__, num, fd);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return;
|
||||
}
|
||||
pesfds.clear();
|
||||
ioctl(fd, DMX_STOP);
|
||||
close(fd);
|
||||
fd = -1;
|
||||
if (measure)
|
||||
return;
|
||||
if (dmx_type == DMX_TP_CHANNEL)
|
||||
{
|
||||
dmx_tp_count--;
|
||||
if (dmx_tp_count < 0)
|
||||
{
|
||||
lt_info("%s dmx_tp_count < 0!!\n", __func__);
|
||||
dmx_tp_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cDemux::Start(bool)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return false;
|
||||
}
|
||||
ioctl(fd, DMX_START);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::Stop(void)
|
||||
{
|
||||
lt_debug("%s #%d fd: %d type: %s\n", __func__, num, fd, DMX_T[dmx_type]);
|
||||
if (fd < 0)
|
||||
{
|
||||
lt_info("%s #%d: not open!\n", __FUNCTION__, num);
|
||||
return false;
|
||||
}
|
||||
ioctl(fd, DMX_STOP);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDemux::Read(unsigned char *buff, int len, int timeout)
|
||||
{
|
||||
#if 0
|
||||
if (len != 4095 && timeout != 100)
|
||||
fprintf(stderr, "cDemux::%s #%d fd: %d type: %s len: %d timeout: %d\n",
|
||||
__FUNCTION__, num, fd, DMX_T[dmx_type], len, timeout);
|
||||
#endif
|
||||
int rc;
|
||||
struct pollfd ufds;
|
||||
ufds.fd = fd;
|
||||
ufds.events = POLLIN|POLLPRI|POLLERR;
|
||||
ufds.revents = 0;
|
||||
|
||||
if (timeout > 0)
|
||||
{
|
||||
retry:
|
||||
rc = ::poll(&ufds, 1, timeout);
|
||||
if (!rc)
|
||||
return 0; // timeout
|
||||
else if (rc < 0)
|
||||
{
|
||||
dmx_err("poll: %s,", strerror(errno), 0)
|
||||
//lt_info("%s poll: %m\n", __FUNCTION__);
|
||||
/* happens, when running under gdb... */
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
if (ufds.revents & POLLERR) /* POLLERR means buffer error, i.e. buffer overflow */
|
||||
{
|
||||
dmx_err("received %s,", "POLLERR", ufds.revents);
|
||||
/* this seems to happen sometimes at recording start, without bad effects */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (ufds.revents & POLLHUP) /* we get POLLHUP if e.g. a too big DMX_BUFFER_SIZE was set */
|
||||
{
|
||||
dmx_err("received %s,", "POLLHUP", ufds.revents);
|
||||
return -1;
|
||||
}
|
||||
if (!(ufds.revents & POLLIN)) /* we requested POLLIN but did not get it? */
|
||||
{
|
||||
dmx_err("received %s, please report!", "POLLIN", ufds.revents);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ::read(fd, buff, len);
|
||||
//fprintf(stderr, "fd %d ret: %d\n", fd, rc);
|
||||
if (rc < 0)
|
||||
dmx_err("read: %s", strerror(errno), 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool cDemux::sectionFilter(unsigned short pid, const unsigned char * const filter,
|
||||
const unsigned char * const mask, int len, int timeout,
|
||||
const unsigned char * const negmask)
|
||||
{
|
||||
memset(&s_flt, 0, sizeof(s_flt));
|
||||
|
||||
if (len > DMX_FILTER_SIZE)
|
||||
{
|
||||
lt_info("%s #%d: len too long: %d, DMX_FILTER_SIZE %d\n", __func__, num, len, DMX_FILTER_SIZE);
|
||||
len = DMX_FILTER_SIZE;
|
||||
}
|
||||
s_flt.pid = pid;
|
||||
s_flt.timeout = timeout;
|
||||
memcpy(s_flt.filter.filter, filter, len);
|
||||
memcpy(s_flt.filter.mask, mask, len);
|
||||
if (negmask != NULL)
|
||||
memcpy(s_flt.filter.mode, negmask, len);
|
||||
|
||||
s_flt.flags = DMX_IMMEDIATE_START|DMX_CHECK_CRC;
|
||||
|
||||
int to = 0;
|
||||
switch (filter[0]) {
|
||||
case 0x00: /* program_association_section */
|
||||
to = 2000;
|
||||
break;
|
||||
case 0x01: /* conditional_access_section */
|
||||
to = 6000;
|
||||
break;
|
||||
case 0x02: /* program_map_section */
|
||||
to = 1500;
|
||||
break;
|
||||
case 0x03: /* transport_stream_description_section */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x04 - 0x3F: reserved */
|
||||
case 0x40: /* network_information_section - actual_network */
|
||||
to = 10000;
|
||||
break;
|
||||
case 0x41: /* network_information_section - other_network */
|
||||
to = 15000;
|
||||
break;
|
||||
case 0x42: /* service_description_section - actual_transport_stream */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x43 - 0x45: reserved for future use */
|
||||
case 0x46: /* service_description_section - other_transport_stream */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x47 - 0x49: reserved for future use */
|
||||
case 0x4A: /* bouquet_association_section */
|
||||
to = 11000;
|
||||
break;
|
||||
/* 0x4B - 0x4D: reserved for future use */
|
||||
case 0x4E: /* event_information_section - actual_transport_stream, present/following */
|
||||
to = 2000;
|
||||
break;
|
||||
case 0x4F: /* event_information_section - other_transport_stream, present/following */
|
||||
to = 10000;
|
||||
break;
|
||||
/* 0x50 - 0x5F: event_information_section - actual_transport_stream, schedule */
|
||||
/* 0x60 - 0x6F: event_information_section - other_transport_stream, schedule */
|
||||
case 0x70: /* time_date_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
//s_flt.pid = 0x0014;
|
||||
to = 30000;
|
||||
break;
|
||||
case 0x71: /* running_status_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x72: /* stuffing_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x73: /* time_offset_section */
|
||||
//s_flt.pid = 0x0014;
|
||||
to = 30000;
|
||||
break;
|
||||
/* 0x74 - 0x7D: reserved for future use */
|
||||
case 0x7E: /* discontinuity_information_section */
|
||||
s_flt.flags &= ~DMX_CHECK_CRC; /* section has no CRC */
|
||||
to = 0;
|
||||
break;
|
||||
case 0x7F: /* selection_information_section */
|
||||
to = 0;
|
||||
break;
|
||||
/* 0x80 - 0x8F: ca_message_section */
|
||||
/* 0x90 - 0xFE: user defined */
|
||||
/* 0xFF: reserved */
|
||||
default:
|
||||
break;
|
||||
// return -1;
|
||||
}
|
||||
/* the negmask == NULL is a hack: the users of negmask are PMT-update
|
||||
* and sectionsd EIT-Version change. And they really want no timeout
|
||||
* if timeout == 0 instead of "default timeout" */
|
||||
if (timeout == 0 && negmask == NULL)
|
||||
s_flt.timeout = to;
|
||||
|
||||
lt_debug("%s #%d pid:0x%04hx fd:%d type:%s len:%d to:%d flags:%x flt[0]:%02x\n", __func__, num,
|
||||
pid, fd, DMX_T[dmx_type], len, s_flt.timeout,s_flt.flags, s_flt.filter.filter[0]);
|
||||
#if 0
|
||||
fprintf(stderr,"filt: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.filter[i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mask: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mask [i]);fprintf(stderr,"\n");
|
||||
fprintf(stderr,"mode: ");for(int i=0;i<DMX_FILTER_SIZE;i++)fprintf(stderr,"%02hhx ",s_flt.filter.mode [i]);fprintf(stderr,"\n");
|
||||
#endif
|
||||
ioctl (fd, DMX_STOP);
|
||||
if (ioctl(fd, DMX_SET_FILTER, &s_flt) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cDemux::pesFilter(const unsigned short pid)
|
||||
{
|
||||
/* allow PID 0 for web streaming e.g.
|
||||
* this check originally is from tuxbox cvs but I'm not sure
|
||||
* what it is good for...
|
||||
if (pid <= 0x0001 && dmx_type != DMX_PCR_ONLY_CHANNEL)
|
||||
return false;
|
||||
*/
|
||||
if ((pid >= 0x0002 && pid <= 0x000f) || pid >= 0x1fff)
|
||||
return false;
|
||||
|
||||
lt_debug("%s #%d pid: 0x%04hx fd: %d type: %s\n", __FUNCTION__, num, pid, fd, DMX_T[dmx_type]);
|
||||
|
||||
memset(&p_flt, 0, sizeof(p_flt));
|
||||
p_flt.pid = pid;
|
||||
p_flt.output = DMX_OUT_DECODER;
|
||||
p_flt.input = DMX_IN_FRONTEND;
|
||||
|
||||
switch (dmx_type) {
|
||||
case DMX_PCR_ONLY_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_PCR;
|
||||
if (HAL_nodec)
|
||||
return true;
|
||||
break;
|
||||
case DMX_AUDIO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||
if (HAL_nodec) /* no need to demux if we don't decode... */
|
||||
return true;
|
||||
break;
|
||||
case DMX_VIDEO_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||
if (HAL_nodec)
|
||||
return true;
|
||||
break;
|
||||
case DMX_PES_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TAP;
|
||||
break;
|
||||
case DMX_TP_CHANNEL:
|
||||
p_flt.pes_type = DMX_PES_OTHER;
|
||||
p_flt.output = DMX_OUT_TSDEMUX_TAP;
|
||||
break;
|
||||
default:
|
||||
lt_info("%s #%d invalid dmx_type %d!\n", __func__, num, dmx_type);
|
||||
return false;
|
||||
}
|
||||
return (ioctl(fd, DMX_SET_PES_FILTER, &p_flt) >= 0);
|
||||
}
|
||||
|
||||
void cDemux::SetSyncMode(AVSYNC_TYPE /*mode*/)
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
}
|
||||
|
||||
void *cDemux::getBuffer()
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *cDemux::getChannel()
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDemux::addPid(unsigned short Pid)
|
||||
{
|
||||
lt_debug("%s: pid 0x%04hx\n", __func__, Pid);
|
||||
pes_pids pfd;
|
||||
int ret;
|
||||
if (dmx_type != DMX_TP_CHANNEL)
|
||||
{
|
||||
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||
return false;
|
||||
}
|
||||
if (fd == -1)
|
||||
lt_info("%s bucketfd not yet opened? pid=%hx\n", __FUNCTION__, Pid);
|
||||
pfd.fd = fd; /* dummy */
|
||||
pfd.pid = Pid;
|
||||
pesfds.push_back(pfd);
|
||||
ret = (ioctl(fd, DMX_ADD_PID, &Pid));
|
||||
if (ret < 0)
|
||||
lt_info("%s: DMX_ADD_PID (%m)\n", __func__);
|
||||
return (ret != -1);
|
||||
}
|
||||
|
||||
void cDemux::removePid(unsigned short Pid)
|
||||
{
|
||||
if (dmx_type != DMX_TP_CHANNEL)
|
||||
{
|
||||
lt_info("%s pes_type %s not implemented yet! pid=%hx\n", __FUNCTION__, DMX_T[dmx_type], Pid);
|
||||
return;
|
||||
}
|
||||
for (std::vector<pes_pids>::iterator i = pesfds.begin(); i != pesfds.end(); ++i)
|
||||
{
|
||||
if ((*i).pid == Pid) {
|
||||
lt_debug("removePid: removing demux fd %d pid 0x%04x\n", fd, Pid);
|
||||
if (ioctl(fd, DMX_REMOVE_PID, Pid) < 0)
|
||||
lt_info("%s: (DMX_REMOVE_PID, 0x%04hx): %m\n", __func__, Pid);
|
||||
pesfds.erase(i);
|
||||
return; /* TODO: what if the same PID is there multiple times */
|
||||
}
|
||||
}
|
||||
lt_info("%s pid 0x%04x not found\n", __FUNCTION__, Pid);
|
||||
}
|
||||
|
||||
void cDemux::getSTC(int64_t * STC)
|
||||
{
|
||||
int64_t pts = 0;
|
||||
if (videoDecoder)
|
||||
pts = videoDecoder->GetPTS();
|
||||
*STC = pts;
|
||||
}
|
||||
|
||||
int cDemux::getUnit(void)
|
||||
{
|
||||
lt_debug("%s #%d\n", __FUNCTION__, num);
|
||||
/* just guessed that this is the right thing to do.
|
||||
right now this is only used by the CA code which is stubbed out
|
||||
anyway */
|
||||
return num;
|
||||
}
|
||||
|
||||
bool cDemux::SetSource(int unit, int source)
|
||||
{
|
||||
lt_info_c("%s(%d, %d): not implemented yet\n", __func__, unit, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
int cDemux::GetSource(int unit)
|
||||
{
|
||||
lt_info_c("%s(%d): not implemented yet\n", __func__, unit);
|
||||
return 0;
|
||||
}
|
1
generic-pc/dmx_cs.h
Normal file
1
generic-pc/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "dmx_lib.h"
|
70
generic-pc/dmx_lib.h
Normal file
70
generic-pc/dmx_lib.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef __DEMUX_TD_H
|
||||
#define __DEMUX_TD_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/dvb/dmx.h>
|
||||
#include "../common/cs_types.h"
|
||||
|
||||
#define MAX_DMX_UNITS 4
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DMX_INVALID = 0,
|
||||
DMX_VIDEO_CHANNEL = 1,
|
||||
DMX_AUDIO_CHANNEL,
|
||||
DMX_PES_CHANNEL,
|
||||
DMX_PSI_CHANNEL,
|
||||
DMX_PIP_CHANNEL,
|
||||
DMX_TP_CHANNEL,
|
||||
DMX_PCR_ONLY_CHANNEL
|
||||
} DMX_CHANNEL_TYPE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
unsigned short pid;
|
||||
} pes_pids;
|
||||
|
||||
class cDemux
|
||||
{
|
||||
private:
|
||||
int num;
|
||||
int fd;
|
||||
int buffersize;
|
||||
bool measure;
|
||||
uint64_t last_measure, last_data;
|
||||
DMX_CHANNEL_TYPE dmx_type;
|
||||
std::vector<pes_pids> pesfds;
|
||||
struct dmx_sct_filter_params s_flt;
|
||||
struct dmx_pes_filter_params p_flt;
|
||||
public:
|
||||
|
||||
bool Open(DMX_CHANNEL_TYPE pes_type, void * x = NULL, int y = 0);
|
||||
void Close(void);
|
||||
bool Start(bool record = false);
|
||||
bool Stop(void);
|
||||
int Read(unsigned char *buff, int len, int Timeout = 0);
|
||||
bool sectionFilter(unsigned short pid, const unsigned char * const filter, const unsigned char * const mask, int len, int Timeout = 0, const unsigned char * const negmask = NULL);
|
||||
bool pesFilter(const unsigned short pid);
|
||||
void SetSyncMode(AVSYNC_TYPE mode);
|
||||
void * getBuffer();
|
||||
void * getChannel();
|
||||
DMX_CHANNEL_TYPE getChannelType(void) { return dmx_type; };
|
||||
bool addPid(unsigned short pid);
|
||||
void getSTC(int64_t * STC);
|
||||
int getUnit(void);
|
||||
static bool SetSource(int unit, int source);
|
||||
static int GetSource(int unit);
|
||||
// TD only functions
|
||||
int getFD(void) { return fd; }; /* needed by cPlayback class */
|
||||
void removePid(unsigned short Pid); /* needed by cRecord class */
|
||||
std::vector<pes_pids> getPesPids(void) { return pesfds; };
|
||||
//
|
||||
cDemux(int num = 0);
|
||||
~cDemux();
|
||||
};
|
||||
|
||||
#endif //__DEMUX_H
|
597
generic-pc/glfb.cpp
Normal file
597
generic-pc/glfb.cpp
Normal file
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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/>.
|
||||
|
||||
openGL based framebuffer implementation
|
||||
based on Carjay's neutrino-hd-dvbapi work, see
|
||||
http://gitorious.org/neutrino-hd/neutrino-hd-dvbapi
|
||||
|
||||
TODO: AV-Sync code is "experimental" at best
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
#include "glfb.h"
|
||||
#include "video_lib.h"
|
||||
#include "audio_lib.h"
|
||||
|
||||
#include "lt_debug.h"
|
||||
|
||||
#define lt_debug_c(args...) _lt_debug(HAL_DEBUG_INIT, NULL, args)
|
||||
#define lt_info_c(args...) _lt_info(HAL_DEBUG_INIT, NULL, args)
|
||||
#define lt_debug(args...) _lt_debug(HAL_DEBUG_INIT, this, args)
|
||||
#define lt_info(args...) _lt_info(HAL_DEBUG_INIT, this, args)
|
||||
|
||||
|
||||
extern cVideo *videoDecoder;
|
||||
extern cAudio *audioDecoder;
|
||||
|
||||
static GLFramebuffer *gThiz = 0; /* GLUT does not allow for an arbitrary argument to the render func */
|
||||
|
||||
GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mInitDone(false)
|
||||
{
|
||||
mState.width = x;
|
||||
mState.height = y;
|
||||
mX = &_mX[0];
|
||||
mY = &_mY[0];
|
||||
*mX = x;
|
||||
*mY = y;
|
||||
av_reduce(&mOA.num, &mOA.den, x, y, INT_MAX);
|
||||
mVA = mOA; /* initial aspect ratios are from the FB resolution, those */
|
||||
_mVA = mVA; /* will be updated by the videoDecoder functions anyway */
|
||||
mVAchanged = true;
|
||||
mCrop = DISPLAY_AR_MODE_PANSCAN;
|
||||
zoom = 1.0;
|
||||
xscale = 1.0;
|
||||
const char *tmp = getenv("GLFB_FULLSCREEN");
|
||||
mFullscreen = !!(tmp);
|
||||
|
||||
mState.blit = true;
|
||||
last_apts = 0;
|
||||
|
||||
/* linux framebuffer compat mode */
|
||||
screeninfo.bits_per_pixel = 32;
|
||||
screeninfo.xres = mState.width;
|
||||
screeninfo.xres_virtual = screeninfo.xres;
|
||||
screeninfo.yres = mState.height;
|
||||
screeninfo.yres_virtual = screeninfo.yres;
|
||||
screeninfo.blue.length = 8;
|
||||
screeninfo.blue.offset = 0;
|
||||
screeninfo.green.length = 8;
|
||||
screeninfo.green.offset = 8;
|
||||
screeninfo.red.length = 8;
|
||||
screeninfo.red.offset = 16;
|
||||
screeninfo.transp.length = 8;
|
||||
screeninfo.transp.offset = 24;
|
||||
|
||||
unlink("/tmp/neutrino.input");
|
||||
mkfifo("/tmp/neutrino.input", 0600);
|
||||
input_fd = open("/tmp/neutrino.input", O_RDWR|O_CLOEXEC|O_NONBLOCK);
|
||||
if (input_fd < 0)
|
||||
lt_info("%s: could not open /tmp/neutrino.input FIFO: %m\n", __func__);
|
||||
initKeys();
|
||||
OpenThreads::Thread::start();
|
||||
while (!mInitDone)
|
||||
usleep(1);
|
||||
}
|
||||
|
||||
GLFramebuffer::~GLFramebuffer()
|
||||
{
|
||||
mShutDown = true;
|
||||
OpenThreads::Thread::join();
|
||||
if (input_fd >= 0)
|
||||
close(input_fd);
|
||||
}
|
||||
|
||||
void GLFramebuffer::initKeys()
|
||||
{
|
||||
mSpecialMap[GLUT_KEY_UP] = KEY_UP;
|
||||
mSpecialMap[GLUT_KEY_DOWN] = KEY_DOWN;
|
||||
mSpecialMap[GLUT_KEY_LEFT] = KEY_LEFT;
|
||||
mSpecialMap[GLUT_KEY_RIGHT] = KEY_RIGHT;
|
||||
|
||||
mSpecialMap[GLUT_KEY_F1] = KEY_RED;
|
||||
mSpecialMap[GLUT_KEY_F2] = KEY_GREEN;
|
||||
mSpecialMap[GLUT_KEY_F3] = KEY_YELLOW;
|
||||
mSpecialMap[GLUT_KEY_F4] = KEY_BLUE;
|
||||
|
||||
mSpecialMap[GLUT_KEY_F5] = KEY_WWW;
|
||||
mSpecialMap[GLUT_KEY_F6] = KEY_SUBTITLE;
|
||||
mSpecialMap[GLUT_KEY_F7] = KEY_MOVE;
|
||||
mSpecialMap[GLUT_KEY_F8] = KEY_SLEEP;
|
||||
|
||||
mSpecialMap[GLUT_KEY_PAGE_UP] = KEY_PAGEUP;
|
||||
mSpecialMap[GLUT_KEY_PAGE_DOWN] = KEY_PAGEDOWN;
|
||||
|
||||
mKeyMap[0x0d] = KEY_OK;
|
||||
mKeyMap[0x1b] = KEY_EXIT;
|
||||
mKeyMap['e'] = KEY_EPG;
|
||||
mKeyMap['i'] = KEY_INFO;
|
||||
mKeyMap['m'] = KEY_MENU;
|
||||
|
||||
mKeyMap['+'] = KEY_VOLUMEUP;
|
||||
mKeyMap['-'] = KEY_VOLUMEDOWN;
|
||||
mKeyMap['.'] = KEY_MUTE;
|
||||
mKeyMap['h'] = KEY_HELP;
|
||||
mKeyMap['p'] = KEY_POWER;
|
||||
|
||||
mKeyMap['0'] = KEY_0;
|
||||
mKeyMap['1'] = KEY_1;
|
||||
mKeyMap['2'] = KEY_2;
|
||||
mKeyMap['3'] = KEY_3;
|
||||
mKeyMap['4'] = KEY_4;
|
||||
mKeyMap['5'] = KEY_5;
|
||||
mKeyMap['6'] = KEY_6;
|
||||
mKeyMap['7'] = KEY_7;
|
||||
mKeyMap['8'] = KEY_8;
|
||||
mKeyMap['9'] = KEY_9;
|
||||
|
||||
mKeyMap['r'] = KEY_RED;
|
||||
mKeyMap['g'] = KEY_GREEN;
|
||||
mKeyMap['y'] = KEY_YELLOW;
|
||||
mKeyMap['b'] = KEY_BLUE;
|
||||
}
|
||||
|
||||
void GLFramebuffer::run()
|
||||
{
|
||||
setupCtx();
|
||||
setupOSDBuffer();
|
||||
mInitDone = true; /* signal that setup is finished */
|
||||
|
||||
/* init the good stuff */
|
||||
GLenum err = glewInit();
|
||||
if(err == GLEW_OK)
|
||||
{
|
||||
if((!GLEW_VERSION_1_5)||(!GLEW_EXT_pixel_buffer_object)||(!GLEW_ARB_texture_non_power_of_two))
|
||||
{
|
||||
lt_info("GLFB: Sorry, your graphics card is not supported. "
|
||||
"Needs at least OpenGL 1.5, pixel buffer objects and NPOT textures.\n");
|
||||
lt_info("incompatible graphics card: %m");
|
||||
_exit(1); /* Life is hard */
|
||||
}
|
||||
else
|
||||
{
|
||||
gThiz = this;
|
||||
glutSetCursor(GLUT_CURSOR_NONE);
|
||||
glutDisplayFunc(GLFramebuffer::rendercb);
|
||||
glutKeyboardFunc(GLFramebuffer::keyboardcb);
|
||||
glutSpecialFunc(GLFramebuffer::specialcb);
|
||||
glutReshapeFunc(GLFramebuffer::resizecb);
|
||||
setupGLObjects(); /* needs GLEW prototypes */
|
||||
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
|
||||
glutMainLoop();
|
||||
releaseGLObjects();
|
||||
}
|
||||
}
|
||||
else
|
||||
lt_info("GLFB: error initializing glew: %d\n", err);
|
||||
lt_info("GLFB: GL thread stopping\n");
|
||||
}
|
||||
|
||||
|
||||
void GLFramebuffer::setupCtx()
|
||||
{
|
||||
int argc = 1;
|
||||
/* some dummy commandline for GLUT to be happy */
|
||||
char const *argv[2] = { "neutrino", 0 };
|
||||
lt_info("GLFB: GL thread starting\n");
|
||||
glutInit(&argc, const_cast<char **>(argv));
|
||||
glutInitWindowSize(mX[0], mY[0]);
|
||||
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
|
||||
glutCreateWindow("Neutrino");
|
||||
}
|
||||
|
||||
void GLFramebuffer::setupOSDBuffer()
|
||||
{ /* the OSD buffer size can be decoupled from the actual
|
||||
window size since the GL can blit-stretch with no
|
||||
trouble at all, ah, the luxury of ignorance... */
|
||||
// mMutex.lock();
|
||||
if (mState.width && mState.height)
|
||||
{
|
||||
/* 32bit FB depth, *2 because tuxtxt uses a shadow buffer */
|
||||
int fbmem = mState.width * mState.height * 4 * 2;
|
||||
mOSDBuffer.resize(fbmem);
|
||||
lt_info("GLFB: OSD buffer set to %d bytes\n", fbmem);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFramebuffer::setupGLObjects()
|
||||
{
|
||||
unsigned char buf[4] = { 0, 0, 0, 0 }; /* 1 black pixel */
|
||||
glGenTextures(1, &mState.osdtex);
|
||||
glGenTextures(1, &mState.displaytex);
|
||||
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mState.width, mState.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mState.displaytex); /* we do not yet know the size so will set that inline */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
glGenBuffers(1, &mState.pbo);
|
||||
glGenBuffers(1, &mState.displaypbo);
|
||||
|
||||
/* hack to start with black video buffer instead of white */
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.displaypbo);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, sizeof(buf), buf, GL_STREAM_DRAW_ARB);
|
||||
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
|
||||
void GLFramebuffer::releaseGLObjects()
|
||||
{
|
||||
glDeleteBuffers(1, &mState.pbo);
|
||||
glDeleteBuffers(1, &mState.displaypbo);
|
||||
glDeleteTextures(1, &mState.osdtex);
|
||||
glDeleteTextures(1, &mState.displaytex);
|
||||
}
|
||||
|
||||
|
||||
/* static */ void GLFramebuffer::rendercb()
|
||||
{
|
||||
gThiz->render();
|
||||
}
|
||||
|
||||
|
||||
/* static */ void GLFramebuffer::keyboardcb(unsigned char key, int /*x*/, int /*y*/)
|
||||
{
|
||||
lt_debug_c("GLFB::%s: 0x%x\n", __func__, key);
|
||||
struct input_event ev;
|
||||
if (key == 'f')
|
||||
{
|
||||
lt_info_c("GLFB::%s: toggle fullscreen %s\n", __func__, gThiz->mFullscreen?"off":"on");
|
||||
gThiz->mFullscreen = !(gThiz->mFullscreen);
|
||||
gThiz->mReInit = true;
|
||||
return;
|
||||
}
|
||||
std::map<unsigned char, int>::const_iterator i = gThiz->mKeyMap.find(key);
|
||||
if (i == gThiz->mKeyMap.end())
|
||||
return;
|
||||
ev.code = i->second;
|
||||
ev.value = 1; /* key own */
|
||||
ev.type = EV_KEY;
|
||||
gettimeofday(&ev.time, NULL);
|
||||
lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code);
|
||||
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||
ev.value = 0; /* neutrino is stupid, so push key up directly after key down */
|
||||
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
/* static */ void GLFramebuffer::specialcb(int key, int /*x*/, int /*y*/)
|
||||
{
|
||||
lt_debug_c("GLFB::%s: 0x%x\n", __func__, key);
|
||||
struct input_event ev;
|
||||
std::map<int, int>::const_iterator i = gThiz->mSpecialMap.find(key);
|
||||
if (i == gThiz->mSpecialMap.end())
|
||||
return;
|
||||
ev.code = i->second;
|
||||
ev.value = 1;
|
||||
ev.type = EV_KEY;
|
||||
gettimeofday(&ev.time, NULL);
|
||||
lt_debug_c("GLFB::%s: pushing 0x%x\n", __func__, ev.code);
|
||||
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||
ev.value = 0;
|
||||
write(gThiz->input_fd, &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
int sleep_us = 30000;
|
||||
|
||||
void GLFramebuffer::render()
|
||||
{
|
||||
if(mShutDown)
|
||||
glutLeaveMainLoop();
|
||||
|
||||
mReInitLock.lock();
|
||||
if (mReInit)
|
||||
{
|
||||
int xoff = 0;
|
||||
int yoff = 0;
|
||||
mVAchanged = true;
|
||||
mReInit = false;
|
||||
mX = &_mX[mFullscreen];
|
||||
mY = &_mY[mFullscreen];
|
||||
if (mFullscreen) {
|
||||
int x = glutGet(GLUT_SCREEN_WIDTH);
|
||||
int y = glutGet(GLUT_SCREEN_HEIGHT);
|
||||
*mX = x;
|
||||
*mY = y;
|
||||
AVRational a = { x, y };
|
||||
if (av_cmp_q(a, mOA) < 0)
|
||||
*mY = x * mOA.den / mOA.num;
|
||||
else if (av_cmp_q(a, mOA) > 0)
|
||||
*mX = y * mOA.num / mOA.den;
|
||||
xoff = (x - *mX) / 2;
|
||||
yoff = (y - *mY) / 2;
|
||||
glutFullScreen();
|
||||
} else
|
||||
*mX = *mY * mOA.num / mOA.den;
|
||||
lt_info("%s: reinit mX:%d mY:%d xoff:%d yoff:%d fs %d\n",
|
||||
__func__, *mX, *mY, xoff, yoff, mFullscreen);
|
||||
glViewport(xoff, yoff, *mX, *mY);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
float aspect = static_cast<float>(*mX)/ *mY;
|
||||
float osdaspect = static_cast<float>(mOA.den) / mOA.num;
|
||||
|
||||
glOrtho(aspect*-osdaspect, aspect*osdaspect, -1.0, 1.0, -1.0, 1.0 );
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
mReInitLock.unlock();
|
||||
if (!mFullscreen && (*mX != glutGet(GLUT_WINDOW_WIDTH) || *mY != glutGet(GLUT_WINDOW_HEIGHT)))
|
||||
glutReshapeWindow(*mX, *mY);
|
||||
|
||||
bltDisplayBuffer(); /* decoded video stream */
|
||||
if (mState.blit) {
|
||||
/* only blit manually after fb->blit(), this helps to find missed blit() calls */
|
||||
mState.blit = false;
|
||||
lt_debug("GLFB::%s blit!\n", __func__);
|
||||
bltOSDBuffer(); /* OSD */
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if (mVAchanged)
|
||||
{
|
||||
mVAchanged = false;
|
||||
zoom = 1.0;
|
||||
xscale = 1.0;
|
||||
int cmp = (mCrop == DISPLAY_AR_MODE_NONE) ? 0 : av_cmp_q(mVA, mOA);
|
||||
const AVRational a149 = { 14, 9 };
|
||||
switch (cmp) {
|
||||
default:
|
||||
case INT_MIN: /* invalid */
|
||||
case 0: /* identical */
|
||||
lt_debug("%s: mVA == mOA (or fullscreen mode :-)\n", __func__);
|
||||
break;
|
||||
case 1: /* mVA > mOA -- video is wider than display */
|
||||
lt_debug("%s: mVA > mOA\n", __func__);
|
||||
xscale = av_q2d(mVA) / av_q2d(mOA);
|
||||
switch (mCrop) {
|
||||
case DISPLAY_AR_MODE_PANSCAN:
|
||||
break;
|
||||
case DISPLAY_AR_MODE_LETTERBOX:
|
||||
zoom = av_q2d(mOA) / av_q2d(mVA);
|
||||
break;
|
||||
case DISPLAY_AR_MODE_PANSCAN2:
|
||||
zoom = av_q2d(mOA) / av_q2d(a149);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case -1: /* mVA < mOA -- video is taller than display */
|
||||
lt_debug("%s: mVA < mOA\n", __func__);
|
||||
xscale = av_q2d(mVA) / av_q2d(mOA);
|
||||
switch (mCrop) {
|
||||
case DISPLAY_AR_MODE_LETTERBOX:
|
||||
break;
|
||||
case DISPLAY_AR_MODE_PANSCAN2:
|
||||
if (av_cmp_q(a149, mOA) < 0) {
|
||||
zoom = av_q2d(mVA) * av_q2d(a149) / av_q2d(mOA);
|
||||
break;
|
||||
}
|
||||
/* fallthrough for output format 14:9 */
|
||||
case DISPLAY_AR_MODE_PANSCAN:
|
||||
zoom = av_q2d(mOA) / av_q2d(mVA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||
drawSquare(zoom, xscale);
|
||||
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||
drawSquare(1.0, -100);
|
||||
|
||||
glFlush();
|
||||
glutSwapBuffers();
|
||||
|
||||
GLuint err = glGetError();
|
||||
if (err != 0)
|
||||
lt_info("GLFB::%s: GLError:%d 0x%04x\n", __func__, err, err);
|
||||
if (sleep_us > 0)
|
||||
usleep(sleep_us);
|
||||
glutPostRedisplay();
|
||||
}
|
||||
|
||||
/* static */ void GLFramebuffer::resizecb(int w, int h)
|
||||
{
|
||||
gThiz->checkReinit(w, h);
|
||||
}
|
||||
|
||||
void GLFramebuffer::checkReinit(int x, int y)
|
||||
{
|
||||
static int last_x = 0, last_y = 0;
|
||||
|
||||
mReInitLock.lock();
|
||||
if (!mFullscreen && !mReInit && (x != *mX || y != *mY)) {
|
||||
if (x != *mX && abs(x - last_x) > 2) {
|
||||
*mX = x;
|
||||
*mY = *mX * mOA.den / mOA.num;
|
||||
} else if (y != *mY && abs(y - last_y) > 2) {
|
||||
*mY = y;
|
||||
*mX = *mY * mOA.num / mOA.den;
|
||||
}
|
||||
mReInit = true;
|
||||
}
|
||||
mReInitLock.unlock();
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
}
|
||||
|
||||
void GLFramebuffer::drawSquare(float size, float x_factor)
|
||||
{
|
||||
GLfloat vertices[] = {
|
||||
1.0f, 1.0f,
|
||||
-1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
};
|
||||
|
||||
GLubyte indices[] = { 0, 1, 2, 3 };
|
||||
|
||||
GLfloat texcoords[] = {
|
||||
1.0, 0.0,
|
||||
0.0, 0.0,
|
||||
0.0, 1.0,
|
||||
1.0, 1.0,
|
||||
};
|
||||
if (x_factor > -99.0) { /* x_factor == -100 => OSD */
|
||||
if (videoDecoder &&
|
||||
videoDecoder->pig_x > 0 && videoDecoder->pig_y > 0 &&
|
||||
videoDecoder->pig_w > 0 && videoDecoder->pig_h > 0) {
|
||||
/* these calculations even consider cropping and panscan mode
|
||||
* maybe this could be done with some clever opengl tricks? */
|
||||
double w2 = (double)mState.width * 0.5l;
|
||||
double h2 = (double)mState.height * 0.5l;
|
||||
double x = (double)(videoDecoder->pig_x - w2) / w2 / x_factor / size;
|
||||
double y = (double)(h2 - videoDecoder->pig_y) / h2 / size;
|
||||
double w = (double)videoDecoder->pig_w / w2;
|
||||
double h = (double)videoDecoder->pig_h / h2;
|
||||
x += ((1.0l - x_factor * size) / 2.0l) * w / x_factor / size;
|
||||
y += ((size - 1.0l) / 2.0l) * h / size;
|
||||
vertices[0] = x + w; /* top right x */
|
||||
vertices[1] = y; /* top right y */
|
||||
vertices[2] = x; /* top left x */
|
||||
vertices[3] = y; /* top left y */
|
||||
vertices[4] = x; /* bottom left x */
|
||||
vertices[5] = y - h; /* bottom left y */
|
||||
vertices[6] = vertices[0]; /* bottom right x */
|
||||
vertices[7] = vertices[5]; /* bottom right y */
|
||||
}
|
||||
} else
|
||||
x_factor = 1.0; /* OSD */
|
||||
|
||||
glPushMatrix();
|
||||
glScalef(size * x_factor, size, size);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertices);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
|
||||
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, indices);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void GLFramebuffer::bltOSDBuffer()
|
||||
{
|
||||
/* FIXME: copy each time */
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.pbo);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, mOSDBuffer.size(), &mOSDBuffer[0], GL_STREAM_DRAW_ARB);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mState.osdtex);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mState.width, mState.height, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GLFramebuffer::bltDisplayBuffer()
|
||||
{
|
||||
if (!videoDecoder) /* cannot start yet */
|
||||
return;
|
||||
static bool warn = true;
|
||||
cVideo::SWFramebuffer *buf = videoDecoder->getDecBuf();
|
||||
if (!buf) {
|
||||
if (warn)
|
||||
lt_info("GLFB::%s did not get a buffer...\n", __func__);
|
||||
warn = false;
|
||||
return;
|
||||
}
|
||||
warn = true;
|
||||
int w = buf->width(), h = buf->height();
|
||||
if (w == 0 || h == 0)
|
||||
return;
|
||||
|
||||
AVRational a = buf->AR();
|
||||
if (a.den != 0 && a.num != 0 && av_cmp_q(a, _mVA)) {
|
||||
_mVA = a;
|
||||
/* _mVA is the raw buffer's aspect, mVA is the real scaled output aspect */
|
||||
av_reduce(&mVA.num, &mVA.den, w * a.num, h * a.den, INT_MAX);
|
||||
mVAchanged = true;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.displaypbo);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, buf->size(), &(*buf)[0], GL_STREAM_DRAW_ARB);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, mState.displaytex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
/* "rate control" mechanism starts here...
|
||||
* this implementation is pretty naive and not working too well, but
|
||||
* better this than nothing... :-) */
|
||||
int64_t apts = 0;
|
||||
/* 18000 is the magic value for A/V sync in my libao->pulseaudio->intel_hda setup */
|
||||
int64_t vpts = buf->pts() + 18000;
|
||||
if (audioDecoder)
|
||||
apts = audioDecoder->getPts();
|
||||
if (apts != last_apts) {
|
||||
int rate, dummy1, dummy2;
|
||||
if (apts < vpts)
|
||||
sleep_us = (sleep_us * 2 + (vpts - apts)*10/9) / 3;
|
||||
else if (sleep_us > 1000)
|
||||
sleep_us -= 1000;
|
||||
last_apts = apts;
|
||||
videoDecoder->getPictureInfo(dummy1, dummy2, rate);
|
||||
if (rate > 0)
|
||||
rate = 2000000 / rate; /* limit to half the frame rate */
|
||||
else
|
||||
rate = 50000; /* minimum 20 fps */
|
||||
if (sleep_us > rate)
|
||||
sleep_us = rate;
|
||||
else if (sleep_us < 1)
|
||||
sleep_us = 1;
|
||||
}
|
||||
lt_debug("vpts: 0x%" PRIx64 " apts: 0x%" PRIx64 " diff: %6.3f sleep_us %d buf %d\n",
|
||||
buf->pts(), apts, (buf->pts() - apts)/90000.0, sleep_us, videoDecoder->buf_num);
|
||||
}
|
||||
|
||||
void GLFramebuffer::clear()
|
||||
{
|
||||
/* clears front and back buffer */
|
||||
memset(&mOSDBuffer[0], 0, mOSDBuffer.size());
|
||||
}
|
107
generic-pc/glfb.h
Normal file
107
generic-pc/glfb.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Copyright 2010 Carsten Juttner <carjay@gmx.net>
|
||||
Copyright 2012,2013 Stefan Seyfried <seife@tuxboxcvs.slipkontur.de>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef __glthread__
|
||||
#define __glthread__
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <GL/glew.h>
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/gl.h>
|
||||
#include <linux/fb.h> /* for screeninfo etc. */
|
||||
extern "C" {
|
||||
#include <libavutil/rational.h>
|
||||
}
|
||||
|
||||
class GLFramebuffer : public OpenThreads::Thread
|
||||
{
|
||||
public:
|
||||
GLFramebuffer(int x, int y);
|
||||
~GLFramebuffer();
|
||||
|
||||
void run();
|
||||
std::vector<unsigned char> *getOSDBuffer() { return &mOSDBuffer; } /* pointer to OSD bounce buffer */
|
||||
|
||||
int getOSDWidth() { return mState.width; }
|
||||
int getOSDHeight() { return mState.height; }
|
||||
void blit() { mState.blit = true; }
|
||||
|
||||
void setOutputFormat(AVRational a, int h, int c) { mOA = a; *mY = h; mCrop = c; mReInit = true; }
|
||||
|
||||
void clear();
|
||||
fb_var_screeninfo getScreenInfo() { return screeninfo; }
|
||||
|
||||
private:
|
||||
fb_var_screeninfo screeninfo;
|
||||
int *mX;
|
||||
int *mY;
|
||||
int _mX[2]; /* output window size */
|
||||
int _mY[2]; /* [0] = normal, [1] = fullscreen */
|
||||
AVRational mOA; /* output window aspect ratio */
|
||||
AVRational mVA; /* video aspect ratio */
|
||||
AVRational _mVA; /* for detecting changes in mVA */
|
||||
bool mVAchanged;
|
||||
float zoom; /* for cropping */
|
||||
float xscale; /* and aspect ratio */
|
||||
int mCrop; /* DISPLAY_AR_MODE */
|
||||
|
||||
bool mFullscreen; /* fullscreen? */
|
||||
bool mReInit; /* setup things for GL */
|
||||
OpenThreads::Mutex mReInitLock;
|
||||
bool mShutDown; /* if set main loop is left */
|
||||
bool mInitDone; /* condition predicate */
|
||||
// OpenThreads::Condition mInitCond; /* condition variable for init */
|
||||
// mutable OpenThreads::Mutex mMutex; /* lock our data */
|
||||
|
||||
std::vector<unsigned char> mOSDBuffer; /* silly bounce buffer */
|
||||
|
||||
std::map<unsigned char, int> mKeyMap;
|
||||
std::map<int, int> mSpecialMap;
|
||||
int input_fd;
|
||||
int64_t last_apts;
|
||||
|
||||
static void rendercb(); /* callback for GLUT */
|
||||
void render(); /* actual render function */
|
||||
static void keyboardcb(unsigned char key, int x, int y);
|
||||
static void specialcb(int key, int x, int y);
|
||||
static void resizecb(int w, int h);
|
||||
void checkReinit(int w, int h); /* e.g. in case window was resized */
|
||||
|
||||
void initKeys(); /* setup key bindings for window */
|
||||
void setupCtx(); /* create the window and make the context current */
|
||||
void setupOSDBuffer(); /* create the OSD buffer */
|
||||
void setupGLObjects(); /* PBOs, textures and stuff */
|
||||
void releaseGLObjects();
|
||||
void drawSquare(float size, float x_factor = 1); /* do not be square */
|
||||
|
||||
struct {
|
||||
int width; /* width and height, fixed for a framebuffer instance */
|
||||
int height;
|
||||
GLuint osdtex; /* holds the OSD texture */
|
||||
GLuint pbo; /* PBO we use for transfer to texture */
|
||||
GLuint displaytex; /* holds the display texture */
|
||||
GLuint displaypbo;
|
||||
bool blit;
|
||||
} mState;
|
||||
|
||||
void bltOSDBuffer();
|
||||
void bltDisplayBuffer();
|
||||
};
|
||||
#endif
|
37
generic-pc/hardware_caps.c
Normal file
37
generic-pc/hardware_caps.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* determine the capabilities of the hardware.
|
||||
* part of libstb-hal
|
||||
*
|
||||
* (C) 2010-2012 Stefan Seyfried
|
||||
*
|
||||
* License: GPL v2 or later
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <hardware_caps.h>
|
||||
|
||||
static int initialized = 0;
|
||||
static hw_caps_t caps;
|
||||
|
||||
hw_caps_t *get_hwcaps(void)
|
||||
{
|
||||
if (initialized)
|
||||
return ∩︀
|
||||
|
||||
memset(&caps, 0, sizeof(hw_caps_t));
|
||||
|
||||
initialized = 1;
|
||||
caps.can_shutdown = 1; /* for testing */
|
||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
||||
caps.has_HDMI = 1;
|
||||
caps.display_xres = 8;
|
||||
strcpy(caps.boxvendor, "Generic");
|
||||
strcpy(caps.boxname, "PC");
|
||||
|
||||
return ∩︀
|
||||
}
|
54
generic-pc/init.cpp
Normal file
54
generic-pc/init.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "init_lib.h"
|
||||
#include "lt_debug.h"
|
||||
#include "glfb.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_INIT, NULL, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_INIT, NULL, args)
|
||||
|
||||
static bool initialized = false;
|
||||
GLFramebuffer *glfb = NULL;
|
||||
bool HAL_nodec = false;
|
||||
|
||||
void init_td_api()
|
||||
{
|
||||
if (!initialized)
|
||||
lt_debug_init();
|
||||
lt_info("%s begin, initialized=%d, debug=0x%02x\n", __func__, (int)initialized, debuglevel);
|
||||
if (! glfb) {
|
||||
int x = 1280, y = 720; /* default OSD FB resolution */
|
||||
/*
|
||||
* export GLFB_RESOLUTION=720,576
|
||||
* to restore old default behviour
|
||||
*/
|
||||
const char *tmp = getenv("GLFB_RESOLUTION");
|
||||
const char *p = NULL;
|
||||
if (tmp)
|
||||
p = strchr(tmp, ',');
|
||||
if (p) {
|
||||
x = atoi(tmp);
|
||||
y = atoi(p + 1);
|
||||
}
|
||||
lt_info("%s: setting GL Framebuffer size to %dx%d\n", __func__, x, y);
|
||||
if (!p)
|
||||
lt_info("%s: export GLFB_RESOLUTION=\"<w>,<h>\" to set another resolution\n", __func__);
|
||||
|
||||
glfb = new GLFramebuffer(x, y); /* hard coded to PAL resolution for now */
|
||||
}
|
||||
/* allow disabling of Audio/video decoders in case we just want to
|
||||
* valgrind-check other parts... export HAL_NOAVDEC=1 */
|
||||
if (getenv("HAL_NOAVDEC"))
|
||||
HAL_nodec = true;
|
||||
/* hack, this triggers that the simple_display thread does not blit() once per second... */
|
||||
setenv("SPARK_NOBLIT", "1", 1);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void shutdown_td_api()
|
||||
{
|
||||
lt_info("%s, initialized = %d\n", __func__, (int)initialized);
|
||||
if (glfb)
|
||||
delete glfb;
|
||||
initialized = false;
|
||||
}
|
5
generic-pc/init_lib.h
Normal file
5
generic-pc/init_lib.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef __INIT_TD_H
|
||||
#define __INIT_TD_H
|
||||
void init_td_api();
|
||||
void shutdown_td_api();
|
||||
#endif
|
129
generic-pc/playback.cpp
Normal file
129
generic-pc/playback.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "playback.h"
|
||||
|
||||
static const char * FILENAME = "playback-dummy";
|
||||
|
||||
bool cPlayback::Open(playmode_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cPlayback::Close(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool cPlayback::Start(char * filename, int vpid, int vtype, int apid, int ac3, int duration)
|
||||
{
|
||||
printf("%s:%s - filename=%s vpid=%u vtype=%d apid=%u ac3=%d duration=%i\n",
|
||||
FILENAME, __func__, filename, vpid, vtype, apid, ac3, duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetAPid(int pid, bool /*ac3*/)
|
||||
{
|
||||
printf("%s:%s pid %i\n", FILENAME, __func__, pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SelectSubtitles(int pid)
|
||||
{
|
||||
printf("%s:%s pid %i\n", FILENAME, __func__, pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetSpeed(int speed)
|
||||
{
|
||||
printf("%s:%s playing %d speed %d\n", FILENAME, __func__, playing, speed);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::GetSpeed(int &/*speed*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::GetPosition(int &position, int &duration)
|
||||
{
|
||||
printf("%s:%s %d %d\n", FILENAME, __func__, position, duration);
|
||||
position = 0;
|
||||
duration = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cPlayback::SetPosition(int position, bool)
|
||||
{
|
||||
printf("%s:%s %d\n", FILENAME, __func__,position);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllPids(int *, unsigned int *, unsigned int *numpida, std::string *)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __func__);
|
||||
*numpida = 0;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllSubtitlePids(int * /*pids*/, unsigned int *numpids, std::string * /*language*/)
|
||||
{
|
||||
*numpids = 0;
|
||||
}
|
||||
|
||||
bool cPlayback::SetSubtitlePid(int /*pid*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPlayback::GetPts(uint64_t &/*pts*/)
|
||||
{
|
||||
}
|
||||
|
||||
bool cPlayback::SetTeletextPid(int /*pid*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllTeletextsubtitlePids(int *, unsigned int *numpids, std::string *, int *, int *)
|
||||
{
|
||||
*numpids = 0;
|
||||
}
|
||||
|
||||
void cPlayback::SuspendSubtitle(bool /*b*/)
|
||||
{
|
||||
}
|
||||
|
||||
void cPlayback::RequestAbort()
|
||||
{
|
||||
}
|
||||
|
||||
int cPlayback::GetTeletextPid(void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cPlayback::FindAllSubs(int *, unsigned int *, unsigned int *numpida, std::string *)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __func__);
|
||||
*numpida = 0;
|
||||
}
|
||||
|
||||
void cPlayback::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
|
||||
{
|
||||
positions.clear();
|
||||
titles.clear();
|
||||
}
|
||||
|
||||
void cPlayback::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||
{
|
||||
keys.clear();
|
||||
values.clear();
|
||||
}
|
||||
|
||||
cPlayback::cPlayback(int /*num*/)
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __func__);
|
||||
}
|
||||
|
||||
cPlayback::~cPlayback()
|
||||
{
|
||||
printf("%s:%s\n", FILENAME, __func__);
|
||||
}
|
54
generic-pc/playback.h
Normal file
54
generic-pc/playback.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __PLAYBACK_H
|
||||
#define __PLAYBACK_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
typedef enum {
|
||||
PLAYMODE_TS = 0,
|
||||
PLAYMODE_FILE,
|
||||
} playmode_t;
|
||||
|
||||
class cPlayback
|
||||
{
|
||||
private:
|
||||
bool playing;
|
||||
int mAudioStream;
|
||||
int mSubtitleStream;
|
||||
int mTeletextStream;
|
||||
public:
|
||||
cPlayback(int);
|
||||
bool Open(playmode_t PlayMode);
|
||||
void Close(void);
|
||||
bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration);
|
||||
bool SetAPid(int pid, bool ac3);
|
||||
bool SetSubtitlePid(int pid);
|
||||
bool SetTeletextPid(int pid);
|
||||
int GetAPid(void) { return mAudioStream; }
|
||||
int GetSubtitlePid(void) { return mSubtitleStream; }
|
||||
int GetTeletextPid(void);
|
||||
void SuspendSubtitle(bool);
|
||||
int GetFirstTeletextPid(void);
|
||||
bool SetSpeed(int speed);
|
||||
bool GetSpeed(int &speed) const;
|
||||
bool GetPosition(int &position, int &duration);
|
||||
void GetPts(uint64_t &pts);
|
||||
bool SetPosition(int position, bool absolute = false);
|
||||
void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language);
|
||||
void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language);
|
||||
void FindAllTeletextsubtitlePids(int *pids, unsigned int *numpidt, std::string *tlanguage, int *mags, int *pages);
|
||||
void RequestAbort(void);
|
||||
bool IsPlaying(void) { return false; }
|
||||
uint64_t GetReadCount(void);
|
||||
|
||||
void FindAllSubs(int *pids, unsigned int *supported, unsigned int *numpida, std::string *language);
|
||||
bool SelectSubtitles(int pid);
|
||||
|
||||
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
||||
void GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||
//
|
||||
~cPlayback();
|
||||
};
|
||||
|
||||
#endif
|
1
generic-pc/pwrmngr.cpp
Symbolic link
1
generic-pc/pwrmngr.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.cpp
|
1
generic-pc/pwrmngr.h
Symbolic link
1
generic-pc/pwrmngr.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/pwrmngr.h
|
1
generic-pc/record.cpp
Symbolic link
1
generic-pc/record.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record.cpp
|
1
generic-pc/record_lib.h
Symbolic link
1
generic-pc/record_lib.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../libspark/record_lib.h
|
667
generic-pc/video.cpp
Normal file
667
generic-pc/video.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
* (C) 2002-2003 Andreas Oberritter <obi@tuxbox.org>
|
||||
* (C) 2010-2012 Stefan Seyfried
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
|
||||
*
|
||||
* cVideo implementation with decoder.
|
||||
* uses ffmpeg <http://ffmpeg.org> for demuxing / decoding
|
||||
* decoded frames are stored in SWFramebuffer class
|
||||
*
|
||||
* TODO: buffer handling surely needs some locking...
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
/* ffmpeg buf 32k */
|
||||
#define INBUF_SIZE 0x8000
|
||||
/* my own buf 256k */
|
||||
#define DMX_BUF_SZ 0x20000
|
||||
|
||||
#include "video_lib.h"
|
||||
#include "dmx_lib.h"
|
||||
#include "glfb.h"
|
||||
#include "lt_debug.h"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_VIDEO, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_VIDEO, this, args)
|
||||
#define lt_info_c(args...) _lt_info(TRIPLE_DEBUG_VIDEO, NULL, args)
|
||||
|
||||
cVideo *videoDecoder = NULL;
|
||||
extern cDemux *videoDemux;
|
||||
extern GLFramebuffer *glfb;
|
||||
int system_rev = 0;
|
||||
|
||||
extern bool HAL_nodec;
|
||||
|
||||
static uint8_t *dmxbuf;
|
||||
static int bufpos;
|
||||
|
||||
static const AVRational aspect_ratios[6] = {
|
||||
{ 1, 1 },
|
||||
{ 4, 3 },
|
||||
{ 14, 9 },
|
||||
{ 16, 9 },
|
||||
{ 20, 9 },
|
||||
{ -1,-1 }
|
||||
};
|
||||
|
||||
cVideo::cVideo(int, void *, void *, unsigned int)
|
||||
{
|
||||
lt_debug("%s\n", __func__);
|
||||
av_register_all();
|
||||
if (!HAL_nodec)
|
||||
dmxbuf = (uint8_t *)malloc(DMX_BUF_SZ);
|
||||
bufpos = 0;
|
||||
thread_running = false;
|
||||
w_h_changed = false;
|
||||
dec_w = dec_h = 0;
|
||||
buf_num = 0;
|
||||
buf_in = 0;
|
||||
buf_out = 0;
|
||||
pig_x = pig_y = pig_w = pig_h = 0;
|
||||
display_aspect = DISPLAY_AR_16_9;
|
||||
display_crop = DISPLAY_AR_MODE_LETTERBOX;
|
||||
v_format = VIDEO_FORMAT_MPEG2;
|
||||
}
|
||||
|
||||
cVideo::~cVideo(void)
|
||||
{
|
||||
Stop();
|
||||
/* ouch :-( */
|
||||
videoDecoder = NULL;
|
||||
}
|
||||
|
||||
int cVideo::setAspectRatio(int vformat, int cropping)
|
||||
{
|
||||
lt_info("%s(%d, %d)\n", __func__, vformat, cropping);
|
||||
if (vformat >= 0)
|
||||
display_aspect = (DISPLAY_AR) vformat;
|
||||
if (cropping >= 0)
|
||||
display_crop = (DISPLAY_AR_MODE) cropping;
|
||||
if (display_aspect < DISPLAY_AR_RAW) /* don't know what to do with this */
|
||||
glfb->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::getAspectRatio(void)
|
||||
{
|
||||
buf_m.lock();
|
||||
int ret = 0;
|
||||
int w, h, ar;
|
||||
AVRational a;
|
||||
if (buf_num == 0)
|
||||
goto out;
|
||||
a = buffers[buf_out].AR();
|
||||
w = buffers[buf_out].width();
|
||||
h = buffers[buf_out].height();
|
||||
if (a.den == 0 || h == 0)
|
||||
goto out;
|
||||
ar = w * 100 * a.num / h / a.den;
|
||||
if (ar < 100 || ar > 225) /* < 4:3, > 20:9 */
|
||||
; /* ret = 0: N/A */
|
||||
else if (ar < 140) /* 4:3 */
|
||||
ret = 1;
|
||||
else if (ar < 165) /* 14:9 */
|
||||
ret = 2;
|
||||
else if (ar < 200) /* 16:9 */
|
||||
ret = 3;
|
||||
else
|
||||
ret = 4; /* 20:9 */
|
||||
out:
|
||||
buf_m.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cVideo::setCroppingMode(int)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::Start(void *, unsigned short, unsigned short, void *)
|
||||
{
|
||||
lt_debug("%s running %d >\n", __func__, thread_running);
|
||||
if (!thread_running && !HAL_nodec)
|
||||
OpenThreads::Thread::start();
|
||||
lt_debug("%s running %d <\n", __func__, thread_running);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::Stop(bool)
|
||||
{
|
||||
lt_debug("%s running %d >\n", __func__, thread_running);
|
||||
if (thread_running) {
|
||||
thread_running = false;
|
||||
OpenThreads::Thread::join();
|
||||
}
|
||||
lt_debug("%s running %d <\n", __func__, thread_running);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::setBlank(int)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cVideo::SetVideoSystem(int system, bool)
|
||||
{
|
||||
int h;
|
||||
switch(system)
|
||||
{
|
||||
case VIDEO_STD_NTSC:
|
||||
case VIDEO_STD_480P:
|
||||
h = 480;
|
||||
break;
|
||||
case VIDEO_STD_1080I60:
|
||||
case VIDEO_STD_1080I50:
|
||||
case VIDEO_STD_1080P30:
|
||||
case VIDEO_STD_1080P24:
|
||||
case VIDEO_STD_1080P25:
|
||||
case VIDEO_STD_1080P50:
|
||||
h = 1080;
|
||||
break;
|
||||
case VIDEO_STD_720P50:
|
||||
case VIDEO_STD_720P60:
|
||||
h = 720;
|
||||
break;
|
||||
case VIDEO_STD_AUTO:
|
||||
lt_info("%s: VIDEO_STD_AUTO not implemented\n", __func__);
|
||||
// fallthrough
|
||||
case VIDEO_STD_SECAM:
|
||||
case VIDEO_STD_PAL:
|
||||
case VIDEO_STD_576P:
|
||||
h = 576;
|
||||
break;
|
||||
default:
|
||||
lt_info("%s: unhandled value %d\n", __func__, system);
|
||||
return 0;
|
||||
}
|
||||
v_std = (VIDEO_STD) system;
|
||||
output_h = h;
|
||||
if (display_aspect < DISPLAY_AR_RAW) /* don't know what to do with this */
|
||||
glfb->setOutputFormat(aspect_ratios[display_aspect], output_h, display_crop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cVideo::getPlayState(void)
|
||||
{
|
||||
return VIDEO_PLAYING;
|
||||
}
|
||||
|
||||
void cVideo::SetVideoMode(analog_mode_t)
|
||||
{
|
||||
}
|
||||
|
||||
void cVideo::ShowPicture(const char *fname, const char *)
|
||||
{
|
||||
lt_info("%s(%s)\n", __func__, fname);
|
||||
if (access(fname, R_OK))
|
||||
return;
|
||||
|
||||
unsigned int i;
|
||||
int stream_id = -1;
|
||||
int got_frame = 0;
|
||||
int len;
|
||||
AVFormatContext *avfc = NULL;
|
||||
AVCodecContext *c = NULL;
|
||||
AVCodec *codec;
|
||||
AVFrame *frame, *rgbframe;
|
||||
AVPacket avpkt;
|
||||
|
||||
if (avformat_open_input(&avfc, fname, NULL, NULL) < 0) {
|
||||
lt_info("%s: Could not open file %s\n", __func__, fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (avformat_find_stream_info(avfc, NULL) < 0) {
|
||||
lt_info("%s: Could not find file info %s\n", __func__, fname);
|
||||
goto out_close;
|
||||
}
|
||||
for (i = 0; i < avfc->nb_streams; i++) {
|
||||
if (avfc->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
stream_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stream_id < 0)
|
||||
goto out_close;
|
||||
c = avfc->streams[stream_id]->codec;
|
||||
codec = avcodec_find_decoder(c->codec_id);
|
||||
if (!avcodec_open2(c, codec, NULL) < 0) {
|
||||
lt_info("%s: Could not find/open the codec, id 0x%x\n", __func__, c->codec_id);
|
||||
goto out_close;
|
||||
}
|
||||
frame = avcodec_alloc_frame();
|
||||
rgbframe = avcodec_alloc_frame();
|
||||
if (!frame || !rgbframe) {
|
||||
lt_info("%s: Could not allocate video frame\n", __func__);
|
||||
goto out_free;
|
||||
}
|
||||
av_init_packet(&avpkt);
|
||||
if (av_read_frame(avfc, &avpkt) < 0) {
|
||||
lt_info("%s: av_read_frame < 0\n", __func__);
|
||||
goto out_free;
|
||||
}
|
||||
len = avcodec_decode_video2(c, frame, &got_frame, &avpkt);
|
||||
if (len < 0) {
|
||||
lt_info("%s: avcodec_decode_video2 %d\n", __func__, len);
|
||||
av_free_packet(&avpkt);
|
||||
goto out_free;
|
||||
}
|
||||
if (avpkt.size > len)
|
||||
lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len);
|
||||
if (got_frame) {
|
||||
unsigned int need = avpicture_get_size(PIX_FMT_RGB32, c->width, c->height);
|
||||
struct SwsContext *convert = sws_getContext(c->width, c->height, c->pix_fmt,
|
||||
c->width, c->height, PIX_FMT_RGB32,
|
||||
SWS_BICUBIC, 0, 0, 0);
|
||||
if (!convert)
|
||||
lt_info("%s: ERROR setting up SWS context\n", __func__);
|
||||
else {
|
||||
buf_m.lock();
|
||||
SWFramebuffer *f = &buffers[buf_in];
|
||||
if (f->size() < need)
|
||||
f->resize(need);
|
||||
avpicture_fill((AVPicture *)rgbframe, &(*f)[0], PIX_FMT_RGB32,
|
||||
c->width, c->height);
|
||||
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
||||
rgbframe->data, rgbframe->linesize);
|
||||
sws_freeContext(convert);
|
||||
f->width(c->width);
|
||||
f->height(c->height);
|
||||
f->pts(AV_NOPTS_VALUE);
|
||||
AVRational a = av_guess_sample_aspect_ratio(avfc, avfc->streams[stream_id], frame);
|
||||
f->AR(a);
|
||||
buf_in++;
|
||||
buf_in %= VDEC_MAXBUFS;
|
||||
buf_num++;
|
||||
if (buf_num > (VDEC_MAXBUFS - 1)) {
|
||||
lt_info("%s: buf_num overflow\n", __func__);
|
||||
buf_out++;
|
||||
buf_out %= VDEC_MAXBUFS;
|
||||
buf_num--;
|
||||
}
|
||||
buf_m.unlock();
|
||||
}
|
||||
}
|
||||
av_free_packet(&avpkt);
|
||||
out_free:
|
||||
avcodec_close(c);
|
||||
avcodec_free_frame(&frame);
|
||||
avcodec_free_frame(&rgbframe);
|
||||
out_close:
|
||||
avformat_close_input(&avfc);
|
||||
lt_debug("%s(%s) end\n", __func__, fname);
|
||||
}
|
||||
|
||||
void cVideo::StopPicture()
|
||||
{
|
||||
}
|
||||
|
||||
void cVideo::Standby(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
int cVideo::getBlank(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cVideo::Pig(int x, int y, int w, int h, int /*osd_w*/, int /*osd_h*/, int /*startx*/, int /*starty*/, int /*endx*/, int /*endy*/)
|
||||
{
|
||||
pig_x = x;
|
||||
pig_y = y;
|
||||
pig_w = w;
|
||||
pig_h = h;
|
||||
}
|
||||
|
||||
void cVideo::getPictureInfo(int &width, int &height, int &rate)
|
||||
{
|
||||
width = dec_w;
|
||||
height = dec_h;
|
||||
rate = dec_r;
|
||||
}
|
||||
|
||||
void cVideo::SetSyncMode(AVSYNC_TYPE)
|
||||
{
|
||||
};
|
||||
|
||||
int cVideo::SetStreamType(VIDEO_FORMAT v)
|
||||
{
|
||||
v_format = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cVideo::SWFramebuffer *cVideo::getDecBuf(void)
|
||||
{
|
||||
buf_m.lock();
|
||||
if (buf_num == 0) {
|
||||
buf_m.unlock();
|
||||
return NULL;
|
||||
}
|
||||
SWFramebuffer *p = &buffers[buf_out];
|
||||
buf_out++;
|
||||
buf_num--;
|
||||
buf_out %= VDEC_MAXBUFS;
|
||||
buf_m.unlock();
|
||||
return p;
|
||||
}
|
||||
|
||||
static int my_read(void *, uint8_t *buf, int buf_size)
|
||||
{
|
||||
int tmp = 0;
|
||||
if (videoDecoder && bufpos < DMX_BUF_SZ - 4096) {
|
||||
while (bufpos < buf_size && ++tmp < 20) { /* retry max 20 times */
|
||||
int ret = videoDemux->Read(dmxbuf + bufpos, DMX_BUF_SZ - bufpos, 20);
|
||||
if (ret > 0)
|
||||
bufpos += ret;
|
||||
}
|
||||
}
|
||||
if (bufpos == 0)
|
||||
return 0;
|
||||
if (bufpos > buf_size) {
|
||||
memcpy(buf, dmxbuf, buf_size);
|
||||
memmove(dmxbuf, dmxbuf + buf_size, bufpos - buf_size);
|
||||
bufpos -= buf_size;
|
||||
return buf_size;
|
||||
}
|
||||
memcpy(buf, dmxbuf, bufpos);
|
||||
tmp = bufpos;
|
||||
bufpos = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void cVideo::run(void)
|
||||
{
|
||||
lt_info("====================== start decoder thread ================================\n");
|
||||
AVCodec *codec;
|
||||
AVCodecContext *c= NULL;
|
||||
AVFormatContext *avfc = NULL;
|
||||
AVInputFormat *inp;
|
||||
AVFrame *frame, *rgbframe;
|
||||
uint8_t *inbuf = (uint8_t *)av_malloc(INBUF_SIZE);
|
||||
AVPacket avpkt;
|
||||
struct SwsContext *convert = NULL;
|
||||
|
||||
time_t warn_r = 0; /* last read error */
|
||||
time_t warn_d = 0; /* last decode error */
|
||||
|
||||
bufpos = 0;
|
||||
buf_num = 0;
|
||||
buf_in = 0;
|
||||
buf_out = 0;
|
||||
dec_r = 0;
|
||||
|
||||
av_init_packet(&avpkt);
|
||||
inp = av_find_input_format("mpegts");
|
||||
AVIOContext *pIOCtx = avio_alloc_context(inbuf, INBUF_SIZE, // internal Buffer and its size
|
||||
0, // bWriteable (1=true,0=false)
|
||||
NULL, // user data; will be passed to our callback functions
|
||||
my_read, // read callback
|
||||
NULL, // write callback
|
||||
NULL); // seek callback
|
||||
avfc = avformat_alloc_context();
|
||||
avfc->pb = pIOCtx;
|
||||
avfc->iformat = inp;
|
||||
avfc->probesize = 188*5;
|
||||
|
||||
thread_running = true;
|
||||
if (avformat_open_input(&avfc, NULL, inp, NULL) < 0) {
|
||||
lt_info("%s: Could not open input\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
while (avfc->nb_streams < 1)
|
||||
{
|
||||
lt_info("%s: nb_streams %d, should be 1 => retry\n", __func__, avfc->nb_streams);
|
||||
if (av_read_frame(avfc, &avpkt) < 0)
|
||||
lt_info("%s: av_read_frame < 0\n", __func__);
|
||||
av_free_packet(&avpkt);
|
||||
if (! thread_running)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (avfc->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO)
|
||||
lt_info("%s: no video codec? 0x%x\n", __func__, avfc->streams[0]->codec->codec_type);
|
||||
|
||||
c = avfc->streams[0]->codec;
|
||||
codec = avcodec_find_decoder(c->codec_id);
|
||||
if (!codec) {
|
||||
lt_info("%s: Codec for %s not found\n", __func__, avcodec_get_name(c->codec_id));
|
||||
goto out;
|
||||
}
|
||||
if (avcodec_open2(c, codec, NULL) < 0) {
|
||||
lt_info("%s: Could not open codec\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
frame = avcodec_alloc_frame();
|
||||
rgbframe = avcodec_alloc_frame();
|
||||
if (!frame || !rgbframe) {
|
||||
lt_info("%s: Could not allocate video frame\n", __func__);
|
||||
goto out2;
|
||||
}
|
||||
lt_info("decoding %s\n", avcodec_get_name(c->codec_id));
|
||||
while (thread_running) {
|
||||
if (av_read_frame(avfc, &avpkt) < 0) {
|
||||
if (warn_r - time(NULL) > 4) {
|
||||
lt_info("%s: av_read_frame < 0\n", __func__);
|
||||
warn_r = time(NULL);
|
||||
}
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
int got_frame = 0;
|
||||
int len = avcodec_decode_video2(c, frame, &got_frame, &avpkt);
|
||||
if (len < 0) {
|
||||
if (warn_d - time(NULL) > 4) {
|
||||
lt_info("%s: avcodec_decode_video2 %d\n", __func__, len);
|
||||
warn_d = time(NULL);
|
||||
}
|
||||
av_free_packet(&avpkt);
|
||||
continue;
|
||||
}
|
||||
if (avpkt.size > len)
|
||||
lt_info("%s: WARN: pkt->size %d != len %d\n", __func__, avpkt.size, len);
|
||||
if (got_frame) {
|
||||
unsigned int need = avpicture_get_size(PIX_FMT_RGB32, c->width, c->height);
|
||||
convert = sws_getCachedContext(convert,
|
||||
c->width, c->height, c->pix_fmt,
|
||||
c->width, c->height, PIX_FMT_RGB32,
|
||||
SWS_BICUBIC, 0, 0, 0);
|
||||
if (!convert)
|
||||
lt_info("%s: ERROR setting up SWS context\n", __func__);
|
||||
else {
|
||||
buf_m.lock();
|
||||
SWFramebuffer *f = &buffers[buf_in];
|
||||
if (f->size() < need)
|
||||
f->resize(need);
|
||||
avpicture_fill((AVPicture *)rgbframe, &(*f)[0], PIX_FMT_RGB32,
|
||||
c->width, c->height);
|
||||
sws_scale(convert, frame->data, frame->linesize, 0, c->height,
|
||||
rgbframe->data, rgbframe->linesize);
|
||||
if (dec_w != c->width || dec_h != c->height) {
|
||||
lt_info("%s: pic changed %dx%d -> %dx%d\n", __func__,
|
||||
dec_w, dec_h, c->width, c->height);
|
||||
dec_w = c->width;
|
||||
dec_h = c->height;
|
||||
w_h_changed = true;
|
||||
}
|
||||
f->width(c->width);
|
||||
f->height(c->height);
|
||||
int64_t vpts = av_frame_get_best_effort_timestamp(frame);
|
||||
if (v_format == VIDEO_FORMAT_MPEG2)
|
||||
vpts += 90000*3/10; /* 300ms */
|
||||
f->pts(vpts);
|
||||
AVRational a = av_guess_sample_aspect_ratio(avfc, avfc->streams[0], frame);
|
||||
f->AR(a);
|
||||
buf_in++;
|
||||
buf_in %= VDEC_MAXBUFS;
|
||||
buf_num++;
|
||||
if (buf_num > (VDEC_MAXBUFS - 1)) {
|
||||
lt_info("%s: buf_num overflow\n", __func__);
|
||||
buf_out++;
|
||||
buf_out %= VDEC_MAXBUFS;
|
||||
buf_num--;
|
||||
}
|
||||
dec_r = c->time_base.den/(c->time_base.num * c->ticks_per_frame);
|
||||
buf_m.unlock();
|
||||
}
|
||||
lt_debug("%s: time_base: %d/%d, ticks: %d rate: %d pts 0x%" PRIx64 "\n", __func__,
|
||||
c->time_base.num, c->time_base.den, c->ticks_per_frame, dec_r,
|
||||
av_frame_get_best_effort_timestamp(frame));
|
||||
}
|
||||
av_free_packet(&avpkt);
|
||||
}
|
||||
sws_freeContext(convert);
|
||||
out2:
|
||||
avcodec_close(c);
|
||||
avcodec_free_frame(&frame);
|
||||
avcodec_free_frame(&rgbframe);
|
||||
out:
|
||||
avformat_close_input(&avfc);
|
||||
av_free(pIOCtx->buffer);
|
||||
av_free(pIOCtx);
|
||||
/* reset output buffers */
|
||||
bufpos = 0;
|
||||
buf_num = 0;
|
||||
buf_in = 0;
|
||||
buf_out = 0;
|
||||
lt_info("======================== end decoder thread ================================\n");
|
||||
}
|
||||
|
||||
static bool swscale(unsigned char *src, unsigned char *dst, int sw, int sh, int dw, int dh)
|
||||
{
|
||||
bool ret = false;
|
||||
struct SwsContext *scale = NULL;
|
||||
AVFrame *sframe, *dframe;
|
||||
scale = sws_getCachedContext(scale, sw, sh, PIX_FMT_RGB32, dw, dh, PIX_FMT_RGB32, SWS_BICUBIC, 0, 0, 0);
|
||||
if (!scale) {
|
||||
lt_info_c("%s: ERROR setting up SWS context\n", __func__);
|
||||
return false;
|
||||
}
|
||||
sframe = avcodec_alloc_frame();
|
||||
dframe = avcodec_alloc_frame();
|
||||
if (!sframe || !dframe) {
|
||||
lt_info_c("%s: could not alloc sframe (%p) or dframe (%p)\n", __func__, sframe, dframe);
|
||||
goto out;
|
||||
}
|
||||
avpicture_fill((AVPicture *)sframe, &(src[0]), PIX_FMT_RGB32, sw, sh);
|
||||
avpicture_fill((AVPicture *)dframe, &(dst[0]), PIX_FMT_RGB32, dw, dh);
|
||||
sws_scale(scale, sframe->data, sframe->linesize, 0, sh, dframe->data, dframe->linesize);
|
||||
out:
|
||||
avcodec_free_frame(&sframe);
|
||||
avcodec_free_frame(&dframe);
|
||||
sws_freeContext(scale);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool cVideo::GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video, bool get_osd, bool scale_to_video)
|
||||
{
|
||||
lt_info("%s: data 0x%p xres %d yres %d vid %d osd %d scale %d\n",
|
||||
__func__, data, xres, yres, get_video, get_osd, scale_to_video);
|
||||
SWFramebuffer video;
|
||||
std::vector<unsigned char> *osd = NULL;
|
||||
std::vector<unsigned char> s_osd; /* scaled OSD */
|
||||
int vid_w = 0, vid_h = 0;
|
||||
int osd_w = glfb->getOSDWidth();
|
||||
int osd_h = glfb->getOSDHeight();
|
||||
xres = osd_w;
|
||||
yres = osd_h;
|
||||
if (get_video) {
|
||||
buf_m.lock();
|
||||
video = buffers[buf_out];
|
||||
buf_m.unlock();
|
||||
vid_w = video.width();
|
||||
vid_h = video.height();
|
||||
if (scale_to_video || !get_osd) {
|
||||
xres = vid_w;
|
||||
yres = vid_h;
|
||||
AVRational a = video.AR();
|
||||
/* TODO: this does not consider display_aspect and display_crop */
|
||||
if (a.num > 0 && a.den > 0)
|
||||
xres = vid_w * a.num / a.den;
|
||||
}
|
||||
}
|
||||
if (get_osd)
|
||||
osd = glfb->getOSDBuffer();
|
||||
unsigned int need = avpicture_get_size(PIX_FMT_RGB32, xres, yres);
|
||||
data = (unsigned char *)realloc(data, need); /* will be freed by caller */
|
||||
if (data == NULL) /* out of memory? */
|
||||
return false;
|
||||
|
||||
if (get_video) {
|
||||
if (vid_w != xres || vid_h != yres) /* scale video into data... */
|
||||
swscale(&video[0], data, vid_w, vid_h, xres, yres);
|
||||
else /* get_video and no fancy scaling needed */
|
||||
memcpy(data, &video[0], xres * yres * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
if (get_osd && (osd_w != xres || osd_h != yres)) {
|
||||
/* rescale osd */
|
||||
s_osd.resize(need);
|
||||
swscale(&(*osd)[0], &s_osd[0], osd_w, osd_h, xres, yres);
|
||||
osd = &s_osd;
|
||||
}
|
||||
|
||||
if (get_video && get_osd) {
|
||||
/* alpha blend osd onto data (video). TODO: maybe libavcodec can do this? */
|
||||
uint32_t *d = (uint32_t *)data;
|
||||
uint32_t *pixpos = (uint32_t *)&(*osd)[0];
|
||||
for (int count = 0; count < yres; count++) {
|
||||
for (int count2 = 0; count2 < xres; count2++ ) {
|
||||
uint32_t pix = *pixpos;
|
||||
if ((pix & 0xff000000) == 0xff000000)
|
||||
*d = pix;
|
||||
else {
|
||||
uint8_t *in = (uint8_t *)(pixpos);
|
||||
uint8_t *out = (uint8_t *)d;
|
||||
int a = in[3]; /* TODO: big/little endian? */
|
||||
*out = (*out + ((*in - *out) * a) / 256);
|
||||
in++; out++;
|
||||
*out = (*out + ((*in - *out) * a) / 256);
|
||||
in++; out++;
|
||||
*out = (*out + ((*in - *out) * a) / 256);
|
||||
}
|
||||
d++;
|
||||
pixpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (get_osd) /* only get_osd, data is not yet populated */
|
||||
memcpy(data, &(*osd)[0], xres * yres * sizeof(uint32_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t cVideo::GetPTS(void)
|
||||
{
|
||||
int64_t pts = 0;
|
||||
buf_m.lock();
|
||||
if (buf_num != 0)
|
||||
pts = buffers[buf_out].pts();
|
||||
buf_m.unlock();
|
||||
return pts;
|
||||
}
|
||||
|
||||
void cVideo::SetDemux(cDemux *)
|
||||
{
|
||||
lt_debug("%s: not implemented yet\n", __func__);
|
||||
}
|
220
generic-pc/video_lib.h
Normal file
220
generic-pc/video_lib.h
Normal file
@@ -0,0 +1,220 @@
|
||||
#ifndef _VIDEO_TD_H
|
||||
#define _VIDEO_TD_H
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Mutex>
|
||||
#include <vector>
|
||||
#include <linux/dvb/video.h>
|
||||
#include "../common/cs_types.h"
|
||||
#include "dmx_lib.h"
|
||||
extern "C" {
|
||||
#include <libavutil/rational.h>
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
ANALOG_SD_RGB_CINCH = 0x00,
|
||||
ANALOG_SD_YPRPB_CINCH,
|
||||
ANALOG_HD_RGB_CINCH,
|
||||
ANALOG_HD_YPRPB_CINCH,
|
||||
ANALOG_SD_RGB_SCART = 0x10,
|
||||
ANALOG_SD_YPRPB_SCART,
|
||||
ANALOG_HD_RGB_SCART,
|
||||
ANALOG_HD_YPRPB_SCART,
|
||||
ANALOG_SCART_MASK = 0x10
|
||||
} analog_mode_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
VIDEO_FORMAT_MPEG2 = 0,
|
||||
VIDEO_FORMAT_MPEG4,
|
||||
VIDEO_FORMAT_VC1,
|
||||
VIDEO_FORMAT_JPEG,
|
||||
VIDEO_FORMAT_GIF,
|
||||
VIDEO_FORMAT_PNG
|
||||
} VIDEO_FORMAT;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_SD = 0,
|
||||
VIDEO_HD,
|
||||
VIDEO_120x60i,
|
||||
VIDEO_320x240i,
|
||||
VIDEO_1440x800i,
|
||||
VIDEO_360x288i
|
||||
} VIDEO_DEFINITION;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_FRAME_RATE_23_976 = 0,
|
||||
VIDEO_FRAME_RATE_24,
|
||||
VIDEO_FRAME_RATE_25,
|
||||
VIDEO_FRAME_RATE_29_97,
|
||||
VIDEO_FRAME_RATE_30,
|
||||
VIDEO_FRAME_RATE_50,
|
||||
VIDEO_FRAME_RATE_59_94,
|
||||
VIDEO_FRAME_RATE_60
|
||||
} VIDEO_FRAME_RATE;
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_AR_1_1,
|
||||
DISPLAY_AR_4_3,
|
||||
DISPLAY_AR_14_9,
|
||||
DISPLAY_AR_16_9,
|
||||
DISPLAY_AR_20_9,
|
||||
DISPLAY_AR_RAW,
|
||||
} DISPLAY_AR;
|
||||
|
||||
typedef enum {
|
||||
DISPLAY_AR_MODE_PANSCAN = 0,
|
||||
DISPLAY_AR_MODE_LETTERBOX,
|
||||
DISPLAY_AR_MODE_NONE,
|
||||
DISPLAY_AR_MODE_PANSCAN2
|
||||
} DISPLAY_AR_MODE;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_DB_DR_NEITHER = 0,
|
||||
VIDEO_DB_ON,
|
||||
VIDEO_DB_DR_BOTH
|
||||
} VIDEO_DB_DR;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_PLAY_STILL = 0,
|
||||
VIDEO_PLAY_CLIP,
|
||||
VIDEO_PLAY_TRICK,
|
||||
VIDEO_PLAY_MOTION,
|
||||
VIDEO_PLAY_MOTION_NO_SYNC
|
||||
} VIDEO_PLAY_MODE;
|
||||
|
||||
typedef enum {
|
||||
VIDEO_STD_NTSC,
|
||||
VIDEO_STD_SECAM,
|
||||
VIDEO_STD_PAL,
|
||||
VIDEO_STD_480P,
|
||||
VIDEO_STD_576P,
|
||||
VIDEO_STD_720P60,
|
||||
VIDEO_STD_1080I60,
|
||||
VIDEO_STD_720P50,
|
||||
VIDEO_STD_1080I50,
|
||||
VIDEO_STD_1080P30,
|
||||
VIDEO_STD_1080P24,
|
||||
VIDEO_STD_1080P25,
|
||||
VIDEO_STD_AUTO,
|
||||
VIDEO_STD_1080P50, /* SPARK only */
|
||||
VIDEO_STD_MAX
|
||||
} VIDEO_STD;
|
||||
|
||||
/* not used, for dummy functions */
|
||||
typedef enum {
|
||||
VIDEO_HDMI_CEC_MODE_OFF = 0,
|
||||
VIDEO_HDMI_CEC_MODE_TUNER,
|
||||
VIDEO_HDMI_CEC_MODE_RECORDER
|
||||
} VIDEO_HDMI_CEC_MODE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VIDEO_CONTROL_BRIGHTNESS = 0,
|
||||
VIDEO_CONTROL_CONTRAST,
|
||||
VIDEO_CONTROL_SATURATION,
|
||||
VIDEO_CONTROL_HUE,
|
||||
VIDEO_CONTROL_SHARPNESS,
|
||||
VIDEO_CONTROL_MAX = VIDEO_CONTROL_SHARPNESS
|
||||
} VIDEO_CONTROL;
|
||||
|
||||
|
||||
#define VDEC_MAXBUFS 0x30
|
||||
class cVideo : public OpenThreads::Thread
|
||||
{
|
||||
friend class GLFramebuffer;
|
||||
friend class cDemux;
|
||||
private:
|
||||
/* called from GL thread */
|
||||
class SWFramebuffer : public std::vector<unsigned char>
|
||||
{
|
||||
public:
|
||||
SWFramebuffer() : mWidth(0), mHeight(0) {}
|
||||
void width(int w) { mWidth = w; }
|
||||
void height(int h) { mHeight = h; }
|
||||
void pts(uint64_t p) { mPts = p; }
|
||||
void AR(AVRational a) { mAR = a; }
|
||||
int width() const { return mWidth; }
|
||||
int height() const { return mHeight; }
|
||||
int64_t pts() const { return mPts; }
|
||||
AVRational AR() const { return mAR; }
|
||||
private:
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
int64_t mPts;
|
||||
AVRational mAR;
|
||||
};
|
||||
int buf_in, buf_out, buf_num;
|
||||
int64_t GetPTS(void);
|
||||
public:
|
||||
/* constructor & destructor */
|
||||
cVideo(int mode, void *, void *, unsigned int unit = 0);
|
||||
~cVideo(void);
|
||||
|
||||
void * GetTVEnc() { return NULL; };
|
||||
void * GetTVEncSD() { return NULL; };
|
||||
|
||||
/* aspect ratio */
|
||||
int getAspectRatio(void);
|
||||
void getPictureInfo(int &width, int &height, int &rate);
|
||||
int setAspectRatio(int aspect, int mode);
|
||||
|
||||
/* cropping mode */
|
||||
int setCroppingMode(int x = 0 /*vidDispMode_t x = VID_DISPMODE_NORM*/);
|
||||
|
||||
/* get play state */
|
||||
int getPlayState(void);
|
||||
|
||||
/* blank on freeze */
|
||||
int getBlank(void);
|
||||
int setBlank(int enable);
|
||||
|
||||
/* change video play state. Parameters are all unused. */
|
||||
int Start(void *PcrChannel = NULL, unsigned short PcrPid = 0, unsigned short VideoPid = 0, void *x = NULL);
|
||||
int Stop(bool blank = true);
|
||||
bool Pause(void);
|
||||
|
||||
/* set video_system */
|
||||
int SetVideoSystem(int video_system, bool remember = true);
|
||||
int SetStreamType(VIDEO_FORMAT type);
|
||||
void SetSyncMode(AVSYNC_TYPE mode);
|
||||
bool SetCECMode(VIDEO_HDMI_CEC_MODE) { return true; };
|
||||
void SetCECAutoView(bool) { return; };
|
||||
void SetCECAutoStandby(bool) { return; };
|
||||
void ShowPicture(const char * fname, const char * destname = NULL);
|
||||
void StopPicture();
|
||||
void Standby(unsigned int bOn);
|
||||
void Pig(int x, int y, int w, int h, int osd_w = 1064, int osd_h = 600, int startx = 0, int starty = 0, int endx = 1279, int endy = 719);
|
||||
void SetControl(int, int) { return; };
|
||||
void setContrast(int val);
|
||||
void SetVideoMode(analog_mode_t mode);
|
||||
void SetDBDR(int) { return; };
|
||||
void SetAudioHandle(void *) { return; };
|
||||
void SetAutoModes(int [VIDEO_STD_MAX]) { return; };
|
||||
int OpenVBI(int) { return 0; };
|
||||
int CloseVBI(void) { return 0; };
|
||||
int StartVBI(unsigned short) { return 0; };
|
||||
int StopVBI(void) { return 0; };
|
||||
void SetDemux(cDemux *dmx);
|
||||
bool GetScreenImage(unsigned char * &data, int &xres, int &yres, bool get_video = true, bool get_osd = false, bool scale_to_video = false);
|
||||
SWFramebuffer *getDecBuf(void);
|
||||
private:
|
||||
void run();
|
||||
SWFramebuffer buffers[VDEC_MAXBUFS];
|
||||
int dec_w, dec_h;
|
||||
int dec_r;
|
||||
bool w_h_changed;
|
||||
bool thread_running;
|
||||
VIDEO_FORMAT v_format;
|
||||
VIDEO_STD v_std;
|
||||
OpenThreads::Mutex buf_m;
|
||||
DISPLAY_AR display_aspect;
|
||||
DISPLAY_AR_MODE display_crop;
|
||||
int output_h;
|
||||
int pig_x;
|
||||
int pig_y;
|
||||
int pig_w;
|
||||
int pig_h;
|
||||
};
|
||||
|
||||
#endif
|
17
include/audio_td.h
Normal file
17
include/audio_td.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/audio_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/audio_lib.h"
|
||||
#include "../libspark/audio_mixer.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/audio_lib.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/audio_lib.h"
|
||||
#else
|
||||
#include "../generic-pc/audio_lib.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
1
include/ca_cs.h
Normal file
1
include/ca_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "../common/ca.h"
|
16
include/cs_api.h
Normal file
16
include/cs_api.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/cs_api.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/cs_api.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/cs_api.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/cs_api.h"
|
||||
#else
|
||||
#include "../generic-pc/cs_api.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
1
include/dmx_cs.h
Normal file
1
include/dmx_cs.h
Normal file
@@ -0,0 +1 @@
|
||||
#include "dmx_td.h"
|
16
include/dmx_td.h
Normal file
16
include/dmx_td.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/dmx_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/dmx_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/dmx_lib.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/dmx_lib.h"
|
||||
#else
|
||||
#include "../generic-pc/dmx_lib.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
10
include/glfb.h
Normal file
10
include/glfb.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <config.h>
|
||||
#if HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/glfb.h"
|
||||
#else
|
||||
#include "../generic-pc/glfb.h"
|
||||
#endif
|
||||
#else
|
||||
#error glfb.h only works with HAVE_GENERIC_HARDWARE defined
|
||||
#endif
|
46
include/hardware_caps.h
Normal file
46
include/hardware_caps.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* determine the capabilities of the hardware.
|
||||
* part of libstb-hal
|
||||
*
|
||||
* (C) 2010-2012 Stefan Seyfried
|
||||
*
|
||||
* License: GPL v2 or later
|
||||
*/
|
||||
#ifndef __HARDWARE_CAPS_H__
|
||||
#define __HARDWARE_CAPS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef enum
|
||||
{
|
||||
HW_DISPLAY_NONE,
|
||||
HW_DISPLAY_LED_NUM, /* simple 7 segment LED display */
|
||||
HW_DISPLAY_LINE_TEXT, /* 1 line text display */
|
||||
HW_DISPLAY_GFX
|
||||
} display_type_t;
|
||||
|
||||
|
||||
typedef struct hw_caps
|
||||
{
|
||||
int has_fan;
|
||||
int has_HDMI;
|
||||
int has_SCART;
|
||||
int has_SCART_input;
|
||||
int has_YUV_cinch;
|
||||
int can_shutdown;
|
||||
int can_cec;
|
||||
display_type_t display_type;
|
||||
int display_xres; /* x resolution or chars per line */
|
||||
int display_yres;
|
||||
char boxvendor[64];
|
||||
char boxname[64];
|
||||
int boxtype;
|
||||
} hw_caps_t;
|
||||
|
||||
hw_caps_t *get_hwcaps(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
2
include/init_cs.h
Normal file
2
include/init_cs.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#warning using init_cs.h from libstb-hal
|
||||
#include "init_td.h"
|
5
include/init_td.h
Normal file
5
include/init_td.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef __INIT_TD_H
|
||||
#define __INIT_TD_H
|
||||
void init_td_api();
|
||||
void shutdown_td_api();
|
||||
#endif
|
8
include/lt_debug.h
Normal file
8
include/lt_debug.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/playback_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/playback_lib.h"
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
27
include/mmi.h
Normal file
27
include/mmi.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __MMI_H_
|
||||
#define __MMI_H_
|
||||
|
||||
#define MAX_MMI_ITEMS 40
|
||||
#define MAX_MMI_TEXT_LEN 255
|
||||
#define MAX_MMI_CHOICE_TEXT_LEN 255
|
||||
|
||||
typedef struct {
|
||||
int choice_nb;
|
||||
char title[MAX_MMI_TEXT_LEN];
|
||||
char subtitle[MAX_MMI_TEXT_LEN];
|
||||
char bottom[MAX_MMI_TEXT_LEN];
|
||||
char choice_item[MAX_MMI_ITEMS][MAX_MMI_CHOICE_TEXT_LEN];
|
||||
} MMI_MENU_LIST_INFO;
|
||||
|
||||
typedef struct {
|
||||
int blind;
|
||||
int answerlen;
|
||||
char enquiryText[MAX_MMI_TEXT_LEN];
|
||||
} MMI_ENQUIRY_INFO;
|
||||
|
||||
/* compat */
|
||||
#define enguiryText enquiryText
|
||||
#define MMI_ENGUIRY_INFO MMI_ENQUIRY_INFO
|
||||
|
||||
#endif // __MMI_H_
|
||||
|
3
include/playback.h
Normal file
3
include/playback.h
Normal file
@@ -0,0 +1,3 @@
|
||||
/* playback_*.cpp uses off_t */
|
||||
#include <config.h>
|
||||
#include "playback_td.h"
|
16
include/playback_td.h
Normal file
16
include/playback_td.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/playback_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/playback_libeplayer3.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/playback.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/playback.h"
|
||||
#else
|
||||
#include "../generic-pc/playback.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
16
include/pwrmngr.h
Normal file
16
include/pwrmngr.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/pwrmngr.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/pwrmngr.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/pwrmngr.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/pwrmngr.h"
|
||||
#else
|
||||
#include "../generic-pc/pwrmngr.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
16
include/record_td.h
Normal file
16
include/record_td.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/record_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/record_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/record_lib.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/record_lib.h"
|
||||
#else
|
||||
#include "../generic-pc/record_lib.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
20
include/video_td.h
Normal file
20
include/video_td.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <config.h>
|
||||
#if HAVE_TRIPLEDRAGON
|
||||
#include "../libtriple/video_td.h"
|
||||
#elif HAVE_SPARK_HARDWARE
|
||||
#include "../libspark/video_lib.h"
|
||||
#elif HAVE_AZBOX_HARDWARE
|
||||
#include "../azbox/video_lib.h"
|
||||
#elif HAVE_GENERIC_HARDWARE
|
||||
#if BOXMODEL_RASPI
|
||||
#include "../raspi/video_lib.h"
|
||||
#else
|
||||
#include "../generic-pc/video_lib.h"
|
||||
#endif
|
||||
#else
|
||||
#error neither HAVE_TRIPLEDRAGON nor HAVE_SPARK_HARDWARE defined
|
||||
#endif
|
||||
|
||||
#if STB_HAL_VIDEO_HAS_GETSCREENIMAGE
|
||||
#define SCREENSHOT 1
|
||||
#endif
|
15
libeplayer3/Makefile.am
Normal file
15
libeplayer3/Makefile.am
Normal file
@@ -0,0 +1,15 @@
|
||||
noinst_LTLIBRARIES = libeplayer3.la
|
||||
|
||||
AM_CPPFLAGS = -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS
|
||||
AM_CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
|
||||
AM_CPPFLAGS += -I$(srcdir)/include
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
|
||||
libeplayer3_la_SOURCES = \
|
||||
input.cpp output.cpp manager.cpp player.cpp \
|
||||
writer/writer.cpp writer/wmv.cpp writer/ac3.cpp writer/divx.cpp writer/pes.cpp \
|
||||
writer/dts.cpp writer/mpeg2.cpp writer/mp3.cpp writer/misc.cpp writer/h264.cpp \
|
||||
writer/h263.cpp writer/vc1.cpp writer/flac.cpp writer/pcm.cpp
|
||||
|
||||
LIBEPLAYER3_LIBS = libeplayer3.la -lpthread -lavformat -lavcodec -lavutil -lswresample -lm
|
||||
|
78
libeplayer3/README
Normal file
78
libeplayer3/README
Normal file
@@ -0,0 +1,78 @@
|
||||
This is a revised libeplayer3 version for Neutrino, rewritten in C++, with
|
||||
various code parts (e.g. subtitle processing, non-working decoders) removed.
|
||||
--martii
|
||||
|
||||
The original libeplayer3 README follows:
|
||||
|
||||
/*
|
||||
* SCOPE:
|
||||
* -------
|
||||
*
|
||||
* libeplayer3 was developed to create a cleaner and more stable
|
||||
* version of the libeplayer2.
|
||||
* Currently the lib supports only one container, which handle all
|
||||
* files by using the ffmpeg library.
|
||||
*
|
||||
* FEATURES:
|
||||
* -----------------------
|
||||
*
|
||||
* - more stable than libeplayer2.
|
||||
* - more multimedia files are supported than libeplayer2.
|
||||
* - mms stream support.
|
||||
* - new videocodec support:
|
||||
* - wmv and vc1 (sti7109 & sti7111 & sti7105 only).
|
||||
* - flv.
|
||||
* - improved http streaming support
|
||||
* - subtitle rendering (ssa / ass) by using libass
|
||||
*
|
||||
* STYLE GUIDELINES:
|
||||
* ------------------
|
||||
*
|
||||
* If you decide to add some lines of code please ensure the following:
|
||||
* - do not use a windows editor.
|
||||
* - a tab must be emulated by 4 spaces (most editors support this).
|
||||
* If you accidental break this rule use astyle to reorganize indentation,
|
||||
* and dos2unix to remove windows style.
|
||||
*
|
||||
* Programming GUIDLINES:
|
||||
* -----------------------
|
||||
*
|
||||
* - the compiler is intentionally set to Wall, it would be nice if all
|
||||
* programmer looks for warnings and solve them.
|
||||
* - make sanity checks where ever you can.
|
||||
* - freeing memory is an act of solidarity, but it also increases uptime
|
||||
* of your receiver. ;)
|
||||
* - if you detect stuff which may be generic, then make it generic.
|
||||
* - commenting code is not a bad idea.
|
||||
*
|
||||
* KNOWN BUGS / PROBLEMS:
|
||||
* ----------------------
|
||||
*
|
||||
* - reverse playback needs improvement
|
||||
* - some formats makes problems ?
|
||||
* - getting stream info currently leads to a memory leak in e2. this is
|
||||
* not a problem of this implementation its also exists in libeplayer2.
|
||||
* e2 delivers a strdupped variable which is overwritten by what the container
|
||||
* delivers. this is very hacky ;) -> (see comment in container_ffmpeg_get_info)
|
||||
*
|
||||
* License:
|
||||
* --------
|
||||
*
|
||||
* Copyright (C) 2010 crow, schischu, hellmaster1024 and konfetti.
|
||||
*
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
86
libeplayer3/include/input.h
Normal file
86
libeplayer3/include/input.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* input class
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __INPUT_H__
|
||||
#define __INPUT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
class Player;
|
||||
class Track;
|
||||
|
||||
class Input
|
||||
{
|
||||
friend class Player;
|
||||
friend int interrupt_cb(void *arg);
|
||||
|
||||
private:
|
||||
Track *videoTrack;
|
||||
Track *audioTrack;
|
||||
Track *subtitleTrack;
|
||||
Track *teletextTrack;
|
||||
|
||||
int hasPlayThreadStarted;
|
||||
int64_t seek_avts_abs;
|
||||
int64_t seek_avts_rel;
|
||||
bool isContainerRunning;
|
||||
bool abortPlayback;
|
||||
|
||||
Player *player;
|
||||
AVFormatContext *avfc;
|
||||
uint64_t readCount;
|
||||
int64_t calcPts(AVStream * stream, int64_t pts);
|
||||
|
||||
public:
|
||||
Input();
|
||||
~Input();
|
||||
|
||||
bool ReadSubtitle(const char *filename, const char *format, int pid);
|
||||
bool ReadSubtitles(const char *filename);
|
||||
bool Init(const char *filename);
|
||||
bool UpdateTracks();
|
||||
bool Play();
|
||||
bool Stop();
|
||||
bool Seek(int64_t sec, bool absolute);
|
||||
bool GetDuration(int64_t &duration);
|
||||
bool SwitchAudio(Track *track);
|
||||
bool SwitchSubtitle(Track *track);
|
||||
bool SwitchTeletext(Track *track);
|
||||
bool SwitchVideo(Track *track);
|
||||
bool GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||
bool GetReadCount(uint64_t &readcount);
|
||||
};
|
||||
|
||||
#endif
|
89
libeplayer3/include/manager.h
Normal file
89
libeplayer3/include/manager.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* manager class
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MANAGER_H__
|
||||
#define __MANAGER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
class Player;
|
||||
|
||||
struct Track
|
||||
{
|
||||
std::string title;
|
||||
int pid;
|
||||
int64_t duration;
|
||||
AVFormatContext *avfc;
|
||||
AVStream *stream;
|
||||
bool inactive;
|
||||
bool is_static;
|
||||
int ac3flags;
|
||||
int type, mag, page; // for teletext
|
||||
Track() : pid(-1), duration(-1), avfc(NULL), stream(NULL), inactive(0), is_static(0), ac3flags(0) {}
|
||||
};
|
||||
|
||||
class Manager
|
||||
{
|
||||
friend class Player;
|
||||
|
||||
private:
|
||||
Player *player;
|
||||
OpenThreads::Mutex mutex;
|
||||
std::map<int,Track*> videoTracks, audioTracks, subtitleTracks, teletextTracks;
|
||||
void addTrack(std::map<int,Track*> &tracks, Track &track);
|
||||
Track *getTrack(std::map<int,Track*> &tracks, int pid);
|
||||
std::vector<Track> getTracks(std::map<int,Track*> &tracks);
|
||||
public:
|
||||
void addVideoTrack(Track &track);
|
||||
void addAudioTrack(Track &track);
|
||||
void addSubtitleTrack(Track &track);
|
||||
void addTeletextTrack(Track &track);
|
||||
|
||||
std::vector<Track> getVideoTracks();
|
||||
std::vector<Track> getAudioTracks();
|
||||
std::vector<Track> getSubtitleTracks();
|
||||
std::vector<Track> getTeletextTracks();
|
||||
|
||||
Track *getVideoTrack(int pid);
|
||||
Track *getAudioTrack(int pid);
|
||||
Track *getSubtitleTrack(int pid);
|
||||
Track *getTeletextTrack(int pid);
|
||||
|
||||
bool initTrackUpdate();
|
||||
void clearTracks();
|
||||
|
||||
~Manager();
|
||||
};
|
||||
#endif
|
20
libeplayer3/include/misc.h
Normal file
20
libeplayer3/include/misc.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef misc_123
|
||||
#define misc_123
|
||||
|
||||
/* some useful things needed by many files ... */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define INVALID_PTS_VALUE 0x200000000ll
|
||||
|
||||
struct BitPacker_t
|
||||
{
|
||||
uint8_t *Ptr; /* write pointer */
|
||||
unsigned int BitBuffer; /* bitreader shifter */
|
||||
int Remaining; /* number of remaining in the shifter */
|
||||
};
|
||||
|
||||
void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length);
|
||||
void FlushBits(BitPacker_t * ld);
|
||||
|
||||
#endif
|
80
libeplayer3/include/output.h
Normal file
80
libeplayer3/include/output.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* output class
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_H__
|
||||
#define __OUTPUT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
class Player;
|
||||
|
||||
class Output
|
||||
{
|
||||
friend class Player;
|
||||
|
||||
private:
|
||||
int videofd;
|
||||
int audiofd;
|
||||
Writer *videoWriter, *audioWriter;
|
||||
OpenThreads::Mutex audioMutex, videoMutex;
|
||||
AVStream *audioStream, *videoStream;
|
||||
Player *player;
|
||||
public:
|
||||
Output();
|
||||
~Output();
|
||||
bool Open();
|
||||
bool Close();
|
||||
bool Play();
|
||||
bool Stop();
|
||||
bool Pause();
|
||||
bool Continue();
|
||||
bool Mute(bool);
|
||||
bool Flush();
|
||||
bool FastForward(int speed);
|
||||
bool SlowMotion(int speed);
|
||||
bool AVSync(bool);
|
||||
bool Clear();
|
||||
bool ClearAudio();
|
||||
bool ClearVideo();
|
||||
bool GetPts(int64_t &pts);
|
||||
bool GetFrameCount(int64_t &framecount);
|
||||
bool SwitchAudio(AVStream *stream);
|
||||
bool SwitchVideo(AVStream *stream);
|
||||
bool Write(AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t Pts);
|
||||
};
|
||||
|
||||
#endif
|
34
libeplayer3/include/pes.h
Normal file
34
libeplayer3/include/pes.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef pes_123
|
||||
#define pes_123
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PES_MAX_HEADER_SIZE 64
|
||||
#define PES_PRIVATE_DATA_FLAG 0x80
|
||||
#define PES_PRIVATE_DATA_LENGTH 8
|
||||
#define PES_LENGTH_BYTE_0 5
|
||||
#define PES_LENGTH_BYTE_1 4
|
||||
#define PES_FLAGS_BYTE 7
|
||||
#define PES_EXTENSION_DATA_PRESENT 0x01
|
||||
#define PES_HEADER_DATA_LENGTH_BYTE 8
|
||||
#define PES_START_CODE_RESERVED_4 0xfd
|
||||
#define PES_VERSION_FAKE_START_CODE 0x31
|
||||
|
||||
|
||||
#define MAX_PES_PACKET_SIZE 65535
|
||||
|
||||
|
||||
/* start codes */
|
||||
#define PCM_PES_START_CODE 0xbd
|
||||
#define PRIVATE_STREAM_1_PES_START_CODE 0xbd
|
||||
#define H263_VIDEO_PES_START_CODE 0xfe
|
||||
#define H264_VIDEO_PES_START_CODE 0xe2
|
||||
#define MPEG_VIDEO_PES_START_CODE 0xe0
|
||||
#define MPEG_AUDIO_PES_START_CODE 0xc0
|
||||
#define VC1_VIDEO_PES_START_CODE 0xfd
|
||||
#define AAC_AUDIO_PES_START_CODE 0xcf
|
||||
|
||||
int InsertPesHeader(uint8_t *data, int size, uint8_t stream_id, int64_t pts, int pic_start_code);
|
||||
int InsertVideoPrivateDataHeader(uint8_t *data, int payload_size);
|
||||
|
||||
#endif
|
119
libeplayer3/include/player.h
Normal file
119
libeplayer3/include/player.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* player class
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PLAYER_H__
|
||||
#define __PLAYER_H__
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "input.h"
|
||||
#include "output.h"
|
||||
#include "manager.h"
|
||||
|
||||
struct Chapter
|
||||
{
|
||||
std::string title;
|
||||
int64_t start;
|
||||
int64_t end;
|
||||
};
|
||||
|
||||
class Player {
|
||||
friend class Input;
|
||||
friend class Output;
|
||||
friend class Manager;
|
||||
friend class cPlayback;
|
||||
friend int interrupt_cb(void *arg);
|
||||
|
||||
private:
|
||||
Input input;
|
||||
Output output;
|
||||
Manager manager;
|
||||
OpenThreads::Mutex chapterMutex;
|
||||
std::vector<Chapter> chapters;
|
||||
pthread_t playThread;
|
||||
|
||||
bool abortRequested;
|
||||
bool isHttp;
|
||||
bool isPaused;
|
||||
bool isSlowMotion;
|
||||
bool hasThreadStarted;
|
||||
bool isForwarding;
|
||||
bool isBackWard;
|
||||
bool isPlaying;
|
||||
|
||||
int Speed;
|
||||
|
||||
uint64_t readCount;
|
||||
|
||||
std::string url;
|
||||
bool noprobe; /* hack: only minimal probing in av_find_stream_info */
|
||||
|
||||
void SetChapters(std::vector<Chapter> &Chapters);
|
||||
static void* playthread(void*);
|
||||
public:
|
||||
bool SwitchAudio(int pid);
|
||||
bool SwitchVideo(int pid);
|
||||
bool SwitchTeletext(int pid);
|
||||
bool SwitchSubtitle(int pid);
|
||||
|
||||
int GetAudioPid();
|
||||
int GetVideoPid();
|
||||
int GetSubtitlePid();
|
||||
int GetTeletextPid();
|
||||
|
||||
bool GetPts(int64_t &pts);
|
||||
bool GetFrameCount(int64_t &framecount);
|
||||
bool GetDuration(int64_t &duration);
|
||||
|
||||
bool GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||
bool SlowMotion(int repeats);
|
||||
bool FastBackward(int speed);
|
||||
bool FastForward(int speed);
|
||||
|
||||
bool Open(const char *Url, bool noprobe = false);
|
||||
bool Close();
|
||||
bool Play();
|
||||
bool Pause();
|
||||
bool Continue();
|
||||
bool Stop();
|
||||
bool Seek(int64_t pos, bool absolute);
|
||||
void RequestAbort();
|
||||
bool GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
||||
|
||||
Player();
|
||||
};
|
||||
#endif
|
52
libeplayer3/include/writer.h
Normal file
52
libeplayer3/include/writer.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* writer class headers
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __WRITER_H__
|
||||
#define __WRITER_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
#include <linux/dvb/stm_ioctls.h>
|
||||
|
||||
#define AV_CODEC_ID_INJECTPCM AV_CODEC_ID_PCM_S16LE
|
||||
|
||||
class Writer
|
||||
{
|
||||
public:
|
||||
static void Register(Writer *w, enum AVCodecID id, video_encoding_t encoding);
|
||||
static void Register(Writer *w, enum AVCodecID id, audio_encoding_t encoding);
|
||||
static video_encoding_t GetVideoEncoding(enum AVCodecID id);
|
||||
static audio_encoding_t GetAudioEncoding(enum AVCodecID id);
|
||||
static Writer *GetWriter(enum AVCodecID id, enum AVMediaType codec_type);
|
||||
|
||||
virtual void Init(void) { }
|
||||
virtual bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
};
|
||||
#endif
|
622
libeplayer3/input.cpp
Normal file
622
libeplayer3/input.cpp
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* input class
|
||||
*
|
||||
* based on libeplayer3 container_ffmpeg.c, konfetti 2010; based on code from crow
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "player.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define averror(_err,_fun) ({ \
|
||||
if (_err < 0) { \
|
||||
char _error[512]; \
|
||||
av_strerror(_err, _error, sizeof(_error)); \
|
||||
fprintf(stderr, "%s %d: %s: %d (%s)\n", __FILE__, __LINE__, #_fun, _err, _error); \
|
||||
} \
|
||||
_err; \
|
||||
})
|
||||
|
||||
Input::Input()
|
||||
{
|
||||
videoTrack = NULL;
|
||||
audioTrack = NULL;
|
||||
subtitleTrack = NULL;
|
||||
teletextTrack = NULL;
|
||||
|
||||
hasPlayThreadStarted = 0;
|
||||
seek_avts_abs = INT64_MIN;
|
||||
seek_avts_rel = 0;
|
||||
abortPlayback = false;
|
||||
}
|
||||
|
||||
Input::~Input()
|
||||
{
|
||||
}
|
||||
|
||||
int64_t Input::calcPts(AVStream * stream, int64_t pts)
|
||||
{
|
||||
if (pts == AV_NOPTS_VALUE)
|
||||
return INVALID_PTS_VALUE;
|
||||
|
||||
pts = av_rescale(90000ll * stream->time_base.num, pts, stream->time_base.den);
|
||||
if (avfc->start_time != AV_NOPTS_VALUE)
|
||||
pts -= av_rescale(90000ll, avfc->start_time, AV_TIME_BASE);
|
||||
|
||||
if (pts < 0)
|
||||
return INVALID_PTS_VALUE;
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
// from neutrino-mp/lib/libdvbsubtitle/dvbsub.cpp
|
||||
extern void dvbsub_write(AVSubtitle *, int64_t);
|
||||
extern void dvbsub_ass_write(AVCodecContext *c, AVSubtitle *sub, int pid);
|
||||
extern void dvbsub_ass_clear(void);
|
||||
// from neutrino-mp/lib/lib/libtuxtxt/tuxtxt_common.h
|
||||
extern void teletext_write(int pid, uint8_t *data, int size);
|
||||
|
||||
bool Input::Play()
|
||||
{
|
||||
hasPlayThreadStarted = 1;
|
||||
|
||||
int64_t showtime = 0;
|
||||
bool restart_audio_resampling = false;
|
||||
bool bof = false;
|
||||
int warnAudioWrite = 0;
|
||||
int warnVideoWrite = 0;
|
||||
|
||||
while (player->isPlaying && !player->abortRequested) {
|
||||
|
||||
//IF MOVIE IS PAUSED, WAIT
|
||||
if (player->isPaused) {
|
||||
fprintf(stderr, "paused\n");
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
|
||||
int seek_target_flag = 0;
|
||||
int64_t seek_target = INT64_MIN; // in AV_TIME_BASE units
|
||||
|
||||
if (seek_avts_rel) {
|
||||
if (avfc->iformat->flags & AVFMT_TS_DISCONT) {
|
||||
if (avfc->bit_rate) {
|
||||
seek_target_flag = AVSEEK_FLAG_BYTE;
|
||||
seek_target = avio_tell(avfc->pb) + av_rescale(seek_avts_rel, avfc->bit_rate, 8 * AV_TIME_BASE);
|
||||
}
|
||||
} else {
|
||||
int64_t pts;
|
||||
if(player->output.GetPts(pts))
|
||||
seek_target = av_rescale(pts, AV_TIME_BASE, 90000ll) + seek_avts_rel;
|
||||
}
|
||||
seek_avts_rel = 0;
|
||||
} else if (seek_avts_abs != INT64_MIN) {
|
||||
if (avfc->iformat->flags & AVFMT_TS_DISCONT) {
|
||||
if (avfc->bit_rate) {
|
||||
seek_target_flag = AVSEEK_FLAG_BYTE;
|
||||
seek_target = av_rescale(seek_avts_abs, avfc->bit_rate, 8 * AV_TIME_BASE);
|
||||
}
|
||||
} else {
|
||||
seek_target = seek_avts_abs;
|
||||
}
|
||||
seek_avts_abs = INT64_MIN;
|
||||
} else if (player->isBackWard && av_gettime() >= showtime) {
|
||||
player->output.ClearVideo();
|
||||
|
||||
if (bof) {
|
||||
showtime = av_gettime();
|
||||
usleep(100000);
|
||||
continue;
|
||||
}
|
||||
seek_avts_rel = player->Speed * AV_TIME_BASE;
|
||||
showtime = av_gettime() + 300000; //jump back every 300ms
|
||||
continue;
|
||||
} else {
|
||||
bof = false;
|
||||
}
|
||||
|
||||
if (seek_target > INT64_MIN) {
|
||||
int res;
|
||||
if (seek_target < 0)
|
||||
seek_target = 0;
|
||||
res = avformat_seek_file(avfc, -1, INT64_MIN, seek_target, INT64_MAX, seek_target_flag);
|
||||
|
||||
if (res < 0 && player->isBackWard)
|
||||
bof = true;
|
||||
|
||||
seek_target = INT64_MIN;
|
||||
restart_audio_resampling = true;
|
||||
|
||||
// flush streams
|
||||
unsigned int i;
|
||||
for (i = 0; i < avfc->nb_streams; i++)
|
||||
if (avfc->streams[i]->codec && avfc->streams[i]->codec->codec)
|
||||
avcodec_flush_buffers(avfc->streams[i]->codec);
|
||||
player->output.ClearAudio();
|
||||
player->output.ClearVideo();
|
||||
}
|
||||
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
int err = av_read_frame(avfc, &packet);
|
||||
if (err == AVERROR(EAGAIN)) {
|
||||
av_free_packet(&packet);
|
||||
continue;
|
||||
}
|
||||
if (averror(err, av_read_frame)) // EOF?
|
||||
break; // while
|
||||
|
||||
player->readCount += packet.size;
|
||||
|
||||
AVStream *stream = avfc->streams[packet.stream_index];
|
||||
Track *_videoTrack = videoTrack;
|
||||
Track *_audioTrack = audioTrack;
|
||||
Track *_subtitleTrack = subtitleTrack;
|
||||
Track *_teletextTrack = teletextTrack;
|
||||
|
||||
if (_videoTrack && (_videoTrack->stream == stream)) {
|
||||
int64_t pts = calcPts(stream, packet.pts);
|
||||
if (!player->output.Write(avfc, stream, &packet, pts)) {
|
||||
if (warnVideoWrite)
|
||||
warnVideoWrite--;
|
||||
else {
|
||||
fprintf(stderr, "writing data to %s device failed\n", "video");
|
||||
warnVideoWrite = 100;
|
||||
}
|
||||
}
|
||||
} else if (_audioTrack && (_audioTrack->stream == stream)) {
|
||||
if (restart_audio_resampling) {
|
||||
restart_audio_resampling = false;
|
||||
player->output.Write(avfc, stream, NULL, 0);
|
||||
}
|
||||
if (!player->isBackWard) {
|
||||
int64_t pts = calcPts(stream, packet.pts);
|
||||
if (!player->output.Write(avfc, stream, &packet, _videoTrack ? pts : 0)) {
|
||||
if (warnAudioWrite)
|
||||
warnAudioWrite--;
|
||||
else {
|
||||
fprintf(stderr, "writing data to %s device failed\n", "audio");
|
||||
warnAudioWrite = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_subtitleTrack && (_subtitleTrack->stream == stream)) {
|
||||
if (stream->codec->codec) {
|
||||
AVSubtitle sub;
|
||||
memset(&sub, 0, sizeof(sub));
|
||||
int got_sub_ptr = 0;
|
||||
|
||||
err = avcodec_decode_subtitle2(stream->codec, &sub, &got_sub_ptr, &packet);
|
||||
averror(err, avcodec_decode_subtitle2);
|
||||
|
||||
if (got_sub_ptr && sub.num_rects > 0) {
|
||||
switch (sub.rects[0]->type) {
|
||||
case SUBTITLE_TEXT: // FIXME?
|
||||
case SUBTITLE_ASS:
|
||||
dvbsub_ass_write(stream->codec, &sub, stream->id);
|
||||
break;
|
||||
case SUBTITLE_BITMAP: {
|
||||
int64_t pts = calcPts(stream, packet.pts);
|
||||
dvbsub_write(&sub, pts);
|
||||
// avsubtitle_free() will be called by handler
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_teletextTrack && (_teletextTrack->stream == stream)) {
|
||||
teletext_write(stream->id, packet.data, packet.size);
|
||||
}
|
||||
|
||||
av_free_packet(&packet);
|
||||
} /* while */
|
||||
|
||||
if (player->abortRequested)
|
||||
player->output.Clear();
|
||||
else
|
||||
player->output.Flush();
|
||||
|
||||
dvbsub_ass_clear();
|
||||
abortPlayback = true;
|
||||
hasPlayThreadStarted = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static*/ int interrupt_cb(void *arg)
|
||||
{
|
||||
Player *player = (Player *) arg;
|
||||
bool res = player->input.abortPlayback || player->abortRequested;
|
||||
if (res)
|
||||
fprintf(stderr, "%s %s %d: abort requested (%d/%d)\n", __FILE__, __func__, __LINE__, player->input.abortPlayback, player->abortRequested);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void log_callback(void *ptr __attribute__ ((unused)), int lvl __attribute__ ((unused)), const char *format, va_list ap)
|
||||
{
|
||||
// if (debug_level > 10)
|
||||
vfprintf(stderr, format, ap);
|
||||
}
|
||||
|
||||
bool Input::ReadSubtitle(const char *filename, const char *format, int pid)
|
||||
{
|
||||
const char *lastDot = strrchr(filename, '.');
|
||||
if (!lastDot)
|
||||
return false;
|
||||
char *subfile = (char *) alloca(strlen(filename) + strlen(format));
|
||||
strcpy(subfile, filename);
|
||||
strcpy(subfile + (lastDot + 1 - filename), format);
|
||||
|
||||
if (access(subfile, R_OK))
|
||||
return false;
|
||||
|
||||
AVFormatContext *subavfc = avformat_alloc_context();
|
||||
int err = avformat_open_input(&subavfc, subfile, av_find_input_format(format), 0);
|
||||
if (averror(err, avformat_open_input)) {
|
||||
avformat_free_context(subavfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
avformat_find_stream_info(subavfc, NULL);
|
||||
if (subavfc->nb_streams != 1) {
|
||||
avformat_free_context(subavfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVCodecContext *c = subavfc->streams[0]->codec;
|
||||
AVCodec *codec = avcodec_find_decoder(c->codec_id);
|
||||
if (!codec) {
|
||||
avformat_free_context(subavfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = avcodec_open2(c, codec, NULL);
|
||||
if (averror(err, avcodec_open2)) {
|
||||
avformat_free_context(subavfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
||||
if (c->subtitle_header)
|
||||
fprintf(stderr, "%s\n", c->subtitle_header);
|
||||
|
||||
while (av_read_frame(subavfc, &packet) > -1) {
|
||||
AVSubtitle sub;
|
||||
memset(&sub, 0, sizeof(sub));
|
||||
int got_sub = 0;
|
||||
avcodec_decode_subtitle2(c, &sub, &got_sub, &packet);
|
||||
if (got_sub)
|
||||
dvbsub_ass_write(c, &sub, pid);
|
||||
av_free_packet(&packet);
|
||||
}
|
||||
avformat_close_input(&subavfc);
|
||||
avformat_free_context(subavfc);
|
||||
|
||||
Track track;
|
||||
track.title = format;
|
||||
track.is_static = 1;
|
||||
track.pid = pid;
|
||||
player->manager.addSubtitleTrack(track);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::ReadSubtitles(const char *filename) {
|
||||
if (strncmp(filename, "file://", 7))
|
||||
return false;
|
||||
filename += 7;
|
||||
bool ret = false;
|
||||
ret |= ReadSubtitle(filename, "srt", 0xFFFF);
|
||||
ret |= ReadSubtitle(filename, "ass", 0xFFFE);
|
||||
ret |= ReadSubtitle(filename, "ssa", 0xFFFD);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Input::Init(const char *filename)
|
||||
{
|
||||
abortPlayback = false;
|
||||
av_log_set_callback(log_callback);
|
||||
|
||||
if (!filename) {
|
||||
fprintf(stderr, "filename NULL\n");
|
||||
return false;
|
||||
}
|
||||
fprintf(stderr, "%s %s %d: %s\n", __FILE__, __func__, __LINE__, filename);
|
||||
|
||||
avcodec_register_all();
|
||||
av_register_all();
|
||||
avformat_network_init();
|
||||
|
||||
videoTrack = NULL;
|
||||
audioTrack = NULL;
|
||||
subtitleTrack = NULL;
|
||||
teletextTrack = NULL;
|
||||
|
||||
again:
|
||||
avfc = avformat_alloc_context();
|
||||
avfc->interrupt_callback.callback = interrupt_cb;
|
||||
avfc->interrupt_callback.opaque = (void *) player;
|
||||
|
||||
int err = avformat_open_input(&avfc, filename, NULL, 0);
|
||||
if (averror(err, avformat_open_input)) {
|
||||
avformat_free_context(avfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
avfc->iformat->flags |= AVFMT_SEEK_TO_PTS;
|
||||
avfc->flags = AVFMT_FLAG_GENPTS;
|
||||
if (player->noprobe) {
|
||||
avfc->max_analyze_duration = 1;
|
||||
avfc->probesize = 131072;
|
||||
}
|
||||
|
||||
err = avformat_find_stream_info(avfc, NULL);
|
||||
if (averror(err, avformat_find_stream_info)) {
|
||||
avformat_close_input(&avfc);
|
||||
if (player->noprobe) {
|
||||
player->noprobe = false;
|
||||
goto again;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = UpdateTracks();
|
||||
|
||||
if (!videoTrack && !audioTrack) {
|
||||
avformat_close_input(&avfc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (videoTrack)
|
||||
player->output.SwitchVideo(videoTrack->stream);
|
||||
if (audioTrack)
|
||||
player->output.SwitchAudio(audioTrack->stream);
|
||||
|
||||
ReadSubtitles(filename);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Input::UpdateTracks()
|
||||
{
|
||||
if (abortPlayback)
|
||||
return true;
|
||||
|
||||
std::vector<Chapter> chapters;
|
||||
for (unsigned int i = 0; i < avfc->nb_chapters; i++) {
|
||||
AVChapter *ch = avfc->chapters[i];
|
||||
AVDictionaryEntry* title = av_dict_get(ch->metadata, "title", NULL, 0);
|
||||
Chapter chapter;
|
||||
chapter.title = title ? title->value : "";
|
||||
chapter.start = av_rescale(ch->time_base.num * AV_TIME_BASE, ch->start, ch->time_base.den);
|
||||
chapter.end = av_rescale(ch->time_base.num * AV_TIME_BASE, ch->end, ch->time_base.den);
|
||||
chapters.push_back(chapter);
|
||||
}
|
||||
player->SetChapters(chapters);
|
||||
|
||||
player->manager.initTrackUpdate();
|
||||
|
||||
av_dump_format(avfc, 0, player->url.c_str(), 0);
|
||||
|
||||
for (unsigned int n = 0; n < avfc->nb_streams; n++) {
|
||||
AVStream *stream = avfc->streams[n];
|
||||
|
||||
if (!stream->id)
|
||||
stream->id = n + 1;
|
||||
|
||||
Track track;
|
||||
track.avfc = avfc;
|
||||
track.stream = stream;
|
||||
AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0);
|
||||
track.title = lang ? lang->value : "";
|
||||
track.pid = stream->id;
|
||||
if (stream->duration == AV_NOPTS_VALUE)
|
||||
track.duration = avfc->duration;
|
||||
else
|
||||
track.duration = av_rescale(stream->time_base.num * AV_TIME_BASE, stream->duration, stream->time_base.den);
|
||||
|
||||
switch (stream->codec->codec_type) {
|
||||
case AVMEDIA_TYPE_VIDEO: {
|
||||
player->manager.addVideoTrack(track);
|
||||
if (!videoTrack)
|
||||
videoTrack = player->manager.getVideoTrack(track.pid);
|
||||
break;
|
||||
}
|
||||
case AVMEDIA_TYPE_AUDIO: {
|
||||
switch(stream->codec->codec_id) {
|
||||
case AV_CODEC_ID_MP2:
|
||||
track.ac3flags = 9;
|
||||
break;
|
||||
case AV_CODEC_ID_MP3:
|
||||
track.ac3flags = 4;
|
||||
break;
|
||||
case AV_CODEC_ID_AC3:
|
||||
track.ac3flags = 1;
|
||||
break;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
track.ac3flags = 7;
|
||||
break;
|
||||
case AV_CODEC_ID_DTS:
|
||||
track.ac3flags = 6;
|
||||
break;
|
||||
case AV_CODEC_ID_AAC:
|
||||
track.ac3flags = 5;
|
||||
break;
|
||||
default:
|
||||
track.ac3flags = 0;
|
||||
}
|
||||
player->manager.addAudioTrack(track);
|
||||
if (!audioTrack)
|
||||
audioTrack = player->manager.getAudioTrack(track.pid);
|
||||
break;
|
||||
}
|
||||
case AVMEDIA_TYPE_SUBTITLE: {
|
||||
if (stream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
|
||||
std::string l = lang ? lang->value : "";
|
||||
uint8_t *data = stream->codec->extradata;
|
||||
int size = stream->codec->extradata_size;
|
||||
if (size > 0 && 2 * size - 1 == (int) l.length())
|
||||
for (int i = 0; i < size; i += 2) {
|
||||
track.title = l.substr(i * 2, 3);
|
||||
track.type = data[i] >> 3;
|
||||
track.mag = data[i] & 7;
|
||||
track.page = data[i + 1];
|
||||
player->manager.addTeletextTrack(track);
|
||||
}
|
||||
} else {
|
||||
if (!stream->codec->codec) {
|
||||
stream->codec->codec = avcodec_find_decoder(stream->codec->codec_id);
|
||||
if (!stream->codec->codec)
|
||||
fprintf(stderr, "avcodec_find_decoder failed for subtitle track %d\n", n);
|
||||
else {
|
||||
int err = avcodec_open2(stream->codec, stream->codec->codec, NULL);
|
||||
if (averror(err, avcodec_open2))
|
||||
stream->codec->codec = NULL;
|
||||
}
|
||||
}
|
||||
if (stream->codec->codec)
|
||||
player->manager.addSubtitleTrack(track);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "not handled or unknown codec_type %d\n", stream->codec->codec_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::Stop()
|
||||
{
|
||||
abortPlayback = true;
|
||||
|
||||
while (hasPlayThreadStarted != 0)
|
||||
usleep(100000);
|
||||
|
||||
if (avfc)
|
||||
avformat_close_input(&avfc);
|
||||
|
||||
avformat_network_deinit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::Seek(int64_t avts, bool absolute)
|
||||
{
|
||||
if (absolute)
|
||||
seek_avts_abs = avts, seek_avts_rel = 0;
|
||||
else
|
||||
seek_avts_abs = INT64_MIN, seek_avts_rel = avts;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::GetDuration(int64_t &duration)
|
||||
{
|
||||
duration = 0;
|
||||
|
||||
Track *track = videoTrack;
|
||||
if (track && track->duration) {
|
||||
duration = track->duration;
|
||||
return true;
|
||||
}
|
||||
track = audioTrack;
|
||||
if (track && track->duration) {
|
||||
duration = track->duration;
|
||||
return true;
|
||||
}
|
||||
track = subtitleTrack;
|
||||
if (track && track->duration) {
|
||||
duration = track->duration;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Input::SwitchAudio(Track *track)
|
||||
{
|
||||
audioTrack = track;
|
||||
player->output.SwitchAudio(track ? track->stream : NULL);
|
||||
player->Seek(-5000, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::SwitchSubtitle(Track *track)
|
||||
{
|
||||
subtitleTrack = track;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::SwitchTeletext(Track *track)
|
||||
{
|
||||
teletextTrack = track;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::SwitchVideo(Track *track)
|
||||
{
|
||||
videoTrack = track;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||
{
|
||||
keys.clear();
|
||||
values.clear();
|
||||
|
||||
if (avfc) {
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
|
||||
if (avfc->metadata)
|
||||
while ((tag = av_dict_get(avfc->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
keys.push_back(tag->key);
|
||||
values.push_back(tag->value);
|
||||
}
|
||||
|
||||
if (videoTrack)
|
||||
while ((tag = av_dict_get(videoTrack->stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
keys.push_back(tag->key);
|
||||
values.push_back(tag->value);
|
||||
}
|
||||
|
||||
if (audioTrack)
|
||||
while ((tag = av_dict_get(audioTrack->stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
keys.push_back(tag->key);
|
||||
values.push_back(tag->value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Input::GetReadCount(uint64_t &readcount)
|
||||
{
|
||||
readcount = readCount;
|
||||
return true;
|
||||
}
|
160
libeplayer3/manager.cpp
Normal file
160
libeplayer3/manager.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* manager class
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "manager.h"
|
||||
#include "player.h"
|
||||
|
||||
void Manager::addTrack(std::map<int,Track*> &tracks, Track &track)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||
std::map<int,Track*>::iterator it = tracks.find(track.pid);
|
||||
if (it == tracks.end()) {
|
||||
Track *t = new Track;
|
||||
*t = track;
|
||||
tracks[track.pid] = t;
|
||||
} else
|
||||
*it->second = track;
|
||||
}
|
||||
|
||||
void Manager::addVideoTrack(Track &track)
|
||||
{
|
||||
addTrack(videoTracks, track);
|
||||
}
|
||||
|
||||
void Manager::addAudioTrack(Track &track)
|
||||
{
|
||||
addTrack(audioTracks, track);
|
||||
}
|
||||
|
||||
void Manager::addSubtitleTrack(Track &track)
|
||||
{
|
||||
addTrack(subtitleTracks, track);
|
||||
}
|
||||
|
||||
void Manager::addTeletextTrack(Track &track)
|
||||
{
|
||||
addTrack(teletextTracks, track);
|
||||
}
|
||||
|
||||
std::vector<Track> Manager::getTracks(std::map<int,Track*> &tracks)
|
||||
{
|
||||
player->input.UpdateTracks();
|
||||
std::vector<Track> res;
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||
for(std::map<int,Track*>::iterator it = tracks.begin(); it != tracks.end(); ++it)
|
||||
if (!it->second->inactive)
|
||||
res.push_back(*it->second);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Track> Manager::getVideoTracks()
|
||||
{
|
||||
return getTracks(videoTracks);
|
||||
}
|
||||
|
||||
std::vector<Track> Manager::getAudioTracks()
|
||||
{
|
||||
return getTracks(audioTracks);
|
||||
}
|
||||
|
||||
std::vector<Track> Manager::getSubtitleTracks()
|
||||
{
|
||||
return getTracks(subtitleTracks);
|
||||
}
|
||||
|
||||
std::vector<Track> Manager::getTeletextTracks()
|
||||
{
|
||||
return getTracks(teletextTracks);
|
||||
}
|
||||
|
||||
Track *Manager::getTrack(std::map<int,Track*> &tracks, int pid)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||
std::map<int,Track*>::iterator it = tracks.find(pid);
|
||||
if (it != tracks.end() && !it->second->inactive)
|
||||
return it->second;
|
||||
return NULL;
|
||||
}
|
||||
Track *Manager::getVideoTrack(int pid)
|
||||
{
|
||||
return getTrack(videoTracks, pid);
|
||||
}
|
||||
|
||||
Track *Manager::getAudioTrack(int pid)
|
||||
{
|
||||
return getTrack(audioTracks, pid);
|
||||
}
|
||||
|
||||
Track *Manager::getSubtitleTrack(int pid)
|
||||
{
|
||||
return getTrack(subtitleTracks, pid);
|
||||
}
|
||||
|
||||
Track *Manager::getTeletextTrack(int pid)
|
||||
{
|
||||
return getTrack(teletextTracks, pid);
|
||||
}
|
||||
|
||||
bool Manager::initTrackUpdate()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||
|
||||
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
||||
it->second->inactive = !it->second->is_static;
|
||||
|
||||
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
||||
it->second->inactive = !it->second->is_static;
|
||||
|
||||
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
||||
it->second->inactive = !it->second->is_static;
|
||||
|
||||
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
||||
it->second->inactive = !it->second->is_static;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Manager::clearTracks()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||
|
||||
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
||||
delete it->second;
|
||||
audioTracks.clear();
|
||||
|
||||
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
||||
delete it->second;
|
||||
videoTracks.clear();
|
||||
|
||||
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
||||
delete it->second;
|
||||
subtitleTracks.clear();
|
||||
|
||||
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
||||
delete it->second;
|
||||
teletextTracks.clear();
|
||||
}
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
clearTracks();
|
||||
}
|
348
libeplayer3/output.cpp
Normal file
348
libeplayer3/output.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* output class
|
||||
*
|
||||
* based on libeplayer3 LinuxDVB Output handling.
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/dvb/video.h>
|
||||
#include <linux/dvb/audio.h>
|
||||
#include <linux/dvb/stm_ioctls.h>
|
||||
#include <memory.h>
|
||||
#include <asm/types.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <OpenThreads/ScopedLock>
|
||||
#include <OpenThreads/Thread>
|
||||
#include <OpenThreads/Condition>
|
||||
|
||||
#include "player.h"
|
||||
#include "output.h"
|
||||
#include "writer.h"
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
|
||||
#define dioctl(fd,req,arg) ({ \
|
||||
int _r = ioctl(fd,req,arg); \
|
||||
if (_r) \
|
||||
fprintf(stderr, "%s %d: ioctl '%s' failed: %d (%s)\n", __FILE__, __LINE__, #req, errno, strerror(errno)); \
|
||||
_r; \
|
||||
})
|
||||
|
||||
#define VIDEODEV "/dev/dvb/adapter0/video0"
|
||||
#define AUDIODEV "/dev/dvb/adapter0/audio0"
|
||||
|
||||
Output::Output()
|
||||
{
|
||||
videofd = audiofd = -1;
|
||||
videoWriter = audioWriter = NULL;
|
||||
videoStream = audioStream = NULL;
|
||||
}
|
||||
|
||||
Output::~Output()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool Output::Open()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd < 0)
|
||||
videofd = open(VIDEODEV, O_RDWR);
|
||||
|
||||
if (videofd < 0)
|
||||
return false;
|
||||
|
||||
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
|
||||
dioctl(videofd, VIDEO_SELECT_SOURCE, (void *) VIDEO_SOURCE_MEMORY);
|
||||
dioctl(videofd, VIDEO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM);
|
||||
dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
|
||||
|
||||
if (audiofd < 0)
|
||||
audiofd = open(AUDIODEV, O_RDWR);
|
||||
|
||||
if (audiofd < 0) {
|
||||
close(videofd);
|
||||
videofd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
|
||||
dioctl(audiofd, AUDIO_SELECT_SOURCE, (void *) AUDIO_SOURCE_MEMORY);
|
||||
dioctl(audiofd, AUDIO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Output::Close()
|
||||
{
|
||||
Stop();
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd > -1) {
|
||||
close(videofd);
|
||||
videofd = -1;
|
||||
}
|
||||
if (audiofd > -1) {
|
||||
close(audiofd);
|
||||
audiofd = -1;
|
||||
}
|
||||
|
||||
videoStream = NULL;
|
||||
audioStream = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Output::Play()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videoStream && videofd > -1) {
|
||||
videoWriter = Writer::GetWriter(videoStream->codec->codec_id, videoStream->codec->codec_type);
|
||||
videoWriter->Init();
|
||||
if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(videoStream->codec->codec_id))
|
||||
|| dioctl(videofd, VIDEO_PLAY, NULL))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (audioStream && audiofd > -1) {
|
||||
audioWriter = Writer::GetWriter(audioStream->codec->codec_id, audioStream->codec->codec_type);
|
||||
audioWriter->Init();
|
||||
if (dioctl(audiofd, AUDIO_SET_ENCODING, audioWriter->GetAudioEncoding(audioStream->codec->codec_id))
|
||||
|| dioctl(audiofd, AUDIO_PLAY, NULL))
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Output::Stop()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd > -1) {
|
||||
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
|
||||
/* set back to normal speed (end trickmodes) */
|
||||
dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
|
||||
if (dioctl(videofd, VIDEO_STOP, NULL))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (audiofd > -1) {
|
||||
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
|
||||
/* set back to normal speed (end trickmodes) */
|
||||
dioctl(audiofd, AUDIO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
|
||||
if (dioctl(audiofd, AUDIO_STOP, NULL))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Output::Pause()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd > -1) {
|
||||
if (dioctl(videofd, VIDEO_FREEZE, NULL))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (audiofd > -1) {
|
||||
if (dioctl(audiofd, AUDIO_PAUSE, NULL))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Output::Continue()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd > -1 && dioctl(videofd, VIDEO_CONTINUE, NULL))
|
||||
ret = false;
|
||||
|
||||
if (audiofd > -1 && dioctl(audiofd, AUDIO_CONTINUE, NULL))
|
||||
ret = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Output::Mute(bool b)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
//AUDIO_SET_MUTE has no effect with new player
|
||||
return audiofd > -1 && !dioctl(audiofd, b ? AUDIO_STOP : AUDIO_PLAY, NULL);
|
||||
}
|
||||
|
||||
|
||||
bool Output::Flush()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
|
||||
if (videofd > -1 && ioctl(videofd, VIDEO_FLUSH, NULL))
|
||||
ret = false;
|
||||
|
||||
if (audiofd > -1 && ioctl(audiofd, AUDIO_FLUSH, NULL))
|
||||
ret = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Output::FastForward(int speed)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
return videofd > -1 && !dioctl(videofd, VIDEO_FAST_FORWARD, speed);
|
||||
}
|
||||
|
||||
bool Output::SlowMotion(int speed)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
return videofd > -1 && !dioctl(videofd, VIDEO_SLOWMOTION, speed);
|
||||
}
|
||||
|
||||
bool Output::AVSync(bool b)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
return audiofd > -1 && !dioctl(audiofd, AUDIO_SET_AV_SYNC, b);
|
||||
}
|
||||
|
||||
bool Output::ClearAudio()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
return audiofd > -1 && !ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
|
||||
}
|
||||
|
||||
bool Output::ClearVideo()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
return videofd > -1 && !ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
|
||||
}
|
||||
|
||||
bool Output::Clear()
|
||||
{
|
||||
bool aret = ClearAudio();
|
||||
bool vret = ClearVideo();
|
||||
return aret && vret;
|
||||
}
|
||||
|
||||
bool Output::GetPts(int64_t &pts)
|
||||
{
|
||||
pts = 0;
|
||||
return ((videofd > -1 && !ioctl(videofd, VIDEO_GET_PTS, (void *) &pts)) ||
|
||||
(audiofd > -1 && !ioctl(audiofd, AUDIO_GET_PTS, (void *) &pts)));
|
||||
}
|
||||
|
||||
bool Output::GetFrameCount(int64_t &framecount)
|
||||
{
|
||||
dvb_play_info_t playInfo;
|
||||
|
||||
if ((videofd > -1 && !dioctl(videofd, VIDEO_GET_PLAY_INFO, (void *) &playInfo)) ||
|
||||
(audiofd > -1 && !dioctl(audiofd, AUDIO_GET_PLAY_INFO, (void *) &playInfo))) {
|
||||
framecount = playInfo.frame_count;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Output::SwitchAudio(AVStream *stream)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
if (stream == audioStream)
|
||||
return true;
|
||||
if (audiofd > -1) {
|
||||
dioctl(audiofd, AUDIO_STOP, NULL);
|
||||
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
|
||||
}
|
||||
audioStream = stream;
|
||||
if (stream) {
|
||||
audioWriter = Writer::GetWriter(stream->codec->codec_id, stream->codec->codec_type);
|
||||
audioWriter->Init();
|
||||
if (audiofd > -1) {
|
||||
dioctl (audiofd, AUDIO_SET_ENCODING, Writer::GetAudioEncoding(stream->codec->codec_id));
|
||||
dioctl(audiofd, AUDIO_PLAY, NULL);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Output::SwitchVideo(AVStream *stream)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
if (stream == videoStream)
|
||||
return true;
|
||||
if (videofd > -1) {
|
||||
dioctl(videofd, VIDEO_STOP, NULL);
|
||||
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
|
||||
}
|
||||
videoStream = stream;
|
||||
if (stream) {
|
||||
videoWriter = Writer::GetWriter(stream->codec->codec_id, stream->codec->codec_type);
|
||||
videoWriter->Init();
|
||||
if (videofd > -1) {
|
||||
dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(stream->codec->codec_id));
|
||||
dioctl(videofd, VIDEO_PLAY, NULL);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Output::Write(AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
switch (stream->codec->codec_type) {
|
||||
case AVMEDIA_TYPE_VIDEO: {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
||||
return videofd > -1 && videoWriter && videoWriter->Write(videofd, avfc, stream, packet, pts);
|
||||
}
|
||||
case AVMEDIA_TYPE_AUDIO: {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
||||
return audiofd > -1 && audioWriter && audioWriter->Write(audiofd, avfc, stream, packet, pts);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
410
libeplayer3/player.cpp
Normal file
410
libeplayer3/player.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 duckbox
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "player.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define cMaxSpeed_ff 128 /* fixme: revise */
|
||||
#define cMaxSpeed_fr -320 /* fixme: revise */
|
||||
|
||||
Player::Player()
|
||||
{
|
||||
input.player = this;
|
||||
output.player = this;
|
||||
manager.player = this;
|
||||
hasThreadStarted = false;
|
||||
|
||||
isPaused = false;
|
||||
isPlaying = false;
|
||||
isForwarding = false;
|
||||
isBackWard = false;
|
||||
isSlowMotion = false;
|
||||
Speed = 0;
|
||||
}
|
||||
|
||||
void *Player::playthread(void *arg)
|
||||
{
|
||||
char threadname[17];
|
||||
strncpy(threadname, __func__, sizeof(threadname));
|
||||
threadname[16] = 0;
|
||||
prctl(PR_SET_NAME, (unsigned long) threadname);
|
||||
|
||||
Player *player = (Player *) arg;
|
||||
player->hasThreadStarted = true;
|
||||
player->input.Play();
|
||||
player->hasThreadStarted = false;
|
||||
player->Stop();
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
bool Player::Open(const char *Url, bool _noprobe)
|
||||
{
|
||||
fprintf(stderr, "URL=%s\n", Url);
|
||||
|
||||
isHttp = false;
|
||||
noprobe = _noprobe;
|
||||
abortRequested = false;
|
||||
|
||||
manager.clearTracks();
|
||||
|
||||
if (!strncmp("mms://", Url, 6)) {
|
||||
url = "mmst";
|
||||
url += Url + 3;
|
||||
isHttp = true;
|
||||
} else if (strstr(Url, "://")) {
|
||||
url = Url;
|
||||
} else {
|
||||
fprintf(stderr, "%s %s %d: Unknown stream (%s)\n", __FILE__, __func__, __LINE__, Url);
|
||||
return false;
|
||||
}
|
||||
|
||||
return input.Init(url.c_str());
|
||||
}
|
||||
|
||||
bool Player::Close()
|
||||
{
|
||||
isPaused = false;
|
||||
isPlaying = false;
|
||||
isForwarding = false;
|
||||
isBackWard = false;
|
||||
isSlowMotion = false;
|
||||
Speed = 0;
|
||||
url.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::Play()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (!isPlaying) {
|
||||
output.AVSync(true);
|
||||
|
||||
ret = output.Play();
|
||||
|
||||
if (ret) {
|
||||
isPlaying = true;
|
||||
isPaused = false;
|
||||
isForwarding = false;
|
||||
if (isBackWard) {
|
||||
isBackWard = false;
|
||||
output.Mute(false);
|
||||
}
|
||||
isSlowMotion = false;
|
||||
Speed = 1;
|
||||
|
||||
if (!hasThreadStarted) {
|
||||
int err = pthread_create(&playThread, NULL, playthread, this);
|
||||
|
||||
if (err) {
|
||||
fprintf(stderr, "%s %s %d: pthread_create: %d (%s)\n", __FILE__, __func__, __LINE__, err, strerror(err));
|
||||
ret = false;
|
||||
isPlaying = false;
|
||||
} else {
|
||||
pthread_detach(playThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr,"playback already running\n");
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::Pause()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (isPlaying && !isPaused) {
|
||||
|
||||
if (isSlowMotion)
|
||||
output.Clear();
|
||||
|
||||
output.Pause();
|
||||
|
||||
isPaused = true;
|
||||
//isPlaying = 1;
|
||||
isForwarding = false;
|
||||
if (isBackWard) {
|
||||
isBackWard = false;
|
||||
output.Mute(false);
|
||||
}
|
||||
isSlowMotion = false;
|
||||
Speed = 1;
|
||||
} else {
|
||||
fprintf(stderr,"playback not playing or already in pause mode\n");
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::Continue()
|
||||
{
|
||||
int ret = true;
|
||||
|
||||
if (isPlaying && (isPaused || isForwarding || isBackWard || isSlowMotion)) {
|
||||
|
||||
if (isSlowMotion)
|
||||
output.Clear();
|
||||
|
||||
output.Continue();
|
||||
|
||||
isPaused = false;
|
||||
//isPlaying = 1;
|
||||
isForwarding = false;
|
||||
if (isBackWard) {
|
||||
isBackWard = false;
|
||||
output.Mute(false);
|
||||
}
|
||||
isSlowMotion = false;
|
||||
Speed = 1;
|
||||
} else {
|
||||
fprintf(stderr,"continue not possible\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::Stop()
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (isPlaying) {
|
||||
isPaused = false;
|
||||
isPlaying = false;
|
||||
isForwarding = false;
|
||||
if (isBackWard) {
|
||||
isBackWard = false;
|
||||
output.Mute(false);
|
||||
}
|
||||
isSlowMotion = false;
|
||||
Speed = 0;
|
||||
|
||||
output.Stop();
|
||||
input.Stop();
|
||||
|
||||
} else {
|
||||
fprintf(stderr,"stop not possible\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
while (hasThreadStarted)
|
||||
usleep(100000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::FastForward(int speed)
|
||||
{
|
||||
int ret = true;
|
||||
|
||||
/* Audio only forwarding not supported */
|
||||
if (input.videoTrack && !isHttp && !isBackWard && (!isPaused || isPlaying)) {
|
||||
|
||||
if ((speed <= 0) || (speed > cMaxSpeed_ff)) {
|
||||
fprintf(stderr, "speed %d out of range (1 - %d) \n", speed, cMaxSpeed_ff);
|
||||
return false;
|
||||
}
|
||||
|
||||
isForwarding = 1;
|
||||
Speed = speed;
|
||||
output.FastForward(speed);
|
||||
} else {
|
||||
fprintf(stderr,"fast forward not possible\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::FastBackward(int speed)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
/* Audio only reverse play not supported */
|
||||
if (input.videoTrack && !isForwarding && (!isPaused || isPlaying)) {
|
||||
|
||||
if ((speed > 0) || (speed < cMaxSpeed_fr)) {
|
||||
fprintf(stderr, "speed %d out of range (0 - %d) \n", speed, cMaxSpeed_fr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (speed == 0) {
|
||||
isBackWard = false;
|
||||
Speed = 0; /* reverse end */
|
||||
} else {
|
||||
Speed = speed;
|
||||
isBackWard = true;
|
||||
}
|
||||
|
||||
output.Clear();
|
||||
#if 0
|
||||
if (output->Command(player, OUTPUT_REVERSE, NULL) < 0) {
|
||||
fprintf(stderr,"OUTPUT_REVERSE failed\n");
|
||||
isBackWard = false;
|
||||
Speed = 1;
|
||||
ret = false;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
fprintf(stderr,"fast backward not possible\n");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (isBackWard)
|
||||
output.Mute(true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Player::SlowMotion(int repeats)
|
||||
{
|
||||
if (input.videoTrack && !isHttp && isPlaying) {
|
||||
if (isPaused)
|
||||
Continue();
|
||||
|
||||
switch (repeats) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
isSlowMotion = true;
|
||||
break;
|
||||
default:
|
||||
repeats = 0;
|
||||
}
|
||||
|
||||
output.SlowMotion(repeats);
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr, "slowmotion not possible\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Player::Seek(int64_t pos, bool absolute)
|
||||
{
|
||||
output.Clear();
|
||||
return input.Seek(pos, absolute);
|
||||
}
|
||||
|
||||
bool Player::GetPts(int64_t &pts)
|
||||
{
|
||||
pts = INVALID_PTS_VALUE;
|
||||
return isPlaying && output.GetPts(pts);
|
||||
}
|
||||
|
||||
bool Player::GetFrameCount(int64_t &frameCount)
|
||||
{
|
||||
return isPlaying && output.GetFrameCount(frameCount);
|
||||
}
|
||||
|
||||
bool Player::GetDuration(int64_t &duration)
|
||||
{
|
||||
duration = -1;
|
||||
return isPlaying && input.GetDuration(duration);
|
||||
}
|
||||
|
||||
bool Player::SwitchVideo(int pid)
|
||||
{
|
||||
Track *track = manager.getVideoTrack(pid);
|
||||
return input.SwitchVideo(track);
|
||||
}
|
||||
|
||||
bool Player::SwitchAudio(int pid)
|
||||
{
|
||||
Track *track = manager.getAudioTrack(pid);
|
||||
return input.SwitchAudio(track);
|
||||
}
|
||||
|
||||
bool Player::SwitchSubtitle(int pid)
|
||||
{
|
||||
Track *track = manager.getSubtitleTrack(pid);
|
||||
return input.SwitchSubtitle(track);
|
||||
}
|
||||
|
||||
bool Player::SwitchTeletext(int pid)
|
||||
{
|
||||
Track *track = manager.getTeletextTrack(pid);
|
||||
return input.SwitchTeletext(track);
|
||||
}
|
||||
|
||||
bool Player::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||
{
|
||||
return input.GetMetadata(keys, values);
|
||||
}
|
||||
|
||||
bool Player::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
|
||||
{
|
||||
positions.clear();
|
||||
titles.clear();
|
||||
input.UpdateTracks();
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(chapterMutex);
|
||||
for (std::vector<Chapter>::iterator it = chapters.begin(); it != chapters.end(); ++it) {
|
||||
positions.push_back(it->start/1000);
|
||||
titles.push_back(it->title);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::SetChapters(std::vector<Chapter> &Chapters)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(chapterMutex);
|
||||
chapters = Chapters;
|
||||
}
|
||||
|
||||
void Player::RequestAbort()
|
||||
{
|
||||
abortRequested = true;
|
||||
}
|
||||
|
||||
int Player::GetVideoPid()
|
||||
{
|
||||
Track *track = input.videoTrack;
|
||||
return track ? track->pid : 0;
|
||||
}
|
||||
|
||||
int Player::GetAudioPid()
|
||||
{
|
||||
Track *track = input.audioTrack;
|
||||
return track ? track->pid : 0;
|
||||
}
|
||||
|
||||
int Player::GetSubtitlePid()
|
||||
{
|
||||
Track *track = input.subtitleTrack;
|
||||
return track ? track->pid : 0;
|
||||
}
|
||||
|
||||
int Player::GetTeletextPid()
|
||||
{
|
||||
Track *track = input.teletextTrack;
|
||||
return track ? track->pid : 0;
|
||||
}
|
62
libeplayer3/writer/ac3.cpp
Normal file
62
libeplayer3/writer/ac3.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterAC3 : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterAC3();
|
||||
};
|
||||
|
||||
bool WriterAC3::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
struct iovec iov[2];
|
||||
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, packet->size, PRIVATE_STREAM_1_PES_START_CODE, pts, 0);
|
||||
iov[1].iov_base = packet->data;
|
||||
iov[1].iov_len = packet->size;
|
||||
|
||||
return writev(fd, iov, 2) > -1;
|
||||
}
|
||||
|
||||
WriterAC3::WriterAC3()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_AC3, AUDIO_ENCODING_AC3);
|
||||
Register(this, AV_CODEC_ID_EAC3, AUDIO_ENCODING_AC3);
|
||||
}
|
||||
|
||||
static WriterAC3 writer_ac3 __attribute__ ((init_priority (300)));
|
107
libeplayer3/writer/divx.cpp
Normal file
107
libeplayer3/writer/divx.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterDIVX : public Writer
|
||||
{
|
||||
private:
|
||||
bool initialHeader;
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
void Init();
|
||||
WriterDIVX();
|
||||
};
|
||||
|
||||
void WriterDIVX::Init()
|
||||
{
|
||||
initialHeader = true;
|
||||
}
|
||||
|
||||
bool WriterDIVX::Write(int fd, AVFormatContext * /* avfc */, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
uint8_t FakeHeaders[64] = { 0 }; // 64bytes should be enough to make the fake headers
|
||||
unsigned int FakeHeaderLength;
|
||||
uint8_t Version = 5;
|
||||
unsigned int FakeStartCode = (Version << 8) | PES_VERSION_FAKE_START_CODE;
|
||||
BitPacker_t ld = { FakeHeaders, 0, 32 };
|
||||
|
||||
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
|
||||
|
||||
/* Create info record for frame parser */
|
||||
/* divx4 & 5
|
||||
VOS
|
||||
PutBits(&ld, 0x0, 8);
|
||||
PutBits(&ld, 0x0, 8);
|
||||
*/
|
||||
PutBits(&ld, 0x1b0, 32); // startcode
|
||||
PutBits(&ld, 0, 8); // profile = reserved
|
||||
PutBits(&ld, 0x1b2, 32); // startcode (user data)
|
||||
PutBits(&ld, 0x53545443, 32); // STTC - an embedded ST timecode from an avi file
|
||||
PutBits(&ld, usecPerFrame, 32); // microseconds per frame
|
||||
FlushBits(&ld);
|
||||
|
||||
FakeHeaderLength = (ld.Ptr - FakeHeaders);
|
||||
|
||||
struct iovec iov[4];
|
||||
int ic = 0;
|
||||
iov[ic].iov_base = PesHeader;
|
||||
iov[ic++].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
|
||||
iov[ic].iov_base = FakeHeaders;
|
||||
iov[ic++].iov_len = FakeHeaderLength;
|
||||
|
||||
if (initialHeader) {
|
||||
iov[ic].iov_base = stream->codec->extradata;
|
||||
iov[ic++].iov_len = stream->codec->extradata_size;
|
||||
initialHeader = false;
|
||||
}
|
||||
iov[ic].iov_base = packet->data;
|
||||
iov[ic++].iov_len = packet->size;
|
||||
|
||||
return writev(fd, iov, ic) > -1;
|
||||
}
|
||||
|
||||
WriterDIVX::WriterDIVX()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_MPEG4, VIDEO_ENCODING_MPEG4P2);
|
||||
Register(this, AV_CODEC_ID_MSMPEG4V1, VIDEO_ENCODING_MPEG4P2);
|
||||
Register(this, AV_CODEC_ID_MSMPEG4V2, VIDEO_ENCODING_MPEG4P2);
|
||||
Register(this, AV_CODEC_ID_MSMPEG4V3, VIDEO_ENCODING_MPEG4P2);
|
||||
Init();
|
||||
}
|
||||
|
||||
static WriterDIVX writer_divx __attribute__ ((init_priority (300)));
|
82
libeplayer3/writer/dts.cpp
Normal file
82
libeplayer3/writer/dts.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
#define PES_AUDIO_PRIVATE_HEADER_SIZE 16 // consider maximum private header size.
|
||||
#define PES_AUDIO_HEADER_SIZE (32 + PES_AUDIO_PRIVATE_HEADER_SIZE)
|
||||
|
||||
class WriterDTS : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterDTS();
|
||||
};
|
||||
|
||||
bool WriterDTS::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_AUDIO_HEADER_SIZE];
|
||||
|
||||
// #define DO_BYTESWAP
|
||||
#ifdef DO_BYTESWAP
|
||||
uint8_t Data[packet->size];
|
||||
memcpy(Data, packet->data, packet->size);
|
||||
|
||||
/* 16-bit byte swap all data before injecting it */
|
||||
for (i = 0; i < packet->size; i += 2) {
|
||||
uint8_t Tmp = Data[i];
|
||||
Data[i] = Data[i + 1];
|
||||
Data[i + 1] = Tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct iovec iov[2];
|
||||
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_AUDIO_PES_START_CODE /*PRIVATE_STREAM_1_PES_START_CODE */ , pts, 0);
|
||||
#ifdef DO_BYTESPWAP
|
||||
iov[1].iov_base = Data;
|
||||
#else
|
||||
iov[1].iov_base = packet->data;
|
||||
#endif
|
||||
iov[1].iov_len = packet->size;
|
||||
|
||||
return writev(fd, iov, 2) > -1;
|
||||
}
|
||||
|
||||
WriterDTS::WriterDTS()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_DTS, AUDIO_ENCODING_DTS);
|
||||
}
|
||||
|
||||
static WriterDTS writer_dts __attribute__ ((init_priority (300)));
|
61
libeplayer3/writer/flac.cpp
Normal file
61
libeplayer3/writer/flac.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "pes.h"
|
||||
#include "misc.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterFLAC : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext * /* avfc */, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterFLAC();
|
||||
};
|
||||
|
||||
bool WriterFLAC::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
struct iovec iov[2];
|
||||
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_AUDIO_PES_START_CODE, pts, 0);
|
||||
iov[1].iov_base = packet->data;
|
||||
iov[1].iov_len = packet->size;
|
||||
|
||||
return writev(fd, iov, 2) > -1;
|
||||
}
|
||||
|
||||
WriterFLAC::WriterFLAC()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_FLAC, AUDIO_ENCODING_LPCM);
|
||||
}
|
||||
|
||||
static WriterFLAC writer_flac __attribute__ ((init_priority (300)));
|
74
libeplayer3/writer/h263.cpp
Normal file
74
libeplayer3/writer/h263.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 crow
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterH263 : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterH263();
|
||||
};
|
||||
|
||||
bool WriterH263::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
|
||||
int HeaderLength = InsertPesHeader(PesHeader, packet->size, H263_VIDEO_PES_START_CODE, pts, 0);
|
||||
|
||||
int PrivateHeaderLength = InsertVideoPrivateDataHeader(&PesHeader[HeaderLength], packet->size);
|
||||
|
||||
int PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength;
|
||||
|
||||
PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff;
|
||||
PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff;
|
||||
PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength;
|
||||
PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT;
|
||||
|
||||
HeaderLength += PrivateHeaderLength;
|
||||
|
||||
struct iovec iov[2];
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = HeaderLength;
|
||||
iov[1].iov_base = packet->data;
|
||||
iov[1].iov_len = packet->size;
|
||||
return writev(fd, iov, 2) > -1;
|
||||
}
|
||||
|
||||
WriterH263::WriterH263()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_H263, VIDEO_ENCODING_H263);
|
||||
Register(this, AV_CODEC_ID_H263P, VIDEO_ENCODING_H263);
|
||||
Register(this, AV_CODEC_ID_H263I, VIDEO_ENCODING_H263);
|
||||
Register(this, AV_CODEC_ID_FLV1, VIDEO_ENCODING_FLV1);
|
||||
}
|
||||
|
||||
static WriterH263 writer_h263 __attribute__ ((init_priority (300)));
|
255
libeplayer3/writer/h264.cpp
Normal file
255
libeplayer3/writer/h264.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24
|
||||
#define CONTAINER_PARAMETERS_VERSION 0x00
|
||||
|
||||
typedef struct avcC_s {
|
||||
uint8_t Version; // configurationVersion
|
||||
uint8_t Profile; // AVCProfileIndication
|
||||
uint8_t Compatibility; // profile_compatibility
|
||||
uint8_t Level; // AVCLevelIndication
|
||||
uint8_t NalLengthMinusOne; // held in bottom two bits
|
||||
uint8_t NumParamSets; // held in bottom 5 bits
|
||||
uint8_t Params[1]; // {length,params}{length,params}...sequence then picture
|
||||
} avcC_t;
|
||||
|
||||
static uint8_t Head[] = { 0, 0, 0, 1 };
|
||||
|
||||
class WriterH264 : public Writer
|
||||
{
|
||||
private:
|
||||
bool initialHeader;
|
||||
unsigned int NalLengthBytes;
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
void Init();
|
||||
WriterH264();
|
||||
};
|
||||
|
||||
void WriterH264::Init(void)
|
||||
{
|
||||
initialHeader = true;
|
||||
NalLengthBytes = 1;
|
||||
}
|
||||
|
||||
bool WriterH264::Write(int fd, AVFormatContext * /* avfc */, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
unsigned int TimeDelta;
|
||||
unsigned int TimeScale;
|
||||
int len = 0;
|
||||
int ic = 0;
|
||||
struct iovec iov[128];
|
||||
|
||||
TimeDelta = av_rescale(1000ll, stream->r_frame_rate.num, stream->r_frame_rate.den);
|
||||
TimeScale = (TimeDelta < 23970) ? 1001 : 1000; /* fixme: revise this */
|
||||
|
||||
if ((packet->size > 3)
|
||||
&& ((packet->data[0] == 0x00 && packet->data[1] == 0x00 && packet->data[2] == 0x00 && packet->data[3] == 0x01)
|
||||
|| (packet->data[0] == 0xff && packet->data[1] == 0xff && packet->data[2] == 0xff && packet->data[3] == 0xff))) {
|
||||
unsigned int FakeStartCode = /* (call->Version << 8) | */ PES_VERSION_FAKE_START_CODE;
|
||||
iov[ic++].iov_base = PesHeader;
|
||||
if (initialHeader) {
|
||||
initialHeader = false;
|
||||
iov[ic].iov_base = stream->codec->extradata;
|
||||
iov[ic++].iov_len = stream->codec->extradata_size;
|
||||
len += stream->codec->extradata_size;
|
||||
}
|
||||
iov[ic].iov_base = packet->data;
|
||||
iov[ic++].iov_len = packet->size;
|
||||
len += packet->size;
|
||||
// Hellmaster1024:
|
||||
// some packets will only be accepted by the player if we send one byte more than data is available.
|
||||
// The content of this byte does not matter. It will be ignored by the player
|
||||
iov[ic].iov_base = (void *) "";
|
||||
iov[ic++].iov_len = 1;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
|
||||
return writev(fd, iov, ic) > -1;
|
||||
}
|
||||
|
||||
if (initialHeader) {
|
||||
avcC_t *avcCHeader = (avcC_t *) stream->codec->extradata;
|
||||
|
||||
if (!avcCHeader) {
|
||||
fprintf(stderr, "stream->codec->extradata == NULL\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (avcCHeader->Version != 1)
|
||||
fprintf(stderr, "Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version);
|
||||
|
||||
uint8_t Header[19];
|
||||
unsigned int HeaderLen = 0;
|
||||
Header[HeaderLen++] = 0x00; // Start code
|
||||
Header[HeaderLen++] = 0x00;
|
||||
Header[HeaderLen++] = 0x01;
|
||||
Header[HeaderLen++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS;
|
||||
// Container message version - changes when/if we vary the format of the message
|
||||
Header[HeaderLen++] = CONTAINER_PARAMETERS_VERSION;
|
||||
Header[HeaderLen++] = 0xff; // Field separator
|
||||
|
||||
if (TimeDelta == 0xffffffff)
|
||||
TimeDelta = (TimeScale > 1000) ? 1001 : 1;
|
||||
|
||||
Header[HeaderLen++] = (TimeScale >> 24) & 0xff; // Output the timescale
|
||||
Header[HeaderLen++] = (TimeScale >> 16) & 0xff;
|
||||
Header[HeaderLen++] = 0xff;
|
||||
Header[HeaderLen++] = (TimeScale >> 8) & 0xff;
|
||||
Header[HeaderLen++] = TimeScale & 0xff;
|
||||
Header[HeaderLen++] = 0xff;
|
||||
|
||||
Header[HeaderLen++] = (TimeDelta >> 24) & 0xff; // Output frame period
|
||||
Header[HeaderLen++] = (TimeDelta >> 16) & 0xff;
|
||||
Header[HeaderLen++] = 0xff;
|
||||
Header[HeaderLen++] = (TimeDelta >> 8) & 0xff;
|
||||
Header[HeaderLen++] = TimeDelta & 0xff;
|
||||
Header[HeaderLen++] = 0xff;
|
||||
Header[HeaderLen++] = 0x80; // Rsbp trailing bits
|
||||
|
||||
ic = 0;
|
||||
iov[ic].iov_base = PesHeader;
|
||||
iov[ic++].iov_len = InsertPesHeader(PesHeader, HeaderLen, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||
iov[ic].iov_base = Header;
|
||||
iov[ic++].iov_len = HeaderLen;
|
||||
len = writev(fd, iov, ic);
|
||||
if (len < 0)
|
||||
return false;
|
||||
|
||||
NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1;
|
||||
unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f;
|
||||
unsigned int ParamOffset = 0;
|
||||
unsigned int InitialHeaderLength = 0;
|
||||
|
||||
ic = 0;
|
||||
iov[ic++].iov_base = PesHeader;
|
||||
for (unsigned int i = 0; i < ParamSets; i++) {
|
||||
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset + 1];
|
||||
|
||||
iov[ic].iov_base = (char *) Head;
|
||||
iov[ic++].iov_len = sizeof(Head);
|
||||
InitialHeaderLength += sizeof(Head);
|
||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
||||
iov[ic++].iov_len = PsLength;
|
||||
InitialHeaderLength += PsLength;
|
||||
ParamOffset += PsLength + 2;
|
||||
}
|
||||
|
||||
ParamSets = avcCHeader->Params[ParamOffset++];
|
||||
|
||||
for (unsigned int i = 0; i < ParamSets; i++) {
|
||||
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset + 1];
|
||||
|
||||
iov[ic].iov_base = (char *) Head;
|
||||
iov[ic++].iov_len = sizeof(Head);
|
||||
InitialHeaderLength += sizeof(Head);
|
||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
||||
iov[ic++].iov_len = PsLength;
|
||||
InitialHeaderLength += PsLength;
|
||||
ParamOffset += PsLength + 2;
|
||||
}
|
||||
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, InitialHeaderLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||
ssize_t l = writev(fd, iov, ic);
|
||||
if (l < 0)
|
||||
return false;
|
||||
len += l;
|
||||
|
||||
initialHeader = 0;
|
||||
}
|
||||
|
||||
unsigned int SampleSize = packet->size;
|
||||
unsigned int NalStart = 0;
|
||||
unsigned int VideoPosition = 0;
|
||||
|
||||
do {
|
||||
unsigned int NalLength;
|
||||
uint8_t NalData[4];
|
||||
|
||||
memcpy(NalData, packet->data + VideoPosition, NalLengthBytes);
|
||||
VideoPosition += NalLengthBytes;
|
||||
NalStart += NalLengthBytes;
|
||||
switch (NalLengthBytes) {
|
||||
case 1:
|
||||
NalLength = (NalData[0]);
|
||||
break;
|
||||
case 2:
|
||||
NalLength = (NalData[0] << 8) | (NalData[1]);
|
||||
break;
|
||||
case 3:
|
||||
NalLength = (NalData[0] << 16) | (NalData[1] << 8) | (NalData[2]);
|
||||
break;
|
||||
default:
|
||||
NalLength = (NalData[0] << 24) | (NalData[1] << 16) | (NalData[2] << 8) | (NalData[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (NalStart + NalLength > SampleSize) {
|
||||
fprintf(stderr, "nal length past end of buffer - size %u frame offset %u left %u\n", NalLength, NalStart, SampleSize - NalStart);
|
||||
NalStart = SampleSize;
|
||||
} else {
|
||||
NalStart += NalLength;
|
||||
ic = 0;
|
||||
iov[ic++].iov_base = PesHeader;
|
||||
|
||||
iov[ic].iov_base = Head;
|
||||
iov[ic++].iov_len = sizeof(Head);
|
||||
|
||||
iov[ic].iov_base = packet->data + VideoPosition;
|
||||
iov[ic++].iov_len = NalLength;
|
||||
|
||||
VideoPosition += NalLength;
|
||||
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, NalLength, MPEG_VIDEO_PES_START_CODE, pts, 0);
|
||||
ssize_t l = writev(fd, iov, ic);
|
||||
if (l < 0)
|
||||
return false;
|
||||
len += l;
|
||||
|
||||
pts = INVALID_PTS_VALUE;
|
||||
}
|
||||
} while (NalStart < SampleSize);
|
||||
|
||||
return len > -1;
|
||||
}
|
||||
|
||||
WriterH264::WriterH264()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_H264, VIDEO_ENCODING_H264);
|
||||
Init();
|
||||
}
|
||||
|
||||
static WriterH264 writerh264 __attribute__ ((init_priority (300)));
|
88
libeplayer3/writer/misc.cpp
Normal file
88
libeplayer3/writer/misc.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* LinuxDVB Output handling.
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <memory.h>
|
||||
#include <asm/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length)
|
||||
{
|
||||
unsigned int bit_buf;
|
||||
unsigned int bit_left;
|
||||
|
||||
bit_buf = ld->BitBuffer;
|
||||
bit_left = ld->Remaining;
|
||||
|
||||
#ifdef DEBUG_PUTBITS
|
||||
if (ld->debug)
|
||||
dprintf("code = %d, length = %d, bit_buf = 0x%x, bit_left = %d\n",
|
||||
code, length, bit_buf, bit_left);
|
||||
#endif
|
||||
|
||||
if (length < bit_left) {
|
||||
/* fits into current buffer */
|
||||
bit_buf = (bit_buf << length) | code;
|
||||
bit_left -= length;
|
||||
} else {
|
||||
/* doesn't fit */
|
||||
bit_buf <<= bit_left;
|
||||
bit_buf |= code >> (length - bit_left);
|
||||
ld->Ptr[0] = (uint8_t) (bit_buf >> 24);
|
||||
ld->Ptr[1] = (uint8_t) (bit_buf >> 16);
|
||||
ld->Ptr[2] = (uint8_t) (bit_buf >> 8);
|
||||
ld->Ptr[3] = (uint8_t) bit_buf;
|
||||
ld->Ptr += 4;
|
||||
length -= bit_left;
|
||||
bit_buf = code & ((1 << length) - 1);
|
||||
bit_left = 32 - length;
|
||||
bit_buf = code;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PUTBITS
|
||||
if (ld->debug)
|
||||
dprintf("bit_left = %d, bit_buf = 0x%x\n", bit_left, bit_buf);
|
||||
#endif
|
||||
|
||||
/* writeback */
|
||||
ld->BitBuffer = bit_buf;
|
||||
ld->Remaining = bit_left;
|
||||
}
|
||||
|
||||
void FlushBits(BitPacker_t * ld)
|
||||
{
|
||||
ld->BitBuffer <<= ld->Remaining;
|
||||
while (ld->Remaining < 32) {
|
||||
#ifdef DEBUG_PUTBITS
|
||||
if (ld->debug)
|
||||
dprintf("flushing 0x%2.2x\n", ld->BitBuffer >> 24);
|
||||
#endif
|
||||
*ld->Ptr++ = ld->BitBuffer >> 24;
|
||||
ld->BitBuffer <<= 8;
|
||||
ld->Remaining += 8;
|
||||
}
|
||||
ld->Remaining = 32;
|
||||
ld->BitBuffer = 0;
|
||||
}
|
63
libeplayer3/writer/mp3.cpp
Normal file
63
libeplayer3/writer/mp3.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterMP3 : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterMP3();
|
||||
};
|
||||
|
||||
bool WriterMP3::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
struct iovec iov[2];
|
||||
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_AUDIO_PES_START_CODE, pts, 0);
|
||||
iov[1].iov_base = packet->data;
|
||||
iov[1].iov_len = packet->size;
|
||||
|
||||
return writev(fd, iov, 2) > -1;
|
||||
}
|
||||
|
||||
WriterMP3::WriterMP3()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_MP3, AUDIO_ENCODING_MP3);
|
||||
Register(this, AV_CODEC_ID_MP2, AUDIO_ENCODING_MPEG2);
|
||||
// Register(this, AV_CODEC_ID_VORBIS, AUDIO_ENCODING_VORBIS);
|
||||
}
|
||||
|
||||
static WriterMP3 writer_mp3 __attribute__ ((init_priority (300)));
|
73
libeplayer3/writer/mpeg2.cpp
Normal file
73
libeplayer3/writer/mpeg2.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
class WriterMPEG2 : public Writer
|
||||
{
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
WriterMPEG2();
|
||||
};
|
||||
|
||||
bool WriterMPEG2::Write(int fd, AVFormatContext * /* avfc */, AVStream * /* stream */, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
|
||||
for (int Position = 0; Position < packet->size; ) {
|
||||
int PacketLength = std::min(packet->size - Position, MAX_PES_PACKET_SIZE);
|
||||
struct iovec iov[2];
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, 0xe0, pts, 0);
|
||||
iov[1].iov_base = packet->data + Position;
|
||||
iov[1].iov_len = PacketLength;
|
||||
|
||||
ssize_t l = writev(fd, iov, 2);
|
||||
if (l < 0) {
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
Position += PacketLength;
|
||||
pts = INVALID_PTS_VALUE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WriterMPEG2::WriterMPEG2()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_MPEG2TS, VIDEO_ENCODING_AUTO);
|
||||
}
|
||||
|
||||
static WriterMPEG2 writer_mpeg2 __attribute__ ((init_priority (300)));
|
355
libeplayer3/writer/pcm.cpp
Normal file
355
libeplayer3/writer/pcm.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling.
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <linux/dvb/audio.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
|
||||
// reference: search for TypeLpcmDVDAudio in player/frame_parser/frame_parser_audio_lpcm.cpp
|
||||
static const uint8_t clpcm_prv[14] = {
|
||||
0xA0, //sub_stream_id
|
||||
0, 0, //resvd and UPC_EAN_ISRC stuff, unused
|
||||
0x0A, //private header length
|
||||
0, 9, //first_access_unit_pointer
|
||||
0x00, //emph,rsvd,stereo,downmix
|
||||
0x0F, //quantisation word length 1,2
|
||||
0x0F, //audio sampling freqency 1,2
|
||||
0, //resvd, multi channel type
|
||||
0, //bit shift on channel GR2, assignment
|
||||
0x80, //dynamic range control
|
||||
0, 0 //resvd for copyright management
|
||||
};
|
||||
|
||||
class WriterPCM : public Writer
|
||||
{
|
||||
private:
|
||||
unsigned int SubFrameLen;
|
||||
unsigned int SubFramesPerPES;
|
||||
uint8_t lpcm_prv[14];
|
||||
uint8_t injectBuffer[2048];
|
||||
uint8_t breakBuffer[sizeof(injectBuffer)];
|
||||
unsigned int breakBufferFillSize;
|
||||
int uNoOfChannels;
|
||||
int uSampleRate;
|
||||
int uBitsPerSample;
|
||||
|
||||
SwrContext *swr;
|
||||
AVFrame *decoded_frame;
|
||||
int out_sample_rate;
|
||||
int out_channels;
|
||||
uint64_t out_channel_layout;
|
||||
bool initialHeader;
|
||||
bool restart_audio_resampling;
|
||||
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
bool prepareClipPlay();
|
||||
bool writePCM(int fd, int64_t Pts, uint8_t *data, unsigned int size);
|
||||
void Init();
|
||||
WriterPCM();
|
||||
};
|
||||
|
||||
bool WriterPCM::prepareClipPlay()
|
||||
{
|
||||
SubFrameLen = 0;
|
||||
SubFramesPerPES = 0;
|
||||
breakBufferFillSize = 0;
|
||||
|
||||
memcpy(lpcm_prv, clpcm_prv, sizeof(lpcm_prv));
|
||||
|
||||
// figure out size of subframe and set up sample rate
|
||||
switch (uSampleRate) {
|
||||
case 48000:
|
||||
SubFrameLen = 40;
|
||||
break;
|
||||
case 96000:
|
||||
lpcm_prv[8] |= 0x10;
|
||||
SubFrameLen = 80;
|
||||
break;
|
||||
case 192000:
|
||||
lpcm_prv[8] |= 0x20;
|
||||
SubFrameLen = 160;
|
||||
break;
|
||||
case 44100:
|
||||
lpcm_prv[8] |= 0x80;
|
||||
SubFrameLen = 40;
|
||||
break;
|
||||
case 88200:
|
||||
lpcm_prv[8] |= 0x90;
|
||||
SubFrameLen = 80;
|
||||
break;
|
||||
case 176400:
|
||||
lpcm_prv[8] |= 0xA0;
|
||||
SubFrameLen = 160;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SubFrameLen *= uNoOfChannels;
|
||||
SubFrameLen *= uBitsPerSample / 8;
|
||||
|
||||
//rewrite PES size to have as many complete subframes per PES as we can
|
||||
// FIXME: PES header size was hardcoded to 18 in earlier code. Actual size returned by InsertPesHeader is 14.
|
||||
SubFramesPerPES = ((sizeof(injectBuffer) - 18) - sizeof(lpcm_prv)) / SubFrameLen;
|
||||
SubFrameLen *= SubFramesPerPES;
|
||||
|
||||
//set number of channels
|
||||
lpcm_prv[10] = uNoOfChannels - 1;
|
||||
|
||||
switch (uBitsPerSample) {
|
||||
case 24:
|
||||
lpcm_prv[7] |= 0x20;
|
||||
case 16:
|
||||
break;
|
||||
default:
|
||||
printf("inappropriate bits per sample (%d) - must be 16 or 24\n", uBitsPerSample);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriterPCM::writePCM(int fd, int64_t Pts, uint8_t *data, unsigned int size)
|
||||
{
|
||||
bool res = true;
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
|
||||
if (initialHeader) {
|
||||
initialHeader = false;
|
||||
prepareClipPlay();
|
||||
ioctl(fd, AUDIO_CLEAR_BUFFER, NULL);
|
||||
}
|
||||
|
||||
size += breakBufferFillSize;
|
||||
|
||||
while (size >= SubFrameLen) {
|
||||
if (breakBufferFillSize)
|
||||
memcpy(injectBuffer, breakBuffer, breakBufferFillSize);
|
||||
memcpy(injectBuffer + breakBufferFillSize, data, SubFrameLen - breakBufferFillSize);
|
||||
size -= SubFrameLen;
|
||||
data += SubFrameLen - breakBufferFillSize;
|
||||
breakBufferFillSize = 0;
|
||||
|
||||
//write the PCM data
|
||||
if (uBitsPerSample == 16) {
|
||||
for (unsigned int n = 0; n < SubFrameLen; n += 2) {
|
||||
uint8_t tmp = injectBuffer[n];
|
||||
injectBuffer[n] = injectBuffer[n + 1];
|
||||
injectBuffer[n + 1] = tmp;
|
||||
}
|
||||
} else {
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11
|
||||
// A1c A1b A1a B1c B1b B1a A2c A2b A2a B2c B2b B2a
|
||||
// to A1a A1b B1a B1b A2a A2b B2a B2b A1c B1c A2c B2c
|
||||
for (unsigned int n = 0; n < SubFrameLen; n += 12) {
|
||||
uint8_t t, *p = injectBuffer + n;
|
||||
t = p[0];
|
||||
p[0] = p[2];
|
||||
p[2] = p[5];
|
||||
p[5] = p[7];
|
||||
p[7] = p[11];
|
||||
p[11] = p[9];
|
||||
p[9] = p[3];
|
||||
p[3] = p[4];
|
||||
p[4] = p[8];
|
||||
p[8] = t;
|
||||
}
|
||||
}
|
||||
|
||||
//increment err... subframe count?
|
||||
lpcm_prv[1] = ((lpcm_prv[1] + SubFramesPerPES) & 0x1F);
|
||||
|
||||
struct iovec iov[3];
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[1].iov_base = lpcm_prv;
|
||||
iov[1].iov_len = sizeof(lpcm_prv);
|
||||
iov[2].iov_base = injectBuffer;
|
||||
iov[2].iov_len = SubFrameLen;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len + iov[2].iov_len, PCM_PES_START_CODE, Pts, 0);
|
||||
int len = writev(fd, iov, 3);
|
||||
if (len < 0) {
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
breakBufferFillSize = size;
|
||||
memcpy(breakBuffer, data, size);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void WriterPCM::Init()
|
||||
{
|
||||
initialHeader = true;
|
||||
restart_audio_resampling = true;
|
||||
}
|
||||
|
||||
bool WriterPCM::Write(int fd, AVFormatContext * /*avfc*/, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
if (!packet) {
|
||||
restart_audio_resampling = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
AVCodecContext *c = stream->codec;
|
||||
unsigned int packet_size = packet->size;
|
||||
|
||||
if (restart_audio_resampling) {
|
||||
restart_audio_resampling = false;
|
||||
initialHeader = true;
|
||||
|
||||
if (swr)
|
||||
swr_free(&swr);
|
||||
if (decoded_frame)
|
||||
av_frame_free(&decoded_frame);
|
||||
|
||||
AVCodec *codec = avcodec_find_decoder(c->codec_id);
|
||||
|
||||
if (!codec || avcodec_open2(c, codec, NULL)) {
|
||||
fprintf(stderr, "%s %d: avcodec_open2 failed\n", __func__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!swr) {
|
||||
int rates[] = { 48000, 96000, 192000, 44100, 88200, 176400, 0 };
|
||||
int *rate = rates;
|
||||
int in_rate = c->sample_rate;
|
||||
while (*rate && ((*rate / in_rate) * in_rate != *rate) && (in_rate / *rate) * *rate != in_rate)
|
||||
rate++;
|
||||
out_sample_rate = *rate ? *rate : 44100;
|
||||
out_channels = c->channels;
|
||||
if (c->channel_layout == 0) {
|
||||
// FIXME -- need to guess, looks pretty much like a bug in the FFMPEG WMA decoder
|
||||
c->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
}
|
||||
|
||||
out_channel_layout = c->channel_layout;
|
||||
// player2 won't play mono
|
||||
if (out_channel_layout == AV_CH_LAYOUT_MONO) {
|
||||
out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||
out_channels = 2;
|
||||
}
|
||||
|
||||
uSampleRate = out_sample_rate;
|
||||
uNoOfChannels = av_get_channel_layout_nb_channels(out_channel_layout);
|
||||
uBitsPerSample = 16;
|
||||
|
||||
swr = swr_alloc();
|
||||
if (!swr) {
|
||||
fprintf(stderr, "%s %d: swr_alloc failed\n", __func__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
av_opt_set_int(swr, "in_channel_layout", c->channel_layout, 0);
|
||||
av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
|
||||
av_opt_set_int(swr, "in_sample_rate", c->sample_rate, 0);
|
||||
av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr, "in_sample_fmt", c->sample_fmt, 0);
|
||||
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
||||
|
||||
int e = swr_init(swr);
|
||||
if (e < 0) {
|
||||
fprintf(stderr, "swr_init: %d (icl=%d ocl=%d isr=%d osr=%d isf=%d osf=%d\n",
|
||||
-e, (int) c->channel_layout,
|
||||
(int) out_channel_layout, c->sample_rate, out_sample_rate, c->sample_fmt, AV_SAMPLE_FMT_S16);
|
||||
swr_free(&swr);
|
||||
restart_audio_resampling = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (packet_size > 0) {
|
||||
int got_frame = 0;
|
||||
if (decoded_frame)
|
||||
av_frame_unref(decoded_frame);
|
||||
else if (!(decoded_frame = av_frame_alloc())) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, packet);
|
||||
if (len < 0) {
|
||||
restart_audio_resampling = true;
|
||||
break;
|
||||
}
|
||||
|
||||
packet_size -= len;
|
||||
|
||||
if (!got_frame)
|
||||
continue;
|
||||
|
||||
int in_samples = decoded_frame->nb_samples;
|
||||
int out_samples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + in_samples, out_sample_rate, c->sample_rate, AV_ROUND_UP);
|
||||
|
||||
uint8_t *output = NULL;
|
||||
int e = av_samples_alloc(&output, NULL, out_channels, out_samples, AV_SAMPLE_FMT_S16, 1);
|
||||
if (e < 0) {
|
||||
fprintf(stderr, "av_samples_alloc: %d\n", -e);
|
||||
break;
|
||||
}
|
||||
out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **) &decoded_frame->data[0], in_samples);
|
||||
|
||||
if(!writePCM(fd, pts, output, out_samples * sizeof(short) * out_channels)) {
|
||||
restart_audio_resampling = true;
|
||||
av_free(output);
|
||||
break;
|
||||
}
|
||||
|
||||
av_free(output);
|
||||
pts = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WriterPCM::WriterPCM()
|
||||
{
|
||||
swr = NULL;
|
||||
decoded_frame = NULL;
|
||||
|
||||
Register(this, AV_CODEC_ID_INJECTPCM, AUDIO_ENCODING_LPCMA);
|
||||
Init();
|
||||
}
|
||||
|
||||
static WriterPCM writer_pcm __attribute__ ((init_priority (300)));
|
116
libeplayer3/writer/pes.cpp
Normal file
116
libeplayer3/writer/pes.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling.
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <memory.h>
|
||||
#include <asm/types.h>
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
|
||||
int InsertVideoPrivateDataHeader(uint8_t *data, int payload_size)
|
||||
{
|
||||
BitPacker_t ld2 = { data, 0, 32 };
|
||||
int i;
|
||||
|
||||
PutBits(&ld2, PES_PRIVATE_DATA_FLAG, 8);
|
||||
PutBits(&ld2, payload_size & 0xff, 8);
|
||||
PutBits(&ld2, (payload_size >> 8) & 0xff, 8);
|
||||
PutBits(&ld2, (payload_size >> 16) & 0xff, 8);
|
||||
|
||||
for (i = 4; i < (PES_PRIVATE_DATA_LENGTH + 1); i++)
|
||||
PutBits(&ld2, 0, 8);
|
||||
|
||||
FlushBits(&ld2);
|
||||
|
||||
return PES_PRIVATE_DATA_LENGTH + 1;
|
||||
|
||||
}
|
||||
|
||||
int InsertPesHeader(uint8_t *data, int size, uint8_t stream_id, int64_t pts, int pic_start_code)
|
||||
{
|
||||
BitPacker_t ld2 = { data, 0, 32 };
|
||||
|
||||
if (size > MAX_PES_PACKET_SIZE)
|
||||
size = 0; // unbounded
|
||||
|
||||
PutBits(&ld2, 0x0, 8);
|
||||
PutBits(&ld2, 0x0, 8);
|
||||
PutBits(&ld2, 0x1, 8); // Start Code
|
||||
PutBits(&ld2, stream_id, 8); // Stream_id = Audio Stream
|
||||
//4
|
||||
PutBits(&ld2, size + 3 + (pts != INVALID_PTS_VALUE ? 5 : 0) + (pic_start_code ? (5) : 0), 16); // PES_packet_length
|
||||
//6 = 4+2
|
||||
PutBits(&ld2, 0x2, 2); // 10
|
||||
PutBits(&ld2, 0x0, 2); // PES_Scrambling_control
|
||||
PutBits(&ld2, 0x0, 1); // PES_Priority
|
||||
PutBits(&ld2, 0x0, 1); // data_alignment_indicator
|
||||
PutBits(&ld2, 0x0, 1); // Copyright
|
||||
PutBits(&ld2, 0x0, 1); // Original or Copy
|
||||
//7 = 6+1
|
||||
|
||||
if (pts != INVALID_PTS_VALUE)
|
||||
PutBits(&ld2, 0x2, 2);
|
||||
else
|
||||
PutBits(&ld2, 0x0, 2); // PTS_DTS flag
|
||||
|
||||
PutBits(&ld2, 0x0, 1); // ESCR_flag
|
||||
PutBits(&ld2, 0x0, 1); // ES_rate_flag
|
||||
PutBits(&ld2, 0x0, 1); // DSM_trick_mode_flag
|
||||
PutBits(&ld2, 0x0, 1); // additional_copy_ingo_flag
|
||||
PutBits(&ld2, 0x0, 1); // PES_CRC_flag
|
||||
PutBits(&ld2, 0x0, 1); // PES_extension_flag
|
||||
//8 = 7+1
|
||||
|
||||
if (pts != INVALID_PTS_VALUE)
|
||||
PutBits(&ld2, 0x5, 8);
|
||||
else
|
||||
PutBits(&ld2, 0x0, 8); // PES_header_data_length
|
||||
//9 = 8+1
|
||||
|
||||
if (pts != INVALID_PTS_VALUE) {
|
||||
PutBits(&ld2, 0x2, 4);
|
||||
PutBits(&ld2, (pts >> 30) & 0x7, 3);
|
||||
PutBits(&ld2, 0x1, 1);
|
||||
PutBits(&ld2, (pts >> 15) & 0x7fff, 15);
|
||||
PutBits(&ld2, 0x1, 1);
|
||||
PutBits(&ld2, pts & 0x7fff, 15);
|
||||
PutBits(&ld2, 0x1, 1);
|
||||
}
|
||||
//14 = 9+5
|
||||
|
||||
if (pic_start_code) {
|
||||
PutBits(&ld2, 0x0, 8);
|
||||
PutBits(&ld2, 0x0, 8);
|
||||
PutBits(&ld2, 0x1, 8); // Start Code
|
||||
PutBits(&ld2, pic_start_code & 0xff, 8); // 00, for picture start
|
||||
PutBits(&ld2, (pic_start_code >> 8) & 0xff, 8); // For any extra information (like in mpeg4p2, the pic_start_code)
|
||||
//14 + 4 = 18
|
||||
}
|
||||
|
||||
FlushBits(&ld2);
|
||||
|
||||
return (ld2.Ptr - data);
|
||||
}
|
187
libeplayer3/writer/vc1.cpp
Normal file
187
libeplayer3/writer/vc1.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
#define WMV3_PRIVATE_DATA_LENGTH 4
|
||||
|
||||
#define METADATA_STRUCT_A_START 12
|
||||
#define METADATA_STRUCT_B_START 24
|
||||
#define METADATA_STRUCT_B_FRAMERATE_START 32
|
||||
#define METADATA_STRUCT_C_START 8
|
||||
|
||||
|
||||
#define VC1_SEQUENCE_LAYER_METADATA_START_CODE 0x80
|
||||
#define VC1_FRAME_START_CODE 0x0d
|
||||
|
||||
class WriterVC1 : public Writer
|
||||
{
|
||||
private:
|
||||
bool initialHeader;
|
||||
uint8_t FrameHeaderSeen;
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
void Init();
|
||||
WriterVC1();
|
||||
};
|
||||
|
||||
void WriterVC1::Init()
|
||||
{
|
||||
initialHeader = true;
|
||||
}
|
||||
|
||||
bool WriterVC1::Write(int fd, AVFormatContext * /* avfc */, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
if (initialHeader) {
|
||||
initialHeader = false;
|
||||
FrameHeaderSeen = false;
|
||||
|
||||
const uint8_t SequenceLayerStartCode[] =
|
||||
{ 0x00, 0x00, 0x01, VC1_SEQUENCE_LAYER_METADATA_START_CODE };
|
||||
|
||||
|
||||
const uint8_t Metadata[] = {
|
||||
0x00, 0x00, 0x00, 0xc5,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile */
|
||||
0x00, 0x00, 0x00, 0x00, /* Struct A */
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00,
|
||||
0x60, 0x00, 0x00, 0x00, /* Struct B */
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
uint8_t PesPayload[128];
|
||||
uint8_t *PesPtr;
|
||||
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
|
||||
struct iovec iov[2];
|
||||
|
||||
|
||||
memset(PesPayload, 0, sizeof(PesPayload));
|
||||
|
||||
PesPtr = PesPayload;
|
||||
|
||||
memcpy(PesPtr, SequenceLayerStartCode, sizeof(SequenceLayerStartCode));
|
||||
PesPtr += sizeof(SequenceLayerStartCode);
|
||||
|
||||
memcpy(PesPtr, Metadata, sizeof(Metadata));
|
||||
PesPtr += METADATA_STRUCT_C_START;
|
||||
PesPtr += WMV3_PRIVATE_DATA_LENGTH;
|
||||
|
||||
/* Metadata Header Struct A */
|
||||
*PesPtr++ = (stream->codec->height >> 0) & 0xff;
|
||||
*PesPtr++ = (stream->codec->height >> 8) & 0xff;
|
||||
*PesPtr++ = (stream->codec->height >> 16) & 0xff;
|
||||
*PesPtr++ = stream->codec->height >> 24;
|
||||
*PesPtr++ = (stream->codec->width >> 0) & 0xff;
|
||||
*PesPtr++ = (stream->codec->width >> 8) & 0xff;
|
||||
*PesPtr++ = (stream->codec->width >> 16) & 0xff;
|
||||
*PesPtr++ = stream->codec->width >> 24;
|
||||
|
||||
PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */
|
||||
|
||||
*PesPtr++ = (usecPerFrame >> 0) & 0xff;
|
||||
*PesPtr++ = (usecPerFrame >> 8) & 0xff;
|
||||
*PesPtr++ = (usecPerFrame >> 16) & 0xff;
|
||||
*PesPtr++ = usecPerFrame >> 24;
|
||||
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[1].iov_base = PesPayload;
|
||||
iov[1].iov_len = PesPtr - PesPayload;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||
if (writev(fd, iov, 2) < 0)
|
||||
return false;
|
||||
|
||||
/* For VC1 the codec private data is a standard vc1 sequence header so we just copy it to the output */
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[1].iov_base = stream->codec->extradata;
|
||||
iov[1].iov_len = stream->codec->extradata_size;
|
||||
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||
if (writev(fd, iov, 2) < 0)
|
||||
return false;
|
||||
|
||||
initialHeader = false;
|
||||
}
|
||||
|
||||
if (packet->size > 0) {
|
||||
int Position = 0;
|
||||
bool insertSampleHeader = true;
|
||||
|
||||
while (Position < packet->size) {
|
||||
int PacketLength = std::min(packet->size - Position, MAX_PES_PACKET_SIZE);
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||
int HeaderLength = InsertPesHeader(PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, pts, 0);
|
||||
|
||||
if (insertSampleHeader) {
|
||||
const uint8_t Vc1FrameStartCode[] = { 0, 0, 1, VC1_FRAME_START_CODE };
|
||||
|
||||
if (!FrameHeaderSeen && (packet->size > 3) && (memcmp(packet->data, Vc1FrameStartCode, 4) == 0))
|
||||
FrameHeaderSeen = true;
|
||||
if (!FrameHeaderSeen) {
|
||||
memcpy(&PesHeader[HeaderLength], Vc1FrameStartCode, sizeof(Vc1FrameStartCode));
|
||||
HeaderLength += sizeof(Vc1FrameStartCode);
|
||||
}
|
||||
insertSampleHeader = false;
|
||||
}
|
||||
|
||||
struct iovec iov[2];
|
||||
iov[0].iov_base = PesHeader;
|
||||
iov[0].iov_len = HeaderLength;
|
||||
iov[1].iov_base = packet->data + Position;
|
||||
iov[1].iov_len = PacketLength;
|
||||
|
||||
ssize_t l = writev(fd, iov, 2);
|
||||
if (l < 0)
|
||||
return false;
|
||||
|
||||
Position += PacketLength;
|
||||
pts = INVALID_PTS_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WriterVC1::WriterVC1()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_VC1, VIDEO_ENCODING_VC1);
|
||||
Init();
|
||||
}
|
||||
|
||||
static WriterVC1 writer_vc1 __attribute__ ((init_priority (300)));
|
170
libeplayer3/writer/wmv.cpp
Normal file
170
libeplayer3/writer/wmv.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
||||
* Copyright (C) 2014 martii (based on code from libeplayer3)
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define WMV3_PRIVATE_DATA_LENGTH 4
|
||||
|
||||
static const uint8_t Metadata[] = {
|
||||
0x00, 0x00, 0x00, 0xc5,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
#define METADATA_STRUCT_C_START 8
|
||||
0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile */
|
||||
#define METADATA_STRUCT_A_START 12
|
||||
0x00, 0x00, 0x00, 0x00, /* Struct A */
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00,
|
||||
#define METADATA_STRUCT_B_START 24
|
||||
0x60, 0x00, 0x00, 0x00, /* Struct B */
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
#define METADATA_STRUCT_B_FRAMERATE_START 32
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
class WriterWMV : public Writer
|
||||
{
|
||||
private:
|
||||
bool initialHeader;
|
||||
public:
|
||||
bool Write(int fd, AVFormatContext *avfc, AVStream *stream, AVPacket *packet, int64_t pts);
|
||||
void Init();
|
||||
WriterWMV();
|
||||
};
|
||||
|
||||
void WriterWMV::Init()
|
||||
{
|
||||
initialHeader = true;
|
||||
}
|
||||
|
||||
bool WriterWMV::Write(int fd, AVFormatContext * /* avfc */, AVStream *stream, AVPacket *packet, int64_t pts)
|
||||
{
|
||||
if (fd < 0 || !packet)
|
||||
return false;
|
||||
|
||||
if (initialHeader) {
|
||||
#define PES_MIN_HEADER_SIZE 9
|
||||
uint8_t PesPacket[PES_MIN_HEADER_SIZE + 128];
|
||||
uint8_t *PesPtr;
|
||||
unsigned int MetadataLength;
|
||||
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
|
||||
|
||||
PesPtr = &PesPacket[PES_MIN_HEADER_SIZE];
|
||||
|
||||
memcpy(PesPtr, Metadata, sizeof(Metadata));
|
||||
PesPtr += METADATA_STRUCT_C_START;
|
||||
|
||||
uint8_t privateData[WMV3_PRIVATE_DATA_LENGTH] = { 0 };
|
||||
memcpy(privateData, stream->codec->extradata, stream->codec->extradata_size > WMV3_PRIVATE_DATA_LENGTH ? WMV3_PRIVATE_DATA_LENGTH : stream->codec->extradata_size);
|
||||
|
||||
memcpy(PesPtr, privateData, WMV3_PRIVATE_DATA_LENGTH);
|
||||
PesPtr += WMV3_PRIVATE_DATA_LENGTH;
|
||||
|
||||
/* Metadata Header Struct A */
|
||||
*PesPtr++ = (stream->codec->height >> 0) & 0xff;
|
||||
*PesPtr++ = (stream->codec->height >> 8) & 0xff;
|
||||
*PesPtr++ = (stream->codec->height >> 16) & 0xff;
|
||||
*PesPtr++ = stream->codec->height >> 24;
|
||||
*PesPtr++ = (stream->codec->width >> 0) & 0xff;
|
||||
*PesPtr++ = (stream->codec->width >> 8) & 0xff;
|
||||
*PesPtr++ = (stream->codec->width >> 16) & 0xff;
|
||||
*PesPtr++ = stream->codec->width >> 24;
|
||||
|
||||
PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */
|
||||
|
||||
*PesPtr++ = (usecPerFrame >> 0) & 0xff;
|
||||
*PesPtr++ = (usecPerFrame >> 8) & 0xff;
|
||||
*PesPtr++ = (usecPerFrame >> 16) & 0xff;
|
||||
*PesPtr++ = usecPerFrame >> 24;
|
||||
|
||||
MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE];
|
||||
|
||||
int HeaderLength = InsertPesHeader(PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||
|
||||
if (write(fd, PesPacket, HeaderLength + MetadataLength) < 0)
|
||||
return false;
|
||||
|
||||
initialHeader = false;
|
||||
}
|
||||
|
||||
if (packet->size > 0 && packet->data) {
|
||||
int Position = 0;
|
||||
bool insertSampleHeader = true;
|
||||
|
||||
while (Position < packet->size) {
|
||||
|
||||
int PacketLength = std::min(packet->size - Position, MAX_PES_PACKET_SIZE);
|
||||
|
||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE] = { 0 };
|
||||
int HeaderLength = InsertPesHeader(PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, pts, 0);
|
||||
|
||||
if (insertSampleHeader) {
|
||||
unsigned int PesLength;
|
||||
unsigned int PrivateHeaderLength;
|
||||
|
||||
PrivateHeaderLength = InsertVideoPrivateDataHeader(&PesHeader[HeaderLength], packet->size);
|
||||
/* Update PesLength */
|
||||
PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength;
|
||||
PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff;
|
||||
PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff;
|
||||
PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength;
|
||||
PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT;
|
||||
|
||||
HeaderLength += PrivateHeaderLength;
|
||||
insertSampleHeader = false;
|
||||
}
|
||||
|
||||
uint8_t PacketStart[packet->size + HeaderLength];
|
||||
memcpy(PacketStart, PesHeader, HeaderLength);
|
||||
memcpy(PacketStart + HeaderLength, packet->data + Position, PacketLength);
|
||||
if (write(fd, PacketStart, PacketLength + HeaderLength) < 0)
|
||||
return false;
|
||||
|
||||
Position += PacketLength;
|
||||
pts = INVALID_PTS_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WriterWMV::WriterWMV()
|
||||
{
|
||||
Register(this, AV_CODEC_ID_WMV1, VIDEO_ENCODING_WMV);
|
||||
Register(this, AV_CODEC_ID_WMV2, VIDEO_ENCODING_WMV);
|
||||
Register(this, AV_CODEC_ID_WMV3, VIDEO_ENCODING_WMV);
|
||||
Init();
|
||||
}
|
||||
|
||||
static WriterWMV writer_wmv __attribute__ ((init_priority (300)));
|
103
libeplayer3/writer/writer.cpp
Normal file
103
libeplayer3/writer/writer.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* linuxdvb output/writer handling
|
||||
*
|
||||
* Copyright (C) 2014 martii
|
||||
*
|
||||
* 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; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "pes.h"
|
||||
#include "writer.h"
|
||||
|
||||
// This does suck ... the original idea was to just link the object files and let them register themselves.
|
||||
// Alas, that didn't work as expected.
|
||||
#include "ac3.cpp"
|
||||
#include "divx.cpp"
|
||||
#include "dts.cpp"
|
||||
#include "flac.cpp"
|
||||
#include "h263.cpp"
|
||||
#include "h264.cpp"
|
||||
#include "mp3.cpp"
|
||||
#include "mpeg2.cpp"
|
||||
#include "pcm.cpp"
|
||||
#include "vc1.cpp"
|
||||
#include "wmv.cpp"
|
||||
|
||||
static std::map<enum AVCodecID,Writer *>writers __attribute__ ((init_priority (200)));
|
||||
static std::map<enum AVCodecID,video_encoding_t>vencoding __attribute__ ((init_priority (200)));
|
||||
static std::map<enum AVCodecID,audio_encoding_t>aencoding __attribute__ ((init_priority (200)));
|
||||
|
||||
void Writer::Register(Writer *w, enum AVCodecID id, video_encoding_t encoding)
|
||||
{
|
||||
writers[id] = w;
|
||||
vencoding[id] = encoding;
|
||||
}
|
||||
|
||||
void Writer::Register(Writer *w, enum AVCodecID id, audio_encoding_t encoding)
|
||||
{
|
||||
writers[id] = w;
|
||||
aencoding[id] = encoding;
|
||||
}
|
||||
|
||||
bool Writer::Write(int /* fd */, AVFormatContext * /* avfc */, AVStream * /*stream*/, AVPacket * /* packet */, int64_t /* pts */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static Writer writer __attribute__ ((init_priority (300)));
|
||||
|
||||
Writer *Writer::GetWriter(enum AVCodecID id, enum AVMediaType codec_type)
|
||||
{
|
||||
std::map<enum AVCodecID,Writer*>::iterator it = writers.find(id);
|
||||
if (it != writers.end())
|
||||
return it->second;
|
||||
switch (codec_type) {
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
if (id == AV_CODEC_ID_INJECTPCM) // should not happen
|
||||
break;
|
||||
return GetWriter(AV_CODEC_ID_INJECTPCM, codec_type);
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
if (id == AV_CODEC_ID_MPEG2TS) // should not happen
|
||||
break;
|
||||
return GetWriter(AV_CODEC_ID_MPEG2TS, codec_type);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return &writer;
|
||||
}
|
||||
|
||||
video_encoding_t Writer::GetVideoEncoding(enum AVCodecID id)
|
||||
{
|
||||
std::map<enum AVCodecID,video_encoding_t>::iterator it = vencoding.find(id);
|
||||
if (it != vencoding.end())
|
||||
return it->second;
|
||||
return VIDEO_ENCODING_AUTO;
|
||||
}
|
||||
|
||||
audio_encoding_t Writer::GetAudioEncoding(enum AVCodecID id)
|
||||
{
|
||||
std::map<enum AVCodecID,audio_encoding_t>::iterator it = aencoding.find(id);
|
||||
if (it != aencoding.end())
|
||||
return it->second;
|
||||
return AUDIO_ENCODING_LPCMA;
|
||||
}
|
27
libspark/Makefile.am
Normal file
27
libspark/Makefile.am
Normal file
@@ -0,0 +1,27 @@
|
||||
noinst_LTLIBRARIES = libspark.la
|
||||
|
||||
AM_CPPFLAGS = -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
|
||||
AM_CPPFLAGS += \
|
||||
-I$(top_srcdir)/common \
|
||||
-I$(top_srcdir)/libeplayer3/include
|
||||
|
||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
||||
|
||||
AM_LDFLAGS = -lpthread -lasound -lass -lrt\
|
||||
@AVFORMAT_LIBS@ \
|
||||
@AVUTIL_LIBS@ \
|
||||
@AVCODEC_LIBS@ \
|
||||
@SWRESAMPLE_LIBS@
|
||||
|
||||
libspark_la_SOURCES = \
|
||||
hardware_caps.c \
|
||||
dmx.cpp \
|
||||
video.cpp \
|
||||
audio.cpp \
|
||||
audio_mixer.cpp \
|
||||
init.cpp \
|
||||
playback_libeplayer3.cpp \
|
||||
pwrmngr.cpp \
|
||||
record.cpp
|
||||
|
||||
#AM_CPPFLAGS = -DF_INTERRUPTS=20000 -DIRMP_EMBED -DLIRC_IRMP
|
72
libspark/README.libtriple
Normal file
72
libspark/README.libtriple
Normal file
@@ -0,0 +1,72 @@
|
||||
libtriple reimplements the interfaces of the libcoolstrem library for
|
||||
the Tripledragon receiver.
|
||||
|
||||
There are a few debugging or configuration helpers which affect the
|
||||
way libtriple does some things. They are all configured by exporting
|
||||
environment variables, which are described here:
|
||||
|
||||
TRIPLE_NOSCART=1 - makes neutrino *not* do any voltage switching on
|
||||
SCART pin 8, probably not useful for anyone but me
|
||||
|
||||
TRIPLE_DEBUG=... - controls various debugging levels in libtriple
|
||||
valid values for the different component:
|
||||
audio 0x01
|
||||
video 0x02
|
||||
demux 0x04
|
||||
play 0x08
|
||||
power 0x10
|
||||
init 0x20
|
||||
ca 0x40
|
||||
record 0x80
|
||||
all 0xff
|
||||
multiple levels are added / ORed together, so if you want to
|
||||
debug record and playback code, do "export TRIPLE_DEBUG=0x88"
|
||||
for audio & video use TRIPLE_DEBUG=0x3
|
||||
|
||||
DSP_DEVICE
|
||||
MIX_DEVICE - alternative audio devices for the audioplayer and internet
|
||||
radio. Those are used to output music to e.g. USB audio devices.
|
||||
Here is what you need to do:
|
||||
* look in /dev/sound which devices are already there. Probably
|
||||
/dev/sound/dsp and /dev/sound/mixer, created by the tdoss driver
|
||||
* make sure that the USB HC driver is loaded:
|
||||
modprobe ohci-hcd
|
||||
* load the USB audio driver:
|
||||
modprobe audio
|
||||
* plug in your USB audio device, check with "dmesg" that it is
|
||||
recognised by the kernel
|
||||
* look in /dev/sound which new devices are there. Probably it's
|
||||
/dev/sound/dsp1 and /dev/sound/mixer1. If there are more - well
|
||||
it's time to experiment ;)
|
||||
* export DSP_DEVICE=/dev/sound/dsp1 and MIX_DEVICE=/dev/sound/mixer1
|
||||
(of course the devices you found in the last step)
|
||||
* from the same shell you exported the variables, start neutrino
|
||||
(make sure that an already running neutrino is stopped before you
|
||||
do that)
|
||||
* start the audioplayer, play a track. Look for log lines like
|
||||
[LT:106b5788:audio ] PrepareClipPlay: dsp_dev /dev/sound/dsp1 mix_dev /dev/sound/mixer1
|
||||
* if it works - fine :-)
|
||||
* if it does not work, look for:
|
||||
PrepareClipPlay: DSP_DEVICE is set (/dev/sound/dsp1) but cannot be opened, fall back to /dev/sound/dsp
|
||||
PrepareClipPlay: dsp_dev /dev/sound/dsp mix_dev /dev/sound/mixer1
|
||||
PrepareClipPlay: open mixer /dev/sound/mixer1 failed (No such file or directory)
|
||||
* this basically means that the device is not there. Different errors
|
||||
will get different messages - I cannot trigger those now, so you'll
|
||||
need to find them out by yourself ;)
|
||||
* another possible messag you may get is:
|
||||
PrepareClipPlay: more than one mixer control: devmask 00000021 stereo 00000021
|
||||
This means that your device has more than one mixer. The set bit
|
||||
numbers in the devmask are the different mixers, in this case
|
||||
it would be number 0 and 5. To select one of those, export
|
||||
MIX_NUMBER=5 or MIX_NUMBER=0 (this code is untested, there may
|
||||
be bugs)
|
||||
|
||||
So now I found out what devices to use, but how do I make that permanent?
|
||||
That's easy:
|
||||
* create or extend /etc/rcS.local with
|
||||
modprobe ohci-hcd
|
||||
modprobe audio
|
||||
* create or extend /etc/profile.local with
|
||||
export DSP_DEVICE=/dev/sound/dsp1
|
||||
export MIX_DEVICE=/dev/sound/mixer1
|
||||
* reboot. Enjoy.
|
416
libspark/audio.cpp
Normal file
416
libspark/audio.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/dvb/audio.h>
|
||||
#include "audio_lib.h"
|
||||
#include "lt_debug.h"
|
||||
|
||||
#define AUDIO_DEVICE "/dev/dvb/adapter0/audio0"
|
||||
#define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args)
|
||||
#define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args)
|
||||
|
||||
#include <linux/soundcard.h>
|
||||
|
||||
cAudio * audioDecoder = NULL;
|
||||
|
||||
static int proc_put(const char *path, const char *value, const int len)
|
||||
{
|
||||
int ret, ret2;
|
||||
int pfd = open(path, O_WRONLY);
|
||||
if (pfd < 0)
|
||||
return pfd;
|
||||
ret = write(pfd, value, len);
|
||||
ret2 = close(pfd);
|
||||
if (ret2 < 0)
|
||||
return ret2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
cAudio::cAudio(void *, void *, void *)
|
||||
{
|
||||
fd = -1;
|
||||
clipfd = -1;
|
||||
mixer_fd = -1;
|
||||
openDevice();
|
||||
Muted = false;
|
||||
}
|
||||
|
||||
cAudio::~cAudio(void)
|
||||
{
|
||||
closeDevice();
|
||||
}
|
||||
|
||||
void cAudio::openDevice(void)
|
||||
{
|
||||
if (fd < 0)
|
||||
{
|
||||
if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0)
|
||||
lt_info("openDevice: open failed (%m)\n");
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
do_mute(true, false);
|
||||
}
|
||||
else
|
||||
lt_info("openDevice: already open (fd = %d)\n", fd);
|
||||
}
|
||||
|
||||
void cAudio::closeDevice(void)
|
||||
{
|
||||
if (fd > -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
if (clipfd > -1) {
|
||||
close(clipfd);
|
||||
clipfd = -1;
|
||||
}
|
||||
if (mixer_fd > -1) {
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int cAudio::do_mute(bool enable, bool remember)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember);
|
||||
char str[4];
|
||||
|
||||
if (remember)
|
||||
Muted = enable;
|
||||
|
||||
sprintf(str, "%d", Muted);
|
||||
proc_put("/proc/stb/audio/j1_mute", str, strlen(str));
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
int f = open("/proc/stb/avs/0/volume", O_RDWR);
|
||||
read(f, str, 4);
|
||||
close(f);
|
||||
str[3] = '\0';
|
||||
proc_put("/proc/stb/avs/0/volume", str, strlen(str));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int map_volume(const int volume)
|
||||
{
|
||||
unsigned char vol = volume;
|
||||
if (vol > 100)
|
||||
vol = 100;
|
||||
|
||||
vol = 63 - vol * 63 / 100;
|
||||
return vol;
|
||||
}
|
||||
|
||||
|
||||
int cAudio::setVolume(unsigned int left, unsigned int right)
|
||||
{
|
||||
lt_debug("%s(%d, %d)\n", __func__, left, right);
|
||||
|
||||
volume = (left + right) / 2;
|
||||
int v = map_volume(volume);
|
||||
#if 0
|
||||
if (clipfd != -1 && mixer_fd != -1) {
|
||||
int tmp = 0;
|
||||
/* not sure if left / right is correct here, but it is always the same anyways ;-) */
|
||||
if (! Muted)
|
||||
tmp = left << 8 | right;
|
||||
int ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp);
|
||||
if (ret == -1)
|
||||
lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
char str[4];
|
||||
sprintf(str, "%d", v);
|
||||
|
||||
proc_put("/proc/stb/avs/0/volume", str, strlen(str));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::Start(void)
|
||||
{
|
||||
int ret;
|
||||
ret = ioctl(fd, AUDIO_PLAY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cAudio::Stop(void)
|
||||
{
|
||||
return ioctl(fd, AUDIO_STOP);
|
||||
}
|
||||
|
||||
bool cAudio::Pause(bool Pcm)
|
||||
{
|
||||
ioctl(fd, Pcm ? AUDIO_PAUSE : AUDIO_CONTINUE, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cAudio::SetSyncMode(AVSYNC_TYPE Mode)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, Mode);
|
||||
ioctl(fd, AUDIO_SET_AV_SYNC, Mode);
|
||||
}
|
||||
|
||||
// E2 streamtype values. These correspond to
|
||||
// player2/linux/drivers/media/dvb/stm/dvb/dvb_audio.c:AudioIoctlSetBypassMode
|
||||
#define AUDIO_STREAMTYPE_AC3 0
|
||||
#define AUDIO_STREAMTYPE_MPEG 1
|
||||
#define AUDIO_STREAMTYPE_DTS 2
|
||||
#define AUDIO_STREAMTYPE_AAC 8
|
||||
#define AUDIO_STREAMTYPE_AACHE 9
|
||||
|
||||
void cAudio::SetStreamType(AUDIO_FORMAT type)
|
||||
{
|
||||
int bypass = AUDIO_STREAMTYPE_MPEG;
|
||||
lt_debug("%s %d\n", __FUNCTION__, type);
|
||||
StreamType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AUDIO_FMT_DD_PLUS:
|
||||
case AUDIO_FMT_DOLBY_DIGITAL:
|
||||
bypass = AUDIO_STREAMTYPE_AC3;
|
||||
break;
|
||||
case AUDIO_FMT_AAC:
|
||||
bypass = AUDIO_STREAMTYPE_AAC;
|
||||
break;
|
||||
case AUDIO_FMT_AAC_PLUS:
|
||||
bypass = AUDIO_STREAMTYPE_AACHE;
|
||||
break;
|
||||
case AUDIO_FMT_DTS:
|
||||
bypass = AUDIO_STREAMTYPE_DTS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Normaly the encoding should be set using AUDIO_SET_ENCODING
|
||||
// But as we implemented the behavior to bypass (cause of e2) this is correct here
|
||||
if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
|
||||
lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__);
|
||||
}
|
||||
|
||||
int cAudio::setChannel(int channel)
|
||||
{
|
||||
lt_debug("%s %d\n", __FUNCTION__, channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian)
|
||||
{
|
||||
int fmt;
|
||||
unsigned int devmask, stereo, usable;
|
||||
const char *dsp_dev = getenv("DSP_DEVICE");
|
||||
const char *mix_dev = getenv("MIX_DEVICE");
|
||||
lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian);
|
||||
if (clipfd > -1) {
|
||||
lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd);
|
||||
return -1;
|
||||
}
|
||||
mixer_num = -1;
|
||||
/* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE
|
||||
* if this device cannot be opened, we fall back to the internal OSS device
|
||||
* Example:
|
||||
* modprobe snd-usb-audio
|
||||
* export DSP_DEVICE=/dev/sound/dsp2
|
||||
* export MIX_DEVICE=/dev/sound/mixer2
|
||||
* neutrino
|
||||
*/
|
||||
if ((!dsp_dev) || (access(dsp_dev, W_OK))) {
|
||||
if (dsp_dev)
|
||||
lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened,"
|
||||
" fall back to /dev/dsp1\n", __func__, dsp_dev);
|
||||
dsp_dev = "/dev/dsp1";
|
||||
}
|
||||
lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */
|
||||
/* the tdoss dsp driver seems to work only on the second open(). really. */
|
||||
clipfd = open(dsp_dev, O_WRONLY);
|
||||
if (clipfd < 0) {
|
||||
lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
fcntl(clipfd, F_SETFD, FD_CLOEXEC);
|
||||
/* no idea if we ever get little_endian == 0 */
|
||||
if (little_endian)
|
||||
fmt = AFMT_S16_BE;
|
||||
else
|
||||
fmt = AFMT_S16_LE;
|
||||
if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt))
|
||||
perror("SNDCTL_DSP_SETFMT");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch))
|
||||
perror("SNDCTL_DSP_CHANNELS");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate))
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
if (ioctl(clipfd, SNDCTL_DSP_RESET))
|
||||
perror("SNDCTL_DSP_RESET");
|
||||
|
||||
if (!mix_dev)
|
||||
return 0;
|
||||
|
||||
mixer_fd = open(mix_dev, O_RDWR);
|
||||
if (mixer_fd < 0) {
|
||||
lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev);
|
||||
/* not a real error */
|
||||
return 0;
|
||||
}
|
||||
if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
|
||||
lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__);
|
||||
devmask = 0;
|
||||
}
|
||||
if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) {
|
||||
lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__);
|
||||
stereo = 0;
|
||||
}
|
||||
usable = devmask & stereo;
|
||||
if (usable == 0) {
|
||||
lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n",
|
||||
__func__, devmask, stereo);
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
return 0; /* TODO: should we treat this as error? */
|
||||
}
|
||||
/* __builtin_popcount needs GCC, it counts the set bits... */
|
||||
if (__builtin_popcount (usable) != 1) {
|
||||
/* TODO: this code is not yet tested as I have only single-mixer devices... */
|
||||
lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n"
|
||||
"%s: querying MIX_NUMBER environment variable...\n",
|
||||
__func__, devmask, stereo, __func__);
|
||||
const char *tmp = getenv("MIX_NUMBER");
|
||||
if (tmp)
|
||||
mixer_num = atoi(tmp);
|
||||
lt_info("%s: mixer_num is %d -> device %08x\n",
|
||||
__func__, mixer_num, (mixer_num >= 0) ? (1 << mixer_num) : 0);
|
||||
/* no error checking, you'd better know what you are doing... */
|
||||
} else {
|
||||
mixer_num = 0;
|
||||
while (!(usable & 0x01)) {
|
||||
mixer_num++;
|
||||
usable >>= 1;
|
||||
}
|
||||
}
|
||||
setVolume(volume, volume);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cAudio::WriteClip(unsigned char *buffer, int size)
|
||||
{
|
||||
int ret;
|
||||
// lt_debug("cAudio::%s\n", __FUNCTION__);
|
||||
if (clipfd < 0) {
|
||||
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
ret = write(clipfd, buffer, size);
|
||||
if (ret < 0)
|
||||
lt_info("%s: write error (%m)\n", __FUNCTION__);
|
||||
return ret;
|
||||
};
|
||||
|
||||
int cAudio::StopClip()
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
if (clipfd < 0) {
|
||||
lt_info("%s: clipfd not yet opened\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
close(clipfd);
|
||||
clipfd = -1;
|
||||
if (mixer_fd >= -1) {
|
||||
close(mixer_fd);
|
||||
mixer_fd = -1;
|
||||
}
|
||||
setVolume(volume, volume);
|
||||
return 0;
|
||||
};
|
||||
|
||||
void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
type = 0;
|
||||
layer = 0;
|
||||
freq = 0;
|
||||
bitrate = 0;
|
||||
mode = 0;
|
||||
#if 0
|
||||
unsigned int atype;
|
||||
static const int freq_mpg[] = {44100, 48000, 32000, 0};
|
||||
static const int freq_ac3[] = {48000, 44100, 32000, 0};
|
||||
scratchl2 i;
|
||||
if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0)
|
||||
perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP");
|
||||
if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0)
|
||||
perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS");
|
||||
|
||||
type = atype;
|
||||
#if 0
|
||||
/* this does not work, some of the values are negative?? */
|
||||
AMPEGStatus A;
|
||||
memcpy(&A, &i.word00, sizeof(i.word00));
|
||||
layer = A.audio_mpeg_layer;
|
||||
mode = A.audio_mpeg_mode;
|
||||
bitrate = A.audio_mpeg_bitrate;
|
||||
switch(A.audio_mpeg_frequency)
|
||||
#endif
|
||||
/* layer and bitrate are not used anyway... */
|
||||
layer = 0; //(i.word00 >> 17) & 3;
|
||||
bitrate = 0; //(i.word00 >> 12) & 3;
|
||||
switch (type)
|
||||
{
|
||||
case 0: /* MPEG */
|
||||
mode = (i.word00 >> 6) & 3;
|
||||
freq = freq_mpg[(i.word00 >> 10) & 3];
|
||||
break;
|
||||
case 1: /* AC3 */
|
||||
mode = (i.word00 >> 28) & 7;
|
||||
freq = freq_ac3[(i.word00 >> 16) & 3];
|
||||
break;
|
||||
default:
|
||||
mode = 0;
|
||||
freq = 0;
|
||||
}
|
||||
//fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode);
|
||||
#endif
|
||||
};
|
||||
|
||||
void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/)
|
||||
{
|
||||
lt_debug("%s\n", __FUNCTION__);
|
||||
};
|
||||
|
||||
void cAudio::SetHdmiDD(bool enable)
|
||||
{
|
||||
const char *opt[] = { "pcm", "spdif" };
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
proc_put("/proc/stb/hdmi/audio_source", opt[enable], strlen(opt[enable]));
|
||||
}
|
||||
|
||||
void cAudio::SetSpdifDD(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __func__, enable);
|
||||
setBypassMode(!enable);
|
||||
}
|
||||
|
||||
void cAudio::ScheduleMute(bool On)
|
||||
{
|
||||
lt_debug("%s %d\n", __FUNCTION__, On);
|
||||
}
|
||||
|
||||
void cAudio::EnableAnalogOut(bool enable)
|
||||
{
|
||||
lt_debug("%s %d\n", __FUNCTION__, enable);
|
||||
}
|
||||
|
||||
#define AUDIO_BYPASS_ON 0
|
||||
#define AUDIO_BYPASS_OFF 1
|
||||
void cAudio::setBypassMode(bool disable)
|
||||
{
|
||||
const char *opt[] = { "passthrough", "downmix" };
|
||||
lt_debug("%s %d\n", __func__, disable);
|
||||
proc_put("/proc/stb/audio/ac3", opt[disable], strlen(opt[disable]));
|
||||
}
|
98
libspark/audio_lib.h
Normal file
98
libspark/audio_lib.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* public header file */
|
||||
|
||||
#ifndef _AUDIO_TD_H_
|
||||
#define _AUDIO_TD_H_
|
||||
#include "../common/cs_types.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_SYNC_WITH_PTS,
|
||||
AUDIO_NO_SYNC,
|
||||
AUDIO_SYNC_AUDIO_MASTER
|
||||
} AUDIO_SYNC_MODE;
|
||||
|
||||
typedef enum {
|
||||
HDMI_ENCODED_OFF,
|
||||
HDMI_ENCODED_AUTO,
|
||||
HDMI_ENCODED_FORCED
|
||||
} HDMI_ENCODED_MODE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AUDIO_FMT_AUTO = 0,
|
||||
AUDIO_FMT_MPEG,
|
||||
AUDIO_FMT_MP3,
|
||||
AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_BASIC = AUDIO_FMT_DOLBY_DIGITAL,
|
||||
AUDIO_FMT_AAC,
|
||||
AUDIO_FMT_AAC_PLUS,
|
||||
AUDIO_FMT_DD_PLUS,
|
||||
AUDIO_FMT_DTS,
|
||||
AUDIO_FMT_AVS,
|
||||
AUDIO_FMT_MLP,
|
||||
AUDIO_FMT_WMA,
|
||||
AUDIO_FMT_MPG1, // TD only. For Movieplayer / cPlayback
|
||||
AUDIO_FMT_ADVANCED = AUDIO_FMT_MLP
|
||||
} AUDIO_FORMAT;
|
||||
|
||||
class cAudio
|
||||
{
|
||||
friend class cPlayback;
|
||||
private:
|
||||
int fd;
|
||||
bool Muted;
|
||||
|
||||
int clipfd; /* for pcm playback */
|
||||
int mixer_fd; /* if we are using the OSS mixer */
|
||||
int mixer_num; /* oss mixer to use, if any */
|
||||
|
||||
AUDIO_FORMAT StreamType;
|
||||
AUDIO_SYNC_MODE SyncMode;
|
||||
bool started;
|
||||
|
||||
int volume;
|
||||
|
||||
void openDevice(void);
|
||||
void closeDevice(void);
|
||||
|
||||
int do_mute(bool enable, bool remember);
|
||||
void setBypassMode(bool disable);
|
||||
public:
|
||||
/* construct & destruct */
|
||||
cAudio(void *, void *, void *);
|
||||
~cAudio(void);
|
||||
|
||||
void *GetHandle() { return NULL; };
|
||||
/* shut up */
|
||||
int mute(bool remember = true) { return do_mute(true, remember); };
|
||||
int unmute(bool remember = true) { return do_mute(false, remember); };
|
||||
|
||||
/* volume, min = 0, max = 255 */
|
||||
int setVolume(unsigned int left, unsigned int right);
|
||||
int getVolume(void) { return volume;}
|
||||
bool getMuteStatus(void) { return Muted; };
|
||||
|
||||
/* start and stop audio */
|
||||
int Start(void);
|
||||
int Stop(void);
|
||||
bool Pause(bool Pcm = true);
|
||||
void SetStreamType(AUDIO_FORMAT type);
|
||||
AUDIO_FORMAT GetStreamType(void) { return StreamType; }
|
||||
void SetSyncMode(AVSYNC_TYPE Mode);
|
||||
|
||||
/* select channels */
|
||||
int setChannel(int channel);
|
||||
int PrepareClipPlay(int uNoOfChannels, int uSampleRate, int uBitsPerSample, int bLittleEndian);
|
||||
int WriteClip(unsigned char * buffer, int size);
|
||||
int StopClip();
|
||||
void getAudioInfo(int &type, int &layer, int& freq, int &bitrate, int &mode);
|
||||
void SetSRS(int iq_enable, int nmgr_enable, int iq_mode, int iq_level);
|
||||
bool IsHdmiDDSupported() { return true; };
|
||||
void SetHdmiDD(bool enable);
|
||||
void SetSpdifDD(bool enable);
|
||||
void ScheduleMute(bool On);
|
||||
void EnableAnalogOut(bool enable);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user