mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
12
Makefile.am
12
Makefile.am
@@ -24,15 +24,6 @@ libstb_hal_la_LIBADD += \
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BOXTYPE_DUCKBOX
|
|
||||||
#libstb_hal_test_LDADD += -lasound
|
|
||||||
SUBDIRS += libduckbox libeplayer3-sh4 libdvbci
|
|
||||||
libstb_hal_la_LIBADD += \
|
|
||||||
libduckbox/libduckbox.la \
|
|
||||||
libeplayer3-sh4/libeplayer3_sh4.la \
|
|
||||||
libdvbci/libdvbci.la
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BOXTYPE_ARMBOX
|
if BOXTYPE_ARMBOX
|
||||||
if BOXMODEL_HD60
|
if BOXMODEL_HD60
|
||||||
SUBDIRS += libarmbox
|
SUBDIRS += libarmbox
|
||||||
@@ -69,18 +60,17 @@ endif
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
if !ENABLE_GSTREAMER
|
|
||||||
SUBDIRS += libeplayer3
|
SUBDIRS += libeplayer3
|
||||||
libstb_hal_la_LIBADD += \
|
libstb_hal_la_LIBADD += \
|
||||||
libeplayer3/libeplayer3.la
|
libeplayer3/libeplayer3.la
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
if BOXTYPE_MIPSBOX
|
if BOXTYPE_MIPSBOX
|
||||||
SUBDIRS += libmipsbox libdvbci
|
SUBDIRS += libmipsbox libdvbci
|
||||||
libstb_hal_la_LIBADD += \
|
libstb_hal_la_LIBADD += \
|
||||||
libmipsbox/libmipsbox.la \
|
libmipsbox/libmipsbox.la \
|
||||||
libdvbci/libdvbci.la
|
libdvbci/libdvbci.la
|
||||||
|
|
||||||
SUBDIRS += libeplayer3
|
SUBDIRS += libeplayer3
|
||||||
libstb_hal_la_LIBADD += \
|
libstb_hal_la_LIBADD += \
|
||||||
libeplayer3/libeplayer3.la
|
libeplayer3/libeplayer3.la
|
||||||
|
71
acinclude.m4
71
acinclude.m4
@@ -75,9 +75,9 @@ AC_SYS_LARGEFILE
|
|||||||
|
|
||||||
AC_DEFUN([TUXBOX_BOXTYPE], [
|
AC_DEFUN([TUXBOX_BOXTYPE], [
|
||||||
AC_ARG_WITH(boxtype,
|
AC_ARG_WITH(boxtype,
|
||||||
AS_HELP_STRING([--with-boxtype], [valid values: generic, duckbox, armbox, mipsbox]),
|
AS_HELP_STRING([--with-boxtype], [valid values: generic, armbox, mipsbox]),
|
||||||
[case "${withval}" in
|
[case "${withval}" in
|
||||||
generic|duckbox|armbox|mipsbox)
|
generic|armbox|mipsbox)
|
||||||
BOXTYPE="$withval"
|
BOXTYPE="$withval"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -88,7 +88,6 @@ AC_ARG_WITH(boxtype,
|
|||||||
|
|
||||||
AC_ARG_WITH(boxmodel,
|
AC_ARG_WITH(boxmodel,
|
||||||
AS_HELP_STRING([--with-boxmodel], [valid for generic: generic, raspi])
|
AS_HELP_STRING([--with-boxmodel], [valid for generic: generic, raspi])
|
||||||
AS_HELP_STRING([], [valid for duckbox: ufs910, ufs912, ufs913, ufs922, atevio7500, fortis_hdbox, octagon1008, cuberevo, cuberevo_mini, cuberevo_mini2, cuberevo_250hd, cuberevo_2000hd, cuberevo_3000hd, ipbox9900, ipbox99, ipbox55, tf7700])
|
|
||||||
AS_HELP_STRING([], [valid for armbox: hd60, hd61, multiboxse, hd51, bre2ze4k, h7, osmini4k, osmio4k, osmio4kplus, vusolo4k, vuduo4k, vuduo4kse, vuultimo4k, vuuno4k, vuuno4kse, vuzero4k])
|
AS_HELP_STRING([], [valid for armbox: hd60, hd61, multiboxse, hd51, bre2ze4k, h7, osmini4k, osmio4k, osmio4kplus, vusolo4k, vuduo4k, vuduo4kse, vuultimo4k, vuuno4k, vuuno4kse, vuzero4k])
|
||||||
AS_HELP_STRING([], [valid for mipsbox: vuduo, vuduo2, gb800se, osnino, osninoplus, osninopro]),
|
AS_HELP_STRING([], [valid for mipsbox: vuduo, vuduo2, gb800se, osnino, osninoplus, osninopro]),
|
||||||
[case "${withval}" in
|
[case "${withval}" in
|
||||||
@@ -99,13 +98,6 @@ AS_HELP_STRING([], [valid for mipsbox: vuduo, vuduo2, gb800se, osnino, osninoplu
|
|||||||
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
ufs910|ufs912|ufs913|ufs922|atevio7500|fortis_hdbox|octagon1008|cuberevo|cuberevo_mini|cuberevo_mini2|cuberevo_250hd|cuberevo_2000hd|cuberevo_3000hd|ipbox9900|ipbox99|ipbox55|tf7700)
|
|
||||||
if test "$BOXTYPE" = "duckbox"; then
|
|
||||||
BOXMODEL="$withval"
|
|
||||||
else
|
|
||||||
AC_MSG_ERROR([unknown model $withval for boxtype $BOXTYPE])
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
hd60|hd61|multiboxse|hd51|bre2ze4k|h7|osmini4k|osmio4k|osmio4kplus|vusolo4k|vuduo4k|vuduo4kse|vuultimo4k|vuuno4k|vuuno4kse|vuzero4k)
|
hd60|hd61|multiboxse|hd51|bre2ze4k|h7|osmini4k|osmio4k|osmio4kplus|vusolo4k|vuduo4k|vuduo4kse|vuultimo4k|vuuno4k|vuuno4kse|vuzero4k)
|
||||||
if test "$BOXTYPE" = "armbox"; then
|
if test "$BOXTYPE" = "armbox"; then
|
||||||
BOXMODEL="$withval"
|
BOXMODEL="$withval"
|
||||||
@@ -130,7 +122,6 @@ AC_SUBST(BOXTYPE)
|
|||||||
AC_SUBST(BOXMODEL)
|
AC_SUBST(BOXMODEL)
|
||||||
|
|
||||||
AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic")
|
AM_CONDITIONAL(BOXTYPE_GENERIC, test "$BOXTYPE" = "generic")
|
||||||
AM_CONDITIONAL(BOXTYPE_DUCKBOX, test "$BOXTYPE" = "duckbox")
|
|
||||||
AM_CONDITIONAL(BOXTYPE_ARMBOX, test "$BOXTYPE" = "armbox")
|
AM_CONDITIONAL(BOXTYPE_ARMBOX, test "$BOXTYPE" = "armbox")
|
||||||
AM_CONDITIONAL(BOXTYPE_MIPSBOX, test "$BOXTYPE" = "mipsbox")
|
AM_CONDITIONAL(BOXTYPE_MIPSBOX, test "$BOXTYPE" = "mipsbox")
|
||||||
|
|
||||||
@@ -138,25 +129,6 @@ AM_CONDITIONAL(BOXTYPE_MIPSBOX, test "$BOXTYPE" = "mipsbox")
|
|||||||
AM_CONDITIONAL(BOXMODEL_GENERIC, test "$BOXMODEL" = "generic")
|
AM_CONDITIONAL(BOXMODEL_GENERIC, test "$BOXMODEL" = "generic")
|
||||||
AM_CONDITIONAL(BOXMODEL_RASPI, test "$BOXMODEL" = "raspi")
|
AM_CONDITIONAL(BOXMODEL_RASPI, test "$BOXMODEL" = "raspi")
|
||||||
|
|
||||||
# duckbox
|
|
||||||
AM_CONDITIONAL(BOXMODEL_UFS910, test "$BOXMODEL" = "ufs910")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_UFS912, test "$BOXMODEL" = "ufs912")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_UFS913, test "$BOXMODEL" = "ufs913")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_UFS922, test "$BOXMODEL" = "ufs922")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_ATEVIO7500, test "$BOXMODEL" = "atevio7500")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_FORTIS_HDBOX, test "$BOXMODEL" = "fortis_hdbox")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_OCTAGON1008, test "$BOXMODEL" = "octagon1008")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO, test "$BOXMODEL" = "cuberevo")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO_MINI, test "$BOXMODEL" = "cuberevo_mini")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO_MINI2, test "$BOXMODEL" = "cuberevo_mini2")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO_250HD, test "$BOXMODEL" = "cuberevo_250hd")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO_2000HD, test "$BOXMODEL" = "cuberevo_2000hd")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_CUBEREVO_3000HD, test "$BOXMODEL" = "cuberevo_3000hd")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_IPBOX9900, test "$BOXMODEL" = "ipbox9900")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_IPBOX99, test "$BOXMODEL" = "ipbox99")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_IPBOX55, test "$BOXMODEL" = "ipbox55")
|
|
||||||
AM_CONDITIONAL(BOXMODEL_TF7700, test "$BOXMODEL" = "tf7700")
|
|
||||||
|
|
||||||
# armbox
|
# armbox
|
||||||
AM_CONDITIONAL(BOXMODEL_HD60, test "$BOXMODEL" = "hd60")
|
AM_CONDITIONAL(BOXMODEL_HD60, test "$BOXMODEL" = "hd60")
|
||||||
AM_CONDITIONAL(BOXMODEL_HD61, test "$BOXMODEL" = "hd61")
|
AM_CONDITIONAL(BOXMODEL_HD61, test "$BOXMODEL" = "hd61")
|
||||||
@@ -190,9 +162,6 @@ AM_CONDITIONAL(BOXMODEL_OSNINOPRO, test "$BOXMODEL" = "osninopro")
|
|||||||
|
|
||||||
if test "$BOXTYPE" = "generic"; then
|
if test "$BOXTYPE" = "generic"; then
|
||||||
AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC])
|
AC_DEFINE(HAVE_GENERIC_HARDWARE, 1, [building for a generic device like a standard PC])
|
||||||
elif test "$BOXTYPE" = "duckbox"; then
|
|
||||||
AC_DEFINE(HAVE_DUCKBOX_HARDWARE, 1, [building for a duckbox])
|
|
||||||
AC_DEFINE(HAVE_SH4_HARDWARE, 1, [building for a sh4 box])
|
|
||||||
elif test "$BOXTYPE" = "armbox"; then
|
elif test "$BOXTYPE" = "armbox"; then
|
||||||
AC_DEFINE(HAVE_ARM_HARDWARE, 1, [building for an armbox])
|
AC_DEFINE(HAVE_ARM_HARDWARE, 1, [building for an armbox])
|
||||||
elif test "$BOXTYPE" = "mipsbox"; then
|
elif test "$BOXTYPE" = "mipsbox"; then
|
||||||
@@ -205,42 +174,6 @@ if test "$BOXMODEL" = "generic"; then
|
|||||||
elif test "$BOXMODEL" = "raspi"; then
|
elif test "$BOXMODEL" = "raspi"; then
|
||||||
AC_DEFINE(BOXMODEL_RASPI, 1, [raspberry pi])
|
AC_DEFINE(BOXMODEL_RASPI, 1, [raspberry pi])
|
||||||
|
|
||||||
# duckbox
|
|
||||||
elif test "$BOXMODEL" = "ufs910"; then
|
|
||||||
AC_DEFINE(BOXMODEL_UFS910, 1, [ufs910])
|
|
||||||
elif test "$BOXMODEL" = "ufs912"; then
|
|
||||||
AC_DEFINE(BOXMODEL_UFS912, 1, [ufs912])
|
|
||||||
elif test "$BOXMODEL" = "ufs913"; then
|
|
||||||
AC_DEFINE(BOXMODEL_UFS913, 1, [ufs913])
|
|
||||||
elif test "$BOXMODEL" = "ufs922"; then
|
|
||||||
AC_DEFINE(BOXMODEL_UFS922, 1, [ufs922])
|
|
||||||
elif test "$BOXMODEL" = "atevio7500"; then
|
|
||||||
AC_DEFINE(BOXMODEL_ATEVIO7500, 1, [atevio7500])
|
|
||||||
elif test "$BOXMODEL" = "fortis_hdbox"; then
|
|
||||||
AC_DEFINE(BOXMODEL_FORTIS_HDBOX, 1, [fortis_hdbox])
|
|
||||||
elif test "$BOXMODEL" = "octagon1008"; then
|
|
||||||
AC_DEFINE(BOXMODEL_OCTAGON1008, 1, [octagon1008])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO, 1, [cuberevo])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo_mini"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO_MINI, 1, [cuberevo_mini])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo_mini2"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO_MINI2, 1, [cuberevo_mini2])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo_250hd"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO_250HD, 1, [cuberevo_250hd])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo_2000hd"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO_2000HD, 1, [cuberevo_2000hd])
|
|
||||||
elif test "$BOXMODEL" = "cuberevo_3000hd"; then
|
|
||||||
AC_DEFINE(BOXMODEL_CUBEREVO_3000HD, 1, [cuberevo_3000hd])
|
|
||||||
elif test "$BOXMODEL" = "ipbox9900"; then
|
|
||||||
AC_DEFINE(BOXMODEL_IPBOX9900, 1, [ipbox9900])
|
|
||||||
elif test "$BOXMODEL" = "ipbox99"; then
|
|
||||||
AC_DEFINE(BOXMODEL_IPBOX99, 1, [ipbox99])
|
|
||||||
elif test "$BOXMODEL" = "ipbox55"; then
|
|
||||||
AC_DEFINE(BOXMODEL_IPBOX55, 1, [ipbox55])
|
|
||||||
elif test "$BOXMODEL" = "tf7700"; then
|
|
||||||
AC_DEFINE(BOXMODEL_TF7700, 1, [tf7700])
|
|
||||||
|
|
||||||
# armbox
|
# armbox
|
||||||
elif test "$BOXMODEL" = "hd60"; then
|
elif test "$BOXMODEL" = "hd60"; then
|
||||||
AC_DEFINE(BOXMODEL_HD60, 1, [hd60])
|
AC_DEFINE(BOXMODEL_HD60, 1, [hd60])
|
||||||
|
@@ -7,11 +7,6 @@ AM_CXXFLAGS += \
|
|||||||
|
|
||||||
AM_LDFLAGS = -lpthread
|
AM_LDFLAGS = -lpthread
|
||||||
|
|
||||||
if BOXTYPE_DUCKBOX
|
|
||||||
AM_CXXFLAGS += \
|
|
||||||
-I $(top_srcdir)/libdvbci
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BOXTYPE_ARMBOX
|
if BOXTYPE_ARMBOX
|
||||||
AM_CXXFLAGS += \
|
AM_CXXFLAGS += \
|
||||||
-I $(top_srcdir)/libdvbci
|
-I $(top_srcdir)/libdvbci
|
||||||
@@ -26,10 +21,6 @@ if BOXTYPE_MIPSBOX
|
|||||||
libcommon_la_SOURCES = \
|
libcommon_la_SOURCES = \
|
||||||
ca_ci.cpp
|
ca_ci.cpp
|
||||||
else
|
else
|
||||||
if BOXTYPE_DUCKBOX
|
|
||||||
libcommon_la_SOURCES = \
|
|
||||||
ca_ci.cpp
|
|
||||||
else
|
|
||||||
if BOXTYPE_ARMBOX
|
if BOXTYPE_ARMBOX
|
||||||
if BOXMODEL_HD60
|
if BOXMODEL_HD60
|
||||||
libcommon_la_SOURCES = \
|
libcommon_la_SOURCES = \
|
||||||
@@ -58,7 +49,6 @@ libcommon_la_SOURCES = \
|
|||||||
ca.cpp
|
ca.cpp
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
|
||||||
|
|
||||||
libcommon_la_SOURCES += \
|
libcommon_la_SOURCES += \
|
||||||
hal_debug.cpp \
|
hal_debug.cpp \
|
||||||
|
338
common/ca_ci.cpp
338
common/ca_ci.cpp
@@ -37,10 +37,6 @@
|
|||||||
#define hal_debug(args...) _hal_debug(HAL_DEBUG_CA, this, args)
|
#define hal_debug(args...) _hal_debug(HAL_DEBUG_CA, this, args)
|
||||||
|
|
||||||
static const char *FILENAME = "[ca_ci]";
|
static const char *FILENAME = "[ca_ci]";
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
const char ci_path[] = "/dev/dvb/adapter0/ci%d";
|
|
||||||
ca_slot_info_t info;
|
|
||||||
#endif
|
|
||||||
#if HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE
|
#if HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE
|
||||||
const char ci_path[] = "/dev/ci%d";
|
const char ci_path[] = "/dev/ci%d";
|
||||||
static int last_source = -1;
|
static int last_source = -1;
|
||||||
@@ -322,168 +318,6 @@ eData sendData(eDVBCISlot *slot, unsigned char *data, int len)
|
|||||||
return eDataReady;
|
return eDataReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
|
|
||||||
//send a transport connection create request
|
|
||||||
bool sendCreateTC(eDVBCISlot *slot)
|
|
||||||
{
|
|
||||||
unsigned char data[5];
|
|
||||||
data[0] = slot->slot;
|
|
||||||
data[1] = slot->slot + 1; /* conid */
|
|
||||||
data[2] = T_CREATE_T_C;
|
|
||||||
data[3] = 1;
|
|
||||||
data[4] = slot->slot + 1 /* conid */;
|
|
||||||
printf("Create TC: ");
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
printf("%02x ", data[i]);
|
|
||||||
printf("\n");
|
|
||||||
write(slot->fd, data, 5);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sendDataLast(eDVBCISlot *slot)
|
|
||||||
{
|
|
||||||
unsigned char data[5];
|
|
||||||
slot->pollConnection = false;
|
|
||||||
slot->DataLast = false;
|
|
||||||
data[0] = slot->slot;
|
|
||||||
data[1] = slot->connection_id;
|
|
||||||
data[2] = T_DATA_LAST;
|
|
||||||
data[3] = 1;
|
|
||||||
data[4] = slot->connection_id;
|
|
||||||
#if tsb_debug
|
|
||||||
printf("*** > Data Last: ");
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
printf("%02x ", data[i]);
|
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
write(slot->fd, data, 5);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sendRCV(eDVBCISlot *slot)
|
|
||||||
{
|
|
||||||
unsigned char send_data[5];
|
|
||||||
slot->pollConnection = false;
|
|
||||||
slot->DataRCV = false;
|
|
||||||
send_data[0] = slot->slot;
|
|
||||||
send_data[1] = slot->connection_id;
|
|
||||||
send_data[2] = T_RCV;
|
|
||||||
send_data[3] = 1;
|
|
||||||
send_data[4] = slot->connection_id;
|
|
||||||
#if tsb_debug
|
|
||||||
printf("*** > T_RCV: ");
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
printf("%02x ", send_data[i]);
|
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
write(slot->fd, send_data, 5);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cCA::process_tpdu(eDVBCISlot *slot, unsigned char tpdu_tag, __u8 *data, int asn_data_length, int /*con_id*/)
|
|
||||||
{
|
|
||||||
switch (tpdu_tag)
|
|
||||||
{
|
|
||||||
case T_C_T_C_REPLY:
|
|
||||||
#if x_debug
|
|
||||||
printf("%s -> %s > Got CTC Replay (slot %d, con %d)\n", FILENAME, __FUNCTION__, slot->slot, slot->connection_id);
|
|
||||||
#endif
|
|
||||||
/*answer with data last (and if we have with data)
|
|
||||||
--> DataLast flag will be generated in next loop from received APDU*/
|
|
||||||
break;
|
|
||||||
case T_DELETE_T_C:
|
|
||||||
//FIXME: close sessions etc; slot->reset ?
|
|
||||||
//we must answer here with t_c_replay
|
|
||||||
printf("%s %s Got \"Delete Transport Connection\" from module ->currently not handled!\n", FILENAME, __FUNCTION__);
|
|
||||||
break;
|
|
||||||
case T_D_T_C_REPLY:
|
|
||||||
printf("%s %s Got \"Delete Transport Connection Replay\" from module!\n", FILENAME, __FUNCTION__);
|
|
||||||
break;
|
|
||||||
case T_REQUEST_T_C:
|
|
||||||
printf("%s %s Got \"Request Transport Connection\" from Module ->currently not handled!\n", FILENAME, __FUNCTION__);
|
|
||||||
break;
|
|
||||||
case T_DATA_MORE:
|
|
||||||
{
|
|
||||||
int new_data_length = slot->receivedLen + asn_data_length;
|
|
||||||
printf("%s %s Got \"Data More\" from Module\n", FILENAME, __FUNCTION__);
|
|
||||||
__u8 *new_data_buffer = (__u8 *) realloc(slot->receivedData, new_data_length);
|
|
||||||
slot->receivedData = new_data_buffer;
|
|
||||||
memcpy(slot->receivedData + slot->receivedLen, data, asn_data_length);
|
|
||||||
slot->receivedLen = new_data_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case T_DATA_LAST:
|
|
||||||
/* single package */
|
|
||||||
if (slot->receivedData == NULL)
|
|
||||||
{
|
|
||||||
printf("%s -> %s > single package\n", FILENAME, __func__);
|
|
||||||
#if z_debug
|
|
||||||
printf("%s -> calling receiveData with data (len %d)\n", FILENAME, asn_data_length);
|
|
||||||
for (int i = 0; i < asn_data_length; i++)
|
|
||||||
printf("%02x ", data[i]);
|
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
/* to avoid illegal session number: only if > 0 */
|
|
||||||
if (asn_data_length)
|
|
||||||
{
|
|
||||||
eDVBCISession::receiveData(slot, data, asn_data_length);
|
|
||||||
eDVBCISession::pollAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* chained package
|
|
||||||
?? DBO: I never have seen one */
|
|
||||||
int new_data_length = slot->receivedLen + asn_data_length;
|
|
||||||
printf("%s -> chained data\n", FILENAME);
|
|
||||||
__u8 *new_data_buffer = (__u8 *) realloc(slot->receivedData, new_data_length);
|
|
||||||
slot->receivedData = new_data_buffer;
|
|
||||||
memcpy(slot->receivedData + slot->receivedLen, data, asn_data_length);
|
|
||||||
slot->receivedLen = new_data_length;
|
|
||||||
#if z_debug
|
|
||||||
printf("%s -> calling receiveData with data (len %d)\n", FILENAME, asn_data_length);
|
|
||||||
for (int i = 0; i < slot->receivedLen; i++)
|
|
||||||
printf("%02x ", slot->receivedData[i]);
|
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
eDVBCISession::receiveData(slot, slot->receivedData, slot->receivedLen);
|
|
||||||
eDVBCISession::pollAll();
|
|
||||||
|
|
||||||
free(slot->receivedData);
|
|
||||||
slot->receivedData = NULL;
|
|
||||||
slot->receivedLen = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_SB:
|
|
||||||
{
|
|
||||||
if (data[0] & 0x80)
|
|
||||||
{
|
|
||||||
/* we now wait for an answer so dont poll */
|
|
||||||
slot->pollConnection = false;
|
|
||||||
/* set the RCV Flag and set DataLast Flag false */
|
|
||||||
slot->DataRCV = true;
|
|
||||||
slot->DataLast = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* set DataLast Flag if it is false*/
|
|
||||||
if (!slot->DataLast)
|
|
||||||
{
|
|
||||||
slot->DataLast = true;
|
|
||||||
#if tsb_debug
|
|
||||||
printf("**** > T_SB\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
printf("%s unhandled tpdu_tag 0x%0x\n", FILENAME, tpdu_tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool cCA::SendMessage(const CA_MESSAGE *msg)
|
bool cCA::SendMessage(const CA_MESSAGE *msg)
|
||||||
{
|
{
|
||||||
hal_debug("%s\n", __func__);
|
hal_debug("%s\n", __func__);
|
||||||
@@ -1329,13 +1163,7 @@ cCA::cCA(int Slots)
|
|||||||
{
|
{
|
||||||
printf("failed to open %s ->%m", slot->ci_dev);
|
printf("failed to open %s ->%m", slot->ci_dev);
|
||||||
}
|
}
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
/* now reset the slot so the poll pri can happen in the thread */
|
|
||||||
if (ioctl(slot->fd, CA_RESET, i) < 0)
|
|
||||||
printf("IOCTL CA_RESET failed for slot %p\n", slot);
|
|
||||||
#else
|
|
||||||
ioctl(slot->fd, 0);
|
ioctl(slot->fd, 0);
|
||||||
#endif
|
|
||||||
usleep(200000);
|
usleep(200000);
|
||||||
/* create a thread for each slot */
|
/* create a thread for each slot */
|
||||||
if (slot->fd > 0)
|
if (slot->fd > 0)
|
||||||
@@ -1417,12 +1245,7 @@ void cCA::ModuleReset(enum CA_SLOT_TYPE, uint32_t slot)
|
|||||||
(*it)->sendqueue.pop();
|
(*it)->sendqueue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
if (ioctl((*it)->fd, CA_RESET, (*it)->slot) < 0)
|
|
||||||
printf("IOCTL CA_RESET failed for slot %d\n", slot);
|
|
||||||
#else
|
|
||||||
ioctl((*it)->fd, 0);
|
ioctl((*it)->fd, 0);
|
||||||
#endif
|
|
||||||
usleep(200000);
|
usleep(200000);
|
||||||
(*it)->status = eStatusNone;
|
(*it)->status = eStatusNone;
|
||||||
}
|
}
|
||||||
@@ -1440,12 +1263,8 @@ void cCA::ci_inserted(eDVBCISlot *slot)
|
|||||||
slot->mmiOpened = false;
|
slot->mmiOpened = false;
|
||||||
slot->init = false;
|
slot->init = false;
|
||||||
sprintf(slot->name, "unknown module %d", slot->slot);
|
sprintf(slot->name, "unknown module %d", slot->slot);
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
slot->status = eStatusNone;
|
|
||||||
#else
|
|
||||||
slot->status = eStatusWait;
|
slot->status = eStatusWait;
|
||||||
slot->connection_id = slot->slot + 1;
|
slot->connection_id = slot->slot + 1;
|
||||||
#endif
|
|
||||||
/* Send a message to Neutrino cam_menu handler */
|
/* Send a message to Neutrino cam_menu handler */
|
||||||
CA_MESSAGE *pMsg = (CA_MESSAGE *) malloc(sizeof(CA_MESSAGE));
|
CA_MESSAGE *pMsg = (CA_MESSAGE *) malloc(sizeof(CA_MESSAGE));
|
||||||
memset(pMsg, 0, sizeof(CA_MESSAGE));
|
memset(pMsg, 0, sizeof(CA_MESSAGE));
|
||||||
@@ -1638,164 +1457,7 @@ FROM_FIRST:
|
|||||||
printf("unknown state %d\n", slot->status);
|
printf("unknown state %d\n", slot->status);
|
||||||
break;
|
break;
|
||||||
} /* switch(slot->status) */
|
} /* switch(slot->status) */
|
||||||
#else /* Duckbox */
|
|
||||||
int len = 1024 * 4;
|
|
||||||
unsigned char *d;
|
|
||||||
eData status;
|
|
||||||
|
|
||||||
switch (slot->status)
|
|
||||||
{
|
|
||||||
case eStatusReset:
|
|
||||||
while (slot->status == eStatusReset)
|
|
||||||
{
|
|
||||||
usleep(1000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case eStatusNone:
|
|
||||||
{
|
|
||||||
if (slot->camIsReady)
|
|
||||||
{
|
|
||||||
if (sendCreateTC(slot))
|
|
||||||
{
|
|
||||||
slot->status = eStatusWait;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* wait for pollpri */
|
|
||||||
status = waitData(slot->fd, data, &len);
|
|
||||||
if (status == eDataStatusChanged)
|
|
||||||
{
|
|
||||||
info.num = slot->slot;
|
|
||||||
|
|
||||||
if (ioctl(slot->fd, CA_GET_SLOT_INFO, &info) < 0)
|
|
||||||
printf("IOCTL CA_GET_SLOT_INFO failed for slot %d\n", slot->slot);
|
|
||||||
|
|
||||||
printf("flags %d %d %d ->slot %d\n", info.flags, CA_CI_MODULE_READY, info.flags & CA_CI_MODULE_READY, slot->slot);
|
|
||||||
|
|
||||||
if (info.flags & CA_CI_MODULE_READY)
|
|
||||||
{
|
|
||||||
ci_inserted(slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} /* case statusnone */
|
|
||||||
break;
|
|
||||||
case eStatusWait:
|
|
||||||
{
|
|
||||||
status = waitData(slot->fd, data, &len);
|
|
||||||
if (status == eDataReady)
|
|
||||||
{
|
|
||||||
slot->pollConnection = false;
|
|
||||||
d = data;
|
|
||||||
#if z_debug
|
|
||||||
if ((len == 6 && d[4] == 0x80) || len > 6)
|
|
||||||
{
|
|
||||||
printf("slot: %d con-id: %d tpdu-tag: %02X len: %d\n", d[0], d[1], d[2], len);
|
|
||||||
printf("received data: >");
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
printf("%02x ", data[i]);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* taken from the dvb-apps */
|
|
||||||
int data_length = len - 2;
|
|
||||||
d += 2; /* remove leading slot and connection id */
|
|
||||||
while (data_length > 0)
|
|
||||||
{
|
|
||||||
unsigned char tpdu_tag = d[0];
|
|
||||||
unsigned short asn_data_length;
|
|
||||||
int length_field_len;
|
|
||||||
if ((length_field_len = asn_1_decode(&asn_data_length, d + 1, data_length - 1)) < 0)
|
|
||||||
{
|
|
||||||
printf("Received data with invalid asn from module on slot %02x\n", slot->slot);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((asn_data_length < 1) || (asn_data_length > (data_length - (1 + length_field_len))))
|
|
||||||
{
|
|
||||||
printf("Received data with invalid length from module on slot %02x\n", slot->slot);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
slot->connection_id = d[1 + length_field_len];
|
|
||||||
#if z_debug
|
|
||||||
printf("Setting connection_id from received data to %d\n", slot->connection_id);
|
|
||||||
#endif
|
|
||||||
d += 1 + length_field_len + 1;
|
|
||||||
data_length -= (1 + length_field_len + 1);
|
|
||||||
asn_data_length--;
|
|
||||||
#if z_debug
|
|
||||||
printf("****tpdu_tag: 0x%02X\n", tpdu_tag);
|
|
||||||
#endif
|
|
||||||
process_tpdu(slot, tpdu_tag, d, asn_data_length, slot->connection_id);
|
|
||||||
// skip over the consumed data
|
|
||||||
d += asn_data_length;
|
|
||||||
data_length -= asn_data_length;
|
|
||||||
} // while (data_length)
|
|
||||||
} /*if data ready */
|
|
||||||
else if (status == eDataWrite)
|
|
||||||
{
|
|
||||||
/* only writing any data here while status = eDataWrite */
|
|
||||||
if (!slot->sendqueue.empty())
|
|
||||||
{
|
|
||||||
const queueData &qe = slot->sendqueue.top();
|
|
||||||
int res = write(slot->fd, qe.data, qe.len);
|
|
||||||
if (res >= 0 && (unsigned int)res == qe.len)
|
|
||||||
{
|
|
||||||
delete [] qe.data;
|
|
||||||
slot->sendqueue.pop();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("r = %d, %m\n", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* check for activate the pollConnection */
|
|
||||||
if (!checkQueueSize(slot) && (slot->DataRCV || slot->mmiOpened || slot->counter > 5))
|
|
||||||
{
|
|
||||||
slot->pollConnection = true;
|
|
||||||
}
|
|
||||||
if (slot->counter < 6)
|
|
||||||
slot->counter++;
|
|
||||||
else
|
|
||||||
slot->counter = 0;
|
|
||||||
/* if Flag: send a DataLast */
|
|
||||||
if (!checkQueueSize(slot) && slot->pollConnection && slot->DataLast)
|
|
||||||
{
|
|
||||||
sendDataLast(slot);
|
|
||||||
}
|
|
||||||
/* if Flag: send a RCV */
|
|
||||||
if (!checkQueueSize(slot) && slot->pollConnection && slot->DataRCV)
|
|
||||||
{
|
|
||||||
sendRCV(slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (status == eDataStatusChanged)
|
|
||||||
{
|
|
||||||
info.num = slot->slot;
|
|
||||||
|
|
||||||
if (ioctl(slot->fd, CA_GET_SLOT_INFO, &info) < 0)
|
|
||||||
printf("IOCTL CA_GET_SLOT_INFO failed for slot %d\n", slot->slot);
|
|
||||||
|
|
||||||
printf("flags %d %d %d ->slot %d\n", info.flags, CA_CI_MODULE_READY, info.flags & CA_CI_MODULE_READY, slot->slot);
|
|
||||||
|
|
||||||
if ((slot->camIsReady) && (!(info.flags & CA_CI_MODULE_READY)))
|
|
||||||
{
|
|
||||||
ci_removed(slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("unknown state %d\n", slot->status);
|
|
||||||
break;
|
|
||||||
} /* switch(slot->status) */
|
|
||||||
#endif /* end Duckbox */
|
|
||||||
#if HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE
|
|
||||||
if (!slot->init && slot->camIsReady && last_source > -1)
|
if (!slot->init && slot->camIsReady && last_source > -1)
|
||||||
{
|
{
|
||||||
slot->source = (u8)last_source;
|
slot->source = (u8)last_source;
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include "pwrmngr.h"
|
#include "pwrmngr.h"
|
||||||
@@ -60,47 +61,15 @@ unsigned long cCpuFreqManager::GetDelta(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
unsigned long cCpuFreqManager::GetCpuFreq(void)
|
|
||||||
{
|
|
||||||
int freq = 0;
|
|
||||||
if (FILE *pll0 = fopen("/proc/cpu_frequ/pll0_ndiv_mdiv", "r"))
|
|
||||||
{
|
|
||||||
char buffer[120];
|
|
||||||
while (fgets(buffer, sizeof(buffer), pll0))
|
|
||||||
{
|
|
||||||
if (1 == sscanf(buffer, "SH4 = %d MHZ", &freq))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fclose(pll0);
|
|
||||||
return 1000 * 1000 * (unsigned long) freq;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
unsigned long cCpuFreqManager::GetCpuFreq(void)
|
unsigned long cCpuFreqManager::GetCpuFreq(void)
|
||||||
{
|
{
|
||||||
hal_debug("%s\n", __func__);
|
hal_debug("%s\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool cCpuFreqManager::SetCpuFreq(unsigned long f)
|
bool cCpuFreqManager::SetCpuFreq(unsigned long f)
|
||||||
{
|
{
|
||||||
hal_info("%s(%lu) => set standby = %s\n", __func__, f, f ? "true" : "false");
|
hal_info("%s(%lu) => set standby = %s\n", __func__, f, f ? "true" : "false");
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
|
||||||
if (f)
|
|
||||||
{
|
|
||||||
FILE *pll0 = fopen("/proc/cpu_frequ/pll0_ndiv_mdiv", "w");
|
|
||||||
if (pll0)
|
|
||||||
{
|
|
||||||
f /= 1000000;
|
|
||||||
fprintf(pll0, "%lu\n", (f / 10 << 8) | 3);
|
|
||||||
fclose(pll0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -87,10 +87,8 @@ Makefile
|
|||||||
common/Makefile
|
common/Makefile
|
||||||
libmipsbox/Makefile
|
libmipsbox/Makefile
|
||||||
libarmbox/Makefile
|
libarmbox/Makefile
|
||||||
libduckbox/Makefile
|
|
||||||
libdvbci/Makefile
|
libdvbci/Makefile
|
||||||
libeplayer3/Makefile
|
libeplayer3/Makefile
|
||||||
libeplayer3-sh4/Makefile
|
|
||||||
libgeneric-pc/Makefile
|
libgeneric-pc/Makefile
|
||||||
libraspi/Makefile
|
libraspi/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
#if HAVE_ARM_HARDWARE
|
||||||
#include "../libduckbox/audio_lib.h"
|
|
||||||
#include "../libduckbox/audio_mixer.h"
|
|
||||||
#elif HAVE_ARM_HARDWARE
|
|
||||||
#include "../libarmbox/audio_lib.h"
|
#include "../libarmbox/audio_lib.h"
|
||||||
#elif HAVE_MIPS_HARDWARE
|
#elif HAVE_MIPS_HARDWARE
|
||||||
#include "../libmipsbox/audio_lib.h"
|
#include "../libmipsbox/audio_lib.h"
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
#if HAVE_DUCKBOX_HARDWARE \
|
#if HAVE_MIPS_HARDWARE \
|
||||||
|| HAVE_MIPS_HARDWARE \
|
|
||||||
|| (HAVE_ARM_HARDWARE \
|
|| (HAVE_ARM_HARDWARE \
|
||||||
&& !BOXMODEL_HD60 \
|
&& !BOXMODEL_HD60 \
|
||||||
&& !BOXMODEL_MULTIBOXSE \
|
&& !BOXMODEL_MULTIBOXSE \
|
||||||
|
@@ -23,8 +23,7 @@ inline void cs_api_exit()
|
|||||||
#define cs_free_uncached free
|
#define cs_free_uncached free
|
||||||
|
|
||||||
// Callback function helpers
|
// Callback function helpers
|
||||||
#if HAVE_DUCKBOX_HARDWARE \
|
#if HAVE_MIPS_HARDWARE \
|
||||||
|| HAVE_MIPS_HARDWARE \
|
|
||||||
|| (HAVE_ARM_HARDWARE \
|
|| (HAVE_ARM_HARDWARE \
|
||||||
&& !BOXMODEL_HD60 \
|
&& !BOXMODEL_HD60 \
|
||||||
&& !BOXMODEL_MULTIBOXSE \
|
&& !BOXMODEL_MULTIBOXSE \
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
#if HAVE_ARM_HARDWARE
|
||||||
#include "../libduckbox/playback_libeplayer3.h"
|
|
||||||
#elif HAVE_ARM_HARDWARE
|
|
||||||
#include "../libarmbox/playback_libeplayer3.h"
|
#include "../libarmbox/playback_libeplayer3.h"
|
||||||
#elif HAVE_MIPS_HARDWARE
|
#elif HAVE_MIPS_HARDWARE
|
||||||
#include "../libmipsbox/playback_libeplayer3.h"
|
#include "../libmipsbox/playback_libeplayer3.h"
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
#if HAVE_ARM_HARDWARE
|
||||||
#include "../libduckbox/record_lib.h"
|
|
||||||
#elif HAVE_ARM_HARDWARE
|
|
||||||
#include "../libarmbox/record_lib.h"
|
#include "../libarmbox/record_lib.h"
|
||||||
#elif HAVE_MIPS_HARDWARE
|
#elif HAVE_MIPS_HARDWARE
|
||||||
#include "../libmipsbox/record_lib.h"
|
#include "../libmipsbox/record_lib.h"
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#if HAVE_DUCKBOX_HARDWARE
|
#if HAVE_ARM_HARDWARE
|
||||||
#include "../libduckbox/video_lib.h"
|
|
||||||
#elif HAVE_ARM_HARDWARE
|
|
||||||
#include "../libarmbox/video_lib.h"
|
#include "../libarmbox/video_lib.h"
|
||||||
#include "../libarmbox/hdmi_cec.h"
|
#include "../libarmbox/hdmi_cec.h"
|
||||||
#elif HAVE_MIPS_HARDWARE
|
#elif HAVE_MIPS_HARDWARE
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
noinst_LTLIBRARIES = libduckbox.la
|
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
|
||||||
-I$(top_srcdir)/common \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_srcdir)/libeplayer3-sh4/include
|
|
||||||
|
|
||||||
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
|
||||||
@AVFORMAT_LIBS@ \
|
|
||||||
@AVUTIL_LIBS@ \
|
|
||||||
@AVCODEC_LIBS@ \
|
|
||||||
@SWRESAMPLE_LIBS@ \
|
|
||||||
-lpthread -lasound -lass -lrt
|
|
||||||
|
|
||||||
libduckbox_la_SOURCES = \
|
|
||||||
hardware_caps.c \
|
|
||||||
dmx.cpp \
|
|
||||||
video.cpp \
|
|
||||||
audio.cpp \
|
|
||||||
audio_mixer.cpp \
|
|
||||||
init.cpp \
|
|
||||||
playback_libeplayer3.cpp \
|
|
||||||
record.cpp
|
|
||||||
|
|
||||||
AM_CPPFLAGS += -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/audio.cpp
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/audio_lib.h
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/audio_mixer.cpp
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/audio_mixer.h
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/dmx.cpp
|
|
@@ -1,294 +0,0 @@
|
|||||||
/*
|
|
||||||
* determine the capabilities of the hardware.
|
|
||||||
* part of libstb-hal
|
|
||||||
*
|
|
||||||
* (C) 2010-2012 Stefan Seyfried
|
|
||||||
*
|
|
||||||
* License: GPL v2 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#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));
|
|
||||||
|
|
||||||
if (access("/dev/dvb/adapter0/video1", F_OK) != -1)
|
|
||||||
caps.can_pip = 1;
|
|
||||||
|
|
||||||
#if BOXMODEL_UFS910
|
|
||||||
strcpy(caps.boxvendor, "KATHREIN");
|
|
||||||
strcpy(caps.boxname, "UFS910");
|
|
||||||
strcpy(caps.boxarch, "STX7100");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
// caps.has_SCART_input = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 16;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_UFS912
|
|
||||||
strcpy(caps.boxvendor, "KATHREIN");
|
|
||||||
strcpy(caps.boxname, "UFS912");
|
|
||||||
strcpy(caps.boxarch, "STX7111");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
// caps.has_SCART_input = 1;
|
|
||||||
caps.can_cec = 1;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 16;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_UFS913
|
|
||||||
strcpy(caps.boxvendor, "KATHREIN");
|
|
||||||
strcpy(caps.boxname, "UFS913");
|
|
||||||
strcpy(caps.boxarch, "STX7105");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
// caps.has_SCART_input = 1;
|
|
||||||
caps.can_cec = 1;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 16;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_UFS922
|
|
||||||
strcpy(caps.boxvendor, "KATHREIN");
|
|
||||||
strcpy(caps.boxname, "UFS922");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 16;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_ATEVIO7500
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "ATEVIO7500");
|
|
||||||
strcpy(caps.boxarch, "STX7105");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
// caps.has_SCART_input = 1;
|
|
||||||
caps.can_cec = 1;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 12;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_FORTIS_HDBOX
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "HDBOX");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
// caps.has_SCART_input = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 12;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_OCTAGON1008
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "OCTAGON1008");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 8;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO_MINI
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO-MINI");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO_MINI2
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO-MINI2");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
caps.display_can_set_brightness = 1;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.display_type = HW_DISPLAY_LINE_TEXT;
|
|
||||||
caps.display_xres = 14;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO_250HD
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO-250HD");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 0;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO_2000HD
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO-2000HD");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 0;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_CUBEREVO_3000HD
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "CUBEREVO-3000HD");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_IPBOX9900
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "IPBOX9900");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 2;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_IPBOX99
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "IPBOX99");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 1;
|
|
||||||
caps.has_CI = 0;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_IPBOX55
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "IPBOX55");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 0;
|
|
||||||
#endif
|
|
||||||
#if BOXMODEL_TF7700
|
|
||||||
strcpy(caps.boxvendor, "DUCKBOX");
|
|
||||||
strcpy(caps.boxname, "TF7700");
|
|
||||||
strcpy(caps.boxarch, "STX7109");
|
|
||||||
caps.can_shutdown = 1;
|
|
||||||
caps.display_can_set_brightness = 0;
|
|
||||||
caps.display_can_deepstandby = 0;
|
|
||||||
caps.display_has_statusline = 0;
|
|
||||||
caps.has_HDMI = 1;
|
|
||||||
caps.has_SCART = 1;
|
|
||||||
caps.can_cec = 0;
|
|
||||||
caps.has_fan = 0;
|
|
||||||
caps.has_CI = 2;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
initialized = 1;
|
|
||||||
return ∩︀
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <linux/dvb/dmx.h>
|
|
||||||
|
|
||||||
#include "init.h"
|
|
||||||
#include "pwrmngr.h"
|
|
||||||
|
|
||||||
#include "hal_debug.h"
|
|
||||||
#define hal_debug(args...) _hal_debug(HAL_DEBUG_INIT, NULL, args)
|
|
||||||
#define hal_info(args...) _hal_info(HAL_DEBUG_INIT, NULL, args)
|
|
||||||
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
void hal_api_init()
|
|
||||||
{
|
|
||||||
if (!initialized)
|
|
||||||
hal_debug_init();
|
|
||||||
hal_info("%s begin, initialized=%d, debug=0x%02x\n", __FUNCTION__, (int)initialized, debuglevel);
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
/* this is a strange hack: the drivers seem to only work correctly after
|
|
||||||
* demux0 has been used once. After that, we can use demux1,2,... */
|
|
||||||
struct dmx_pes_filter_params p;
|
|
||||||
int dmx = open("/dev/dvb/adapter0/demux0", O_RDWR | O_CLOEXEC);
|
|
||||||
if (dmx < 0)
|
|
||||||
hal_info("%s: ERROR open /dev/dvb/adapter0/demux0 (%m)\n", __func__);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memset(&p, 0, sizeof(p));
|
|
||||||
p.output = DMX_OUT_DECODER;
|
|
||||||
p.input = DMX_IN_FRONTEND;
|
|
||||||
p.flags = DMX_IMMEDIATE_START;
|
|
||||||
p.pes_type = DMX_PES_VIDEO;
|
|
||||||
ioctl(dmx, DMX_SET_PES_FILTER, &p);
|
|
||||||
ioctl(dmx, DMX_STOP);
|
|
||||||
close(dmx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initialized = true;
|
|
||||||
hal_info("%s end\n", __FUNCTION__);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hal_api_exit()
|
|
||||||
{
|
|
||||||
hal_info("%s, initialized = %d\n", __FUNCTION__, (int)initialized);
|
|
||||||
initialized = false;
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/playback_libeplayer3.cpp
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/playback_libeplayer3.h
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/record.cpp
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/record_lib.h
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/video.cpp
|
|
@@ -1 +0,0 @@
|
|||||||
../libspark/video_lib.h
|
|
@@ -1,23 +0,0 @@
|
|||||||
AUTOMAKE_OPTIONS = subdir-objects
|
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libeplayer3_sh4.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_sh4_la_SOURCES = \
|
|
||||||
input.cpp output.cpp manager.cpp player.cpp \
|
|
||||||
writer/writer.cpp \
|
|
||||||
writer/pes.cpp \
|
|
||||||
writer/misc.cpp
|
|
||||||
|
|
||||||
# writer/wmv.cpp writer/ac3.cpp writer/divx.cpp
|
|
||||||
# writer/dts.cpp writer/mpeg2.cpp writer/mp3.cpp
|
|
||||||
# writer/h264.cpp
|
|
||||||
# writer/h263.cpp writer/vc1.cpp writer/pcm.cpp writer/aac.cpp
|
|
||||||
|
|
||||||
LIBEPLAYER3_LIBS = libeplayer3_sh4.la
|
|
||||||
# -lpthread -lavformat -lavcodec -lavutil -lswresample -lm
|
|
||||||
|
|
@@ -1,78 +0,0 @@
|
|||||||
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
|
|
||||||
*
|
|
||||||
*/
|
|
@@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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>
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
#define EPLAYER_MAX_CODECS 16
|
|
||||||
struct CodecList
|
|
||||||
{
|
|
||||||
AVCodecContext *codec;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class Player;
|
|
||||||
class Track;
|
|
||||||
|
|
||||||
class Input
|
|
||||||
{
|
|
||||||
friend class Player;
|
|
||||||
friend class WriterPCM; // needs calcPts()
|
|
||||||
friend int interrupt_cb(void *arg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
OpenThreads::Mutex mutex;
|
|
||||||
|
|
||||||
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;
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
CodecList codecs[EPLAYER_MAX_CODECS];
|
|
||||||
#endif
|
|
||||||
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, std::string headers = "");
|
|
||||||
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);
|
|
||||||
AVFormatContext *GetAVFormatContext();
|
|
||||||
AVCodecContext *GetCodecContext(unsigned int index);
|
|
||||||
void ReleaseAVFormatContext();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
AVStream *stream;
|
|
||||||
bool inactive;
|
|
||||||
bool hidden; // not part of currently selected program
|
|
||||||
bool is_static;
|
|
||||||
int ac3flags;
|
|
||||||
int type, mag, page; // for teletext
|
|
||||||
Track() : pid(-1), stream(NULL), inactive(false), hidden(false), is_static(false), ac3flags(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Program
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
std::string title;
|
|
||||||
std::vector<AVStream *> streams;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Manager
|
|
||||||
{
|
|
||||||
friend class Player;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Player *player;
|
|
||||||
OpenThreads::Mutex mutex;
|
|
||||||
std::map<int, Track *> videoTracks, audioTracks, subtitleTracks, teletextTracks;
|
|
||||||
std::map<int, Program> Programs;
|
|
||||||
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);
|
|
||||||
void addProgram(Program &program);
|
|
||||||
|
|
||||||
std::vector<Track> getVideoTracks();
|
|
||||||
std::vector<Track> getAudioTracks();
|
|
||||||
std::vector<Track> getSubtitleTracks();
|
|
||||||
std::vector<Track> getTeletextTracks();
|
|
||||||
std::vector<Program> getPrograms();
|
|
||||||
bool selectProgram(const int id);
|
|
||||||
|
|
||||||
Track *getVideoTrack(int pid);
|
|
||||||
Track *getAudioTrack(int pid);
|
|
||||||
Track *getSubtitleTrack(int pid);
|
|
||||||
Track *getTeletextTrack(int pid);
|
|
||||||
|
|
||||||
bool initTrackUpdate();
|
|
||||||
void clearTracks();
|
|
||||||
|
|
||||||
~Manager();
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,20 +0,0 @@
|
|||||||
#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
|
|
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
Track *audioTrack, *videoTrack;
|
|
||||||
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(Track *track);
|
|
||||||
bool SwitchVideo(Track *track);
|
|
||||||
bool Write(AVStream *stream, AVPacket *packet, int64_t Pts);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,34 +0,0 @@
|
|||||||
#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
|
|
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 class WriterPCM;
|
|
||||||
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, std::string headers = "");
|
|
||||||
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);
|
|
||||||
|
|
||||||
AVFormatContext *GetAVFormatContext()
|
|
||||||
{
|
|
||||||
return input.GetAVFormatContext();
|
|
||||||
}
|
|
||||||
void ReleaseAVFormatContext()
|
|
||||||
{
|
|
||||||
input.ReleaseAVFormatContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values);
|
|
||||||
bool SelectProgram(int key);
|
|
||||||
bool SelectProgram(std::string &key);
|
|
||||||
|
|
||||||
Player();
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/* wrapper */
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
static AVCodecParameters __attribute__((unused)) *get_codecpar(AVStream *stream)
|
|
||||||
{
|
|
||||||
return stream->codecpar;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static AVCodecContext __attribute__((unused)) *get_codecpar(AVStream *stream)
|
|
||||||
{
|
|
||||||
return stream->codec;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (LIBAVCODEC_VERSION_MAJOR > 55)
|
|
||||||
#define av_free_packet av_packet_unref
|
|
||||||
#else
|
|
||||||
#define av_packet_unref av_free_packet
|
|
||||||
#endif
|
|
||||||
/* end wrapper */
|
|
||||||
|
|
||||||
class Player;
|
|
||||||
|
|
||||||
class Writer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
int fd;
|
|
||||||
Player *player;
|
|
||||||
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, int track_type);
|
|
||||||
|
|
||||||
virtual void Init(int _fd, AVStream * /*stream*/, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
player = _player;
|
|
||||||
}
|
|
||||||
virtual bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
};
|
|
||||||
#endif
|
|
@@ -1,941 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ENABLE_LOGGING 1
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "player.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
static const char *FILENAME = "eplayer/input.cpp";
|
|
||||||
|
|
||||||
#define averror(_err,_fun) ({ \
|
|
||||||
if (_err < 0) { \
|
|
||||||
char _error[512]; \
|
|
||||||
av_strerror(_err, _error, sizeof(_error)); \
|
|
||||||
fprintf(stderr, "%s %d: %s: %d (%s)\n", FILENAME, __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;
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
for (int n = 0; n < EPLAYER_MAX_CODECS; n++)
|
|
||||||
codecs[n].codec = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Input::~Input()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(58, 133, 100)
|
|
||||||
static void get_packet_defaults(AVPacket *pkt)
|
|
||||||
{
|
|
||||||
memset(pkt, 0, sizeof(*pkt));
|
|
||||||
|
|
||||||
pkt->pts = AV_NOPTS_VALUE;
|
|
||||||
pkt->dts = AV_NOPTS_VALUE;
|
|
||||||
pkt->pos = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int64_t Input::calcPts(AVStream *stream, int64_t pts)
|
|
||||||
{
|
|
||||||
if (pts == AV_NOPTS_VALUE)
|
|
||||||
return INVALID_PTS_VALUE;
|
|
||||||
|
|
||||||
pts = 90000 * (double)pts * stream->time_base.num / stream->time_base.den;
|
|
||||||
if (avfc->start_time != AV_NOPTS_VALUE)
|
|
||||||
pts -= 90000 * 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);
|
|
||||||
|
|
||||||
static std::string lastlog_message;
|
|
||||||
static unsigned int lastlog_repeats;
|
|
||||||
|
|
||||||
static void log_callback(void *ptr __attribute__((unused)), int lvl __attribute__((unused)), const char *format, va_list ap)
|
|
||||||
{
|
|
||||||
char m[1024];
|
|
||||||
if (sizeof(m) - 1 > (unsigned int) vsnprintf(m, sizeof(m), format, ap))
|
|
||||||
{
|
|
||||||
if (lastlog_message.compare(m) || lastlog_repeats > 999)
|
|
||||||
{
|
|
||||||
if (lastlog_repeats)
|
|
||||||
fprintf(stderr, "last message repeated %u times\n", lastlog_repeats);
|
|
||||||
lastlog_message = m;
|
|
||||||
lastlog_repeats = 0;
|
|
||||||
fprintf(stderr, "%s", m);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
lastlog_repeats++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void logprintf(const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
log_callback(NULL, 0, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *Input::GetCodecContext(unsigned int index)
|
|
||||||
{
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
if (codecs[index].codec)
|
|
||||||
{
|
|
||||||
return codecs[index].codec;
|
|
||||||
}
|
|
||||||
AVCodec *codec = avcodec_find_decoder(avfc->streams[index]->codecpar->codec_id);
|
|
||||||
codecs[index].codec = avcodec_alloc_context3(codec);
|
|
||||||
if (!codecs[index].codec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "context3 alloc for stream %d failed\n", (int)index);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (avcodec_parameters_to_context(codecs[index].codec, avfc->streams[index]->codecpar) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "copy parameters to codec context for stream %d failed\n", (int)index);
|
|
||||||
avcodec_free_context(&codecs[index].codec);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!codec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "decoder for codec_id:(0x%X) stream:(%d) not found\n", avfc->streams[index]->codecpar->codec_id, (int)index);;
|
|
||||||
return codecs[index].codec;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "decoder for codec_id:(0x%X) stream:(%d) found\n", avfc->streams[index]->codecpar->codec_id, (int)index);;
|
|
||||||
}
|
|
||||||
int err = avcodec_open2(codecs[index].codec, codec, NULL);
|
|
||||||
if (averror(err, avcodec_open2))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "open codec context for stream:(%d) failed}n", (int)index);
|
|
||||||
avcodec_free_context(&codecs[index].codec);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return codecs[index].codec;
|
|
||||||
#else
|
|
||||||
return avfc->streams[index]->codec;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Input::Play()
|
|
||||||
{
|
|
||||||
hasPlayThreadStarted = 1;
|
|
||||||
|
|
||||||
int64_t showtime = 0;
|
|
||||||
bool restart_audio_resampling = false;
|
|
||||||
bool bof = false;
|
|
||||||
|
|
||||||
// HACK: Dropping all video frames until the first audio frame was seen will keep player2 from stuttering.
|
|
||||||
// Oddly, this seems to be necessary for network streaming only ...
|
|
||||||
bool audioSeen = !audioTrack || !player->isHttp;
|
|
||||||
|
|
||||||
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_relative() >= showtime)
|
|
||||||
{
|
|
||||||
player->output.ClearVideo();
|
|
||||||
|
|
||||||
if (bof)
|
|
||||||
{
|
|
||||||
showtime = av_gettime_relative();
|
|
||||||
usleep(100000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
seek_avts_rel = player->Speed * AV_TIME_BASE;
|
|
||||||
showtime = av_gettime_relative() + 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;
|
|
||||||
|
|
||||||
// clear streams
|
|
||||||
for (unsigned int i = 0; i < avfc->nb_streams; i++)
|
|
||||||
{
|
|
||||||
AVCodecContext *avcctx = GetCodecContext(i);
|
|
||||||
if (avcctx && avcctx->codec)
|
|
||||||
avcodec_flush_buffers(avcctx);
|
|
||||||
}
|
|
||||||
player->output.ClearAudio();
|
|
||||||
player->output.ClearVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
AVPacket packet;
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100)
|
|
||||||
av_init_packet(&packet);
|
|
||||||
#else
|
|
||||||
get_packet_defaults(&packet);
|
|
||||||
#endif
|
|
||||||
int err = av_read_frame(avfc, &packet);
|
|
||||||
if (err == AVERROR(EAGAIN))
|
|
||||||
{
|
|
||||||
av_packet_unref(&packet);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (averror(err, av_read_frame)) // EOF?
|
|
||||||
{
|
|
||||||
av_packet_unref(&packet);
|
|
||||||
break; // while
|
|
||||||
}
|
|
||||||
|
|
||||||
player->readCount += packet.size;
|
|
||||||
|
|
||||||
AVStream *stream = avfc->streams[packet.stream_index];
|
|
||||||
AVCodecContext *avcctx = GetCodecContext((unsigned int)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 (audioSeen && !player->output.Write(stream, &packet, pts))
|
|
||||||
logprintf("writing data to video device failed\n");
|
|
||||||
}
|
|
||||||
else if (_audioTrack && (_audioTrack->stream == stream))
|
|
||||||
{
|
|
||||||
if (restart_audio_resampling)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = false;
|
|
||||||
player->output.Write(stream, NULL, 0);
|
|
||||||
}
|
|
||||||
if (!player->isBackWard)
|
|
||||||
{
|
|
||||||
int64_t pts = calcPts(stream, packet.pts);
|
|
||||||
//if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0)) // DBO: why pts only at video tracks ?
|
|
||||||
if (!player->output.Write(stream, &packet, pts))
|
|
||||||
logprintf("writing data to audio device failed\n");
|
|
||||||
}
|
|
||||||
audioSeen = true;
|
|
||||||
}
|
|
||||||
else if (_subtitleTrack && (_subtitleTrack->stream == stream))
|
|
||||||
{
|
|
||||||
if (avcctx->codec)
|
|
||||||
{
|
|
||||||
AVSubtitle sub;
|
|
||||||
memset(&sub, 0, sizeof(sub));
|
|
||||||
int got_sub_ptr = 0;
|
|
||||||
|
|
||||||
err = avcodec_decode_subtitle2(avcctx, &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(avcctx, &sub, _subtitleTrack->pid);
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
if (packet.data && packet.size > 1)
|
|
||||||
teletext_write(_teletextTrack->pid, packet.data + 1, packet.size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
av_packet_unref(&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", FILENAME, __func__, __LINE__, player->input.abortPlayback, player->abortRequested);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_MAJOR < 58
|
|
||||||
static int lock_callback(void **mutex, enum AVLockOp op)
|
|
||||||
{
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case AV_LOCK_CREATE:
|
|
||||||
*mutex = (void *) new OpenThreads::Mutex;
|
|
||||||
return !*mutex;
|
|
||||||
case AV_LOCK_DESTROY:
|
|
||||||
delete static_cast<OpenThreads::Mutex *>(*mutex);
|
|
||||||
*mutex = NULL;
|
|
||||||
return 0;
|
|
||||||
case AV_LOCK_OBTAIN:
|
|
||||||
static_cast<OpenThreads::Mutex *>(*mutex)->lock();
|
|
||||||
return 0;
|
|
||||||
case AV_LOCK_RELEASE:
|
|
||||||
static_cast<OpenThreads::Mutex *>(*mutex)->unlock();
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool Input::ReadSubtitle(const char *filename, const char *format, int pid)
|
|
||||||
{
|
|
||||||
const char *lastDot = strrchr(filename, '.');
|
|
||||||
if (!lastDot)
|
|
||||||
return false;
|
|
||||||
char subfile[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 = NULL;
|
|
||||||
AVCodec *codec = NULL;
|
|
||||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57,25,101)
|
|
||||||
c = subavfc->streams[0]->codec;
|
|
||||||
#else
|
|
||||||
c = avcodec_alloc_context3(codec);
|
|
||||||
if (avcodec_parameters_to_context(c, subavfc->streams[0]->codecpar) < 0)
|
|
||||||
{
|
|
||||||
avcodec_free_context(&c);
|
|
||||||
avformat_close_input(&subavfc);
|
|
||||||
avformat_free_context(subavfc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100)
|
|
||||||
av_init_packet(&packet);
|
|
||||||
#else
|
|
||||||
get_packet_defaults(&packet);
|
|
||||||
#endif
|
|
||||||
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_packet_unref(&packet);
|
|
||||||
}
|
|
||||||
avcodec_close(c);
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
avcodec_free_context(&c);
|
|
||||||
#endif
|
|
||||||
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, std::string headers)
|
|
||||||
{
|
|
||||||
bool find_info = true;
|
|
||||||
abortPlayback = false;
|
|
||||||
#if LIBAVCODEC_VERSION_MAJOR < 58
|
|
||||||
av_lockmgr_register(lock_callback);
|
|
||||||
#endif
|
|
||||||
#if ENABLE_LOGGING
|
|
||||||
av_log_set_flags(AV_LOG_SKIP_REPEATED);
|
|
||||||
av_log_set_level(AV_LOG_INFO);
|
|
||||||
/* out commented here for using ffmpeg default: av_log_default_callback
|
|
||||||
because of better log level handling */
|
|
||||||
//av_log_set_callback(log_callback);
|
|
||||||
#else
|
|
||||||
av_log_set_level(AV_LOG_PANIC);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!filename)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "filename NULL\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!headers.empty())
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %s %d: %s\n%s\n", FILENAME, __func__, __LINE__, filename, headers.c_str());
|
|
||||||
headers += "\r\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %s %d: %s\n", FILENAME, __func__, __LINE__, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
|
||||||
avcodec_register_all();
|
|
||||||
av_register_all();
|
|
||||||
#endif
|
|
||||||
avformat_network_init();
|
|
||||||
|
|
||||||
videoTrack = NULL;
|
|
||||||
audioTrack = NULL;
|
|
||||||
subtitleTrack = NULL;
|
|
||||||
teletextTrack = NULL;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
again:
|
|
||||||
#endif
|
|
||||||
avfc = avformat_alloc_context();
|
|
||||||
avfc->interrupt_callback.callback = interrupt_cb;
|
|
||||||
avfc->interrupt_callback.opaque = (void *) player;
|
|
||||||
|
|
||||||
AVDictionary *options = NULL;
|
|
||||||
av_dict_set(&options, "auth_type", "basic", 0);
|
|
||||||
if (!headers.empty())
|
|
||||||
{
|
|
||||||
av_dict_set(&options, "headers", headers.c_str(), 0);
|
|
||||||
}
|
|
||||||
#if ENABLE_LOGGING
|
|
||||||
av_log_set_level(AV_LOG_DEBUG);
|
|
||||||
#endif
|
|
||||||
int err = avformat_open_input(&avfc, filename, NULL, &options);
|
|
||||||
#if ENABLE_LOGGING
|
|
||||||
av_log_set_level(AV_LOG_INFO);
|
|
||||||
#endif
|
|
||||||
av_dict_free(&options);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(55, 43, 100) || \
|
|
||||||
LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57, 25, 0)
|
|
||||||
avfc->max_analyze_duration = 1;
|
|
||||||
#else
|
|
||||||
avfc->max_analyze_duration2 = 1;
|
|
||||||
#endif
|
|
||||||
avfc->probesize = 131072;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
if (!player->isHttp)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < avfc->nb_streams; i++)
|
|
||||||
{
|
|
||||||
if (avfc->streams[i]->codec->codec_id == AV_CODEC_ID_AAC)
|
|
||||||
find_info = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (find_info)
|
|
||||||
err = avformat_find_stream_info(avfc, NULL);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (averror(err, avformat_find_stream_info))
|
|
||||||
{
|
|
||||||
avformat_close_input(&avfc);
|
|
||||||
if (player->noprobe)
|
|
||||||
{
|
|
||||||
player->noprobe = false;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool res = UpdateTracks();
|
|
||||||
|
|
||||||
if (!videoTrack && !audioTrack)
|
|
||||||
{
|
|
||||||
avformat_close_input(&avfc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoTrack)
|
|
||||||
player->output.SwitchVideo(videoTrack);
|
|
||||||
if (audioTrack)
|
|
||||||
player->output.SwitchAudio(audioTrack);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
bool use_index_as_pid = false;
|
|
||||||
for (unsigned int n = 0; n < avfc->nb_streams; n++)
|
|
||||||
{
|
|
||||||
AVStream *stream = avfc->streams[n];
|
|
||||||
|
|
||||||
AVCodecContext *avcctx = GetCodecContext(n);
|
|
||||||
|
|
||||||
Track track;
|
|
||||||
track.stream = stream;
|
|
||||||
AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0);
|
|
||||||
track.title = lang ? lang->value : "";
|
|
||||||
|
|
||||||
if (!use_index_as_pid)
|
|
||||||
switch (avcctx->codec_type)
|
|
||||||
{
|
|
||||||
case AVMEDIA_TYPE_VIDEO:
|
|
||||||
case AVMEDIA_TYPE_AUDIO:
|
|
||||||
case AVMEDIA_TYPE_SUBTITLE:
|
|
||||||
if (!stream->id)
|
|
||||||
use_index_as_pid = true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
track.pid = use_index_as_pid ? n + 1 : stream->id;
|
|
||||||
track.ac3flags = 0;
|
|
||||||
|
|
||||||
switch (avcctx->codec_type)
|
|
||||||
{
|
|
||||||
case AVMEDIA_TYPE_VIDEO:
|
|
||||||
player->manager.addVideoTrack(track);
|
|
||||||
if (!videoTrack)
|
|
||||||
videoTrack = player->manager.getVideoTrack(track.pid);
|
|
||||||
break;
|
|
||||||
case AVMEDIA_TYPE_AUDIO:
|
|
||||||
switch (avcctx->codec_id)
|
|
||||||
{
|
|
||||||
case AV_CODEC_ID_MP2:
|
|
||||||
track.ac3flags = 1;
|
|
||||||
break;
|
|
||||||
case AV_CODEC_ID_MP3:
|
|
||||||
track.ac3flags = 2;
|
|
||||||
break;
|
|
||||||
case AV_CODEC_ID_AC3:
|
|
||||||
track.ac3flags = 3;
|
|
||||||
break;
|
|
||||||
case AV_CODEC_ID_DTS:
|
|
||||||
track.ac3flags = 4;
|
|
||||||
break;
|
|
||||||
case AV_CODEC_ID_AAC:
|
|
||||||
{
|
|
||||||
unsigned int extradata_size = avcctx->extradata_size;
|
|
||||||
unsigned int object_type = 2;
|
|
||||||
if (extradata_size >= 2)
|
|
||||||
object_type = avcctx->extradata[0] >> 3;
|
|
||||||
if (extradata_size <= 1 || object_type == 1 || object_type == 5)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "use resampling for AAC\n");
|
|
||||||
track.ac3flags = 6;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
track.ac3flags = 5;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AV_CODEC_ID_FLAC:
|
|
||||||
track.ac3flags = 8;
|
|
||||||
break;
|
|
||||||
case AV_CODEC_ID_WMAV1:
|
|
||||||
case AV_CODEC_ID_WMAV2:
|
|
||||||
case AV_CODEC_ID_WMAVOICE:
|
|
||||||
case AV_CODEC_ID_WMAPRO:
|
|
||||||
case AV_CODEC_ID_WMALOSSLESS:
|
|
||||||
track.ac3flags = 9;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
track.ac3flags = 0;
|
|
||||||
}
|
|
||||||
player->manager.addAudioTrack(track);
|
|
||||||
if (!audioTrack)
|
|
||||||
audioTrack = player->manager.getAudioTrack(track.pid);
|
|
||||||
break;
|
|
||||||
case AVMEDIA_TYPE_SUBTITLE:
|
|
||||||
if (avcctx->codec_id == AV_CODEC_ID_DVB_TELETEXT)
|
|
||||||
{
|
|
||||||
std::string l = lang ? lang->value : "";
|
|
||||||
uint8_t *data = avcctx->extradata;
|
|
||||||
int size = avcctx->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 (!avcctx->codec)
|
|
||||||
{
|
|
||||||
avcctx->codec = avcodec_find_decoder(avcctx->codec_id);
|
|
||||||
if (!avcctx->codec)
|
|
||||||
fprintf(stderr, "avcodec_find_decoder failed for subtitle track %d\n", n);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int err = avcodec_open2(avcctx, avcctx->codec, NULL);
|
|
||||||
if (averror(err, avcodec_open2))
|
|
||||||
avcctx->codec = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (avcctx->codec)
|
|
||||||
player->manager.addSubtitleTrack(track);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "not handled or unknown codec_type %d\n", avcctx->codec_type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int n = 0; n < avfc->nb_programs; n++)
|
|
||||||
{
|
|
||||||
AVProgram *p = avfc->programs[n];
|
|
||||||
if (p->nb_stream_indexes)
|
|
||||||
{
|
|
||||||
AVDictionaryEntry *name = av_dict_get(p->metadata, "name", NULL, 0);
|
|
||||||
Program program;
|
|
||||||
program.title = name ? name->value : "";
|
|
||||||
program.id = p->id;
|
|
||||||
for (unsigned m = 0; m < p->nb_stream_indexes; m++)
|
|
||||||
program.streams.push_back(avfc->streams[p->stream_index[m]]);
|
|
||||||
player->manager.addProgram(program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Input::Stop()
|
|
||||||
{
|
|
||||||
abortPlayback = true;
|
|
||||||
|
|
||||||
while (hasPlayThreadStarted != 0)
|
|
||||||
usleep(100000);
|
|
||||||
|
|
||||||
av_log(NULL, AV_LOG_QUIET, "%s", "");
|
|
||||||
|
|
||||||
if (avfc)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mutex);
|
|
||||||
for (unsigned int i = 0; i < avfc->nb_streams; i++)
|
|
||||||
{
|
|
||||||
#if LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(57,25,100)
|
|
||||||
if (codecs[i].codec)
|
|
||||||
avcodec_free_context(&codecs[i].codec);
|
|
||||||
#else
|
|
||||||
avcodec_close(avfc->streams[i]->codec);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
avformat_close_input(&avfc);
|
|
||||||
}
|
|
||||||
|
|
||||||
avformat_network_deinit();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFormatContext *Input::GetAVFormatContext()
|
|
||||||
{
|
|
||||||
mutex.lock();
|
|
||||||
if (avfc)
|
|
||||||
return avfc;
|
|
||||||
mutex.unlock();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Input::ReleaseAVFormatContext()
|
|
||||||
{
|
|
||||||
if (avfc)
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (avfc)
|
|
||||||
{
|
|
||||||
duration = avfc->duration;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
duration = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Input::SwitchAudio(Track *track)
|
|
||||||
{
|
|
||||||
audioTrack = track;
|
|
||||||
player->output.SwitchAudio(track ? track : NULL);
|
|
||||||
player->Seek((int64_t)(-8 * AV_TIME_BASE), 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;
|
|
||||||
player->output.SwitchVideo(track ? track : NULL);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the first attached picture, if available
|
|
||||||
for (unsigned int i = 0; i < avfc->nb_streams; i++)
|
|
||||||
{
|
|
||||||
if (avfc->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)
|
|
||||||
{
|
|
||||||
AVPacket *pkt = &avfc->streams[i]->attached_pic;
|
|
||||||
FILE *cover_art = fopen("/tmp/.id3coverart", "wb");
|
|
||||||
if (cover_art)
|
|
||||||
{
|
|
||||||
fwrite(pkt->data, pkt->size, 1, cover_art);
|
|
||||||
fclose(cover_art);
|
|
||||||
}
|
|
||||||
av_packet_unref(pkt);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Input::GetReadCount(uint64_t &readcount)
|
|
||||||
{
|
|
||||||
readcount = readCount;
|
|
||||||
return true;
|
|
||||||
}
|
|
@@ -1,279 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 && !it->second->hidden)
|
|
||||||
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::addProgram(Program &program)
|
|
||||||
{
|
|
||||||
Programs[program.id] = program;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Program> Manager::getPrograms(void)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
|
||||||
std::vector<Program> res;
|
|
||||||
for (std::map<int, Program>::iterator it = Programs.begin(); it != Programs.end(); ++it)
|
|
||||||
res.push_back(it->second);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Manager::selectProgram(const int id)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
|
||||||
std::map<int, Program>::iterator i = Programs.find(id);
|
|
||||||
if (i != Programs.end())
|
|
||||||
{
|
|
||||||
|
|
||||||
// mark all tracks as hidden
|
|
||||||
for (std::map<int, Track *>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
|
||||||
it->second->hidden = true;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
|
||||||
it->second->hidden = true;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
|
||||||
it->second->hidden = true;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
|
||||||
it->second->hidden = true;
|
|
||||||
|
|
||||||
// unhide tracks that are part of the selected program
|
|
||||||
for (unsigned int j = 0; j < i->second.streams.size(); j++)
|
|
||||||
{
|
|
||||||
AVStream *stream = i->second.streams[j];
|
|
||||||
bool h = true;
|
|
||||||
for (std::map<int, Track *>::iterator it = audioTracks.begin(); h && (it != audioTracks.end()); ++it)
|
|
||||||
if (stream == it->second->stream)
|
|
||||||
h = it->second->hidden = false;
|
|
||||||
|
|
||||||
if (!h)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = videoTracks.begin(); h && (it != videoTracks.end()); ++it)
|
|
||||||
if (stream == it->second->stream)
|
|
||||||
h = it->second->hidden = false;
|
|
||||||
|
|
||||||
if (!h)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = subtitleTracks.begin(); h && (it != subtitleTracks.end()); ++it)
|
|
||||||
if (stream == it->second->stream)
|
|
||||||
h = it->second->hidden = false;
|
|
||||||
|
|
||||||
if (!h)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = teletextTracks.begin(); h && (it != teletextTracks.end()); ++it)
|
|
||||||
if (stream == it->second->stream)
|
|
||||||
h = it->second->hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tell ffmpeg what we're interested in
|
|
||||||
for (std::map<int, Track *>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
|
||||||
if (it->second->hidden || it->second->inactive)
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_ALL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_NONE;
|
|
||||||
player->input.SwitchAudio(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
|
||||||
if (it->second->hidden || it->second->inactive)
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_ALL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_NONE;
|
|
||||||
player->input.SwitchVideo(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
|
||||||
if (it->second->hidden || it->second->inactive)
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_ALL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_NONE;
|
|
||||||
player->input.SwitchSubtitle(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::map<int, Track *>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
|
||||||
if (it->second->hidden || it->second->inactive)
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_ALL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it->second->stream->discard = AVDISCARD_NONE;
|
|
||||||
player->input.SwitchTeletext(it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
Programs.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Manager::~Manager()
|
|
||||||
{
|
|
||||||
clearTracks();
|
|
||||||
}
|
|
@@ -1,384 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "player.h"
|
|
||||||
#include "output.h"
|
|
||||||
#include "writer.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "pes.h"
|
|
||||||
|
|
||||||
static const char *FILENAME = "eplayer/output.cpp";
|
|
||||||
|
|
||||||
#define dioctl(fd,req,arg) ({ \
|
|
||||||
int _r = ioctl(fd,req,arg); \
|
|
||||||
if (_r) \
|
|
||||||
fprintf(stderr, "%s %d: ioctl '%s' failed: %d (%s)\n", FILENAME, __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;
|
|
||||||
videoTrack = audioTrack = 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
videoTrack = NULL;
|
|
||||||
audioTrack = NULL;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Output::Play()
|
|
||||||
{
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
|
||||||
|
|
||||||
if (videoTrack && videoTrack->stream && videofd > -1 && get_codecpar(videoTrack->stream))
|
|
||||||
{
|
|
||||||
videoWriter = Writer::GetWriter(get_codecpar(videoTrack->stream)->codec_id, get_codecpar(videoTrack->stream)->codec_type, videoTrack->ac3flags);
|
|
||||||
videoWriter->Init(videofd, videoTrack->stream, player);
|
|
||||||
if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(get_codecpar(videoTrack->stream)->codec_id))
|
|
||||||
|| dioctl(videofd, VIDEO_PLAY, NULL))
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioTrack && audioTrack->stream && audiofd > -1 && get_codecpar(audioTrack->stream))
|
|
||||||
{
|
|
||||||
audioWriter = Writer::GetWriter(get_codecpar(audioTrack->stream)->codec_id, get_codecpar(audioTrack->stream)->codec_type, audioTrack->ac3flags);
|
|
||||||
audioWriter->Init(audiofd, audioTrack->stream, player);
|
|
||||||
audio_encoding_t audioEncoding = AUDIO_ENCODING_LPCMA;
|
|
||||||
if (audioTrack->ac3flags != 6)
|
|
||||||
audioEncoding = audioWriter->GetAudioEncoding(get_codecpar(audioTrack->stream)->codec_id);
|
|
||||||
if (dioctl(audiofd, AUDIO_SET_ENCODING, audioEncoding)
|
|
||||||
|| 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 && audioWriter)
|
|
||||||
{
|
|
||||||
// flush audio decoder
|
|
||||||
AVPacket packet;
|
|
||||||
packet.data = NULL;
|
|
||||||
packet.size = 0;
|
|
||||||
audioWriter->Write(&packet, 0);
|
|
||||||
|
|
||||||
if (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(Track *track)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
|
||||||
if (audioTrack && track->stream == audioTrack->stream)
|
|
||||||
return true;
|
|
||||||
if (audiofd > -1)
|
|
||||||
{
|
|
||||||
dioctl(audiofd, AUDIO_STOP, NULL);
|
|
||||||
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
|
|
||||||
}
|
|
||||||
audioTrack = track;
|
|
||||||
if (track->stream)
|
|
||||||
{
|
|
||||||
if (!get_codecpar(audioTrack->stream))
|
|
||||||
return false;
|
|
||||||
audioWriter = Writer::GetWriter(get_codecpar(audioTrack->stream)->codec_id, get_codecpar(audioTrack->stream)->codec_type, audioTrack->ac3flags);
|
|
||||||
audioWriter->Init(audiofd, audioTrack->stream, player);
|
|
||||||
if (audiofd > -1)
|
|
||||||
{
|
|
||||||
audio_encoding_t audioEncoding = AUDIO_ENCODING_LPCMA;
|
|
||||||
if (audioTrack->ac3flags != 6)
|
|
||||||
audioEncoding = Writer::GetAudioEncoding(get_codecpar(audioTrack->stream)->codec_id);
|
|
||||||
dioctl(audiofd, AUDIO_SET_ENCODING, audioEncoding);
|
|
||||||
dioctl(audiofd, AUDIO_PLAY, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Output::SwitchVideo(Track *track)
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
|
||||||
if (videoTrack && track->stream == videoTrack->stream)
|
|
||||||
return true;
|
|
||||||
if (videofd > -1)
|
|
||||||
{
|
|
||||||
dioctl(videofd, VIDEO_STOP, NULL);
|
|
||||||
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
|
|
||||||
}
|
|
||||||
videoTrack = track;
|
|
||||||
if (track->stream)
|
|
||||||
{
|
|
||||||
if (!get_codecpar(videoTrack->stream))
|
|
||||||
return false;
|
|
||||||
videoWriter = Writer::GetWriter(get_codecpar(videoTrack->stream)->codec_id, get_codecpar(videoTrack->stream)->codec_type, videoTrack->type);
|
|
||||||
videoWriter->Init(videofd, videoTrack->stream, player);
|
|
||||||
if (videofd > -1)
|
|
||||||
{
|
|
||||||
dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(get_codecpar(videoTrack->stream)->codec_id));
|
|
||||||
dioctl(videofd, VIDEO_PLAY, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Output::Write(AVStream *stream, AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
switch (get_codecpar(stream)->codec_type)
|
|
||||||
{
|
|
||||||
case AVMEDIA_TYPE_VIDEO:
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
|
|
||||||
return videofd > -1 && videoWriter && videoWriter->Write(packet, pts);
|
|
||||||
}
|
|
||||||
case AVMEDIA_TYPE_AUDIO:
|
|
||||||
{
|
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
|
|
||||||
return audiofd > -1 && audioWriter && audioWriter->Write(packet, pts);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,492 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <sstream>
|
|
||||||
|
|
||||||
#include "player.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
static const char *FILENAME = "eplayer/player.cpp";
|
|
||||||
|
|
||||||
#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, std::string headers)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
isHttp = strncmp("file://", Url, 7);
|
|
||||||
}
|
|
||||||
else if (!strncmp(Url, "bluray:/", 8))
|
|
||||||
{
|
|
||||||
url = Url;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %s %d: Unknown stream (%s)\n", FILENAME, __func__, __LINE__, Url);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.Init(url.c_str(), headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
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", FILENAME, __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)
|
|
||||||
{
|
|
||||||
if (GetVideoPid())
|
|
||||||
/* Don't Clear if no video track */
|
|
||||||
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 track ? input.SwitchAudio(track) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Player::GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values)
|
|
||||||
{
|
|
||||||
keys.clear();
|
|
||||||
values.clear();
|
|
||||||
|
|
||||||
std::vector<Program> p = manager.getPrograms();
|
|
||||||
|
|
||||||
if (p.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (std::vector<Program>::iterator it = p.begin(); it != p.end(); ++it)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << it->id;
|
|
||||||
keys.push_back(s.str());
|
|
||||||
values.push_back(it->title);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Player::SelectProgram(int key)
|
|
||||||
{
|
|
||||||
return manager.selectProgram(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Player::SelectProgram(std::string &key)
|
|
||||||
{
|
|
||||||
return manager.selectProgram(atoi(key.c_str()));
|
|
||||||
}
|
|
@@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
* linuxdvb output/writer handling.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
|
|
||||||
* Copyright (C) 2014 DboxOldie (based on code from libeplayer3)
|
|
||||||
* inspired by 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 AAC_HEADER_LENGTH 7
|
|
||||||
#define AAC_DEBUG 0
|
|
||||||
|
|
||||||
#if AAC_DEBUG
|
|
||||||
static inline void Hexdump(unsigned char *Data, int length)
|
|
||||||
{
|
|
||||||
int k;
|
|
||||||
for (k = 0; k < length; k++)
|
|
||||||
{
|
|
||||||
printf("%02x ", Data[k]);
|
|
||||||
if (((k + 1) & 31) == 0)
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int aac_get_sample_rate_index(uint32_t sample_rate)
|
|
||||||
{
|
|
||||||
if (96000 <= sample_rate)
|
|
||||||
return 0;
|
|
||||||
else if (88200 <= sample_rate)
|
|
||||||
return 1;
|
|
||||||
else if (64000 <= sample_rate)
|
|
||||||
return 2;
|
|
||||||
else if (48000 <= sample_rate)
|
|
||||||
return 3;
|
|
||||||
else if (44100 <= sample_rate)
|
|
||||||
return 4;
|
|
||||||
else if (32000 <= sample_rate)
|
|
||||||
return 5;
|
|
||||||
else if (24000 <= sample_rate)
|
|
||||||
return 6;
|
|
||||||
else if (22050 <= sample_rate)
|
|
||||||
return 7;
|
|
||||||
else if (16000 <= sample_rate)
|
|
||||||
return 8;
|
|
||||||
else if (12000 <= sample_rate)
|
|
||||||
return 9;
|
|
||||||
else if (11025 <= sample_rate)
|
|
||||||
return 10;
|
|
||||||
else if (8000 <= sample_rate)
|
|
||||||
return 11;
|
|
||||||
else if (7350 <= sample_rate)
|
|
||||||
return 12;
|
|
||||||
else
|
|
||||||
return 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static unsigned char DefaultAACHeader[] = {0xff, 0xf1, 0x50, 0x80, 0x00, 0x1f, 0xfc};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class WriterAAC : public Writer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
uint8_t aacbuf[8];
|
|
||||||
unsigned int aacbuflen;
|
|
||||||
AVStream *stream;
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
void Init(int _fd, AVStream *_stream, Player *_player);
|
|
||||||
WriterAAC();
|
|
||||||
};
|
|
||||||
|
|
||||||
void WriterAAC::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
#if AAC_DEBUG
|
|
||||||
printf("Create AAC ExtraData\n");
|
|
||||||
printf("stream->codec->extradata_size %d\n", stream->codec->extradata_size);
|
|
||||||
Hexdump(stream->codec->extradata, stream->codec->extradata_size);
|
|
||||||
#endif
|
|
||||||
unsigned int object_type = 2; // LC
|
|
||||||
unsigned int sample_index = aac_get_sample_rate_index(stream->codec->sample_rate);
|
|
||||||
unsigned int chan_config = stream->codec->channels;
|
|
||||||
if (stream->codec->extradata_size >= 2)
|
|
||||||
{
|
|
||||||
object_type = stream->codec->extradata[0] >> 3;
|
|
||||||
sample_index = ((stream->codec->extradata[0] & 0x7) << 1) + (stream->codec->extradata[1] >> 7);
|
|
||||||
chan_config = (stream->codec->extradata[1] >> 3) && 0xf;
|
|
||||||
}
|
|
||||||
#if AAC_DEBUG
|
|
||||||
printf("aac object_type %d\n", object_type);
|
|
||||||
printf("aac sample_index %d\n", sample_index);
|
|
||||||
printf("aac chan_config %d\n", chan_config);
|
|
||||||
#endif
|
|
||||||
object_type -= 1; // Cause of ADTS
|
|
||||||
aacbuflen = AAC_HEADER_LENGTH;
|
|
||||||
aacbuf[0] = 0xFF;
|
|
||||||
aacbuf[1] = 0xF1;
|
|
||||||
aacbuf[2] = ((object_type & 0x03) << 6) | (sample_index << 2) | ((chan_config >> 2) & 0x01);
|
|
||||||
aacbuf[3] = (chan_config & 0x03) << 6;
|
|
||||||
aacbuf[4] = 0x00;
|
|
||||||
aacbuf[5] = 0x1F;
|
|
||||||
aacbuf[6] = 0xFC;
|
|
||||||
aacbuf[7] = 0x00;
|
|
||||||
#if AAC_DEBUG
|
|
||||||
printf("AAC_HEADER -> ");
|
|
||||||
Hexdump(aacbuf, 7);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterAAC::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
|
||||||
uint8_t ExtraData[AAC_HEADER_LENGTH];
|
|
||||||
|
|
||||||
for (int pos = 0; pos < packet->size + AAC_HEADER_LENGTH;)
|
|
||||||
{
|
|
||||||
int PacketLength = std::min(packet->size - pos + AAC_HEADER_LENGTH, MAX_PES_PACKET_SIZE);
|
|
||||||
|
|
||||||
memcpy(ExtraData, aacbuf, AAC_HEADER_LENGTH);
|
|
||||||
|
|
||||||
// ExtraData[3] |= (PacketLength >> 11) & 0x3;
|
|
||||||
ExtraData[4] = (PacketLength >> 3) & 0xff;
|
|
||||||
ExtraData[5] |= (PacketLength << 5) & 0xe0;
|
|
||||||
|
|
||||||
struct iovec iov[3];
|
|
||||||
iov[0].iov_base = PesHeader;
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, AAC_AUDIO_PES_START_CODE, pts, 0);
|
|
||||||
iov[1].iov_base = ExtraData;
|
|
||||||
iov[1].iov_len = AAC_HEADER_LENGTH;
|
|
||||||
iov[2].iov_base = packet->data;
|
|
||||||
iov[2].iov_len = packet->size;
|
|
||||||
|
|
||||||
ssize_t l = writev(fd, iov, 3);
|
|
||||||
#if AAC_DEBUG
|
|
||||||
// printf("Packet Size + AAC_HEADER_LENGTH= %d Packet Size= %d Written= %d\n", PacketLength, packet->size, l);
|
|
||||||
#endif
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
pos += PacketLength;
|
|
||||||
pts = INVALID_PTS_VALUE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriterAAC::WriterAAC()
|
|
||||||
{
|
|
||||||
Register(this, AV_CODEC_ID_AAC, AUDIO_ENCODING_AAC);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterAAC writer_aac __attribute__((init_priority(300)));
|
|
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 WriterAC3 : public Writer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
WriterAC3();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool WriterAC3::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
|
||||||
|
|
||||||
for (int pos = 0; pos < packet->size;)
|
|
||||||
{
|
|
||||||
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
|
|
||||||
struct iovec iov[2];
|
|
||||||
iov[0].iov_base = PesHeader;
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, PRIVATE_STREAM_1_PES_START_CODE, pts, 0);
|
|
||||||
iov[1].iov_base = packet->data + pos;
|
|
||||||
iov[1].iov_len = PacketLength;
|
|
||||||
|
|
||||||
ssize_t l = writev(fd, iov, 2);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
pos += PacketLength;
|
|
||||||
pts = INVALID_PTS_VALUE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)));
|
|
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
AVStream *stream;
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
void Init(int fd, AVStream *_stream, Player *player);
|
|
||||||
WriterDIVX();
|
|
||||||
};
|
|
||||||
|
|
||||||
void WriterDIVX::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
initialHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterDIVX::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
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 = get_codecpar(stream)->extradata;
|
|
||||||
iov[ic++].iov_len = get_codecpar(stream)->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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterDIVX writer_divx __attribute__((init_priority(300)));
|
|
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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(AVPacket *packet, int64_t pts);
|
|
||||||
WriterDTS();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool WriterDTS::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
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)));
|
|
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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(AVPacket *packet, int64_t pts);
|
|
||||||
WriterH263();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool WriterH263::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
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)));
|
|
@@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24 // Reference: player/standards/h264.h
|
|
||||||
#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;
|
|
||||||
|
|
||||||
class WriterH264 : public Writer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
bool initialHeader;
|
|
||||||
unsigned int NalLengthBytes;
|
|
||||||
AVStream *stream;
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
void Init(int _fd, AVStream *_stream, Player *_player);
|
|
||||||
WriterH264();
|
|
||||||
};
|
|
||||||
|
|
||||||
void WriterH264::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
initialHeader = true;
|
|
||||||
NalLengthBytes = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterH264::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
return false;
|
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
|
||||||
struct iovec iov[512];
|
|
||||||
|
|
||||||
uint8_t *d = packet->data;
|
|
||||||
|
|
||||||
// byte-stream format
|
|
||||||
if ((packet->size > 3) && ((d[0] == 0x00 && d[1] == 0x00 && d[2] == 0x00 && d[3] == 0x01) // first NAL unit
|
|
||||||
|| (d[0] == 0xff && d[1] == 0xff && d[2] == 0xff && d[3] == 0xff) // FIXME, needed???
|
|
||||||
))
|
|
||||||
{
|
|
||||||
unsigned int FakeStartCode = /* (call->Version << 8) | */ PES_VERSION_FAKE_START_CODE;
|
|
||||||
int ic = 0;
|
|
||||||
iov[ic++].iov_base = PesHeader;
|
|
||||||
unsigned int len = 0;
|
|
||||||
if (initialHeader)
|
|
||||||
{
|
|
||||||
initialHeader = false;
|
|
||||||
iov[ic].iov_base = get_codecpar(stream)->extradata;
|
|
||||||
iov[ic++].iov_len = get_codecpar(stream)->extradata_size;
|
|
||||||
len += get_codecpar(stream)->extradata_size;
|
|
||||||
}
|
|
||||||
iov[ic].iov_base = packet->data;
|
|
||||||
iov[ic++].iov_len = packet->size;
|
|
||||||
len += packet->size;
|
|
||||||
#if 1 // FIXME: needed?
|
|
||||||
// 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;
|
|
||||||
len++;
|
|
||||||
#endif
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
|
|
||||||
return writev(fd, iov, ic) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert NAL units without sync byte sequence to byte-stream format
|
|
||||||
if (initialHeader)
|
|
||||||
{
|
|
||||||
avcC_t *avcCHeader = (avcC_t *) get_codecpar(stream)->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);
|
|
||||||
|
|
||||||
// The player will use FrameRate and TimeScale to calculate the default frame rate.
|
|
||||||
// FIXME: TimeDelta should be used instead of FrameRate. This is a historic implementation bug.
|
|
||||||
// Reference: player/frame_parser/frame_parser_video_h264.cpp FrameParser_VideoH264_c::ReadPlayer2ContainerParameters()
|
|
||||||
unsigned int FrameRate = av_rescale(1000ll, stream->r_frame_rate.num, stream->r_frame_rate.den);
|
|
||||||
unsigned int TimeScale = (FrameRate < 23970) ? 1001 : 1000; /* FIXME: revise this */
|
|
||||||
|
|
||||||
uint8_t Header[20];
|
|
||||||
unsigned int len = 0;
|
|
||||||
Header[len++] = 0x00; // Start code, 00 00 00 01 for first NAL unit
|
|
||||||
Header[len++] = 0x00;
|
|
||||||
Header[len++] = 0x00;
|
|
||||||
Header[len++] = 0x01;
|
|
||||||
Header[len++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS; // NAL unit header
|
|
||||||
// Container message version - changes when/if we vary the format of the message
|
|
||||||
Header[len++] = CONTAINER_PARAMETERS_VERSION;
|
|
||||||
Header[len++] = 0xff; // marker bits
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (FrameRate == 0xffffffff)
|
|
||||||
FrameRate = (TimeScale > 1000) ? 1001 : 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Header[len++] = (TimeScale >> 24) & 0xff; // Output the timescale
|
|
||||||
Header[len++] = (TimeScale >> 16) & 0xff;
|
|
||||||
Header[len++] = 0xff; // marker bits
|
|
||||||
Header[len++] = (TimeScale >> 8) & 0xff;
|
|
||||||
Header[len++] = (TimeScale) & 0xff;
|
|
||||||
Header[len++] = 0xff; // marker bits
|
|
||||||
|
|
||||||
Header[len++] = (FrameRate >> 24) & 0xff; // Output frame period (should be: time delta)
|
|
||||||
Header[len++] = (FrameRate >> 16) & 0xff;
|
|
||||||
Header[len++] = 0xff; // marker bits
|
|
||||||
Header[len++] = (FrameRate >> 8) & 0xff;
|
|
||||||
Header[len++] = (FrameRate) & 0xff;
|
|
||||||
Header[len++] = 0xff; // marker bits
|
|
||||||
|
|
||||||
Header[len++] = 0x80; // Rsbp trailing bits
|
|
||||||
|
|
||||||
int ic = 0;
|
|
||||||
iov[ic].iov_base = PesHeader;
|
|
||||||
iov[ic++].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
|
||||||
iov[ic].iov_base = Header;
|
|
||||||
iov[ic++].iov_len = len;
|
|
||||||
if (writev(fd, iov, ic) < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ic = 0;
|
|
||||||
iov[ic++].iov_base = PesHeader;
|
|
||||||
|
|
||||||
NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1;
|
|
||||||
unsigned int ParamOffset = 0;
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
// sequence parameter set
|
|
||||||
unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f;
|
|
||||||
for (unsigned int i = 0; i < ParamSets; i++)
|
|
||||||
{
|
|
||||||
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) | avcCHeader->Params[ParamOffset + 1];
|
|
||||||
|
|
||||||
iov[ic].iov_base = (uint8_t *) "\0\0\0\1";
|
|
||||||
iov[ic++].iov_len = 4;
|
|
||||||
len += 4;
|
|
||||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
|
||||||
iov[ic++].iov_len = PsLength;
|
|
||||||
len += PsLength;
|
|
||||||
ParamOffset += PsLength + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// picture parameter set
|
|
||||||
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 = (uint8_t *) "\0\0\0\1";
|
|
||||||
iov[ic++].iov_len = 4;
|
|
||||||
len += 4;
|
|
||||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
|
||||||
iov[ic++].iov_len = PsLength;
|
|
||||||
len += PsLength;
|
|
||||||
ParamOffset += PsLength + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
|
||||||
ssize_t l = writev(fd, iov, ic);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
initialHeader = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *de = d + packet->size;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
unsigned int len = 0;
|
|
||||||
switch (NalLengthBytes)
|
|
||||||
{
|
|
||||||
case 4:
|
|
||||||
len = *d;
|
|
||||||
d++;
|
|
||||||
case 3:
|
|
||||||
len <<= 8;
|
|
||||||
len |= *d;
|
|
||||||
d++;
|
|
||||||
case 2:
|
|
||||||
len <<= 8;
|
|
||||||
len |= *d;
|
|
||||||
d++;
|
|
||||||
default:
|
|
||||||
len <<= 8;
|
|
||||||
len |= *d;
|
|
||||||
d++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d + len > de)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "NAL length past end of buffer - size %u frame offset %d left %d\n", len, (int)(d - packet->data), (int)(de - d));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ic = 0;
|
|
||||||
iov[ic++].iov_base = PesHeader;
|
|
||||||
iov[ic].iov_base = (uint8_t *) "\0\0\0\1";
|
|
||||||
iov[ic++].iov_len = 4;
|
|
||||||
|
|
||||||
iov[ic].iov_base = d;
|
|
||||||
iov[ic++].iov_len = len;
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, len + 3, MPEG_VIDEO_PES_START_CODE, pts, 0);
|
|
||||||
ssize_t l = writev(fd, iov, ic);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
d += len;
|
|
||||||
pts = INVALID_PTS_VALUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
while (d < de);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriterH264::WriterH264()
|
|
||||||
{
|
|
||||||
Register(this, AV_CODEC_ID_H264, VIDEO_ENCODING_H264);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterH264 writerh264 __attribute__((init_priority(300)));
|
|
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
@@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 WriterMP3 : public Writer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
WriterMP3();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool WriterMP3::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
|
||||||
|
|
||||||
for (int pos = 0; pos < packet->size;)
|
|
||||||
{
|
|
||||||
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
|
|
||||||
struct iovec iov[2];
|
|
||||||
iov[0].iov_base = PesHeader;
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, MPEG_AUDIO_PES_START_CODE, pts, 0);
|
|
||||||
iov[1].iov_base = packet->data + pos;
|
|
||||||
iov[1].iov_len = PacketLength;
|
|
||||||
|
|
||||||
ssize_t l = writev(fd, iov, 2);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
pos += PacketLength;
|
|
||||||
pts = INVALID_PTS_VALUE;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
Register(this, AV_CODEC_ID_FLAC, AUDIO_ENCODING_LPCM);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterMP3 writer_mp3 __attribute__((init_priority(300)));
|
|
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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(AVPacket *packet, int64_t pts);
|
|
||||||
WriterMPEG2();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool WriterMPEG2::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
|
||||||
|
|
||||||
for (int pos = 0; pos < packet->size;)
|
|
||||||
{
|
|
||||||
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
|
|
||||||
struct iovec iov[2];
|
|
||||||
iov[0].iov_base = PesHeader;
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, MPEG_VIDEO_PES_START_CODE, pts, 0);
|
|
||||||
iov[1].iov_base = packet->data + pos;
|
|
||||||
iov[1].iov_len = PacketLength;
|
|
||||||
|
|
||||||
ssize_t l = writev(fd, iov, 2);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
pos += 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)));
|
|
@@ -1,451 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
#include "player.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)];
|
|
||||||
uint8_t *output;
|
|
||||||
uint8_t out_samples_max;
|
|
||||||
unsigned int breakBufferFillSize;
|
|
||||||
int uNoOfChannels;
|
|
||||||
int uSampleRate;
|
|
||||||
int uBitsPerSample;
|
|
||||||
|
|
||||||
AVStream *stream;
|
|
||||||
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(AVPacket *packet, int64_t pts);
|
|
||||||
bool prepareClipPlay();
|
|
||||||
bool writePCM(int64_t Pts, uint8_t *data, unsigned int size);
|
|
||||||
void Init(int _fd, AVStream *_stream, Player *_player);
|
|
||||||
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
|
|
||||||
SubFramesPerPES = ((sizeof(injectBuffer) - 14) - 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(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 && res)
|
|
||||||
{
|
|
||||||
breakBufferFillSize = size;
|
|
||||||
memcpy(breakBuffer, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriterPCM::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
initialHeader = true;
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterPCM::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *c = player->input.GetCodecContext((unsigned int)stream->index);
|
|
||||||
|
|
||||||
if (restart_audio_resampling)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = false;
|
|
||||||
initialHeader = true;
|
|
||||||
|
|
||||||
if (swr)
|
|
||||||
{
|
|
||||||
swr_free(&swr);
|
|
||||||
swr = NULL;
|
|
||||||
}
|
|
||||||
if (decoded_frame)
|
|
||||||
{
|
|
||||||
av_frame_free(&decoded_frame);
|
|
||||||
decoded_frame = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57,25,101)
|
|
||||||
AVCodec *codec = avcodec_find_decoder(c->codec_id);
|
|
||||||
if (!codec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %d: avcodec_find_decoder(%llx)\n", __func__, __LINE__, (unsigned long long) c->codec_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
avcodec_close(c);
|
|
||||||
if (avcodec_open2(c, codec, NULL))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s %d: avcodec_open2 failed\n", __func__, __LINE__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!swr)
|
|
||||||
{
|
|
||||||
int in_rate = c->sample_rate;
|
|
||||||
// rates in descending order
|
|
||||||
int rates[] = {192000, 176400, 96000, 88200, 48000, 44100, 0};
|
|
||||||
int i = 0;
|
|
||||||
// find the next equal or smallest rate
|
|
||||||
while (rates[i] && in_rate < rates[i])
|
|
||||||
i++;
|
|
||||||
out_sample_rate = rates[i] ? rates[i] : 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);
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int packet_size = packet->size;
|
|
||||||
while (packet_size > 0 || (!packet_size && !packet->data))
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!decoded_frame)
|
|
||||||
{
|
|
||||||
if (!(decoded_frame = av_frame_alloc()))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
av_frame_unref(decoded_frame);
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,37,100)
|
|
||||||
int got_frame = 0;
|
|
||||||
|
|
||||||
int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, packet);
|
|
||||||
if (len < 0)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet->data)
|
|
||||||
packet_size -= len;
|
|
||||||
|
|
||||||
if (!got_frame)
|
|
||||||
{
|
|
||||||
if (!packet->data || !packet_size)
|
|
||||||
break;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int ret = avcodec_send_packet(c, packet);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
packet_size = 0;
|
|
||||||
}
|
|
||||||
ret = avcodec_receive_frame(c, decoded_frame);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
if (ret == AVERROR_EOF)
|
|
||||||
{
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ret != AVERROR(EAGAIN))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (LIBAVUTIL_VERSION_MAJOR < 54)
|
|
||||||
pts = player->input.calcPts(stream, av_frame_get_best_effort_timestamp(decoded_frame));
|
|
||||||
#else
|
|
||||||
pts = player->input.calcPts(stream, decoded_frame->best_effort_timestamp);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (out_samples > out_samples_max)
|
|
||||||
{
|
|
||||||
if (output)
|
|
||||||
av_freep(&output);
|
|
||||||
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_max = out_samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **) &decoded_frame->data[0], in_samples);
|
|
||||||
|
|
||||||
if (!writePCM(pts, output, out_samples * sizeof(short) * out_channels))
|
|
||||||
{
|
|
||||||
restart_audio_resampling = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
av_frame_free(&decoded_frame);
|
|
||||||
|
|
||||||
return !packet_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriterPCM::WriterPCM()
|
|
||||||
{
|
|
||||||
swr = NULL;
|
|
||||||
output = NULL;
|
|
||||||
out_samples_max = 0;
|
|
||||||
decoded_frame = av_frame_alloc();
|
|
||||||
|
|
||||||
Register(this, AV_CODEC_ID_INJECTPCM, AUDIO_ENCODING_LPCMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterPCM writer_pcm __attribute__((init_priority(300)));
|
|
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 + 5 = 19
|
|
||||||
}
|
|
||||||
|
|
||||||
FlushBits(&ld2);
|
|
||||||
|
|
||||||
return (ld2.Ptr - data);
|
|
||||||
}
|
|
@@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
#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;
|
|
||||||
AVStream *stream;
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
void Init(int _fd, AVStream *_stream, Player *_player);
|
|
||||||
WriterVC1();
|
|
||||||
};
|
|
||||||
|
|
||||||
void WriterVC1::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
initialHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterVC1::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
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++ = (get_codecpar(stream)->height >> 0) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->height >> 8) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->height >> 16) & 0xff;
|
|
||||||
*PesPtr++ = get_codecpar(stream)->height >> 24;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 0) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 8) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 16) & 0xff;
|
|
||||||
*PesPtr++ = get_codecpar(stream)->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 = get_codecpar(stream)->extradata;
|
|
||||||
iov[1].iov_len = get_codecpar(stream)->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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterVC1 writer_vc1 __attribute__((init_priority(300)));
|
|
@@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <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;
|
|
||||||
AVStream *stream;
|
|
||||||
public:
|
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
|
||||||
void Init(int _fd, AVStream *_stream, Player *_player);
|
|
||||||
WriterWMV();
|
|
||||||
};
|
|
||||||
|
|
||||||
void WriterWMV::Init(int _fd, AVStream *_stream, Player *_player)
|
|
||||||
{
|
|
||||||
fd = _fd;
|
|
||||||
stream = _stream;
|
|
||||||
player = _player;
|
|
||||||
initialHeader = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WriterWMV::Write(AVPacket *packet, int64_t pts)
|
|
||||||
{
|
|
||||||
if (!packet || !packet->data)
|
|
||||||
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, get_codecpar(stream)->extradata, get_codecpar(stream)->extradata_size > WMV3_PRIVATE_DATA_LENGTH ? WMV3_PRIVATE_DATA_LENGTH : get_codecpar(stream)->extradata_size);
|
|
||||||
|
|
||||||
memcpy(PesPtr, privateData, WMV3_PRIVATE_DATA_LENGTH);
|
|
||||||
PesPtr += WMV3_PRIVATE_DATA_LENGTH;
|
|
||||||
|
|
||||||
/* Metadata Header Struct A */
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->height >> 0) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->height >> 8) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->height >> 16) & 0xff;
|
|
||||||
*PesPtr++ = get_codecpar(stream)->height >> 24;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 0) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 8) & 0xff;
|
|
||||||
*PesPtr++ = (get_codecpar(stream)->width >> 16) & 0xff;
|
|
||||||
*PesPtr++ = get_codecpar(stream)->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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static WriterWMV writer_wmv __attribute__((init_priority(300)));
|
|
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "h263.cpp"
|
|
||||||
#include "h264.cpp"
|
|
||||||
#include "mp3.cpp"
|
|
||||||
#include "mpeg2.cpp"
|
|
||||||
#include "pcm.cpp"
|
|
||||||
#include "vc1.cpp"
|
|
||||||
#include "wmv.cpp"
|
|
||||||
//#include "aac.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(AVPacket * /* packet */, int64_t /* pts */)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Writer writer __attribute__((init_priority(300)));
|
|
||||||
|
|
||||||
Writer *Writer::GetWriter(enum AVCodecID id, enum AVMediaType codec_type, int track_type)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "GETWRITER %d %d %d", id, codec_type, track_type);
|
|
||||||
if (track_type != 6) // hack for ACC resampling
|
|
||||||
{
|
|
||||||
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, 100);
|
|
||||||
case AVMEDIA_TYPE_VIDEO:
|
|
||||||
if (id == AV_CODEC_ID_MPEG2TS) // should not happen
|
|
||||||
break;
|
|
||||||
return GetWriter(AV_CODEC_ID_MPEG2TS, codec_type, 100);
|
|
||||||
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;
|
|
||||||
}
|
|
Reference in New Issue
Block a user