summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INSTALL.md15
-rw-r--r--configure.ac15
-rw-r--r--doc/example.mux7
-rwxr-xr-xdoc/stats_dabmux_multi.py23
-rw-r--r--gui/muxconfig.py47
-rwxr-xr-xgui/odr-dabmux-gui.py47
-rw-r--r--gui/static/stats.js17
-rw-r--r--gui/views/configeditor.tpl29
-rw-r--r--gui/views/stats.tpl20
-rw-r--r--m4/ax_cxx_compile_stdcxx_11.m4165
-rw-r--r--src/ConfigParser.cpp134
-rw-r--r--src/ConfigParser.h16
-rw-r--r--src/DabMultiplexer.cpp1851
-rw-r--r--src/DabMultiplexer.h514
-rw-r--r--src/DabMux.cpp2030
-rw-r--r--src/DabMux.h364
-rw-r--r--src/Log.h4
-rw-r--r--src/Makefile.am11
-rw-r--r--src/ManagementServer.cpp237
-rw-r--r--src/ManagementServer.h77
-rw-r--r--src/MuxElements.cpp80
-rw-r--r--src/MuxElements.h64
-rw-r--r--src/RemoteControl.cpp134
-rw-r--r--src/RemoteControl.h45
-rw-r--r--src/TcpSocket.cpp2
-rw-r--r--src/UdpSocket.cpp2
-rw-r--r--src/UdpSocket.h2
-rw-r--r--src/crc.c6
-rw-r--r--src/dabInputZmq.cpp11
-rw-r--r--src/dabOutput/dabOutput.h42
-rw-r--r--src/dabOutput/dabOutputFifo.cpp1
-rw-r--r--src/dabOutput/dabOutputFile.cpp1
-rw-r--r--src/dabOutput/edi/AFPacket.cpp3
-rw-r--r--src/dabOutput/edi/AFPacket.h2
-rw-r--r--src/dabOutput/edi/PFT.cpp2
-rw-r--r--src/dabOutput/edi/PFT.h16
-rw-r--r--src/dabOutput/edi/TagPacket.cpp7
-rw-r--r--src/fig/FIG.h96
-rw-r--r--src/fig/FIG0.cpp483
-rw-r--r--src/fig/FIG0.h121
-rw-r--r--src/fig/FIGCarousel.cpp199
-rw-r--r--src/fig/FIGCarousel.h73
-rw-r--r--src/mpeg.c4
-rw-r--r--src/utils.cpp60
-rw-r--r--src/utils.h8
-rw-r--r--src/zmqinput-keygen.c1
46 files changed, 4352 insertions, 2736 deletions
diff --git a/INSTALL.md b/INSTALL.md
index 7e0f59b..73a3471 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -4,7 +4,9 @@ Required dependencies:
* libfec from Phil Karn, with compatibility patch:
[ka9q-fec](https://github.com/Opendigitalradio/ka9q-fec)
* Boost 1.48 or later
-* Optional ZeroMQ 4 from [http://www.zeromq.org](http://www.zeromq.org)
+* Optional ZeroMQ 4 from [http://www.zeromq.org](http://www.zeromq.org).
+ Please prefer the zeromq from your distribution, but mind that some distributions
+ ship ZeroMQ 2, which is not enough.
Use the --disable-output-zeromq ./configure option if you don't have ZeroMQ.
@@ -21,17 +23,6 @@ Install libfec
[as root]
% make install # Install the library
-Install zeromq 4.0.3
---------------------
-
- % wget http://download.zeromq.org/zeromq-4.0.3.tar.gz
- % tar -f zeromq-4.0.3.tar.gz -x
- % cd zeromq-4.0.3
- % ./configure
- % make
- [as root]
- % make install
-
Install odr-dabmux
------------------
diff --git a/configure.ac b/configure.ac
index cc8ce7a..bdc7328 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,10 +52,18 @@ AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_INSTALL
+AX_CXX_COMPILE_STDCXX_11(noext,mandatory)
+
# Checks for libraries.
AC_CHECK_LIB([pthread], [pthread_create], [], AC_MSG_ERROR([libpthread is required]))
AX_BOOST_BASE([1.41.0], [], AC_MSG_ERROR([BOOST 1.41 or later is required]))
+# we need to check for libm before checking for fec
+AC_CHECK_LIB([m], [sin])
+AC_SEARCH_LIBS([init_rs_char], [fec], [], [
+ AC_MSG_ERROR([unable to find libfec])
+])
+
# Checks for header files.
AC_MSG_CHECKING([for OS type])
AC_PREPROC_IFELSE(
@@ -188,6 +196,11 @@ AS_IF([test "x$enable_output_zeromq" = "xyes"],
AC_CHECK_LIB(zmq, zmq_init, [true], [AC_MSG_ERROR([ZeroMQ libzmq is required])]))
AS_IF([test "x$enable_output_zeromq" = "xyes"],
AC_DEFINE(HAVE_OUTPUT_ZEROMQ, [1], [Define if ZeroMQ output is enabled]))
+# EDI
+AC_ARG_ENABLE([output_edi],
+ AS_HELP_STRING([--enable-output-edi], [Enable EDI output]))
+AS_IF([test "x$enable_output_edi" = "xyes"],
+ [AC_DEFINE(HAVE_OUTPUT_EDI, [1], [Define if EDI output is enabled])])
# Link against lzmq
AM_CONDITIONAL([HAVE_ZEROMQ_TEST],
@@ -281,7 +294,7 @@ echo
echo "Outputs:"
enabled=""
disabled=""
-for output in file fifo udp tcp raw simul zeromq
+for output in file fifo udp tcp raw simul zeromq edi
do
eval var=\$enable_output_$output
AS_IF([test "x$var" = "xyes"],
diff --git a/doc/example.mux b/doc/example.mux
index abf01bc..1e6495b 100644
--- a/doc/example.mux
+++ b/doc/example.mux
@@ -296,6 +296,13 @@ outputs {
; Enable the PFT subsystem. If false, AFPackets are sent.
enable_pft false
+ ; How many lost fragments can be recovered by Reed-Solomon
+ fec 3
+
+ ; Length of a RS chunk, can be overriden
+ ;default=207
+ ;chunk_len 207
+
; Save the packets sent over ethernet to the file ./edi.debug
dump false
diff --git a/doc/stats_dabmux_multi.py b/doc/stats_dabmux_multi.py
index 7c21d84..a2587a4 100755
--- a/doc/stats_dabmux_multi.py
+++ b/doc/stats_dabmux_multi.py
@@ -5,11 +5,9 @@
import sys
import json
-import socket
+import zmq
import os
-SOCK_RECV_SIZE = 10240
-
config_top = """
"""
@@ -72,6 +70,8 @@ right.warning -40:0
right.critical -80:0
"""
+ctx = zmq.Context()
+
if not os.environ.get("MUNIN_CAP_MULTIGRAPH"):
print("This needs munin version 1.4 at least")
sys.exit(1)
@@ -81,10 +81,11 @@ def connect():
returns: the socket"""
- sock = socket.socket()
- sock.connect(("localhost", 12720))
+ sock = zmq.Socket(ctx, zmq.REQ)
+ sock.connect("tcp://localhost:12720")
- version = json.loads(sock.recv(SOCK_RECV_SIZE))
+ sock.send("info")
+ version = json.loads(sock.recv())
if not version['service'].startswith("ODR-DabMux"):
sys.stderr.write("Wrong version\n")
@@ -94,8 +95,8 @@ def connect():
if len(sys.argv) == 1:
sock = connect()
- sock.send("values\n")
- values = json.loads(sock.recv(SOCK_RECV_SIZE))['values']
+ sock.send("values")
+ values = json.loads(sock.recv())['values']
munin_values = ""
for ident in values:
@@ -115,9 +116,9 @@ if len(sys.argv) == 1:
elif len(sys.argv) == 2 and sys.argv[1] == "config":
sock = connect()
- sock.send("config\n")
+ sock.send("config")
- config = json.loads(sock.recv(SOCK_RECV_SIZE))
+ config = json.loads(sock.recv())
munin_config = config_top
@@ -125,4 +126,6 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config":
munin_config += config_template.format(ident=conf)
print(munin_config)
+else:
+ sys.exit(1)
diff --git a/gui/muxconfig.py b/gui/muxconfig.py
index 64321a2..4b307fd 100644
--- a/gui/muxconfig.py
+++ b/gui/muxconfig.py
@@ -20,7 +20,7 @@
#
# You should have received a copy of the GNU General Public License
# along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
-import socket
+import zmq
import json
class General(object):
@@ -95,23 +95,45 @@ class ConfigurationHandler(object):
# local copy of the configuration
self._server_version = None
self._config = None
+ self._statistics = None
+
+ self._ctx = zmq.Context()
+ self.sock = zmq.Socket(self._ctx, zmq.REQ)
+ self.sock.connect("tcp://{}:{}".format(self._host, self._port))
def load(self):
"""Load the configuration from the multiplexer and
save it locally"""
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.send(b'info')
+ server_info = self.sock.recv()
+
+ self.sock.send(b'getptree')
+ config_info = self.sock.recv()
+
+ print("Config '%r'" % config_info)
+
+ self._server_version = json.loads(server_info)['service']
+ self._config = json.loads(config_info)
- s.connect((self._host, self._port))
- s.sendall(b'getptree\n')
- server_info = s.recv(32768)
- config_info = s.recv(32768)
- s.close()
+ def update_stats(self):
+ """Load the statistics from the multiplexer and
+ save them locally"""
- self._server_version = json.loads(server_info.decode())['service']
- self._config = json.loads(config_info.decode())
+ self.sock.send(b'info')
+ server_info = self.sock.recv()
+
+ self.sock.send(b'values')
+ stats_info = self.sock.recv()
+
+ self._statistics = json.loads(stats_info)['values']
def get_full_configuration(self):
- return self._config
+ return json.dumps(self._config, indent=4)
+
+ def set_full_configuration(self, config_json):
+ self.sock.send(b'setptree', flags=zmq.SNDMORE)
+ self.sock.send(config_json)
+ return self.sock.recv() == "OK"
def get_mux_version(self):
return self._server_version
@@ -130,3 +152,8 @@ class ConfigurationHandler(object):
def get_general_options(self):
return General(self._config)
+
+ def get_stats_dict(self):
+ """Return a dictionary with all stats"""
+ self.update_stats()
+ return self._statistics
diff --git a/gui/odr-dabmux-gui.py b/gui/odr-dabmux-gui.py
index 84331b5..caa4ea7 100755
--- a/gui/odr-dabmux-gui.py
+++ b/gui/odr-dabmux-gui.py
@@ -28,13 +28,46 @@
# along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
from muxconfig import *
-from bottle import route, run, template, static_file
+from bottle import route, run, template, static_file, request
import json
conf = ConfigurationHandler('localhost')
@route('/config')
def config():
+ """Show the JSON ptree in a textbox for editing"""
+
+ conf.load()
+
+ return template('configeditor',
+ version = conf.get_mux_version(),
+ config = conf.get_full_configuration(),
+ message = "")
+
+@route('/config', method="POST")
+def config_json_post():
+ """Update the ODR-DabMux configuration with the JSON ptree
+ given as POST data"""
+
+ new_config = request.forms.get('config')
+ print("New config %s" % new_config)
+
+ success = conf.set_full_configuration(new_config)
+
+ if success:
+ successmessage = "Success"
+ else:
+ successmessage = "Failure"
+
+ conf.load()
+
+ return template('configeditor',
+ version = conf.get_mux_version(),
+ config = conf.get_full_configuration(),
+ message = successmessage)
+
+@route('/config.json', method="GET")
+def config_json_get():
"""Return a application/json containing the full
ptree of the mux"""
@@ -43,6 +76,7 @@ def config():
return {'version': conf.get_mux_version(),
'config': conf.get_full_configuration()}
+
@route('/')
def index():
conf.load()
@@ -62,6 +96,17 @@ def index():
version = conf.get_mux_version(),
services = conf.get_services())
+@route('/stats')
+def index():
+ conf.load()
+
+ return template('stats',
+ version = conf.get_mux_version())
+
+@route('/stats.json')
+def stats_json():
+ return conf.get_stats_dict()
+
@route('/static/<filename:path>')
def send_static(filename):
diff --git a/gui/static/stats.js b/gui/static/stats.js
new file mode 100644
index 0000000..7b07099
--- /dev/null
+++ b/gui/static/stats.js
@@ -0,0 +1,17 @@
+var updatefunc = function(event) {
+ $('#statdata p').remove();
+ $.getJSON('/stats.json', function(result) {
+ $.each(result, function(name) {
+ // TODO: use a hidden template inside the DOM instead
+ // of building the HTML here
+ $("<p></p>")
+ .append(result[name]['inputstat']['num_underruns'])
+ .appendTo('#statdata');
+ });
+ });
+}
+
+// Handle clicks on the to change visiblity of panes
+setInterval(updatefunc, 1000);
+
+
diff --git a/gui/views/configeditor.tpl b/gui/views/configeditor.tpl
new file mode 100644
index 0000000..9d1d193
--- /dev/null
+++ b/gui/views/configeditor.tpl
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>ODR-DabMux Configuration Editor</title>
+ <link rel="stylesheet" href="static/style.css" type="text/css" media="screen" charset="utf-8"/>
+ <script type="text/javascript" src="static/jquery-1.7.1.min.js"></script>
+</head>
+<body>
+ <h1>Configuration for {{version}}</h1>
+
+ <p><a href="/config">Reload</a></p>
+
+ % if message:
+ <p>{{message}}</p>
+ % end
+
+ <form action="/config" method="post">
+ <p>
+ <textarea name="config" cols="60" rows="50">{{config}}</textarea>
+ </p>
+
+ <p>
+ <input type="submit" value="Update ODR-DabMux configuration"></input>
+ </p>
+ </form>
+
+</body>
+</html>
+
diff --git a/gui/views/stats.tpl b/gui/views/stats.tpl
new file mode 100644
index 0000000..30d82c5
--- /dev/null
+++ b/gui/views/stats.tpl
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>ODR-DabMux Statistics</title>
+ <link rel="stylesheet" href="static/style.css" type="text/css" media="screen" charset="utf-8"/>
+ <script type="text/javascript" src="static/jquery-1.7.1.min.js"></script>
+</head>
+<body>
+ <h1>Subchannel stats for {{version}}</h1>
+
+ <a id="update">Update</a>
+
+ <div id="subchannels">
+ <p>Subchannels</p>
+ <div id="statdata"></div>
+ </div>
+ <script type="text/javascript" src="static/stats.js"></script>
+</body>
+</html>
+
diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4
new file mode 100644
index 0000000..a9a8f58
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx_11.m4
@@ -0,0 +1,165 @@
+# ============================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the C++11
+# standard; if necessary, add switches to CXXFLAGS to enable support.
+#
+# The first argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for an extended mode.
+#
+# The second argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline C++11 support is required and that the macro
+# should error out if no mode with that support is found. If specified
+# 'optional', then configuration proceeds regardless, after defining
+# HAVE_CXX11 if and only if a supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ struct Base {
+ virtual void f() {}
+ };
+ struct Child : public Base {
+ virtual void f() override {}
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);
+
+ auto d = a;
+ auto l = [](){};
+ // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable]
+ struct use_l { use_l() { l(); } };
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this
+ namespace test_template_alias_sfinae {
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() {
+ func<foo>(0);
+ }
+ }
+]])
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
+ m4_if([$1], [], [],
+ [$1], [ext], [],
+ [$1], [noext], [],
+ [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
+ m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
+ [$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
+ [$2], [optional], [ax_cxx_compile_cxx11_required=false],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+ AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
+ ax_cv_cxx_compile_cxx11,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+ [ax_cv_cxx_compile_cxx11=yes],
+ [ax_cv_cxx_compile_cxx11=no])])
+ if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+ ac_success=yes
+ fi
+
+ m4_if([$1], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for switch in -std=gnu++11 -std=gnu++0x; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+ $cachevar,
+ [ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXXFLAGS="$ac_save_CXXFLAGS"])
+ if eval test x\$$cachevar = xyes; then
+ CXXFLAGS="$CXXFLAGS $switch"
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$1], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ for switch in -std=c++11 -std=c++0x; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+ $cachevar,
+ [ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXXFLAGS="$ac_save_CXXFLAGS"])
+ if eval test x\$$cachevar = xyes; then
+ CXXFLAGS="$CXXFLAGS $switch"
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx11_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
+ fi
+ else
+ if test x$ac_success = xno; then
+ HAVE_CXX11=0
+ AC_MSG_NOTICE([No compiler with C++11 support was found])
+ else
+ HAVE_CXX11=1
+ AC_DEFINE(HAVE_CXX11,1,
+ [define if the compiler supports basic C++11 syntax])
+ fi
+
+ AC_SUBST(HAVE_CXX11)
+ fi
+])
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 443e26d..6092e66 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -125,15 +125,8 @@ int hexparse(std::string input)
void parse_ptree(boost::property_tree::ptree& pt,
- vector<dabOutput*> &outputs,
- dabEnsemble* ensemble,
- bool* enableTist,
- unsigned* FICL,
- bool* factumAnalyzer,
- unsigned long* limit,
- BaseRemoteController** rc,
- int* mgmtserverport,
- edi_configuration_t* edi
+ boost::shared_ptr<dabEnsemble> ensemble,
+ boost::shared_ptr<BaseRemoteController> rc
)
{
using boost::property_tree::ptree;
@@ -150,38 +143,11 @@ void parse_ptree(boost::property_tree::ptree& pt,
ensemble->mode = 0;
}
- if (ensemble->mode == 3) {
- *FICL = 32;
- }
- else {
- *FICL = 24;
- }
-
- /* Number of frames to generate */
- *limit = pt_general.get("nbframes", 0);
-
/* Enable Logging to syslog conditionally */
if (pt_general.get<bool>("syslog", false)) {
etiLog.register_backend(new LogToSyslog()); // TODO don't leak the LogToSyslog backend
}
- *factumAnalyzer = pt_general.get("writescca", false);
-
- *enableTist = pt_general.get("tist", false);
-
- *mgmtserverport = pt_general.get<int>("managementport",
- pt_general.get<int>("statsserverport", 0) );
-
- /************** READ REMOTE CONTROL PARAMETERS *************/
- ptree pt_rc = pt.get_child("remotecontrol");
- int telnetport = pt_rc.get<int>("telnetport", 0);
-
- if (telnetport != 0) {
- *rc = new RemoteControllerTelnet(telnetport);
- }
- else {
- *rc = new RemoteControllerDummy();
- }
/******************** READ ENSEMBLE PARAMETERS *************/
ptree pt_ensemble = pt.get_child("ensemble");
@@ -254,19 +220,35 @@ void parse_ptree(boost::property_tree::ptree& pt,
/******************** READ SERVICES PARAMETERS *************/
- map<string, DabService*> allservices;
+ map<string, shared_ptr<DabService> > allservices;
/* For each service, we keep a separate SCIdS counter */
- map<DabService*, int> SCIdS_per_service;
+ map<shared_ptr<DabService>, int> SCIdS_per_service;
ptree pt_services = pt.get_child("services");
for (ptree::iterator it = pt_services.begin();
it != pt_services.end(); ++it) {
string serviceuid = it->first;
ptree pt_service = it->second;
- DabService* service = new DabService(serviceuid);
- ensemble->services.push_back(service);
- service->enrol_at(**rc);
+
+ shared_ptr<DabService> service;
+
+ bool service_already_existing = false;
+
+ for (auto srv : ensemble->services)
+ {
+ if (srv->uid == serviceuid) {
+ service = srv;
+ service_already_existing = true;
+ break;
+ }
+ }
+
+ if (not service_already_existing) {
+ auto new_srv = make_shared<DabService>(serviceuid);
+ ensemble->services.push_back(new_srv);
+ service = new_srv;
+ }
int success = -5;
@@ -332,13 +314,13 @@ void parse_ptree(boost::property_tree::ptree& pt,
ptree pt_subchans = pt.get_child("subchannels");
for (ptree::iterator it = pt_subchans.begin(); it != pt_subchans.end(); ++it) {
string subchanuid = it->first;
- dabSubchannel* subchan = new dabSubchannel();
+ dabSubchannel* subchan = new dabSubchannel(subchanuid);
ensemble->subchannels.push_back(subchan);
try {
setup_subchannel_from_ptree(subchan, it->second, ensemble,
- subchanuid, *rc);
+ subchanuid, rc);
}
catch (runtime_error &e) {
etiLog.log(error,
@@ -365,7 +347,7 @@ void parse_ptree(boost::property_tree::ptree& pt,
string componentuid = it->first;
ptree pt_comp = it->second;
- DabService* service;
+ shared_ptr<DabService> service;
try {
// Those two uids serve as foreign keys to select the service+subchannel
string service_uid = pt_comp.get<string>("service");
@@ -407,8 +389,6 @@ void parse_ptree(boost::property_tree::ptree& pt,
DabComponent* component = new DabComponent(componentuid);
- component->enrol_at(**rc);
-
component->serviceId = service->id;
component->subchId = subchannel->id;
component->SCIdS = SCIdS_per_service[service]++;
@@ -495,65 +475,13 @@ void parse_ptree(boost::property_tree::ptree& pt,
}
- /******************** READ OUTPUT PARAMETERS ***************/
- map<string, dabOutput*> alloutputs;
- ptree pt_outputs = pt.get_child("outputs");
- for (ptree::iterator it = pt_outputs.begin(); it != pt_outputs.end(); ++it) {
- string outputuid = it->first;
-
- if (outputuid == "edi") {
- ptree pt_edi = pt_outputs.get_child("edi");
-
- edi->enabled = true;
-
- edi->dest_addr = pt_edi.get<string>("destination");
- edi->dest_port = pt_edi.get<unsigned int>("port");
- edi->source_port = pt_edi.get<unsigned int>("sourceport");
-
- edi->dump = pt_edi.get<bool>("dump");
- edi->enable_pft = pt_edi.get<bool>("enable_pft");
- edi->verbose = pt_edi.get<bool>("verbose");
- }
- else {
- string uri = pt_outputs.get<string>(outputuid);
-
- size_t proto_pos = uri.find("://");
- if (proto_pos == std::string::npos) {
- stringstream ss;
- ss << "Output with uid " << outputuid << " no protocol defined!";
- throw runtime_error(ss.str());
- }
-
- char* uri_c = new char[512];
- memset(uri_c, 0, 512);
- uri.copy(uri_c, 511);
-
- uri_c[proto_pos] = '\0';
-
- char* outputName = uri_c + proto_pos + 3;
-
- dabOutput* output = new dabOutput(uri_c, outputName);
- outputs.push_back(output);
-
- // keep outputs in map, and check for uniqueness of the uid
- if (alloutputs.count(outputuid) == 0) {
- alloutputs[outputuid] = output;
- }
- else {
- stringstream ss;
- ss << "output with uid " << outputuid << " not unique!";
- throw runtime_error(ss.str());
- }
- }
- }
-
}
void setup_subchannel_from_ptree(dabSubchannel* subchan,
boost::property_tree::ptree &pt,
- dabEnsemble* ensemble,
+ boost::shared_ptr<dabEnsemble> ensemble,
string subchanuid,
- BaseRemoteController* rc)
+ boost::shared_ptr<BaseRemoteController> rc)
{
using boost::property_tree::ptree;
using boost::property_tree::ptree_error;
@@ -857,7 +785,7 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,
if (nonblock) {
switch (subchan->type) {
#ifdef HAVE_FORMAT_PACKET
- case 3:
+ case Packet:
if (operations == dabInputPacketFileOperations) {
operations = dabInputFifoOperations;
#ifdef HAVE_FORMAT_EPM
@@ -873,7 +801,7 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,
break;
#endif // defined(HAVE_FORMAT_PACKET)
#ifdef HAVE_FORMAT_MPEG
- case 0:
+ case Audio:
if (operations == dabInputMpegFileOperations) {
operations = dabInputMpegFifoOperations;
} else if (operations == dabInputDabplusFileOperations) {
@@ -886,6 +814,8 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,
}
break;
#endif // defined(HAVE_FORMAT_MPEG)
+ case DataDmb:
+ case Fidc:
default:
stringstream ss;
ss << "Subchannel with uid " << subchanuid <<
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
index 4af010a..1eec783 100644
--- a/src/ConfigParser.h
+++ b/src/ConfigParser.h
@@ -36,23 +36,17 @@
#include "MuxElements.h"
#include "DabMux.h"
#include <boost/property_tree/ptree.hpp>
+#include <boost/shared_ptr.hpp>
void parse_ptree(boost::property_tree::ptree& pt,
- std::vector<dabOutput*> &outputs,
- dabEnsemble* ensemble,
- bool* enableTist,
- unsigned* FICL,
- bool* factumAnalyzer,
- unsigned long* limit,
- BaseRemoteController** rc,
- int* mgmtserverport,
- edi_configuration_t* edi);
+ boost::shared_ptr<dabEnsemble> ensemble,
+ boost::shared_ptr<BaseRemoteController> rc);
void setup_subchannel_from_ptree(dabSubchannel* subchan,
boost::property_tree::ptree &pt,
- dabEnsemble* ensemble,
+ boost::shared_ptr<dabEnsemble> ensemble,
std::string subchanuid,
- BaseRemoteController* rc);
+ boost::shared_ptr<BaseRemoteController> rc);
#endif
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
new file mode 100644
index 0000000..db158da
--- /dev/null
+++ b/src/DabMultiplexer.cpp
@@ -0,0 +1,1851 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "DabMultiplexer.h"
+#include "ConfigParser.h"
+#include <boost/make_shared.hpp>
+#include "fig/FIG.h"
+
+using namespace std;
+using namespace boost;
+
+static unsigned char Padding_FIB[] = {
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+// Protection levels and bitrates for UEP.
+const unsigned char ProtectionLevelTable[64] = {
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 1,
+ 4, 2, 0
+};
+
+const unsigned short BitRateTable[64] = {
+ 32, 32, 32, 32, 32,
+ 48, 48, 48, 48, 48,
+ 56, 56, 56, 56,
+ 64, 64, 64, 64, 64,
+ 80, 80, 80, 80, 80,
+ 96, 96, 96, 96, 96,
+ 112, 112, 112, 112,
+ 128, 128, 128, 128, 128,
+ 160, 160, 160, 160, 160,
+ 192, 192, 192, 192, 192,
+ 224, 224, 224, 224, 224,
+ 256, 256, 256, 256, 256,
+ 320, 320, 320,
+ 384, 384, 384
+};
+
+DabMultiplexer::DabMultiplexer(
+ boost::shared_ptr<BaseRemoteController> rc,
+ boost::property_tree::ptree pt) :
+ m_pt(pt),
+ m_rc(rc),
+ timestamp(0),
+ MNSC_increment_time(false),
+ m_watermarkSize(0),
+ m_watermarkPos(0),
+ sync(0x49C5F8),
+ currentFrame(0),
+ insertFIG(0),
+ rotateFIB(0),
+ ensemble(boost::make_shared<dabEnsemble>()),
+ fig_carousel(ensemble)
+{
+ prepare_watermark();
+}
+
+void DabMultiplexer::prepare_watermark()
+{
+ uint8_t buffer[sizeof(m_watermarkData) / 2];
+ snprintf((char*)buffer, sizeof(buffer),
+ "%s %s, compiled at %s, %s",
+ PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__);
+
+ memset(m_watermarkData, 0, sizeof(m_watermarkData));
+ m_watermarkData[0] = 0x55; // Sync
+ m_watermarkData[1] = 0x55;
+ m_watermarkSize = 16;
+ for (unsigned i = 0; i < strlen((char*)buffer); ++i) {
+ for (int j = 0; j < 8; ++j) {
+ uint8_t bit = (buffer[m_watermarkPos >> 3] >> (7 - (m_watermarkPos & 0x07))) & 1;
+ m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07));
+ ++m_watermarkSize;
+ bit = 1;
+ m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07));
+ ++m_watermarkSize;
+ ++m_watermarkPos;
+ }
+ }
+ m_watermarkPos = 0;
+}
+
+void DabMultiplexer::update_config(boost::property_tree::ptree pt)
+{
+ ensemble_next = boost::make_shared<dabEnsemble>();
+
+ m_pt_next = pt;
+
+ reconfigure();
+}
+
+void DabMultiplexer::reconfigure()
+{
+ parse_ptree(m_pt_next, ensemble_next, m_rc);
+}
+
+void DabMultiplexer::set_edi_config(const edi_configuration_t& new_edi_conf)
+{
+ edi_conf = new_edi_conf;
+
+#if HAVE_OUTPUT_EDI
+ if (edi_conf.verbose) {
+ etiLog.log(info, "Setup EDI");
+ }
+
+ if (edi_conf.dump) {
+ edi_debug_file.open("./edi.debug");
+ }
+
+ if (edi_conf.enabled) {
+ edi_output.create(edi_conf.source_port);
+ }
+
+ if (edi_conf.verbose) {
+ etiLog.log(info, "EDI set up");
+ }
+
+ // The TagPacket will then be placed into an AFPacket
+ AFPacketiser afPacketiser;
+ edi_afPacketiser = afPacketiser;
+
+ // The AF Packet will be protected with reed-solomon and split in fragments
+ PFT pft(edi_conf);
+ edi_pft = pft;
+#endif
+}
+
+
+// Run a set of checks on the configuration
+void DabMultiplexer::prepare()
+{
+ parse_ptree(m_pt, ensemble, m_rc);
+
+ ensemble->enrol_at(m_rc);
+
+ prepare_subchannels();
+ prepare_services_components();
+ prepare_data_inputs();
+
+ if (ensemble->subchannels.size() == 0) {
+ etiLog.log(error, "can't multiplex no subchannel!\n");
+ throw MuxInitException();
+ }
+
+ vector<dabSubchannel*>::iterator subchannel =
+ ensemble->subchannels.end() - 1;
+
+ if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) {
+ etiLog.log(error, "Total size in CU exceeds 864\n");
+ printSubchannels(ensemble->subchannels);
+ throw MuxInitException();
+ }
+
+ /* These iterators are used to fill the respective FIG.
+ * It is necessary to cycle through all the FIGs that have
+ * to be transmitted because they do not fit in a single
+ * FIB.
+ *
+ * ETSI EN 300 799 Clauses 5.2 and 8.1
+ */
+ serviceProgFIG0_2 = ensemble->services.end();
+ serviceDataFIG0_2 = ensemble->services.end();
+ componentProgFIG0_8 = ensemble->components.end();
+ componentDataFIG0_8 = ensemble->components.end();
+ componentFIG0_13 = ensemble->components.end();
+ transmitFIG0_13programme = false;
+ serviceFIG0_17 = ensemble->services.end();
+ subchannelFIG0_1 = ensemble->subchannels.end();
+
+
+ /* TODO:
+ * In a SFN, when reconfiguring the ensemble, the multiplexer
+ * has to be restarted (odr-dabmux doesn't support reconfiguration).
+ * Ideally, we must be able to restart transmission s.t. the receiver
+ * synchronisation is preserved.
+ */
+ gettimeofday(&mnsc_time, NULL);
+}
+
+
+// Check and adjust subchannels
+void DabMultiplexer::prepare_subchannels()
+{
+ set<unsigned char> ids;
+
+ for (auto subchannel : ensemble->subchannels) {
+ if (ids.find(subchannel->id) != ids.end()) {
+ etiLog.log(error,
+ "Subchannel %u is set more than once!\n",
+ subchannel->id);
+ throw MuxInitException();
+ }
+ ids.insert(subchannel->id);
+ }
+}
+
+// Check and adjust services and components
+void DabMultiplexer::prepare_services_components()
+{
+ set<uint32_t> ids;
+ dabProtection* protection = NULL;
+
+ vector<DabComponent*>::iterator component;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ for (auto service : ensemble->services) {
+ if (ids.find(service->id) != ids.end()) {
+ etiLog.log(error,
+ "Service id 0x%x (%u) is set more than once!\n",
+ service->id, service->id);
+ throw MuxInitException();
+ }
+
+ // Get first component of this service
+ component = getComponent(ensemble->components, service->id);
+ if (component == ensemble->components.end()) {
+ etiLog.log(error,
+ "Service id 0x%x (%u) includes no component!\n",
+ service->id, service->id);
+ throw MuxInitException();
+ }
+
+ // Adjust service type from this first component
+ switch (service->getType(ensemble)) {
+ case 0: // Audio
+ service->program = true;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ service->program = false;
+ break;
+ default:
+ etiLog.log(error,
+ "Error, unknown service type: %u\n",
+ service->getType(ensemble));
+ throw MuxInitException();
+ }
+
+ service->enrol_at(m_rc);
+
+ // Adjust components type for DAB+
+ while (component != ensemble->components.end()) {
+ subchannel =
+ getSubchannel(ensemble->subchannels, (*component)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error, "Error, service %u component "
+ "links to the invalid subchannel %u\n",
+ (*component)->serviceId, (*component)->subchId);
+ throw MuxInitException();
+ }
+
+ protection = &(*subchannel)->protection;
+ switch ((*subchannel)->type) {
+ case Audio:
+ {
+ if (protection->form == EEP) {
+ (*component)->type = 0x3f; // DAB+
+ }
+ }
+ break;
+ case DataDmb:
+ case Fidc:
+ case Packet:
+ break;
+ default:
+ etiLog.log(error,
+ "Error, unknown subchannel type\n");
+ throw MuxInitException();
+ }
+ component = getComponent(ensemble->components,
+ service->id, component);
+ }
+ }
+
+ // Init packet components SCId
+ int cur_packetid = 0;
+ for (auto component : ensemble->components) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ component->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ component->subchId, component->serviceId);
+ throw MuxInitException();
+ }
+ if ((*subchannel)->type != Packet) continue;
+
+ component->packet.id = cur_packetid++;
+
+ component->enrol_at(m_rc);
+
+ }
+
+}
+
+void DabMultiplexer::prepare_data_inputs()
+{
+ dabProtection* protection = NULL;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ // Prepare and check the data inputs
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ protection = &(*subchannel)->protection;
+ if (subchannel == ensemble->subchannels.begin()) {
+ (*subchannel)->startAddress = 0;
+ } else {
+ (*subchannel)->startAddress = (*(subchannel - 1))->startAddress +
+ getSizeCu(*(subchannel - 1));
+ }
+ if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) {
+ perror((*subchannel)->inputUri.c_str());
+ throw MuxInitException();
+ }
+
+ // TODO Check errors
+ int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate);
+ if (subch_bitrate <= 0) {
+ etiLog.level(error) << "can't set bitrate for source " <<
+ (*subchannel)->inputUri;
+ throw MuxInitException();
+ }
+ (*subchannel)->bitrate = subch_bitrate;
+
+ /* Use EEP unless we find a UEP configuration
+ * UEP is only used for MPEG audio, but some bitrates don't
+ * have a UEP profile (EN 300 401 Clause 6.2.1).
+ * For these bitrates, we must switch to EEP.
+ *
+ * AAC audio and data is already EEP
+ */
+ if (protection->form == UEP) {
+ protection->form = EEP;
+ for (int i = 0; i < 64; i++) {
+ if ( (*subchannel)->bitrate == BitRateTable[i] &&
+ protection->level == ProtectionLevelTable[i] ) {
+ protection->form = UEP;
+ protection->uep.tableIndex = i;
+ }
+ }
+ }
+
+ /* EEP B can only be used for subchannels with bitrates
+ * multiple of 32kbit/s
+ */
+ if ( protection->form == EEP &&
+ protection->eep.profile == EEP_B &&
+ subch_bitrate % 32 != 0 ) {
+ etiLog.level(error) <<
+ "Cannot use EEP_B protection for subchannel " <<
+ (*subchannel)->inputUri <<
+ ": bitrate not multiple of 32kbit/s";
+ throw MuxInitException();
+ }
+ }
+}
+
+/* Each call creates one ETI frame */
+void DabMultiplexer::mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs)
+{
+ int cur;
+ time_t date;
+ unsigned char etiFrame[6144];
+ unsigned short index = 0;
+
+ vector<std::shared_ptr<DabService> >::iterator service;
+ vector<DabComponent*>::iterator component;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32
+ unsigned FICL = (ensemble->mode == 3 ? 32 : 24);
+
+ // For EDI, save ETI(LI) Management data into a TAG Item DETI
+ TagDETI edi_tagDETI;
+ TagStarPTR edi_tagStarPtr;
+ list<TagESTn> edi_subchannels;
+ map<dabSubchannel*, TagESTn*> edi_subchannelToTag;
+
+ // The above Tag Items will be assembled into a TAG Packet
+ TagPacket edi_tagpacket;
+
+ edi_tagDETI.atstf = 1;
+ edi_tagDETI.utco = 0;
+ edi_tagDETI.seconds = 0;
+
+ date = getDabTime();
+
+ // Initialise the ETI frame
+ memset(etiFrame, 0, 6144);
+
+ /**********************************************************************
+ ********** Section SYNC of ETI(NI, G703) *************************
+ **********************************************************************/
+
+ // See ETS 300 799 Clause 6
+ eti_SYNC *etiSync = (eti_SYNC *) etiFrame;
+
+ etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error
+
+ //****** Field FSYNC *****//
+ // See ETS 300 799, 6.2.1.2
+ sync ^= 0xffffff;
+ etiSync->FSYNC = sync;
+
+ /**********************************************************************
+ *********** Section LIDATA of ETI(NI, G703) **********************
+ **********************************************************************/
+
+ // See ETS 300 799 Figure 5 for a better overview of these fields.
+
+ //****** Section FC ***************************************************/
+ // 4 octets, starts at offset 4
+ eti_FC *fc = (eti_FC *) &etiFrame[4];
+
+ //****** FCT ******//
+ // Incremente for each frame, overflows at 249
+ fc->FCT = currentFrame % 250;
+ edi_tagDETI.dflc = currentFrame % 5000;
+
+ //****** FICF ******//
+ // Fast Information Channel Flag, 1 bit, =1 if FIC present
+ fc->FICF = edi_tagDETI.ficf = 1;
+
+ //****** NST ******//
+ /* Number of audio of data sub-channels, 7 bits, 0-64.
+ * In the 15-frame period immediately preceding a multiplex
+ * re-configuration, NST can take the value 0 (see annex E).
+ */
+ fc->NST = ensemble->subchannels.size();
+
+ //****** FP ******//
+ /* Frame Phase, 3 bit counter, tells the COFDM generator
+ * when to insert the TII. Is also used by the MNSC.
+ */
+ fc->FP = edi_tagDETI.fp = currentFrame & 0x7;
+
+ //****** MID ******//
+ //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV
+ fc->MID = edi_tagDETI.mid = ensemble->mode; //mode 2 needs 3 FIB, 3*32octets = 96octets
+
+ //****** FL ******//
+ /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST
+ * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode.
+ * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details
+ */
+ unsigned short FLtmp = 1 + FICL + (fc->NST);
+
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ // Add STLsbch
+ FLtmp += getSizeWord(*subchannel);
+ }
+
+ fc->setFrameLength(FLtmp);
+ index = 8;
+
+ /******* Section STC **************************************************/
+ // Stream Characterization,
+ // number of channels * 4 octets = nb octets total
+ int edi_stream_id = 1;
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ dabProtection* protection = &(*subchannel)->protection;
+ eti_STC *sstc = (eti_STC *) & etiFrame[index];
+
+ sstc->SCID = (*subchannel)->id;
+ sstc->startAddress_high = (*subchannel)->startAddress / 256;
+ sstc->startAddress_low = (*subchannel)->startAddress % 256;
+ // depends on the desired protection form
+ if (protection->form == UEP) {
+ sstc->TPL = 0x10 |
+ ProtectionLevelTable[protection->uep.tableIndex];
+ }
+ else if (protection->form == EEP) {
+ sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level;
+ }
+
+ // Sub-channel Stream Length, multiple of 64 bits
+ sstc->STL_high = getSizeDWord(*subchannel) / 256;
+ sstc->STL_low = getSizeDWord(*subchannel) % 256;
+
+ TagESTn tag_ESTn(edi_stream_id++);
+ tag_ESTn.scid = (*subchannel)->id;
+ tag_ESTn.sad = (*subchannel)->startAddress;
+ tag_ESTn.tpl = sstc->TPL;
+ tag_ESTn.rfa = 0; // two bits
+ tag_ESTn.mst_length = getSizeByte(*subchannel) / 8;
+ assert(getSizeByte(*subchannel) % 8 == 0);
+
+ edi_subchannels.push_back(tag_ESTn);
+ edi_subchannelToTag[*subchannel] = &edi_subchannels.back();
+ index += 4;
+ }
+
+ /******* Section EOH **************************************************/
+ // End of Header 4 octets
+ eti_EOH *eoh = (eti_EOH *) & etiFrame[index];
+
+ //MNSC Multiplex Network Signalling Channel, 2 octets
+
+ eoh->MNSC = 0;
+
+ struct tm *time_tm = gmtime(&mnsc_time.tv_sec);
+ switch (fc->FP & 0x3)
+ {
+ case 0:
+ if (MNSC_increment_time)
+ {
+ MNSC_increment_time = false;
+ mnsc_time.tv_sec += 1;
+ }
+ {
+
+ eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC;
+ // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2
+ mnsc->type = 0;
+ mnsc->identifier = 0;
+ mnsc->rfa = 0;
+ }
+ break;
+ case 1:
+ {
+ eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ mnsc->accuracy = 1;
+ mnsc->sync_to_frame = 1;
+ }
+ break;
+ case 2:
+ {
+ eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ }
+ break;
+ case 3:
+ {
+ eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ }
+ break;
+ }
+
+ edi_tagDETI.mnsc = eoh->MNSC;
+
+ // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets
+ unsigned short nbBytesCRC = 4 + ((fc->NST) * 4) + 2;
+
+ unsigned short CRCtmp = 0xFFFF;
+ CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC);
+ CRCtmp ^= 0xffff;
+ eoh->CRC = htons(CRCtmp);
+
+ /******* Section MST **************************************************/
+ // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC
+ // (depending on mode)
+ index = ((fc->NST) + 2 + 1) * 4;
+ edi_tagDETI.fic_data = &etiFrame[index];
+ edi_tagDETI.fic_length = FICL * 4;
+
+ // FIC Insertion
+ FIGtype0* fig0;
+ FIGtype0_0 *fig0_0;
+ FIGtype0_1 *figtype0_1;
+
+ FIG_01_SubChannel_ShortF *fig0_1subchShort;
+ FIG_01_SubChannel_LongF *fig0_1subchLong1;
+
+ FIGtype0_2 *fig0_2;
+
+ FIGtype0_2_Service *fig0_2serviceAudio;
+ FIGtype0_2_Service_data *fig0_2serviceData;
+ FIGtype0_2_audio_component* audio_description;
+ FIGtype0_2_data_component* data_description;
+ FIGtype0_2_packet_component* packet_description;
+
+ FIGtype0_3_header *fig0_3_header;
+ FIGtype0_3_data *fig0_3_data;
+ FIGtype0_9 *fig0_9;
+ FIGtype0_10 *fig0_10;
+ FIGtype0_17_programme *programme;
+
+ FIGtype1_0 *fig1_0;
+ FIGtype1_1 *fig1_1;
+ FIGtype1_5 *fig1_5;
+
+ tm* timeData;
+
+ unsigned char figSize = 0;
+
+ // FIB 0 Insertion
+ bool new_fib0_carousel = m_pt.get("general.new_fib0_carousel", false);
+ if (new_fib0_carousel) {
+ // TODO update currentframe in rti
+ figSize += fig_carousel.fib0(&etiFrame[index], 30, currentFrame % 4);
+ index += figSize;
+ }
+ // Skip creating a block for the else because
+ // I don't want to reindent the whole switch block
+ else switch (insertFIG) {
+
+ case 0:
+ case 4:
+ case 8:
+ case 12:
+ // FIG type 0/0, Multiplex Configuration Info (MCI),
+ // Ensemble information
+ fig0_0 = (FIGtype0_0 *) & etiFrame[index];
+
+ fig0_0->FIGtypeNumber = 0;
+ fig0_0->Length = 5;
+ fig0_0->CN = 0;
+ fig0_0->OE = 0;
+ fig0_0->PD = 0;
+ fig0_0->Extension = 0;
+
+ fig0_0->EId = htons(ensemble->id);
+ fig0_0->Change = 0;
+ fig0_0->Al = 0;
+ fig0_0->CIFcnt_hight = (currentFrame / 250) % 20;
+ fig0_0->CIFcnt_low = (currentFrame % 250);
+ index = index + 6;
+ figSize += 6;
+
+ break;
+
+ case 1:
+ case 6:
+ case 10:
+ case 13:
+ // FIG type 0/1, MIC, Sub-Channel Organization,
+ // one instance of the part for each subchannel
+ figtype0_1 = (FIGtype0_1 *) & etiFrame[index];
+
+ figtype0_1->FIGtypeNumber = 0;
+ figtype0_1->Length = 1;
+ figtype0_1->CN = 0;
+ figtype0_1->OE = 0;
+ figtype0_1->PD = 0;
+ figtype0_1->Extension = 1;
+ index = index + 2;
+ figSize += 2;
+
+ // Rotate through the subchannels until there is no more
+ // space in the FIG0/1
+ if (subchannelFIG0_1 == ensemble->subchannels.end()) {
+ subchannelFIG0_1 = ensemble->subchannels.begin();
+ }
+
+ for (; subchannelFIG0_1 != ensemble->subchannels.end();
+ ++subchannelFIG0_1) {
+ dabProtection* protection = &(*subchannelFIG0_1)->protection;
+
+ if ( (protection->form == UEP && figSize > 27) ||
+ (protection->form == EEP && figSize > 26) ) {
+ break;
+ }
+
+ if (protection->form == UEP) {
+ fig0_1subchShort =
+ (FIG_01_SubChannel_ShortF*) &etiFrame[index];
+ fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchShort->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchShort->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchShort->Short_Long_form = 0;
+ fig0_1subchShort->TableSwitch = 0;
+ fig0_1subchShort->TableIndex =
+ protection->uep.tableIndex;
+
+ index = index + 3;
+ figSize += 3;
+ figtype0_1->Length += 3;
+ }
+ else if (protection->form == EEP) {
+ fig0_1subchLong1 =
+ (FIG_01_SubChannel_LongF*) &etiFrame[index];
+ fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchLong1->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchLong1->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchLong1->Short_Long_form = 1;
+ fig0_1subchLong1->Option = protection->eep.GetOption();
+ fig0_1subchLong1->ProtectionLevel =
+ protection->level;
+
+ fig0_1subchLong1->Sub_ChannelSize_high =
+ getSizeCu(*subchannelFIG0_1) / 256;
+ fig0_1subchLong1->Sub_ChannelSize_low =
+ getSizeCu(*subchannelFIG0_1) % 256;
+
+ index = index + 4;
+ figSize += 4;
+ figtype0_1->Length += 4;
+ }
+ }
+ break;
+
+ case 2:
+ case 9:
+ case 11:
+ case 14:
+ // FIG type 0/2, MCI, Service Organization, one instance of
+ // FIGtype0_2_Service for each subchannel
+ fig0_2 = NULL;
+ cur = 0;
+
+ // Rotate through the subchannels until there is no more
+ // space in the FIG0/1
+ if (serviceProgFIG0_2 == ensemble->services.end()) {
+ serviceProgFIG0_2 = ensemble->services.begin();
+ }
+
+ for (; serviceProgFIG0_2 != ensemble->services.end();
+ ++serviceProgFIG0_2) {
+ if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) {
+ continue;
+ }
+
+ if ((*serviceProgFIG0_2)->getType(ensemble) != 0) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *) & etiFrame[index];
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = 0;
+ fig0_2->Extension = 2;
+ index = index + 2;
+ figSize += 2;
+ }
+
+ if (figSize + 3
+ + (*serviceProgFIG0_2)->nbComponent(ensemble->components)
+ * 2 > 30) {
+ break;
+ }
+
+ fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index];
+
+ fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
+ fig0_2serviceAudio->Local_flag = 0;
+ fig0_2serviceAudio->CAId = 0;
+ fig0_2serviceAudio->NbServiceComp =
+ (*serviceProgFIG0_2)->nbComponent(ensemble->components);
+ index += 3;
+ fig0_2->Length += 3;
+ figSize += 3;
+
+ int curCpnt = 0;
+ for (component = getComponent(ensemble->components,
+ (*serviceProgFIG0_2)->id);
+ component != ensemble->components.end();
+ component = getComponent(ensemble->components,
+ (*serviceProgFIG0_2)->id, component)) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ audio_description =
+ (FIGtype0_2_audio_component*)&etiFrame[index];
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ break;
+ case DataDmb:
+ data_description =
+ (FIGtype0_2_data_component*)&etiFrame[index];
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ break;
+ case Packet:
+ packet_description =
+ (FIGtype0_2_packet_component*)&etiFrame[index];
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ index += 2;
+ fig0_2->Length += 2;
+ figSize += 2;
+ if (figSize > 30) {
+ etiLog.log(error,
+ "Sorry, no space left in FIG 0/2 to insert "
+ "component %i of program service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ break;
+
+ case 3:
+ fig0_2 = NULL;
+ cur = 0;
+
+ if (serviceDataFIG0_2 == ensemble->services.end()) {
+ serviceDataFIG0_2 = ensemble->services.begin();
+ }
+ for (; serviceDataFIG0_2 != ensemble->services.end();
+ ++serviceDataFIG0_2) {
+ if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) {
+ continue;
+ }
+
+ unsigned char type = (*serviceDataFIG0_2)->getType(ensemble);
+ if ((type == 0) || (type == 2)) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *) & etiFrame[index];
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = 1;
+ fig0_2->Extension = 2;
+ index = index + 2;
+ figSize += 2;
+ }
+
+ if (figSize + 5
+ + (*serviceDataFIG0_2)->nbComponent(ensemble->components)
+ * 2 > 30) {
+ break;
+ }
+
+ fig0_2serviceData =
+ (FIGtype0_2_Service_data*) &etiFrame[index];
+
+ fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id);
+ fig0_2serviceData->Local_flag = 0;
+ fig0_2serviceData->CAId = 0;
+ fig0_2serviceData->NbServiceComp =
+ (*serviceDataFIG0_2)->nbComponent(ensemble->components);
+ fig0_2->Length += 5;
+ index += 5;
+ figSize += 5;
+
+ int curCpnt = 0;
+ for (component = getComponent(ensemble->components,
+ (*serviceDataFIG0_2)->id);
+ component != ensemble->components.end();
+ component = getComponent(ensemble->components,
+ (*serviceDataFIG0_2)->id, component)) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ audio_description =
+ (FIGtype0_2_audio_component*)&etiFrame[index];
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ break;
+ case DataDmb:
+ data_description =
+ (FIGtype0_2_data_component*)&etiFrame[index];
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ break;
+ case Packet:
+ packet_description =
+ (FIGtype0_2_packet_component*)&etiFrame[index];
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ index += 2;
+ fig0_2->Length += 2;
+ figSize += 2;
+ if (figSize > 30) {
+ etiLog.log(error,
+ "Sorry, no place left in FIG 0/2 to insert "
+ "component %i of data service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ break;
+
+ case 5:
+ fig0_3_header = NULL;
+
+ for (component = ensemble->components.begin();
+ component != ensemble->components.end();
+ ++component) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ((*subchannel)->type != Packet)
+ continue;
+
+ if (fig0_3_header == NULL) {
+ fig0_3_header = (FIGtype0_3_header*)&etiFrame[index];
+ fig0_3_header->FIGtypeNumber = 0;
+ fig0_3_header->Length = 1;
+ fig0_3_header->CN = 0;
+ fig0_3_header->OE = 0;
+ fig0_3_header->PD = 0;
+ fig0_3_header->Extension = 3;
+
+ index += 2;
+ figSize += 2;
+ }
+
+ bool factumAnalyzer = m_pt.get("general.writescca", false);
+
+ /* Warning: When bit SCCA_flag is unset(0), the multiplexer
+ * R&S does not send the SCCA field. But, in the Factum ETI
+ * analyzer, if this field is not there, it is an error.
+ */
+ fig0_3_data = (FIGtype0_3_data*)&etiFrame[index];
+ fig0_3_data->setSCId((*component)->packet.id);
+ fig0_3_data->rfa = 0;
+ fig0_3_data->SCCA_flag = 0;
+ // if 0, datagroups are used
+ fig0_3_data->DG_flag = !(*component)->packet.datagroup;
+ fig0_3_data->rfu = 0;
+ fig0_3_data->DSCTy = (*component)->type;
+ fig0_3_data->SubChId = (*subchannel)->id;
+ fig0_3_data->setPacketAddress((*component)->packet.address);
+ if (factumAnalyzer) {
+ fig0_3_data->SCCA = 0;
+ }
+
+ fig0_3_header->Length += 5;
+ index += 5;
+ figSize += 5;
+ if (factumAnalyzer) {
+ fig0_3_header->Length += 2;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30) {
+ etiLog.log(error,
+ "can't add to Fic Fig 0/3, "
+ "too much packet service\n");
+ throw MuxInitException();
+ }
+ }
+ break;
+
+ case 7:
+ fig0 = NULL;
+ if (serviceFIG0_17 == ensemble->services.end()) {
+ serviceFIG0_17 = ensemble->services.begin();
+ }
+ for (; serviceFIG0_17 != ensemble->services.end();
+ ++serviceFIG0_17) {
+
+ if ( (*serviceFIG0_17)->pty == 0 &&
+ (*serviceFIG0_17)->language == 0) {
+ continue;
+ }
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 17;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*serviceFIG0_17)->language == 0) {
+ if (figSize + 4 > 30) {
+ break;
+ }
+ }
+ else {
+ if (figSize + 5 > 30) {
+ break;
+ }
+ }
+
+ programme =
+ (FIGtype0_17_programme*)&etiFrame[index];
+ programme->SId = htons((*serviceFIG0_17)->id);
+ programme->SD = 1;
+ programme->PS = 0;
+ programme->L = (*serviceFIG0_17)->language != 0;
+ programme->CC = 0;
+ programme->Rfa = 0;
+ programme->NFC = 0;
+ if ((*serviceFIG0_17)->language == 0) {
+ etiFrame[index + 3] = (*serviceFIG0_17)->pty;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+ }
+ else {
+ etiFrame[index + 3] = (*serviceFIG0_17)->language;
+ etiFrame[index + 4] = (*serviceFIG0_17)->pty;
+ fig0->Length += 5;
+ index += 5;
+ figSize += 5;
+ }
+ }
+ break;
+ }
+
+ if (figSize > 30) {
+ etiLog.log(error,
+ "FIG too big (%i > 30)\n", figSize);
+ throw MuxInitException();
+ }
+
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+ figSize = 0;
+ // FIB 1 insertion
+ switch (rotateFIB) {
+ case 0: // FIG 0/8 program
+ fig0 = NULL;
+
+ if (componentProgFIG0_8 == ensemble->components.end()) {
+ componentProgFIG0_8 = ensemble->components.begin();
+ }
+ for (; componentProgFIG0_8 != ensemble->components.end();
+ ++componentProgFIG0_8) {
+ service = getService(*componentProgFIG0_8,
+ ensemble->services);
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentProgFIG0_8)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentProgFIG0_8)->subchId,
+ (*componentProgFIG0_8)->serviceId);
+ throw MuxInitException();
+ }
+
+ if (!(*service)->program)
+ continue;
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 8;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*subchannel)->type == Packet) { // Data packet
+ if (figSize > 30 - 5) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentProgFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 2;
+ index += 2;
+ figSize += 2;
+
+ FIGtype0_8_long* definition =
+ (FIGtype0_8_long*)&etiFrame[index];
+ memset(definition, 0, 3);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
+ definition->LS = 1;
+ definition->setSCId((*componentProgFIG0_8)->packet.id);
+ fig0->Length += 3;
+ index += 3; // 8 minus rfa
+ figSize += 3;
+ }
+ else { // Audio, data stream or FIDC
+ if (figSize > 30 - 4) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentProgFIG0_8)->serviceId) & 0xFF;
+
+ fig0->Length += 2;
+ index += 2;
+ figSize += 2;
+
+ FIGtype0_8_short* definition =
+ (FIGtype0_8_short*)&etiFrame[index];
+ memset(definition, 0, 2);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
+ definition->LS = 0;
+ definition->MscFic = 0;
+ definition->Id = (*componentProgFIG0_8)->subchId;
+ fig0->Length += 2;
+ index += 2; // 4 minus rfa
+ figSize += 2;
+ }
+ }
+ break;
+
+ case 1: // FIG 0/8 data
+ fig0 = NULL;
+
+ if (componentDataFIG0_8 == ensemble->components.end()) {
+ componentDataFIG0_8 = ensemble->components.begin();
+ }
+ for (; componentDataFIG0_8 != ensemble->components.end();
+ ++componentDataFIG0_8) {
+ service = getService(*componentDataFIG0_8,
+ ensemble->services);
+
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentDataFIG0_8)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentDataFIG0_8)->subchId,
+ (*componentDataFIG0_8)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ((*service)->program)
+ continue;
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 1;
+ fig0->Extension = 8;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*subchannel)->type == Packet) { // Data packet
+ if (figSize > 30 - 7) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentDataFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+
+ FIGtype0_8_long* definition =
+ (FIGtype0_8_long*)&etiFrame[index];
+ memset(definition, 0, 3);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
+ definition->LS = 1;
+ definition->setSCId((*componentDataFIG0_8)->packet.id);
+ fig0->Length += 3;
+ index += 3; // 8 minus rfa
+ figSize += 3;
+ }
+ else { // Audio, data stream or FIDC
+ if (figSize > 30 - 6) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentDataFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+
+ FIGtype0_8_short* definition =
+ (FIGtype0_8_short*)&etiFrame[index];
+ memset(definition, 0, 2);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
+ definition->LS = 0;
+ definition->MscFic = 0;
+ definition->Id = (*componentDataFIG0_8)->subchId;
+ fig0->Length += 2;
+ index += 2; // 4 minus rfa
+ figSize += 2;
+ }
+ }
+ break;
+
+ case 3:
+ // FIG type 1/0, Service Information (SI), Ensemble Label
+ fig1_0 = (FIGtype1_0 *) & etiFrame[index];
+
+ fig1_0->Length = 21;
+ fig1_0->FIGtypeNumber = 1;
+ fig1_0->Extension = 0;
+ fig1_0->OE = 0;
+ fig1_0->Charset = 0;
+ fig1_0->EId = htons(ensemble->id);
+ index = index + 4;
+
+ ensemble->label.writeLabel(&etiFrame[index]);
+ index = index + 16;
+
+ etiFrame[index++] = ensemble->label.flag() >> 8;
+ etiFrame[index++] = ensemble->label.flag() & 0xFF;
+
+ figSize += 22;
+ break;
+
+ case 5:
+ case 6:
+ // FIG 0 / 13
+ fig0 = NULL;
+
+ if (componentFIG0_13 == ensemble->components.end()) {
+ componentFIG0_13 = ensemble->components.begin();
+
+ transmitFIG0_13programme = !transmitFIG0_13programme;
+ // Alternate between data and and programme FIG0/13,
+ // do not mix fig0 with PD=0 with extension 13 stuff
+ // that actually needs PD=1, and vice versa
+ }
+
+ for (; componentFIG0_13 != ensemble->components.end();
+ ++componentFIG0_13) {
+
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentFIG0_13)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentFIG0_13)->subchId,
+ (*componentFIG0_13)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ( transmitFIG0_13programme &&
+ (*subchannel)->type == Audio &&
+ (*componentFIG0_13)->audio.uaType != 0xffff) {
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 13;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30 - (3+4+11)) {
+ break;
+ }
+
+ FIG0_13_shortAppInfo* info =
+ (FIG0_13_shortAppInfo*)&etiFrame[index];
+ info->SId = htonl((*componentFIG0_13)->serviceId) >> 16;
+ info->SCIdS = (*componentFIG0_13)->SCIdS;
+ info->No = 1;
+ index += 3;
+ figSize += 3;
+ fig0->Length += 3;
+
+ FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
+ app->setType((*componentFIG0_13)->audio.uaType);
+ app->length = 4;
+ app->xpad = htonl(0x0cbc0000);
+ /* xpad meaning
+ CA = 0
+ CAOrg = 0
+ Rfu = 0
+ AppTy(5) = 12 (MOT, start of X-PAD data group)
+ DG = 0 (MSC data groups used)
+ Rfu = 0
+ DSCTy(6) = 60 (MOT)
+ CAOrg(16) = 0
+ */
+
+ index += 2 + app->length;
+ figSize += 2 + app->length;
+ fig0->Length += 2 + app->length;
+ }
+ else if (!transmitFIG0_13programme &&
+ (*subchannel)->type == Packet &&
+ (*componentFIG0_13)->packet.appType != 0xffff) {
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 1;
+ fig0->Extension = 13;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30 - (5+2)) {
+ break;
+ }
+
+ FIG0_13_longAppInfo* info =
+ (FIG0_13_longAppInfo*)&etiFrame[index];
+ info->SId = htonl((*componentFIG0_13)->serviceId);
+ info->SCIdS = (*componentFIG0_13)->SCIdS;
+ info->No = 1;
+ index += 5;
+ figSize += 5;
+ fig0->Length += 5;
+
+ FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
+ app->setType((*componentFIG0_13)->packet.appType);
+ app->length = 0;
+ index += 2;
+ figSize += 2;
+ fig0->Length += 2;
+ }
+ }
+ break;
+
+ case 7:
+ //Time and country identifier
+ fig0_10 = (FIGtype0_10 *) & etiFrame[index];
+
+ fig0_10->FIGtypeNumber = 0;
+ fig0_10->Length = 5;
+ fig0_10->CN = 0;
+ fig0_10->OE = 0;
+ fig0_10->PD = 0;
+ fig0_10->Extension = 10;
+ index = index + 2;
+
+ timeData = gmtime(&date);
+
+ fig0_10->RFU = 0;
+ fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900,
+ timeData->tm_mon + 1,
+ timeData->tm_mday));
+ fig0_10->LSI = 0;
+ fig0_10->ConfInd = (m_watermarkData[m_watermarkPos >> 3] >>
+ (7 - (m_watermarkPos & 0x07))) & 1;
+ if (++m_watermarkPos == m_watermarkSize) {
+ m_watermarkPos = 0;
+ }
+ fig0_10->UTC = 0;
+ fig0_10->setHours(timeData->tm_hour);
+ fig0_10->Minutes = timeData->tm_min;
+ index = index + 4;
+ figSize += 6;
+
+ fig0_9 = (FIGtype0_9*)&etiFrame[index];
+ fig0_9->FIGtypeNumber = 0;
+ fig0_9->Length = 4;
+ fig0_9->CN = 0;
+ fig0_9->OE = 0;
+ fig0_9->PD = 0;
+ fig0_9->Extension = 9;
+
+ fig0_9->ext = 0;
+ fig0_9->lto = 0; // Unique LTO for ensemble
+
+ if (ensemble->lto_auto) {
+ time_t now = time(NULL);
+ struct tm* ltime = localtime(&now);
+ time_t now2 = timegm(ltime);
+ ensemble->lto = (now2 - now) / 1800;
+ }
+
+ if (ensemble->lto >= 0) {
+ fig0_9->ensembleLto = ensemble->lto;
+ }
+ else {
+ /* Convert to 1-complement representation */
+ fig0_9->ensembleLto = (-ensemble->lto) | (1<<5);
+ }
+
+ fig0_9->ensembleEcc = ensemble->ecc;
+ fig0_9->tableId = ensemble->international_table;
+ index += 5;
+ figSize += 5;
+
+ break;
+ }
+
+ assert(figSize <= 30);
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+
+ figSize = 0;
+ // FIB 2 insertion
+ if (rotateFIB < ensemble->services.size()) {
+ service = ensemble->services.begin() + rotateFIB;
+
+ // FIG type 1/1, SI, Service label, one instance per subchannel
+ if ((*service)->getType(ensemble) == 0) {
+ fig1_1 = (FIGtype1_1 *) & etiFrame[index];
+
+ fig1_1->FIGtypeNumber = 1;
+ fig1_1->Length = 21;
+ fig1_1->Charset = 0;
+ fig1_1->OE = 0;
+ fig1_1->Extension = 1;
+
+ fig1_1->Sld = htons((*service)->id);
+ index += 4;
+ figSize += 4;
+ }
+ else {
+ fig1_5 = (FIGtype1_5 *) & etiFrame[index];
+ fig1_5->FIGtypeNumber = 1;
+ fig1_5->Length = 23;
+ fig1_5->Charset = 0;
+ fig1_5->OE = 0;
+ fig1_5->Extension = 5;
+
+ fig1_5->SId = htonl((*service)->id);
+ index += 6;
+ figSize += 6;
+ }
+ (*service)->label.writeLabel(&etiFrame[index]);
+ index += 16;
+ figSize += 16;
+ etiFrame[index++] = (*service)->label.flag() >> 8;
+ etiFrame[index++] = (*service)->label.flag() & 0xFF;
+ figSize += 2;
+ }
+ else if (rotateFIB <
+ ensemble->services.size() + ensemble->components.size()) {
+ component = ensemble->components.begin() +
+ (rotateFIB - ensemble->services.size());
+
+ service = getService(*component, ensemble->services);
+
+ subchannel =
+ getSubchannel(ensemble->subchannels, (*component)->subchId);
+
+ if (not (*component)->label.long_label().empty() ) {
+ if ((*service)->getType(ensemble) == 0) { // Programme
+ FIGtype1_4_programme *fig1_4;
+ fig1_4 = (FIGtype1_4_programme*)&etiFrame[index];
+
+ fig1_4->FIGtypeNumber = 1;
+ fig1_4->Length = 22;
+ fig1_4->Charset = 0;
+ fig1_4->OE = 0;
+ fig1_4->Extension = 4;
+ fig1_4->PD = 0;
+ fig1_4->rfa = 0;
+ fig1_4->SCIdS = (*component)->SCIdS;
+
+ fig1_4->SId = htons((*service)->id);
+ index += 5;
+ figSize += 5;
+ }
+ else { // Data
+ FIGtype1_4_data *fig1_4;
+ fig1_4 = (FIGtype1_4_data *) & etiFrame[index];
+ fig1_4->FIGtypeNumber = 1;
+ fig1_4->Length = 24;
+ fig1_4->Charset = 0;
+ fig1_4->OE = 0;
+ fig1_4->Extension = 4;
+ fig1_4->PD = 1;
+ fig1_4->rfa = 0;
+ fig1_4->SCIdS = (*component)->SCIdS;
+
+ fig1_4->SId = htonl((*service)->id);
+ index += 7;
+ figSize += 7;
+ }
+ (*component)->label.writeLabel(&etiFrame[index]);
+ index += 16;
+ figSize += 16;
+
+ etiFrame[index++] = (*component)->label.flag() >> 8;
+ etiFrame[index++] = (*component)->label.flag() & 0xFF;
+ figSize += 2;
+ }
+ }
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+ /* ETSI EN 300 799 Table 2:
+ * Only TM3 has a FIB count to CIF count that is
+ * not 3 to 1.
+ */
+ if (ensemble->mode == 3) {
+ memcpy(&etiFrame[index], Padding_FIB, 30);
+ index += 30;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+ }
+
+ if (ensemble->services.size() > 30) {
+ etiLog.log(error,
+ "Sorry, but this software currently can't write "
+ "Service Label of more than 30 services.\n");
+ throw MuxInitException();
+ }
+
+ // counter for FIG 0/0
+ insertFIG = (insertFIG + 1) % 16;
+
+ // We rotate through the FIBs every 30 frames
+ rotateFIB = (rotateFIB + 1) % 30;
+
+ /**********************************************************************
+ ****** Input Data Reading *******************************************
+ **********************************************************************/
+
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+
+ TagESTn* tag = edi_subchannelToTag[*subchannel];
+
+ int sizeSubchannel = getSizeByte(*subchannel);
+ int result = (*subchannel)->input->readFrame(
+ &etiFrame[index], sizeSubchannel);
+
+ if (result < 0) {
+ etiLog.log(info,
+ "Subchannel %d read failed at ETI frame number: %d\n",
+ (*subchannel)->id, currentFrame);
+ }
+
+ // save pointer to Audio or Data Stream into correct TagESTn for EDI
+ tag->mst_data = &etiFrame[index];
+
+ index += sizeSubchannel;
+ }
+
+
+ index = (3 + fc->NST + FICL) * 4;
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ index += getSizeByte(*subchannel);
+ }
+
+ /******* Section EOF **************************************************/
+ // End of Frame, 4 octets
+ index = (FLtmp + 1 + 1) * 4;
+ eti_EOF *eof = (eti_EOF *) & etiFrame[index];
+
+ // CRC of Main Stream data (MST), 16 bits
+ index = ((fc->NST) + 2 + 1) * 4; // MST position
+
+ unsigned short MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4; // data size
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize);
+ CRCtmp ^= 0xffff;
+ eof->CRC = htons(CRCtmp);
+
+ //RFU, Reserved for future use, 2 bytes, should be 0xFFFF
+ eof->RFU = htons(0xFFFF);
+
+ /******* Section TIST *************************************************/
+ // TimeStamps, 24 bits + 1 octet
+ index = (FLtmp + 2 + 1) * 4;
+ eti_TIST *tist = (eti_TIST *) & etiFrame[index];
+
+ bool enableTist = m_pt.get("general.tist", false);
+ if (enableTist) {
+ tist->TIST = htonl(timestamp) | 0xff;
+ }
+ else {
+ tist->TIST = htonl(0xffffff) | 0xff;
+ }
+
+ edi_tagDETI.tsta = tist->TIST;
+
+ timestamp += 3 << 17;
+ if (timestamp > 0xf9ffff)
+ {
+ timestamp -= 0xfa0000;
+
+ // Also update MNSC time for next frame
+ MNSC_increment_time = true;
+ }
+
+
+
+ /**********************************************************************
+ *********** Section FRPD *****************************************
+ **********************************************************************/
+
+ int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4;
+
+ // Give the data to the outputs
+ for (auto output : outputs) {
+ if (output->Write(etiFrame, frame_size) == -1) {
+ etiLog.level(error) <<
+ "Can't write to output " <<
+ output->get_info();
+ }
+ }
+
+#ifdef DUMP_BRIDGE
+ dumpBytes(dumpData, sizeSubChannel, stderr);
+#endif // DUMP_BRIDGE
+
+#if HAVE_OUTPUT_EDI
+ /**********************************************************************
+ *********** Finalise and send EDI ********************************
+ **********************************************************************/
+
+ if (edi_conf.enabled) {
+ // put tags *ptr, DETI and all subchannels into one TagPacket
+ edi_tagpacket.tag_items.push_back(&edi_tagStarPtr);
+ edi_tagpacket.tag_items.push_back(&edi_tagDETI);
+
+ for (auto& tag : edi_subchannels) {
+ edi_tagpacket.tag_items.push_back(&tag);
+ }
+
+ // Assemble into one AF Packet
+ AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket);
+
+ if (edi_conf.enable_pft) {
+ // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation)
+ vector< PFTFragment > edi_fragments =
+ edi_pft.Assemble(edi_afpacket);
+
+ // Send over ethernet
+ for (const auto& edi_frag : edi_fragments) {
+ UdpPacket udppacket;
+
+ InetAddress& addr = udppacket.getAddress();
+ addr.setAddress(edi_conf.dest_addr.c_str());
+ addr.setPort(edi_conf.dest_port);
+
+ udppacket.addData(&(edi_frag.front()), edi_frag.size());
+
+ edi_output.send(udppacket);
+
+ if (edi_conf.dump) {
+ std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
+ std::copy(edi_frag.begin(), edi_frag.end(), debug_iterator);
+ }
+ }
+
+ if (edi_conf.verbose) {
+ fprintf(stderr, "EDI number of PFT fragments %zu\n",
+ edi_fragments.size());
+ }
+ }
+ else {
+ // Send over ethernet
+
+ UdpPacket udppacket;
+
+ InetAddress& addr = udppacket.getAddress();
+ addr.setAddress(edi_conf.dest_addr.c_str());
+ addr.setPort(edi_conf.dest_port);
+
+ udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size());
+
+ edi_output.send(udppacket);
+
+ if (edi_conf.dump) {
+ std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
+ std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator);
+ }
+ }
+ }
+#endif // HAVE_OUTPUT_EDI
+
+ if (currentFrame % 100 == 0) {
+ etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
+ currentFrame, mnsc_time.tv_sec);
+ }
+
+#if _DEBUG
+ /**********************************************************************
+ *********** Output a small message *********************************
+ **********************************************************************/
+ if (currentFrame % 100 == 0) {
+ if (enableTist) {
+ etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n",
+ currentFrame, mnsc_time.tv_sec,
+ (timestamp & 0xFFFFFF) / 16384000.0);
+ }
+ else {
+ etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
+ currentFrame, mnsc_time.tv_sec);
+ }
+ }
+#endif
+
+ currentFrame++;
+}
+
+void DabMultiplexer::print_info(void)
+{
+ return;
+ // Print settings before starting
+ etiLog.log(info, "--- Multiplex configuration ---");
+ printEnsemble(ensemble);
+
+ etiLog.log(info, "--- Subchannels list ---");
+ printSubchannels(ensemble->subchannels);
+
+ etiLog.log(info, "--- Services list ---");
+ printServices(ensemble->services);
+
+ etiLog.log(info, "--- Components list ---");
+ printComponents(ensemble->components);
+}
+
diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h
new file mode 100644
index 0000000..44e9bdb
--- /dev/null
+++ b/src/DabMultiplexer.h
@@ -0,0 +1,514 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __DAB_MULTIPLEXER_H__
+#define __DAB_MULTIPLEXER_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "dabOutput/dabOutput.h"
+#include "dabOutput/edi/TagItems.h"
+#include "dabOutput/edi/TagPacket.h"
+#include "dabOutput/edi/AFPacket.h"
+#include "dabOutput/edi/PFT.h"
+#include "fig/FIGCarousel.h"
+#include "crc.h"
+#include "utils.h"
+#include "UdpSocket.h"
+#include "InetAddress.h"
+#include "dabUtils.h"
+#include "PcDebug.h"
+#include "MuxElements.h"
+#include "RemoteControl.h"
+#include "Eti.h"
+#include <exception>
+#include <vector>
+#include <memory>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+class MuxInitException : public std::exception
+{
+ public:
+ MuxInitException(const std::string m = "ODR-DabMux initialisation error")
+ throw()
+ : msg(m) {}
+ ~MuxInitException(void) throw() {}
+ const char* what() const throw() { return msg.c_str(); }
+ private:
+ std::string msg;
+};
+
+class DabMultiplexer {
+ public:
+ DabMultiplexer(boost::shared_ptr<BaseRemoteController> rc,
+ boost::property_tree::ptree pt);
+ void prepare(void);
+
+ unsigned long getCurrentFrame() { return currentFrame; }
+
+ void mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs);
+
+ void print_info(void);
+
+ void update_config(boost::property_tree::ptree pt);
+
+ void set_edi_config(const edi_configuration_t& new_edi_conf);
+
+ private:
+ void prepare_watermark(void);
+ void prepare_subchannels(void);
+ void prepare_services_components(void);
+ void prepare_data_inputs(void);
+ void reconfigure(void);
+
+ boost::property_tree::ptree m_pt;
+ boost::shared_ptr<BaseRemoteController> m_rc;
+
+ unsigned timestamp;
+ bool MNSC_increment_time;
+ struct timeval mnsc_time;
+
+ edi_configuration_t edi_conf;
+
+
+ uint8_t m_watermarkData[128];
+ size_t m_watermarkSize;
+ size_t m_watermarkPos;
+
+ uint32_t sync;
+ unsigned long currentFrame;
+
+ /* FIG carousel logic and iterators */
+ unsigned int insertFIG;
+ unsigned int rotateFIB;
+
+ std::vector<std::shared_ptr<DabService> >::iterator serviceProgFIG0_2;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceDataFIG0_2;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceFIG0_17;
+
+ std::vector<DabComponent*>::iterator componentProgFIG0_8;
+ std::vector<DabComponent*>::iterator componentDataFIG0_8;
+ std::vector<DabComponent*>::iterator componentFIG0_13;
+ // Alternate between programme and data
+ bool transmitFIG0_13programme;
+
+
+ std::vector<dabSubchannel*>::iterator subchannelFIG0_1;
+
+ boost::shared_ptr<dabEnsemble> ensemble;
+
+ // Multiplex reconfiguration requires two sets of configurations
+ boost::property_tree::ptree m_pt_next;
+ boost::shared_ptr<dabEnsemble> ensemble_next;
+
+#if HAVE_OUTPUT_EDI
+ std::ofstream edi_debug_file;
+ UdpSocket edi_output;
+
+ // The TagPacket will then be placed into an AFPacket
+ AFPacketiser edi_afPacketiser;
+
+ // The AF Packet will be protected with reed-solomon and split in fragments
+ PFT edi_pft;
+#endif // HAVE_OUTPUT_EDI
+
+ /* New FIG Carousel */
+ FIGCarousel fig_carousel;
+};
+
+// DAB Mode
+#define DEFAULT_DAB_MODE 2
+
+// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits,
+// STL3 * 8 = x kbytes par trame ETI
+
+// Data bitrate in kbits/s. Must be 64 kb/s multiple.
+#define DEFAULT_DATA_BITRATE 384
+#define DEFAULT_PACKET_BITRATE 32
+
+/* default ensemble parameters. Label must be max 16 chars, short label
+ * a subset of the label, max 8 chars
+ */
+#define DEFAULT_ENSEMBLE_LABEL "ODR Dab Mux"
+#define DEFAULT_ENSEMBLE_SHORT_LABEL "ODRMux"
+#define DEFAULT_ENSEMBLE_ID 0xc000
+#define DEFAULT_ENSEMBLE_ECC 0xa1
+
+// start value for default service IDs (if not overridden by configuration)
+#define DEFAULT_SERVICE_ID 50
+#define DEFAULT_PACKET_ADDRESS 0
+
+
+/*****************************************************************************
+ ***************** Definition of FIG structures ****************************
+ *****************************************************************************/
+
+#ifdef _WIN32
+# pragma pack(push)
+#endif
+
+struct FIGtype0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIGtype0_0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint16_t EId;
+ uint8_t CIFcnt_hight:5;
+ uint8_t Al:1;
+ uint8_t Change:2;
+ uint8_t CIFcnt_low:8;
+} PACKED;
+
+
+struct FIGtype0_2 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIGtype0_2_Service {
+ uint16_t SId;
+ uint8_t NbServiceComp:4;
+ uint8_t CAId:3;
+ uint8_t Local_flag:1;
+} PACKED;
+
+
+struct FIGtype0_2_Service_data {
+ uint32_t SId;
+ uint8_t NbServiceComp:4;
+ uint8_t CAId:3;
+ uint8_t Local_flag:1;
+} PACKED;
+
+
+struct FIGtype0_2_audio_component {
+ uint8_t ASCTy:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SubChId:6;
+} PACKED;
+
+
+struct FIGtype0_2_data_component {
+ uint8_t DSCTy:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SubChId:6;
+} PACKED;
+
+
+struct FIGtype0_2_packet_component {
+ uint8_t SCId_high:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SCId_low:6;
+ void setSCId(uint16_t SCId) {
+ SCId_high = SCId >> 6;
+ SCId_low = SCId & 0x3f;
+ }
+} PACKED;
+
+
+struct FIGtype0_3_header {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send
+ * the SCCA field. But, in the Factum ETI analyzer, if this field is not there,
+ * it is an error.
+ */
+struct FIGtype0_3_data {
+ uint8_t SCId_high;
+ uint8_t SCCA_flag:1;
+ uint8_t rfa:3;
+ uint8_t SCId_low:4;
+ uint8_t DSCTy:6;
+ uint8_t rfu:1;
+ uint8_t DG_flag:1;
+ uint8_t Packet_address_high:2;
+ uint8_t SubChId:6;
+ uint8_t Packet_address_low;
+ uint16_t SCCA;
+ void setSCId(uint16_t SCId) {
+ SCId_high = SCId >> 4;
+ SCId_low = SCId & 0xf;
+ }
+ void setPacketAddress(uint16_t address) {
+ Packet_address_high = address >> 8;
+ Packet_address_low = address & 0xff;
+ }
+} PACKED;
+
+
+struct FIGtype0_8_short {
+ uint8_t SCIdS:4;
+ uint8_t rfa_1:3;
+ uint8_t ext:1;
+ uint8_t Id:6;
+ uint8_t MscFic:1;
+ uint8_t LS:1;
+ uint8_t rfa_2;
+} PACKED;
+
+
+struct FIGtype0_8_long {
+ uint8_t SCIdS:4;
+ uint8_t rfa_1:3;
+ uint8_t ext:1;
+ uint8_t SCId_high:4;
+ uint8_t rfa:3;
+ uint8_t LS:1;
+ uint8_t SCId_low;
+ uint8_t rfa_2;
+ void setSCId(uint16_t id) {
+ SCId_high = id >> 8;
+ SCId_low = id & 0xff;
+ }
+ uint16_t getSCid() {
+ return (SCId_high << 8) | SCId_low;
+ }
+} PACKED;
+
+
+struct FIGtype0_9 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint8_t ensembleLto:6;
+ uint8_t lto:1;
+ uint8_t ext:1;
+ uint8_t ensembleEcc;
+ uint8_t tableId;
+} PACKED;
+
+
+struct FIGtype0_10 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint8_t MJD_high:7;
+ uint8_t RFU:1;
+ uint8_t MJD_med;
+ uint8_t Hours_high:3;
+ uint8_t UTC:1;
+ uint8_t ConfInd:1;
+ uint8_t LSI:1;
+ uint8_t MJD_low:2;
+ uint8_t Minutes:6;
+ uint8_t Hours_low:2;
+ void setMJD(uint32_t date) {
+ MJD_high = (date >> 10) & 0x7f;
+ MJD_med = (date >> 2) & 0xff;
+ MJD_low = date & 0x03;
+ }
+ void setHours(uint16_t hours) {
+ Hours_high = (hours >> 2) & 0x07;
+ Hours_low = hours & 0x03;
+ }
+} PACKED;
+
+
+struct FIGtype0_17_programme {
+ uint16_t SId;
+ uint8_t NFC:2;
+ uint8_t Rfa:2;
+ uint8_t CC:1;
+ uint8_t L:1;
+ uint8_t PS:1;
+ uint8_t SD:1;
+} PACKED;
+
+
+struct FIGtype0_1 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIG_01_SubChannel_ShortF {
+ uint8_t StartAdress_high:2;
+ uint8_t SubChId:6;
+ uint8_t StartAdress_low:8;
+ uint8_t TableIndex:6;
+ uint8_t TableSwitch:1;
+ uint8_t Short_Long_form:1;
+} PACKED;
+
+
+struct FIG_01_SubChannel_LongF {
+ uint8_t StartAdress_high:2;
+ uint8_t SubChId:6;
+ uint8_t StartAdress_low:8;
+ uint8_t Sub_ChannelSize_high:2;
+ uint8_t ProtectionLevel:2;
+ uint8_t Option:3;
+ uint8_t Short_Long_form:1;
+ uint8_t Sub_ChannelSize_low:8;
+} PACKED;
+
+
+// See EN 300 401, Clause 8.1.20 for the FIG0_13 description
+struct FIG0_13_shortAppInfo {
+ uint16_t SId;
+ uint8_t No:4;
+ uint8_t SCIdS:4;
+} PACKED;
+
+
+struct FIG0_13_longAppInfo {
+ uint32_t SId;
+ uint8_t No:4;
+ uint8_t SCIdS:4;
+} PACKED;
+
+
+struct FIG0_13_app {
+ uint8_t typeHigh;
+ uint8_t length:5;
+ uint8_t typeLow:3;
+ void setType(uint16_t type) {
+ typeHigh = type >> 3;
+ typeLow = type & 0x1f;
+ }
+ uint32_t xpad;
+} PACKED;
+
+#define FIG0_13_APPTYPE_SLIDESHOW 0x2
+#define FIG0_13_APPTYPE_WEBSITE 0x3
+#define FIG0_13_APPTYPE_TPEG 0x4
+#define FIG0_13_APPTYPE_DGPS 0x5
+#define FIG0_13_APPTYPE_TMC 0x6
+#define FIG0_13_APPTYPE_EPG 0x7
+#define FIG0_13_APPTYPE_DABJAVA 0x8
+#define FIG0_13_APPTYPE_JOURNALINE 0x441
+
+
+struct FIGtype1_0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+
+ uint16_t EId;
+} PACKED;
+
+
+struct FIGtype1_1 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+
+ uint16_t Sld;
+} PACKED;
+
+
+struct FIGtype1_5 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint32_t SId;
+} PACKED;
+
+
+struct FIGtype1_4_programme {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint16_t SId;
+} PACKED;
+
+
+struct FIGtype1_4_data {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint32_t SId;
+} PACKED;
+
+
+#ifdef _WIN32
+# pragma pack(pop)
+#endif
+
+#endif
+
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
index 8ef30ba..854c311 100644
--- a/src/DabMux.cpp
+++ b/src/DabMux.cpp
@@ -29,6 +29,7 @@
# include "config.h"
#endif
+#include <boost/shared_ptr.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <cstdio>
@@ -138,51 +139,6 @@ using boost::property_tree::ptree;
using boost::property_tree::ptree_error;
-/* Global stats and config server */
-ManagementServer* mgmt_server;
-
-class MuxInitException : public exception
-{
- public:
- MuxInitException(const string m = "DABMUX initialisation error") throw()
- : msg(m) {}
- ~MuxInitException(void) throw() {}
- const char* what() const throw() { return msg.c_str(); }
- private:
- string msg;
-};
-
-static unsigned char Padding_FIB[] = {
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-
-// Protection levels and bitrates for UEP.
-const unsigned char ProtectionLevelTable[64] = {
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 0, 4, 3, 2, 1, 4, 3,
- 2, 1, 0, 4, 3, 2, 1, 0,
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 4, 3, 2, 1, 0, 4, 3,
- 2, 1, 0, 4, 3, 2, 1, 0,
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 0, 4, 3, 1, 4, 2, 0
-};
-
-const unsigned short BitRateTable[64] = {
- 32, 32, 32, 32, 32, 48, 48, 48,
- 48, 48, 56, 56, 56, 56, 64, 64,
- 64, 64, 64, 80, 80, 80, 80, 80,
- 96, 96, 96, 96, 96, 112, 112, 112,
- 112, 128, 128, 128, 128, 128, 160, 160,
- 160, 160, 160, 192, 192, 192, 192, 192,
- 224, 224, 224, 224, 224, 256, 256, 256,
- 256, 256, 320, 320, 320, 384, 384, 384
-};
-
volatile sig_atomic_t running = 1;
@@ -247,117 +203,23 @@ int main(int argc, char *argv[])
strerror(errno));
}
#else
- if (setpriority(PRIO_PROCESS, 0, -20) == -1) {
- etiLog.log(warn, "Can't increase priority: %s\n",
- strerror(errno));
+ // Use the lowest real-time priority for this thread, and switch to real-time scheduling
+ const int policy = SCHED_RR;
+ sched_param sp;
+ sp.sched_priority = sched_get_priority_min(policy);
+ int thread_prio_ret = pthread_setschedparam(pthread_self(), policy, &sp);
+ if (thread_prio_ret != 0) {
+ etiLog.level(error) << "Could not set real-time priority for thread:" << thread_prio_ret;
}
#endif
- /*sched_param scheduler;
- scheduler.sched_priority = 99; // sched_get_priority_max(SCHED_RR)
- if (sched_setscheduler(0, SCHED_RR, &scheduler) == -1) {
- etiLog.log(warn, "Can't increased priority: %s\n",
- strerror(errno));
- }*/
-
- uint8_t watermarkData[128];
- size_t watermarkSize = 0;
- size_t watermarkPos = 0;
- {
- uint8_t buffer[sizeof(watermarkData) / 2];
- snprintf((char*)buffer, sizeof(buffer),
- "%s %s, compiled at %s, %s",
- PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__);
- memset(watermarkData, 0, sizeof(watermarkData));
- watermarkData[0] = 0x55; // Sync
- watermarkData[1] = 0x55;
- watermarkSize = 16;
- for (unsigned i = 0; i < strlen((char*)buffer); ++i) {
- for (int j = 0; j < 8; ++j) {
- uint8_t bit = (buffer[watermarkPos >> 3] >> (7 - (watermarkPos & 0x07))) & 1;
- watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07));
- ++watermarkSize;
- bit = 1;
- watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07));
- ++watermarkSize;
- ++watermarkPos;
- }
- }
- }
- watermarkPos = 0;
-
-
- dabEnsemble* ensemble = new dabEnsemble;
- ensemble->label.setLabel(DEFAULT_ENSEMBLE_LABEL, DEFAULT_ENSEMBLE_SHORT_LABEL);
- ensemble->mode = DEFAULT_DAB_MODE;
- ensemble->id = DEFAULT_ENSEMBLE_ID;
- ensemble->ecc = DEFAULT_ENSEMBLE_ECC;
-
- vector<dabOutput*> outputs;
- vector<DabService*>::iterator service = ensemble->services.end();
- vector<DabService*>::iterator serviceProgFIG0_2;
- vector<DabService*>::iterator serviceDataFIG0_2;
- vector<DabService*>::iterator serviceFIG0_17;
- vector<DabComponent*>::iterator component = ensemble->components.end();
- vector<DabComponent*>::iterator componentProgFIG0_8;
- vector<DabComponent*>::iterator componentFIG0_13;
- bool transmitFIG0_13programme = false; // Alternate between programme and data
- vector<DabComponent*>::iterator componentDataFIG0_8;
- vector<dabSubchannel*>::iterator subchannel = ensemble->subchannels.end();
- vector<dabSubchannel*>::iterator subchannelFIG0_1;
- vector<dabOutput*>::iterator output;
- dabProtection* protection = NULL;
-
- BaseRemoteController* rc = NULL;
-
- unsigned long currentFrame;
- int returnCode = 0;
- int cur;
- unsigned char etiFrame[6144];
- unsigned short index = 0;
- // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32
- unsigned FICL = (ensemble->mode == 3 ? 32 : 24);
-
- uint32_t sync = 0x49C5F8;
- unsigned short FLtmp = 0;
- unsigned short nbBytesCRC = 0;
- unsigned short CRCtmp = 0xFFFF;
- unsigned short MSTsize = 0;
-
- unsigned int insertFIG = 0;
- unsigned int rotateFIB = 0;
-
- bool factumAnalyzer = false;
- unsigned long limit = 0;
- time_t date;
- bool enableTist = false;
- unsigned timestamp = 0;
-
- int mgmtserverport = 0;
-
- edi_configuration_t edi_conf;
-
- // Defaults for edi
- edi_conf.enabled = false;
- edi_conf.dest_addr = "";
- edi_conf.dest_port = 0;
- edi_conf.source_port = 0;
- edi_conf.dump = false;
- edi_conf.enable_pft = false;
- ptree pt;
- struct timeval mnsc_time;
- /* TODO:
- * In a SFN, when reconfiguring the ensemble, the multiplexer
- * has to be restarted (odr-dabmux doesn't support reconfiguration).
- * Ideally, we must be able to restart transmission s.t. the receiver
- * synchronisation is preserved.
- */
- gettimeofday(&mnsc_time, NULL);
+ int returnCode = 0;
- bool MNSC_increment_time = false;
+ ptree pt;
+ std::vector<boost::shared_ptr<DabOutput> > outputs;
try {
if (argc == 2) { // Assume the only argument is a config file
@@ -365,19 +227,15 @@ int main(int argc, char *argv[])
if (conf_file == "-h") {
printUsage(argv[0], stdout);
- throw MuxInitException();
+ throw MuxInitException("Nothing to do");
}
try {
read_info(conf_file, pt);
- parse_ptree(pt, outputs, ensemble, &enableTist, &FICL,
- &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);
}
catch (runtime_error &e) {
- etiLog.log(error, "Configuration file parsing error: %s\n",
- e.what());
- throw MuxInitException();
+ throw MuxInitException(e.what());
}
}
else if (argc > 1 && strncmp(argv[1], "-e", 2) == 0) { // use external config file
@@ -391,14 +249,9 @@ int main(int argc, char *argv[])
string conf_file = argv[2];
read_info(conf_file, pt);
-
- parse_ptree(pt, outputs, ensemble, &enableTist, &FICL,
- &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);
}
catch (runtime_error &e) {
- etiLog.log(error, "Configuration file parsing error: %s\n",
- e.what());
- throw MuxInitException();
+ throw MuxInitException(e.what());
}
}
#if ENABLE_CMDLINE_OPTIONS
@@ -410,22 +263,31 @@ int main(int argc, char *argv[])
}
#else
else {
- etiLog.level(error) << "You must specify the configuration file";
- throw MuxInitException();
+ throw MuxInitException("No configuration file specified");
}
#endif
- if (mgmtserverport != 0) {
- mgmt_server = new ManagementServer(mgmtserverport);
+ int mgmtserverport = pt.get<int>("general.managementport",
+ pt.get<int>("general.statsserverport", 0) );
+
+ /* Management: stats and config server */
+ get_mgmt_server().open(mgmtserverport);
+
+ /************** READ REMOTE CONTROL PARAMETERS *************/
+ int telnetport = pt.get<int>("remotecontrol.telnetport", 0);
+
+ boost::shared_ptr<BaseRemoteController> rc;
+
+ if (telnetport != 0) {
+ rc = boost::shared_ptr<RemoteControllerTelnet>(
+ new RemoteControllerTelnet(telnetport));
}
else {
- mgmt_server = new ManagementServer();
- }
-
- if (rc) {
- ensemble->enrol_at(*rc);
+ rc = boost::shared_ptr<RemoteControllerDummy>(
+ new RemoteControllerDummy());
}
+ DabMultiplexer mux(rc, pt);
etiLog.level(info) <<
PACKAGE_NAME << " " <<
@@ -436,1759 +298,184 @@ int main(int argc, char *argv[])
#endif
" starting up";
- if (outputs.size() == 0) {
- etiLog.log(emerg, "no output defined");
- throw MuxInitException();
- }
- // Check and adjust subchannels
- {
- set<unsigned char> ids;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- if (ids.find((*subchannel)->id) != ids.end()) {
- etiLog.log(error,
- "Subchannel %u is set more than once!\n",
- (*subchannel)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_configuration_t edi_conf;
+
+ /******************** READ OUTPUT PARAMETERS ***************/
+ set<string> all_output_names;
+ ptree pt_outputs = pt.get_child("outputs");
+ for (auto ptree_pair : pt_outputs) {
+ string outputuid = ptree_pair.first;
+
+ // check for uniqueness of the uid
+ if (all_output_names.count(outputuid) == 0) {
+ all_output_names.insert(outputuid);
+ }
+ else {
+ stringstream ss;
+ ss << "output with uid " << outputuid << " not unique!";
+ throw runtime_error(ss.str());
}
- }
- // Check and adjust services and components
- {
- set<uint32_t> ids;
- for (service = ensemble->services.begin();
- service != ensemble->services.end();
- ++service) {
- if (ids.find((*service)->id) != ids.end()) {
- etiLog.log(error,
- "Service id 0x%x (%u) is set more than once!\n",
- (*service)->id, (*service)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ if (outputuid == "edi") {
+#if HAVE_OUTPUT_EDI
+ ptree pt_edi = pt_outputs.get_child("edi");
- // Get first component of this service
- component = getComponent(ensemble->components, (*service)->id);
- if (component == ensemble->components.end()) {
- etiLog.log(error,
- "Service id 0x%x (%u) includes no component!\n",
- (*service)->id, (*service)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_conf.enabled = true;
- // Adjust service type from this first component
- switch ((*service)->getType(ensemble)) {
- case 0: // Audio
- (*service)->program = true;
- break;
- case 1:
- case 2:
- case 3:
- (*service)->program = false;
- break;
- default:
- etiLog.log(error,
- "Error, unknown service type: %u\n", (*service)->getType(ensemble));
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_conf.dest_addr = pt_edi.get<string>("destination");
+ edi_conf.dest_port = pt_edi.get<unsigned int>("port");
+ edi_conf.source_port = pt_edi.get<unsigned int>("sourceport");
- // Adjust components type for DAB+
- while (component != ensemble->components.end()) {
- subchannel =
- getSubchannel(ensemble->subchannels, (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error, "Error, service %u component "
- "links to the invalid subchannel %u\n",
- (*component)->serviceId, (*component)->subchId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- protection = &(*subchannel)->protection;
- switch ((*subchannel)->type) {
- case Audio:
- {
- if (protection->form == EEP) {
- (*component)->type = 0x3f; // DAB+
- }
- }
- break;
- case DataDmb:
- case Fidc:
- case Packet:
- break;
- default:
- etiLog.log(error,
- "Error, unknown subchannel type\n");
- returnCode = -1;
- throw MuxInitException();
- }
- component = getComponent(ensemble->components,
- (*service)->id, component);
- }
+ edi_conf.dump = pt_edi.get<bool>("dump");
+ edi_conf.enable_pft = pt_edi.get<bool>("enable_pft");
+ edi_conf.verbose = pt_edi.get<bool>("verbose");
+
+ edi_conf.fec = pt_edi.get<unsigned int>("fec");
+ edi_conf.chunk_len = pt_edi.get<unsigned int>("chunk_len", 207);
+
+ mux.set_edi_config(edi_conf);
+#else
+ throw runtime_error("EDI output not compiled in");
+#endif
}
- }
+ else {
+ string uri = pt_outputs.get<string>(outputuid);
+ size_t proto_pos = uri.find("://");
+ if (proto_pos == std::string::npos) {
+ stringstream ss;
+ ss << "Output with uid " << outputuid << " no protocol defined!";
+ throw runtime_error(ss.str());
+ }
+
+ string proto = uri.substr(0, proto_pos);
+ string location = uri.substr(proto_pos + 3);
- for (output = outputs.begin(); output != outputs.end() ; ++output) {
- if (0) {
+ DabOutput *output;
+
+ if (0) {
#if defined(HAVE_OUTPUT_FILE)
- } else if ((*output)->outputProto == "file") {
- (*output)->output = new DabOutputFile();
+ } else if (proto == "file") {
+ output = new DabOutputFile();
#endif // defined(HAVE_OUTPUT_FILE)
#if defined(HAVE_OUTPUT_FIFO)
- } else if ((*output)->outputProto == "fifo") {
- (*output)->output = new DabOutputFifo();
+ } else if (proto == "fifo") {
+ output = new DabOutputFifo();
#endif // !defined(HAVE_OUTPUT_FIFO)
#if defined(HAVE_OUTPUT_RAW)
- } else if ((*output)->outputProto == "raw") {
- (*output)->output = new DabOutputRaw();
+ } else if (proto == "raw") {
+ output = new DabOutputRaw();
#endif // defined(HAVE_OUTPUT_RAW)
#if defined(HAVE_OUTPUT_UDP)
- } else if ((*output)->outputProto == "udp") {
- (*output)->output = new DabOutputUdp();
+ } else if (proto == "udp") {
+ output = new DabOutputUdp();
#endif // defined(HAVE_OUTPUT_UDP)
#if defined(HAVE_OUTPUT_TCP)
- } else if ((*output)->outputProto == "tcp") {
- (*output)->output = new DabOutputTcp();
+ } else if (proto == "tcp") {
+ output = new DabOutputTcp();
#endif // defined(HAVE_OUTPUT_TCP)
#if defined(HAVE_OUTPUT_SIMUL)
- } else if ((*output)->outputProto == "simul") {
- (*output)->output = new DabOutputSimul();
+ } else if (proto == "simul") {
+ output = new DabOutputSimul();
#endif // defined(HAVE_OUTPUT_SIMUL)
#if defined(HAVE_OUTPUT_ZEROMQ)
- } else if ((*output)->outputProto == "zmq+tcp") {
- (*output)->output = new DabOutputZMQ("tcp");
- } else if ((*output)->outputProto == "zmq+ipc") {
- (*output)->output = new DabOutputZMQ("ipc");
- } else if ((*output)->outputProto == "zmq+pgm") {
- (*output)->output = new DabOutputZMQ("pgm");
- } else if ((*output)->outputProto == "zmq+epgm") {
- (*output)->output = new DabOutputZMQ("epgm");
+ } else if (proto == "zmq+tcp") {
+ output = new DabOutputZMQ("tcp");
+ } else if (proto == "zmq+ipc") {
+ output = new DabOutputZMQ("ipc");
+ } else if (proto == "zmq+pgm") {
+ output = new DabOutputZMQ("pgm");
+ } else if (proto == "zmq+epgm") {
+ output = new DabOutputZMQ("epgm");
#endif // defined(HAVE_OUTPUT_ZEROMQ)
- } else {
- etiLog.log(error, "Output protocol unknown: %s\n",
- (*output)->outputProto.c_str());
- throw MuxInitException();
- }
-
- if ((*output)->output == NULL) {
- etiLog.log(error, "Unable to init output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- return -1;
- }
- if ((*output)->output->Open((*output)->outputName)
- == -1) {
- etiLog.log(error, "Unable to open output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- return -1;
- }
- }
+ } else {
+ etiLog.level(error) << "Output protocol unknown: " << proto;
+ throw MuxInitException();
+ }
- // Prepare and check the data inputs
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- protection = &(*subchannel)->protection;
- if (subchannel == ensemble->subchannels.begin()) {
- (*subchannel)->startAddress = 0;
- } else {
- (*subchannel)->startAddress = (*(subchannel - 1))->startAddress +
- getSizeCu(*(subchannel - 1));
- }
- if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) {
- perror((*subchannel)->inputUri.c_str());
- returnCode = -1;
- throw MuxInitException();
- }
+ if (output == NULL) {
+ etiLog.level(error) <<
+ "Unable to init output " <<
+ uri;
+ return -1;
+ }
- // TODO Check errors
- int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate);
- if (subch_bitrate <= 0) {
- etiLog.level(error) << "can't set bitrate for source " <<
- (*subchannel)->inputUri;
- returnCode = -1;
- throw MuxInitException();
- }
- (*subchannel)->bitrate = subch_bitrate;
-
- /* Use EEP unless we find a UEP configuration
- * UEP is only used for MPEG audio, but some bitrates don't
- * have a UEP profile (EN 300 401 Clause 6.2.1).
- * For these bitrates, we must switch to EEP.
- *
- * AAC audio and data is already EEP
- */
- if (protection->form == UEP) {
- protection->form = EEP;
- for (int i = 0; i < 64; i++) {
- if ( (*subchannel)->bitrate == BitRateTable[i] &&
- protection->level == ProtectionLevelTable[i] ) {
- protection->form = UEP;
- protection->uep.tableIndex = i;
- }
+ if (output->Open(location) == -1) {
+ etiLog.level(error) <<
+ "Unable to open output " <<
+ uri;
+ return -1;
}
- }
- /* EEP B can only be used for subchannels with bitrates
- * multiple of 32kbit/s
- */
- if ( protection->form == EEP &&
- protection->eep.profile == EEP_B &&
- subch_bitrate % 32 != 0 ) {
- etiLog.level(error) <<
- "Cannot use EEP_B protection for subchannel " <<
- (*subchannel)->inputUri <<
- ": bitrate not multiple of 32kbit/s";
- returnCode = -1;
- throw MuxInitException();
+ boost::shared_ptr<DabOutput> dabout(output);
+ outputs.push_back(dabout);
+
}
}
- if (ensemble->subchannels.size() == 0) {
- etiLog.log(error, "can't multiplex no subchannel!\n");
- returnCode = -1;
- throw MuxInitException();
- }
- subchannel = ensemble->subchannels.end() - 1;
- if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) {
- etiLog.log(error, "Total size in CU exceeds 864\n");
- printSubchannels(ensemble->subchannels);
- returnCode = -1;
+ if (outputs.size() == 0) {
+ etiLog.log(emerg, "no output defined");
throw MuxInitException();
}
- // Init packet components SCId
- cur = 0;
- for (component = ensemble->components.begin();
- component != ensemble->components.end();
- ++component) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
- if ((*subchannel)->type != Packet) continue;
-
- (*component)->packet.id = cur++;
- }
-
- // Print settings before starting
- etiLog.log(info, "--- Multiplex configuration ---");
- printEnsemble(ensemble);
-
- etiLog.log(info, "--- Subchannels list ---");
- printSubchannels(ensemble->subchannels);
-
- etiLog.log(info, "--- Services list ---");
- printServices(ensemble->services);
-
- etiLog.log(info, "--- Components list ---");
- printComponents(ensemble->components);
+ mux.prepare();
+ mux.print_info();
etiLog.log(info, "--- Output list ---");
printOutputs(outputs);
+#if HAVE_OUTPUT_EDI
if (edi_conf.enabled) {
etiLog.level(warn) << "EXPERIMENTAL EDI OUTPUT ENABLED!";
etiLog.level(info) << "edi to " << edi_conf.dest_addr << ":" << edi_conf.dest_port;
etiLog.level(info) << "source port " << edi_conf.source_port;
etiLog.level(info) << "verbose " << edi_conf.verbose;
}
+#endif
+ size_t limit = pt.get("general.nbframes", 0);
- /* These iterators are used to fill the respective FIG.
- * It is necessary to cycle through all the FIGs that have
- * to be transmitted because they do not fit in a single
- * FIB.
- *
- * ETSI EN 300 799 Clauses 5.2 and 8.1
- */
- serviceProgFIG0_2 = ensemble->services.end();
- serviceDataFIG0_2 = ensemble->services.end();
- componentProgFIG0_8 = ensemble->components.end();
- componentFIG0_13 = ensemble->components.end();
- componentDataFIG0_8 = ensemble->components.end();
- serviceFIG0_17 = ensemble->services.end();
- subchannelFIG0_1 = ensemble->subchannels.end();
-
- if (edi_conf.verbose) {
- etiLog.log(info, "Setup EDI debug");
- }
- std::ofstream edi_debug_file;
-
- if (edi_conf.dump) {
- edi_debug_file.open("./edi.debug");
- }
- UdpSocket edi_output;
-
- if (edi_conf.enabled) {
- edi_output.create(edi_conf.source_port);
- }
-
- if (edi_conf.verbose) {
- etiLog.log(info, "EDI debug set up");
- }
-
- // The TagPacket will then be placed into an AFPacket
- AFPacketiser edi_afPacketiser(edi_conf.verbose);
-
- // The AF Packet will be protected with reed-solomon and split in fragments
- PFT edi_pft(207, 3, edi_conf);
-
+ etiLog.level(info) << "Start loop";
/* Each iteration of the main loop creates one ETI frame */
+ size_t currentFrame;
for (currentFrame = 0; running; currentFrame++) {
- if ((limit > 0) && (currentFrame >= limit)) {
- break;
- }
-
- // For EDI, save ETI(LI) Management data into a TAG Item DETI
- TagDETI edi_tagDETI;
- TagStarPTR edi_tagStarPtr;
- list<TagESTn> edi_subchannels;
- map<dabSubchannel*, TagESTn*> edi_subchannelToTag;
-
- // The above Tag Items will be assembled into a TAG Packet
- TagPacket edi_tagpacket;
-
- edi_tagDETI.atstf = 0; // TODO add ATST support
-
- date = getDabTime();
-
- // Initialise the ETI frame
- memset(etiFrame, 0, 6144);
-
- /**********************************************************************
- ********** Section SYNC of ETI(NI, G703) *************************
- **********************************************************************/
-
- // See ETS 300 799 Clause 6
- eti_SYNC *etiSync = (eti_SYNC *) etiFrame;
-
- etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error
-
- //****** Field FSYNC *****//
- // See ETS 300 799, 6.2.1.2
- sync ^= 0xffffff;
- etiSync->FSYNC = sync;
-
- /**********************************************************************
- *********** Section LIDATA of ETI(NI, G703) **********************
- **********************************************************************/
-
- // See ETS 300 799 Figure 5 for a better overview of these fields.
-
- //****** Section FC ***************************************************/
- // 4 octets, starts at offset 4
- eti_FC *fc = (eti_FC *) &etiFrame[4];
-
- //****** FCT ******//
- // Incremente for each frame, overflows at 249
- fc->FCT = currentFrame % 250;
- edi_tagDETI.dflc = currentFrame % 5000;
-
- //****** FICF ******//
- // Fast Information Channel Flag, 1 bit, =1 if FIC present
- fc->FICF = edi_tagDETI.ficf = 1;
-
- //****** NST ******//
- /* Number of audio of data sub-channels, 7 bits, 0-64.
- * In the 15-frame period immediately preceding a multiplex
- * re-configuration, NST can take the value 0 (see annex E).
- */
- fc->NST = ensemble->subchannels.size();
-
- //****** FP ******//
- /* Frame Phase, 3 bit counter, tells the COFDM generator
- * when to insert the TII. Is also used by the MNSC.
- */
- fc->FP = edi_tagDETI.fp = currentFrame & 0x7;
-
- //****** MID ******//
- //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV
- fc->MID = edi_tagDETI.mid = ensemble->mode; //mode 2 needs 3 FIB, 3*32octets = 96octets
-
- //****** FL ******//
- /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST
- * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode.
- * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details
- */
- FLtmp = 1 + FICL + (fc->NST);
-
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- // Add STLsbch
- FLtmp += getSizeWord(*subchannel);
- }
-
- fc->setFrameLength(FLtmp);
- index = 8;
-
- /******* Section STC **************************************************/
- // Stream Characterization,
- // number of channels * 4 octets = nb octets total
- int edi_stream_id = 1;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- protection = &(*subchannel)->protection;
- eti_STC *sstc = (eti_STC *) & etiFrame[index];
-
- sstc->SCID = (*subchannel)->id;
- sstc->startAddress_high = (*subchannel)->startAddress / 256;
- sstc->startAddress_low = (*subchannel)->startAddress % 256;
- // depends on the desired protection form
- if (protection->form == UEP) {
- sstc->TPL = 0x10 |
- ProtectionLevelTable[protection->uep.tableIndex];
- }
- else if (protection->form == EEP) {
- sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level;
- }
-
- // Sub-channel Stream Length, multiple of 64 bits
- sstc->STL_high = getSizeDWord(*subchannel) / 256;
- sstc->STL_low = getSizeDWord(*subchannel) % 256;
-
- TagESTn tag_ESTn(edi_stream_id++);
- tag_ESTn.scid = (*subchannel)->id;
- tag_ESTn.sad = (*subchannel)->startAddress;
- tag_ESTn.tpl = sstc->TPL;
- tag_ESTn.rfa = 0; // two bits
- tag_ESTn.mst_length = getSizeByte(*subchannel) / 8;
- assert(getSizeByte(*subchannel) % 8 == 0);
-
- edi_subchannels.push_back(tag_ESTn);
- edi_subchannelToTag[*subchannel] = &edi_subchannels.back();
- index += 4;
- }
-
- /******* Section EOH **************************************************/
- // End of Header 4 octets
- eti_EOH *eoh = (eti_EOH *) & etiFrame[index];
-
- //MNSC Multiplex Network Signalling Channel, 2 octets
-
- eoh->MNSC = 0;
-
- struct tm *time_tm = gmtime(&mnsc_time.tv_sec);
- switch (fc->FP & 0x3)
- {
- case 0:
- if (MNSC_increment_time)
- {
- MNSC_increment_time = false;
- mnsc_time.tv_sec += 1;
- }
- {
-
- eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC;
- // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2
- mnsc->type = 0;
- mnsc->identifier = 0;
- mnsc->rfa = 0;
- }
- break;
- case 1:
- {
- eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- mnsc->accuracy = 1;
- mnsc->sync_to_frame = 1;
- }
- break;
- case 2:
- {
- eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- }
- break;
- case 3:
- {
- eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- }
- break;
- }
-
- edi_tagDETI.mnsc = eoh->MNSC;
-
- // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets
- nbBytesCRC = 4 + ((fc->NST) * 4) + 2;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC);
- CRCtmp ^= 0xffff;
- eoh->CRC = htons(CRCtmp);
-
- /******* Section MST **************************************************/
- // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC
- // (depending on mode)
- index = ((fc->NST) + 2 + 1) * 4;
- edi_tagDETI.fic_data = &etiFrame[index];
- edi_tagDETI.fic_length = FICL * 4;
-
- // FIC Insertion
- FIGtype0* fig0;
- FIGtype0_0 *fig0_0;
- FIGtype0_1 *figtype0_1;
-
- FIG_01_SubChannel_ShortF *fig0_1subchShort;
- FIG_01_SubChannel_LongF *fig0_1subchLong1;
-
- FIGtype0_2 *fig0_2;
-
- FIGtype0_2_Service *fig0_2serviceAudio;
- FIGtype0_2_Service_data *fig0_2serviceData;
- FIGtype0_2_audio_component* audio_description;
- FIGtype0_2_data_component* data_description;
- FIGtype0_2_packet_component* packet_description;
-
- FIGtype0_3_header *fig0_3_header;
- FIGtype0_3_data *fig0_3_data;
- FIGtype0_9 *fig0_9;
- FIGtype0_10 *fig0_10;
- FIGtype0_17_programme *programme;
-
- FIGtype1_0 *fig1_0;
- FIGtype1_1 *fig1_1;
- FIGtype1_5 *fig1_5;
-
- tm* timeData;
-
- unsigned char figSize = 0;
-
- // FIB 0 Insertion
- switch (insertFIG) {
-
- case 0:
- case 4:
- case 8:
- case 12:
- // FIG type 0/0, Multiplex Configuration Info (MCI),
- // Ensemble information
- fig0_0 = (FIGtype0_0 *) & etiFrame[index];
-
- fig0_0->FIGtypeNumber = 0;
- fig0_0->Length = 5;
- fig0_0->CN = 0;
- fig0_0->OE = 0;
- fig0_0->PD = 0;
- fig0_0->Extension = 0;
-
- fig0_0->EId = htons(ensemble->id);
- fig0_0->Change = 0;
- fig0_0->Al = 0;
- fig0_0->CIFcnt_hight = (currentFrame / 250) % 20;
- fig0_0->CIFcnt_low = (currentFrame % 250);
- index = index + 6;
- figSize += 6;
-
- break;
-
- case 1:
- case 6:
- case 10:
- case 13:
- // FIG type 0/1, MIC, Sub-Channel Organization,
- // one instance of the part for each subchannel
- figtype0_1 = (FIGtype0_1 *) & etiFrame[index];
-
- figtype0_1->FIGtypeNumber = 0;
- figtype0_1->Length = 1;
- figtype0_1->CN = 0;
- figtype0_1->OE = 0;
- figtype0_1->PD = 0;
- figtype0_1->Extension = 1;
- index = index + 2;
- figSize += 2;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (subchannelFIG0_1 == ensemble->subchannels.end()) {
- subchannelFIG0_1 = ensemble->subchannels.begin();
- }
-
- for (; subchannelFIG0_1 != ensemble->subchannels.end();
- ++subchannelFIG0_1) {
- protection = &(*subchannelFIG0_1)->protection;
-
- if ( (protection->form == UEP && figSize > 27) ||
- (protection->form == EEP && figSize > 26) ) {
- break;
- }
-
- if (protection->form == UEP) {
- fig0_1subchShort =
- (FIG_01_SubChannel_ShortF*) &etiFrame[index];
- fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
-
- fig0_1subchShort->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchShort->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
-
- fig0_1subchShort->Short_Long_form = 0;
- fig0_1subchShort->TableSwitch = 0;
- fig0_1subchShort->TableIndex =
- protection->uep.tableIndex;
-
- index = index + 3;
- figSize += 3;
- figtype0_1->Length += 3;
- }
- else if (protection->form == EEP) {
- fig0_1subchLong1 =
- (FIG_01_SubChannel_LongF*) &etiFrame[index];
- fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
-
- fig0_1subchLong1->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchLong1->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
-
- fig0_1subchLong1->Short_Long_form = 1;
- fig0_1subchLong1->Option = protection->eep.GetOption();
- fig0_1subchLong1->ProtectionLevel =
- protection->level;
-
- fig0_1subchLong1->Sub_ChannelSize_high =
- getSizeCu(*subchannelFIG0_1) / 256;
- fig0_1subchLong1->Sub_ChannelSize_low =
- getSizeCu(*subchannelFIG0_1) % 256;
-
- index = index + 4;
- figSize += 4;
- figtype0_1->Length += 4;
- }
- }
- break;
-
- case 2:
- case 9:
- case 11:
- case 14:
- // FIG type 0/2, MCI, Service Organization, one instance of
- // FIGtype0_2_Service for each subchannel
- fig0_2 = NULL;
- cur = 0;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (serviceProgFIG0_2 == ensemble->services.end()) {
- serviceProgFIG0_2 = ensemble->services.begin();
- }
-
- for (; serviceProgFIG0_2 != ensemble->services.end();
- ++serviceProgFIG0_2) {
- if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) {
- continue;
- }
-
- if ((*serviceProgFIG0_2)->getType(ensemble) != 0) {
- continue;
- }
-
- ++cur;
-
- if (fig0_2 == NULL) {
- fig0_2 = (FIGtype0_2 *) & etiFrame[index];
-
- fig0_2->FIGtypeNumber = 0;
- fig0_2->Length = 1;
- fig0_2->CN = 0;
- fig0_2->OE = 0;
- fig0_2->PD = 0;
- fig0_2->Extension = 2;
- index = index + 2;
- figSize += 2;
- }
-
- if (figSize + 3
- + (*serviceProgFIG0_2)->nbComponent(ensemble->components)
- * 2 > 30) {
- break;
- }
-
- fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index];
-
- fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
- fig0_2serviceAudio->Local_flag = 0;
- fig0_2serviceAudio->CAId = 0;
- fig0_2serviceAudio->NbServiceComp =
- (*serviceProgFIG0_2)->nbComponent(ensemble->components);
- index += 3;
- fig0_2->Length += 3;
- figSize += 3;
-
- int curCpnt = 0;
- for (component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id);
- component != ensemble->components.end();
- component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id, component)) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- switch ((*subchannel)->type) {
- case Audio:
- audio_description =
- (FIGtype0_2_audio_component*)&etiFrame[index];
- audio_description->TMid = 0;
- audio_description->ASCTy = (*component)->type;
- audio_description->SubChId = (*subchannel)->id;
- audio_description->PS = ((curCpnt == 0) ? 1 : 0);
- audio_description->CA_flag = 0;
- break;
- case DataDmb:
- data_description =
- (FIGtype0_2_data_component*)&etiFrame[index];
- data_description->TMid = 1;
- data_description->DSCTy = (*component)->type;
- data_description->SubChId = (*subchannel)->id;
- data_description->PS = ((curCpnt == 0) ? 1 : 0);
- data_description->CA_flag = 0;
- break;
- case Packet:
- packet_description =
- (FIGtype0_2_packet_component*)&etiFrame[index];
- packet_description->TMid = 3;
- packet_description->setSCId((*component)->packet.id);
- packet_description->PS = ((curCpnt == 0) ? 1 : 0);
- packet_description->CA_flag = 0;
- break;
- default:
- etiLog.log(error,
- "Component type not supported\n");
- returnCode = -1;
- throw MuxInitException();
- }
- index += 2;
- fig0_2->Length += 2;
- figSize += 2;
- if (figSize > 30) {
- etiLog.log(error,
- "Sorry, no space left in FIG 0/2 to insert "
- "component %i of program service %i.\n",
- curCpnt, cur);
- returnCode = -1;
- throw MuxInitException();
- }
- ++curCpnt;
- }
- }
- break;
+ mux.mux_frame(outputs);
- case 3:
- fig0_2 = NULL;
- cur = 0;
-
- if (serviceDataFIG0_2 == ensemble->services.end()) {
- serviceDataFIG0_2 = ensemble->services.begin();
- }
- for (; serviceDataFIG0_2 != ensemble->services.end();
- ++serviceDataFIG0_2) {
- if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) {
- continue;
- }
-
- unsigned char type = (*serviceDataFIG0_2)->getType(ensemble);
- if ((type == 0) || (type == 2)) {
- continue;
- }
-
- ++cur;
-
- if (fig0_2 == NULL) {
- fig0_2 = (FIGtype0_2 *) & etiFrame[index];
-
- fig0_2->FIGtypeNumber = 0;
- fig0_2->Length = 1;
- fig0_2->CN = 0;
- fig0_2->OE = 0;
- fig0_2->PD = 1;
- fig0_2->Extension = 2;
- index = index + 2;
- figSize += 2;
- }
-
- if (figSize + 5
- + (*serviceDataFIG0_2)->nbComponent(ensemble->components)
- * 2 > 30) {
- break;
- }
-
- fig0_2serviceData =
- (FIGtype0_2_Service_data*) &etiFrame[index];
-
- fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id);
- fig0_2serviceData->Local_flag = 0;
- fig0_2serviceData->CAId = 0;
- fig0_2serviceData->NbServiceComp =
- (*serviceDataFIG0_2)->nbComponent(ensemble->components);
- fig0_2->Length += 5;
- index += 5;
- figSize += 5;
-
- int curCpnt = 0;
- for (component = getComponent(ensemble->components,
- (*serviceDataFIG0_2)->id);
- component != ensemble->components.end();
- component = getComponent(ensemble->components,
- (*serviceDataFIG0_2)->id, component)) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- switch ((*subchannel)->type) {
- case Audio:
- audio_description =
- (FIGtype0_2_audio_component*)&etiFrame[index];
- audio_description->TMid = 0;
- audio_description->ASCTy = (*component)->type;
- audio_description->SubChId = (*subchannel)->id;
- audio_description->PS = ((curCpnt == 0) ? 1 : 0);
- audio_description->CA_flag = 0;
- break;
- case DataDmb:
- data_description =
- (FIGtype0_2_data_component*)&etiFrame[index];
- data_description->TMid = 1;
- data_description->DSCTy = (*component)->type;
- data_description->SubChId = (*subchannel)->id;
- data_description->PS = ((curCpnt == 0) ? 1 : 0);
- data_description->CA_flag = 0;
- break;
- case Packet:
- packet_description =
- (FIGtype0_2_packet_component*)&etiFrame[index];
- packet_description->TMid = 3;
- packet_description->setSCId((*component)->packet.id);
- packet_description->PS = ((curCpnt == 0) ? 1 : 0);
- packet_description->CA_flag = 0;
- break;
- default:
- etiLog.log(error,
- "Component type not supported\n");
- returnCode = -1;
- throw MuxInitException();
- }
- index += 2;
- fig0_2->Length += 2;
- figSize += 2;
- if (figSize > 30) {
- etiLog.log(error,
- "Sorry, no place left in FIG 0/2 to insert "
- "component %i of data service %i.\n",
- curCpnt, cur);
- returnCode = -1;
- throw MuxInitException();
- }
- ++curCpnt;
- }
- }
+ if (limit && currentFrame >= limit) {
break;
-
- case 5:
- fig0_3_header = NULL;
-
- for (component = ensemble->components.begin();
- component != ensemble->components.end();
- ++component) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ((*subchannel)->type != Packet)
- continue;
-
- if (fig0_3_header == NULL) {
- fig0_3_header = (FIGtype0_3_header*)&etiFrame[index];
- fig0_3_header->FIGtypeNumber = 0;
- fig0_3_header->Length = 1;
- fig0_3_header->CN = 0;
- fig0_3_header->OE = 0;
- fig0_3_header->PD = 0;
- fig0_3_header->Extension = 3;
-
- index += 2;
- figSize += 2;
- }
-
- /* Warning: When bit SCCA_flag is unset(0), the multiplexer
- * R&S does not send the SCCA field. But, in the Factum ETI
- * analyzer, if this field is not there, it is an error.
- */
- fig0_3_data = (FIGtype0_3_data*)&etiFrame[index];
- fig0_3_data->setSCId((*component)->packet.id);
- fig0_3_data->rfa = 0;
- fig0_3_data->SCCA_flag = 0;
- // if 0, datagroups are used
- fig0_3_data->DG_flag = !(*component)->packet.datagroup;
- fig0_3_data->rfu = 0;
- fig0_3_data->DSCTy = (*component)->type;
- fig0_3_data->SubChId = (*subchannel)->id;
- fig0_3_data->setPacketAddress((*component)->packet.address);
- if (factumAnalyzer) {
- fig0_3_data->SCCA = 0;
- }
-
- fig0_3_header->Length += 5;
- index += 5;
- figSize += 5;
- if (factumAnalyzer) {
- fig0_3_header->Length += 2;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30) {
- etiLog.log(error,
- "can't add to Fic Fig 0/3, "
- "too much packet service\n");
- returnCode = -1;
- throw MuxInitException();
- }
- }
- break;
-
- case 7:
- fig0 = NULL;
- if (serviceFIG0_17 == ensemble->services.end()) {
- serviceFIG0_17 = ensemble->services.begin();
- }
- for (; serviceFIG0_17 != ensemble->services.end();
- ++serviceFIG0_17) {
-
- if ( (*serviceFIG0_17)->pty == 0 &&
- (*serviceFIG0_17)->language == 0) {
- continue;
- }
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 17;
- index += 2;
- figSize += 2;
- }
-
- if ((*serviceFIG0_17)->language == 0) {
- if (figSize + 4 > 30) {
- break;
- }
- }
- else {
- if (figSize + 5 > 30) {
- break;
- }
- }
-
- programme =
- (FIGtype0_17_programme*)&etiFrame[index];
- programme->SId = htons((*serviceFIG0_17)->id);
- programme->SD = 1;
- programme->PS = 0;
- programme->L = (*serviceFIG0_17)->language != 0;
- programme->CC = 0;
- programme->Rfa = 0;
- programme->NFC = 0;
- if ((*serviceFIG0_17)->language == 0) {
- etiFrame[index + 3] = (*serviceFIG0_17)->pty;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
- }
- else {
- etiFrame[index + 3] = (*serviceFIG0_17)->language;
- etiFrame[index + 4] = (*serviceFIG0_17)->pty;
- fig0->Length += 5;
- index += 5;
- figSize += 5;
- }
- }
- break;
- }
-
- if (figSize > 30) {
- etiLog.log(error,
- "FIG too big (%i > 30)\n", figSize);
- returnCode = -1;
- throw MuxInitException();
}
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
- figSize = 0;
- // FIB 1 insertion
- switch (rotateFIB) {
- case 0: // FIG 0/8 program
- fig0 = NULL;
-
- if (componentProgFIG0_8 == ensemble->components.end()) {
- componentProgFIG0_8 = ensemble->components.begin();
- }
- for (; componentProgFIG0_8 != ensemble->components.end();
- ++componentProgFIG0_8) {
- service = getService(*componentProgFIG0_8,
- ensemble->services);
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentProgFIG0_8)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentProgFIG0_8)->subchId,
- (*componentProgFIG0_8)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if (!(*service)->program)
- continue;
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 8;
- index += 2;
- figSize += 2;
- }
-
- if ((*subchannel)->type == Packet) { // Data packet
- if (figSize > 30 - 5) {
- break;
- }
- etiFrame[index] =
- ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentProgFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 2;
- index += 2;
- figSize += 2;
-
- FIGtype0_8_long* definition =
- (FIGtype0_8_long*)&etiFrame[index];
- memset(definition, 0, 3);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
- definition->LS = 1;
- definition->setSCId((*componentProgFIG0_8)->packet.id);
- fig0->Length += 3;
- index += 3; // 8 minus rfa
- figSize += 3;
- }
- else { // Audio, data stream or FIDC
- if (figSize > 30 - 4) {
- break;
- }
- etiFrame[index] =
- ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentProgFIG0_8)->serviceId) & 0xFF;
-
- fig0->Length += 2;
- index += 2;
- figSize += 2;
-
- FIGtype0_8_short* definition =
- (FIGtype0_8_short*)&etiFrame[index];
- memset(definition, 0, 2);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
- definition->LS = 0;
- definition->MscFic = 0;
- definition->Id = (*componentProgFIG0_8)->subchId;
- fig0->Length += 2;
- index += 2; // 4 minus rfa
- figSize += 2;
- }
- }
- break;
-
- case 1: // FIG 0/8 data
- fig0 = NULL;
-
- if (componentDataFIG0_8 == ensemble->components.end()) {
- componentDataFIG0_8 = ensemble->components.begin();
- }
- for (; componentDataFIG0_8 != ensemble->components.end();
- ++componentDataFIG0_8) {
- service = getService(*componentDataFIG0_8,
- ensemble->services);
-
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentDataFIG0_8)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentDataFIG0_8)->subchId,
- (*componentDataFIG0_8)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ((*service)->program)
- continue;
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 1;
- fig0->Extension = 8;
- index += 2;
- figSize += 2;
- }
-
- if ((*subchannel)->type == Packet) { // Data packet
- if (figSize > 30 - 7) {
- break;
- }
- etiFrame[index] =
- ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentDataFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
-
- FIGtype0_8_long* definition =
- (FIGtype0_8_long*)&etiFrame[index];
- memset(definition, 0, 3);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
- definition->LS = 1;
- definition->setSCId((*componentDataFIG0_8)->packet.id);
- fig0->Length += 3;
- index += 3; // 8 minus rfa
- figSize += 3;
- }
- else { // Audio, data stream or FIDC
- if (figSize > 30 - 6) {
- break;
- }
- etiFrame[index] =
- ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentDataFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
-
- FIGtype0_8_short* definition =
- (FIGtype0_8_short*)&etiFrame[index];
- memset(definition, 0, 2);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
- definition->LS = 0;
- definition->MscFic = 0;
- definition->Id = (*componentDataFIG0_8)->subchId;
- fig0->Length += 2;
- index += 2; // 4 minus rfa
- figSize += 2;
- }
- }
- break;
-
- case 3:
- // FIG type 1/0, Service Information (SI), Ensemble Label
- fig1_0 = (FIGtype1_0 *) & etiFrame[index];
-
- fig1_0->Length = 21;
- fig1_0->FIGtypeNumber = 1;
- fig1_0->Extension = 0;
- fig1_0->OE = 0;
- fig1_0->Charset = 0;
- fig1_0->EId = htons(ensemble->id);
- index = index + 4;
-
- memcpy(&etiFrame[index], ensemble->label.text(), 16);
- index = index + 16;
-
- etiFrame[index++] = ensemble->label.flag() >> 8;
- etiFrame[index++] = ensemble->label.flag() & 0xFF;
-
- figSize += 22;
- break;
-
- case 5:
- case 6:
- // FIG 0 / 13
- fig0 = NULL;
-
- if (componentFIG0_13 == ensemble->components.end()) {
- componentFIG0_13 = ensemble->components.begin();
-
- transmitFIG0_13programme = !transmitFIG0_13programme;
- // Alternate between data and and programme FIG0/13,
- // do not mix fig0 with PD=0 with extension 13 stuff
- // that actually needs PD=1, and vice versa
- }
-
- for (; componentFIG0_13 != ensemble->components.end();
- ++componentFIG0_13) {
-
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentFIG0_13)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentFIG0_13)->subchId,
- (*componentFIG0_13)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ( transmitFIG0_13programme &&
- (*subchannel)->type == Audio &&
- (*componentFIG0_13)->audio.uaType != 0xffff) {
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 13;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30 - (3+4+11)) {
- break;
- }
-
- FIG0_13_shortAppInfo* info =
- (FIG0_13_shortAppInfo*)&etiFrame[index];
- info->SId = htonl((*componentFIG0_13)->serviceId) >> 16;
- info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- index += 3;
- figSize += 3;
- fig0->Length += 3;
-
- FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
- app->setType((*componentFIG0_13)->audio.uaType);
- app->length = 4;
- app->xpad = htonl(0x0cbc0000);
- /* xpad meaning
- CA = 0
- CAOrg = 0
- Rfu = 0
- AppTy(5) = 12 (MOT, start of X-PAD data group)
- DG = 0 (MSC data groups used)
- Rfu = 0
- DSCTy(6) = 60 (MOT)
- CAOrg(16) = 0
- */
-
- index += 2 + app->length;
- figSize += 2 + app->length;
- fig0->Length += 2 + app->length;
- }
- else if (!transmitFIG0_13programme &&
- (*subchannel)->type == Packet &&
- (*componentFIG0_13)->packet.appType != 0xffff) {
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 1;
- fig0->Extension = 13;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30 - (5+2)) {
- break;
- }
-
- FIG0_13_longAppInfo* info =
- (FIG0_13_longAppInfo*)&etiFrame[index];
- info->SId = htonl((*componentFIG0_13)->serviceId);
- info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- index += 5;
- figSize += 5;
- fig0->Length += 5;
-
- FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
- app->setType((*componentFIG0_13)->packet.appType);
- app->length = 0;
- index += 2;
- figSize += 2;
- fig0->Length += 2;
- }
- }
- break;
-
- case 7:
- //Time and country identifier
- fig0_10 = (FIGtype0_10 *) & etiFrame[index];
-
- fig0_10->FIGtypeNumber = 0;
- fig0_10->Length = 5;
- fig0_10->CN = 0;
- fig0_10->OE = 0;
- fig0_10->PD = 0;
- fig0_10->Extension = 10;
- index = index + 2;
-
- timeData = gmtime(&date);
-
- fig0_10->RFU = 0;
- fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900,
- timeData->tm_mon + 1,
- timeData->tm_mday));
- fig0_10->LSI = 0;
- fig0_10->ConfInd = (watermarkData[watermarkPos >> 3] >>
- (7 - (watermarkPos & 0x07))) & 1;
- if (++watermarkPos == watermarkSize) {
- watermarkPos = 0;
- }
- fig0_10->UTC = 0;
- fig0_10->setHours(timeData->tm_hour);
- fig0_10->Minutes = timeData->tm_min;
- index = index + 4;
- figSize += 6;
-
- fig0_9 = (FIGtype0_9*)&etiFrame[index];
- fig0_9->FIGtypeNumber = 0;
- fig0_9->Length = 4;
- fig0_9->CN = 0;
- fig0_9->OE = 0;
- fig0_9->PD = 0;
- fig0_9->Extension = 9;
-
- fig0_9->ext = 0;
- fig0_9->lto = 0; // Unique LTO for ensemble
-
- if (ensemble->lto_auto) {
- time_t now = time(NULL);
- struct tm* ltime = localtime(&now);
- time_t now2 = timegm(ltime);
- ensemble->lto = (now2 - now) / 1800;
- }
-
- if (ensemble->lto >= 0) {
- fig0_9->ensembleLto = ensemble->lto;
- }
- else {
- /* Convert to 1-complement representation */
- fig0_9->ensembleLto = (-ensemble->lto) | (1<<5);
- }
-
- fig0_9->ensembleEcc = ensemble->ecc;
- fig0_9->tableId = ensemble->international_table;
- index += 5;
- figSize += 5;
-
- break;
- }
-
- assert(figSize <= 30);
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
-
- figSize = 0;
- // FIB 2 insertion
- if (rotateFIB < ensemble->services.size()) {
- service = ensemble->services.begin() + rotateFIB;
-
- // FIG type 1/1, SI, Service label, one instance per subchannel
- if ((*service)->getType(ensemble) == 0) {
- fig1_1 = (FIGtype1_1 *) & etiFrame[index];
-
- fig1_1->FIGtypeNumber = 1;
- fig1_1->Length = 21;
- fig1_1->Charset = 0;
- fig1_1->OE = 0;
- fig1_1->Extension = 1;
-
- fig1_1->Sld = htons((*service)->id);
- index += 4;
- figSize += 4;
- }
- else {
- fig1_5 = (FIGtype1_5 *) & etiFrame[index];
- fig1_5->FIGtypeNumber = 1;
- fig1_5->Length = 23;
- fig1_5->Charset = 0;
- fig1_5->OE = 0;
- fig1_5->Extension = 5;
-
- fig1_5->SId = htonl((*service)->id);
- index += 6;
- figSize += 6;
- }
- memcpy(&etiFrame[index], (*service)->label.text(), 16);
- index += 16;
- figSize += 16;
- etiFrame[index++] = (*service)->label.flag() >> 8;
- etiFrame[index++] = (*service)->label.flag() & 0xFF;
- figSize += 2;
- }
- else if (rotateFIB <
- ensemble->services.size() + ensemble->components.size()) {
- component = ensemble->components.begin() +
- (rotateFIB - ensemble->services.size());
-
- service = getService(*component, ensemble->services);
-
- subchannel =
- getSubchannel(ensemble->subchannels, (*component)->subchId);
-
- if ((*component)->label.text()[0] != 0) {
- if ((*service)->getType(ensemble) == 0) { // Programme
- FIGtype1_4_programme *fig1_4;
- fig1_4 = (FIGtype1_4_programme*)&etiFrame[index];
-
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 22;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 0;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htons((*service)->id);
- index += 5;
- figSize += 5;
- }
- else { // Data
- FIGtype1_4_data *fig1_4;
- fig1_4 = (FIGtype1_4_data *) & etiFrame[index];
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 24;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 1;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htonl((*service)->id);
- index += 7;
- figSize += 7;
- }
- memcpy(&etiFrame[index], (*component)->label.text(), 16);
- index += 16;
- figSize += 16;
-
- etiFrame[index++] = (*component)->label.flag() >> 8;
- etiFrame[index++] = (*component)->label.flag() & 0xFF;
- figSize += 2;
- }
- }
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
- /* ETSI EN 300 799 Table 2:
- * Only TM3 has a FIB count to CIF count that is
- * not 3 to 1.
- */
- if (ensemble->mode == 3) {
- memcpy(&etiFrame[index], Padding_FIB, 30);
- index += 30;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
- }
-
- if (ensemble->services.size() > 30) {
- etiLog.log(error,
- "Sorry, but this software currently can't write "
- "Service Label of more than 30 services.\n");
- returnCode = -1;
- throw MuxInitException();
- }
-
- // counter for FIG 0/0
- insertFIG = (insertFIG + 1) % 16;
-
- // We rotate through the FIBs every 30 frames
- rotateFIB = (rotateFIB + 1) % 30;
-
- /**********************************************************************
- ****** Input Data Reading *******************************************
- **********************************************************************/
-
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
-
- TagESTn* tag = edi_subchannelToTag[*subchannel];
-
- int sizeSubchannel = getSizeByte(*subchannel);
- int result = (*subchannel)->input->readFrame(
- &etiFrame[index], sizeSubchannel);
-
- if (result < 0) {
- etiLog.log(info,
- "Subchannel %d read failed at ETI frame number: %d\n",
- (*subchannel)->id, currentFrame);
- }
-
- // save pointer to Audio or Data Stream into correct TagESTn for EDI
- tag->mst_data = &etiFrame[index];
-
- index += sizeSubchannel;
- }
-
-
- index = (3 + fc->NST + FICL) * 4;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- index += getSizeByte(*subchannel);
- }
-
- /******* Section EOF **************************************************/
- // End of Frame, 4 octets
- index = (FLtmp + 1 + 1) * 4;
- eti_EOF *eof = (eti_EOF *) & etiFrame[index];
-
- // CRC of Main Stream data (MST), 16 bits
- index = ((fc->NST) + 2 + 1) * 4; // MST position
- MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4; // data size
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize);
- CRCtmp ^= 0xffff;
- eof->CRC = htons(CRCtmp);
-
- //RFU, Reserved for future use, 2 bytes, should be 0xFFFF
- eof->RFU = htons(0xFFFF);
-
- /******* Section TIST *************************************************/
- // TimeStamps, 24 bits + 1 octet
- index = (FLtmp + 2 + 1) * 4;
- eti_TIST *tist = (eti_TIST *) & etiFrame[index];
-
- if (enableTist) {
- tist->TIST = htonl(timestamp) | 0xff;
- }
- else {
- tist->TIST = htonl(0xffffff) | 0xff;
- }
-
- timestamp += 3 << 17;
- if (timestamp > 0xf9ffff)
- {
- timestamp -= 0xfa0000;
-
- // Also update MNSC time for next frame
- MNSC_increment_time = true;
- }
-
-
-
- /**********************************************************************
- *********** Section FRPD *****************************************
- **********************************************************************/
-
- int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4;
-
- // Give the data to the outputs
- for (output = outputs.begin() ; output != outputs.end(); ++output) {
- if ((*output)->output->Write(etiFrame, frame_size)
- == -1) {
- etiLog.log(error, "Can't write to output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- }
- }
-
-#ifdef DUMP_BRIDGE
- dumpBytes(dumpData, sizeSubChannel, stderr);
-#endif // DUMP_BRIDGE
-
- /**********************************************************************
- *********** Finalise and send EDI ********************************
- **********************************************************************/
-
- if (edi_conf.enabled) {
- // put tags *ptr, DETI and all subchannels into one TagPacket
- edi_tagpacket.tag_items.push_back(&edi_tagStarPtr);
- edi_tagpacket.tag_items.push_back(&edi_tagDETI);
-
- list<TagESTn>::iterator tag;
- for (tag = edi_subchannels.begin(); tag != edi_subchannels.end(); ++tag) {
- edi_tagpacket.tag_items.push_back(&(*tag));
- }
-
- // Assemble into one AF Packet
- AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket);
-
- if (edi_conf.enable_pft) {
- // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation)
- vector< PFTFragment > edi_fragments =
- edi_pft.Assemble(edi_afpacket);
-
- // Send over ethernet
- vector< vector<uint8_t> >::iterator edi_frag;
- for (edi_frag = edi_fragments.begin();
- edi_frag != edi_fragments.end();
- ++edi_frag) {
-
- UdpPacket udppacket;
-
- InetAddress& addr = udppacket.getAddress();
- addr.setAddress(edi_conf.dest_addr.c_str());
- addr.setPort(edi_conf.dest_port);
-
- udppacket.addData(&(edi_frag->front()), edi_frag->size());
-
- edi_output.send(udppacket);
-
- if (edi_conf.dump) {
- std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
- std::copy(edi_frag->begin(), edi_frag->end(), debug_iterator);
- }
- }
-
- if (edi_conf.verbose) {
- fprintf(stderr, "EDI number of PFT fragments %zu\n",
- edi_fragments.size());
- }
- }
- else {
- // Send over ethernet
-
- UdpPacket udppacket;
-
- InetAddress& addr = udppacket.getAddress();
- addr.setAddress(edi_conf.dest_addr.c_str());
- addr.setPort(edi_conf.dest_port);
-
- udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size());
-
- edi_output.send(udppacket);
- }
-
- if (edi_conf.dump) {
- std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
- std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator);
- }
- }
-
-#if _DEBUG
- /**********************************************************************
- *********** Output a small message *********************************
- **********************************************************************/
- if (currentFrame % 100 == 0) {
- if (enableTist) {
- etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n",
- currentFrame, mnsc_time.tv_sec,
- (timestamp & 0xFFFFFF) / 16384000.0);
- }
- else {
- etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
- currentFrame, mnsc_time.tv_sec);
- }
- }
-#endif
-
/* Check every six seconds if the remote control is still working */
- if (rc && fc->FCT == 249 && rc->fault_detected()) {
- etiLog.level(warn) << "Detected Remote Control fault, restarting it";
- rc->restart();
+ if ((currentFrame % 250 == 249) && rc->fault_detected()) {
+ etiLog.level(warn) << "Detected Remote Control fault, restarting it";
+ rc->restart();
}
/* Same for statistics server */
- if (mgmt_server && fc->FCT % 10 == 0) {
- if (mgmt_server->fault_detected()) {
+ if (currentFrame % 10 == 0) {
+ ManagementServer& mgmt_server = get_mgmt_server();
+
+ if (mgmt_server.fault_detected()) {
etiLog.level(warn) <<
- "Detected Statistics Server fault, restarting it";
- mgmt_server->restart();
+ "Detected Management Server fault, restarting it";
+ mgmt_server.restart();
+ }
+ else if (mgmt_server.request_pending()) {
+ mgmt_server.update_ptree(pt);
}
- else if (mgmt_server->request_pending()) {
- mgmt_server->update_ptree(pt);
+ else if (mgmt_server.retrieve_new_ptree(pt)) {
+ etiLog.level(warn) <<
+ "Detected configuration change";
+ mux.update_config(pt);
}
}
}
-
+ etiLog.level(info) << "Goodbye";
}
catch (const MuxInitException& except) {
- etiLog.level(error) << "Caught multiplex initialisation error: " <<
+ etiLog.level(error) << "Multiplex initialisation aborted: " <<
except.what();
}
catch (const std::invalid_argument& except) {
@@ -2200,30 +487,7 @@ int main(int argc, char *argv[])
etiLog.log(debug, "exiting...\n");
fflush(stderr);
- // close files fichiers
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- if ((*subchannel)->input != NULL) {
- (*subchannel)->input->close();
- }
- delete (*subchannel)->input;
- }
- for (output = outputs.begin() ; output != outputs.end(); ++output) {
- if ((*output)->output) {
- (*output)->output->Close();
- delete ((*output)->output);
- }
- }
- for_each(ensemble->components.begin(), ensemble->components.end(), free);
- for_each(ensemble->services.begin(), ensemble->services.end(), free);
- for_each(ensemble->subchannels.begin(), ensemble->subchannels.end(), free);
- for_each(outputs.begin(), outputs.end(), free);
- ensemble->components.clear();
- ensemble->services.clear();
- ensemble->subchannels.clear();
- delete ensemble;
outputs.clear();
UdpSocket::clean();
diff --git a/src/DabMux.h b/src/DabMux.h
index 89868ac..5dda759 100644
--- a/src/DabMux.h
+++ b/src/DabMux.h
@@ -31,6 +31,7 @@
#include <stdint.h>
#include <string>
#include <vector>
+#include "DabMultiplexer.h"
#include "RemoteControl.h"
#include "dabOutput/dabOutput.h"
#include "dabInput.h"
@@ -43,368 +44,5 @@
# include <sys/time.h>
#endif
-
-// DAB Mode
-#define DEFAULT_DAB_MODE 2
-
-// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits,
-// STL3 * 8 = x kbytes par trame ETI
-
-// Data bitrate in kbits/s. Must be 64 kb/s multiple.
-#define DEFAULT_DATA_BITRATE 384
-#define DEFAULT_PACKET_BITRATE 32
-
-/* default ensemble parameters. Label must be max 16 chars, short label
- * a subset of the label, max 8 chars
- */
-#define DEFAULT_ENSEMBLE_LABEL "ODR Dab Mux"
-#define DEFAULT_ENSEMBLE_SHORT_LABEL "ODRMux"
-#define DEFAULT_ENSEMBLE_ID 0xc000
-#define DEFAULT_ENSEMBLE_ECC 0xa1
-
-// start value for default service IDs (if not overridden by configuration)
-#define DEFAULT_SERVICE_ID 50
-#define DEFAULT_PACKET_ADDRESS 0
-
-/*****************************************************************************
- ***************** Definition of FIG structures ****************************
- *****************************************************************************/
-struct FIGtype0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIGtype0_0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint16_t EId;
- uint8_t CIFcnt_hight:5;
- uint8_t Al:1;
- uint8_t Change:2;
- uint8_t CIFcnt_low:8;
-} PACKED;
-
-
-struct FIGtype0_2 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIGtype0_2_Service {
- uint16_t SId;
- uint8_t NbServiceComp:4;
- uint8_t CAId:3;
- uint8_t Local_flag:1;
-} PACKED;
-
-
-struct FIGtype0_2_Service_data {
- uint32_t SId;
- uint8_t NbServiceComp:4;
- uint8_t CAId:3;
- uint8_t Local_flag:1;
-} PACKED;
-
-
-struct FIGtype0_2_audio_component {
- uint8_t ASCTy:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SubChId:6;
-} PACKED;
-
-
-struct FIGtype0_2_data_component {
- uint8_t DSCTy:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SubChId:6;
-} PACKED;
-
-
-struct FIGtype0_2_packet_component {
- uint8_t SCId_high:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SCId_low:6;
- void setSCId(uint16_t SCId) {
- SCId_high = SCId >> 6;
- SCId_low = SCId & 0x3f;
- }
-} PACKED;
-
-
-struct FIGtype0_3_header {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send
- * the SCCA field. But, in the Factum ETI analyzer, if this field is not there,
- * it is an error.
- */
-struct FIGtype0_3_data {
- uint8_t SCId_high;
- uint8_t SCCA_flag:1;
- uint8_t rfa:3;
- uint8_t SCId_low:4;
- uint8_t DSCTy:6;
- uint8_t rfu:1;
- uint8_t DG_flag:1;
- uint8_t Packet_address_high:2;
- uint8_t SubChId:6;
- uint8_t Packet_address_low;
- uint16_t SCCA;
- void setSCId(uint16_t SCId) {
- SCId_high = SCId >> 4;
- SCId_low = SCId & 0xf;
- }
- void setPacketAddress(uint16_t address) {
- Packet_address_high = address >> 8;
- Packet_address_low = address & 0xff;
- }
-} PACKED;
-
-
-struct FIGtype0_8_short {
- uint8_t SCIdS:4;
- uint8_t rfa_1:3;
- uint8_t ext:1;
- uint8_t Id:6;
- uint8_t MscFic:1;
- uint8_t LS:1;
- uint8_t rfa_2;
-} PACKED;
-
-
-struct FIGtype0_8_long {
- uint8_t SCIdS:4;
- uint8_t rfa_1:3;
- uint8_t ext:1;
- uint8_t SCId_high:4;
- uint8_t rfa:3;
- uint8_t LS:1;
- uint8_t SCId_low;
- uint8_t rfa_2;
- void setSCId(uint16_t id) {
- SCId_high = id >> 8;
- SCId_low = id & 0xff;
- }
- uint16_t getSCid() {
- return (SCId_high << 8) | SCId_low;
- }
-} PACKED;
-
-
-struct FIGtype0_9 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint8_t ensembleLto:6;
- uint8_t lto:1;
- uint8_t ext:1;
- uint8_t ensembleEcc;
- uint8_t tableId;
-} PACKED;
-
-
-struct FIGtype0_10 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint8_t MJD_high:7;
- uint8_t RFU:1;
- uint8_t MJD_med;
- uint8_t Hours_high:3;
- uint8_t UTC:1;
- uint8_t ConfInd:1;
- uint8_t LSI:1;
- uint8_t MJD_low:2;
- uint8_t Minutes:6;
- uint8_t Hours_low:2;
- void setMJD(uint32_t date) {
- MJD_high = (date >> 10) & 0x7f;
- MJD_med = (date >> 2) & 0xff;
- MJD_low = date & 0x03;
- }
- void setHours(uint16_t hours) {
- Hours_high = (hours >> 2) & 0x07;
- Hours_low = hours & 0x03;
- }
-} PACKED;
-
-
-struct FIGtype0_17_programme {
- uint16_t SId;
- uint8_t NFC:2;
- uint8_t Rfa:2;
- uint8_t CC:1;
- uint8_t L:1;
- uint8_t PS:1;
- uint8_t SD:1;
-} PACKED;
-
-
-struct FIGtype0_1 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIG_01_SubChannel_ShortF {
- uint8_t StartAdress_high:2;
- uint8_t SubChId:6;
- uint8_t StartAdress_low:8;
- uint8_t TableIndex:6;
- uint8_t TableSwitch:1;
- uint8_t Short_Long_form:1;
-} PACKED;
-
-
-struct FIG_01_SubChannel_LongF {
- uint8_t StartAdress_high:2;
- uint8_t SubChId:6;
- uint8_t StartAdress_low:8;
- uint8_t Sub_ChannelSize_high:2;
- uint8_t ProtectionLevel:2;
- uint8_t Option:3;
- uint8_t Short_Long_form:1;
- uint8_t Sub_ChannelSize_low:8;
-} PACKED;
-
-
-// See EN 300 401, Clause 8.1.20 for the FIG0_13 description
-struct FIG0_13_shortAppInfo {
- uint16_t SId;
- uint8_t No:4;
- uint8_t SCIdS:4;
-} PACKED;
-
-
-struct FIG0_13_longAppInfo {
- uint32_t SId;
- uint8_t No:4;
- uint8_t SCIdS:4;
-} PACKED;
-
-
-struct FIG0_13_app {
- uint8_t typeHigh;
- uint8_t length:5;
- uint8_t typeLow:3;
- void setType(uint16_t type) {
- typeHigh = type >> 3;
- typeLow = type & 0x1f;
- }
- uint32_t xpad;
-} PACKED;
-
-#define FIG0_13_APPTYPE_SLIDESHOW 0x2
-#define FIG0_13_APPTYPE_WEBSITE 0x3
-#define FIG0_13_APPTYPE_TPEG 0x4
-#define FIG0_13_APPTYPE_DGPS 0x5
-#define FIG0_13_APPTYPE_TMC 0x6
-#define FIG0_13_APPTYPE_EPG 0x7
-#define FIG0_13_APPTYPE_DABJAVA 0x8
-#define FIG0_13_APPTYPE_JOURNALINE 0x441
-
-
-struct FIGtype1_0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
-
- uint16_t EId;
-} PACKED;
-
-
-struct FIGtype1_1 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
-
- uint16_t Sld;
-} PACKED;
-
-
-struct FIGtype1_5 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint32_t SId;
-} PACKED;
-
-
-struct FIGtype1_4_programme {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint8_t SCIdS:4;
- uint8_t rfa:3;
- uint8_t PD:1;
- uint16_t SId;
-} PACKED;
-
-
-struct FIGtype1_4_data {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint8_t SCIdS:4;
- uint8_t rfa:3;
- uint8_t PD:1;
- uint32_t SId;
-} PACKED;
-
-
-#ifdef _WIN32
-# pragma pack(pop)
-#endif
-
#endif
diff --git a/src/Log.h b/src/Log.h
index 85a2f75..7be6df8 100644
--- a/src/Log.h
+++ b/src/Log.h
@@ -73,10 +73,12 @@ class LogToSyslog : public LogBackend {
int syslog_level = LOG_EMERG;
switch (level) {
case debug: syslog_level = LOG_DEBUG; break;
- case alert: syslog_level = LOG_ALERT; break;
case info: syslog_level = LOG_INFO; break;
+ /* we don't have the notice level */
case warn: syslog_level = LOG_WARNING; break;
case error: syslog_level = LOG_ERR; break;
+ default: syslog_level = LOG_CRIT; break;
+ case alert: syslog_level = LOG_ALERT; break;
case emerg: syslog_level = LOG_EMERG; break;
}
diff --git a/src/Makefile.am b/src/Makefile.am
index f3bce5e..2762788 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,7 +21,7 @@
# along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
if IS_GIT_REPO
-GITVERSION_FLAGS = -DGITVERSION="\"`git describe`\""
+GITVERSION_FLAGS = -DGITVERSION="\"`git describe --dirty`\""
else
GITVERSION_FLAGS =
endif
@@ -39,9 +39,11 @@ bin_PROGRAMS=odr-dabmux odr-bridgetest
ZMQ_LIBS =
endif
-odr_dabmux_CPPFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
+odr_dabmux_CFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
+odr_dabmux_CXXFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
odr_dabmux_LDADD =$(FEC_LIBS) $(ZMQ_LIBS) -lpthread -lboost_thread -lboost_system
odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
+ DabMultiplexer.cpp DabMultiplexer.h \
dabInput.h dabInput.cpp \
dabInputBridgeUdp.h dabInputBridgeUdp.cpp \
dabInputDabplusFifo.h dabInputDabplusFifo.cpp \
@@ -95,7 +97,10 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
ManagementServer.h ManagementServer.cpp \
TcpServer.h TcpServer.cpp \
TcpSocket.h TcpSocket.cpp \
- zmq.hpp
+ zmq.hpp \
+ fig/FIG0.cpp fig/FIG0.h \
+ fig/FIGCarousel.cpp fig/FIGCarousel.h \
+ fig/FIG.h
odr_bridgetest_CFLAGS =-DBRIDGE_TEST
odr_bridgetest_SOURCES =bridge.c \
diff --git a/src/ManagementServer.cpp b/src/ManagementServer.cpp
index 9ebdfeb..9687278 100644
--- a/src/ManagementServer.cpp
+++ b/src/ManagementServer.cpp
@@ -39,6 +39,18 @@
#include "ManagementServer.h"
#include "Log.h"
+ManagementServer& get_mgmt_server()
+{
+ static ManagementServer mgmt_server;
+
+ return mgmt_server;
+
+ /* Warning, do not use the mgmt_server in the destructor
+ * of another global object: you don't know which one
+ * gets destroyed first
+ */
+}
+
void ManagementServer::registerInput(InputStat* is)
{
boost::mutex::scoped_lock lock(m_statsmutex);
@@ -47,7 +59,7 @@ void ManagementServer::registerInput(InputStat* is)
if (m_inputStats.count(id) == 1) {
etiLog.level(error) <<
- "Double registration in Stats Server with id '" <<
+ "Double registration in MGMT Server with id '" <<
id << "'";
return;
}
@@ -71,7 +83,7 @@ bool ManagementServer::isInputRegistered(std::string& id)
if (m_inputStats.count(id) == 0) {
etiLog.level(error) <<
- "Stats Server id '" <<
+ "Management Server: id '" <<
id << "' does was not registered";
return false;
}
@@ -185,132 +197,139 @@ void ManagementServer::restart_thread(long)
void ManagementServer::serverThread()
{
+ m_running = true;
m_fault = false;
- try {
- int accepted_sock;
- char buffer[256];
- char welcome_msg[256];
- struct sockaddr_in serv_addr, cli_addr;
- int n;
-
- int welcome_msg_len = snprintf(welcome_msg, 256,
- "{ \"service\": \""
- "%s %s Stats Server\" }\n",
- PACKAGE_NAME,
-#if defined(GITVERSION)
- GITVERSION
-#else
- PACKAGE_VERSION
-#endif
- );
-
+ std::stringstream bind_addr;
+ bind_addr << "tcp://127.0.0.1:" << m_listenport;
+ m_zmq_sock.bind(bind_addr.str().c_str());
- m_sock = socket(AF_INET, SOCK_STREAM, 0);
- if (m_sock < 0) {
- etiLog.level(error) << "Error opening Stats Server socket: " <<
- strerror(errno);
- m_fault = true;
- return;
- }
+ while (m_running) {
+ zmq::message_t zmq_message;
+ m_zmq_sock.recv(&zmq_message);
- memset(&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY; // TODO listen only on 127.0.0.1
- serv_addr.sin_port = htons(m_listenport);
- if (bind(m_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
- etiLog.level(error) << "Error binding Stats Server socket: " <<
- strerror(errno);
- goto end_serverthread;
- }
+ handle_message(zmq_message);
+ }
- if (listen(m_sock, 5) < 0) {
- etiLog.level(error) << "Error listening on Stats Server socket: " <<
- strerror(errno);
- goto end_serverthread;
- }
+ m_fault = true;
+}
- m_running = true;
+bool ManagementServer::handle_setptree(
+ zmq::message_t& zmq_message, std::stringstream& answer)
+{
+ try {
+ if (zmq_message.more()) {
+ zmq::message_t zmq_new_ptree;
+ m_zmq_sock.recv(&zmq_new_ptree);
+ std::string new_ptree(
+ (char*)zmq_new_ptree.data(), zmq_new_ptree.size()
+ );
- while (m_running) {
- socklen_t cli_addr_len = sizeof(cli_addr);
+ etiLog.level(info) << "Received ptree " << new_ptree;
- /* Accept actual connection from the client */
- accepted_sock = accept(m_sock,
- (struct sockaddr *)&cli_addr,
- &cli_addr_len);
+ boost::unique_lock<boost::mutex> lock(m_configmutex);
+ m_pt.clear();
- if (accepted_sock < 0) {
- etiLog.level(warn) << "Stats Server cound not accept connection: " <<
- strerror(errno);
- continue;
- }
- /* Send welcome message with version */
- n = write(accepted_sock, welcome_msg, welcome_msg_len);
- if (n < 0) {
- etiLog.level(warn) << "Error writing to Stats Server socket " <<
- strerror(errno);
- close(accepted_sock);
- continue;
- }
+ std::stringstream json_stream;
+ json_stream << new_ptree;
+ boost::property_tree::json_parser::read_json(json_stream, m_pt);
- /* receive command */
- memset(buffer, 0, 256);
- int n = read(accepted_sock, buffer, 255);
- if (n < 0) {
- etiLog.level(warn) << "Error reading from Stats Server socket " <<
- strerror(errno);
- close(accepted_sock);
- continue;
- }
+ m_retrieve_pending = true;
+ answer << "OK";
- if (strcmp(buffer, "config\n") == 0) {
- std::string json = getStatConfigJSON();
- n = write(accepted_sock, json.c_str(), json.size());
- }
- else if (strcmp(buffer, "values\n") == 0) {
- std::string json = getValuesJSON();
- n = write(accepted_sock, json.c_str(), json.size());
- }
- else if (strcmp(buffer, "state\n") == 0) {
- std::string json = getStateJSON();
- n = write(accepted_sock, json.c_str(), json.size());
- }
- if (strcmp(buffer, "getptree\n") == 0) {
- boost::unique_lock<boost::mutex> lock(m_configmutex);
- m_pending = true;
+ return true;
+ }
+ else {
+ etiLog.level(error) <<
+ "MGMT: setptree command is missing data.";
+ }
+ }
+ catch (std::exception& e) {
+ etiLog.level(error) <<
+ "MGMT: setptree error." << e.what();
+ }
+ return false;
+}
- while (m_pending) {
- m_condition.wait(lock);
- }
- std::stringstream ss;
- boost::property_tree::json_parser::write_json(ss, m_pt);
+void ManagementServer::handle_message(zmq::message_t& zmq_message)
+{
+ std::stringstream answer;
+ std::string data((char*)zmq_message.data(), zmq_message.size());
- std::string response = ss.str();
+ try {
+ etiLog.level(info) << "RC: Accepted";
- n = write(accepted_sock, response.c_str(), response.size());
- }
- else {
- int len = snprintf(buffer, 256, "Invalid command\n");
- n = write(accepted_sock, buffer, len);
- }
+ if (data == "info") {
+ answer <<
+ "{ \"service\": \"" <<
+ PACKAGE_NAME << " " <<
+#if defined(GITVERSION)
+ GITVERSION <<
+#else
+ PACKAGE_VERSION <<
+#endif
+ " MGMT Server\" }\n";
+ }
+ else if (data == "config") {
+ answer << getStatConfigJSON();
+ }
+ else if (data == "values") {
+ answer << getValuesJSON();
+ }
+ else if (data == "state") {
+ answer << getStateJSON();
+ }
+ else if (data == "setptree") {
+ handle_setptree(zmq_message, answer);
+ }
+ else if (data == "getptree") {
+ boost::unique_lock<boost::mutex> lock(m_configmutex);
+ m_pending = true;
- if (n < 0) {
- etiLog.level(warn) << "Error writing to Stats Server socket " <<
- strerror(errno);
+ while (m_pending && !m_retrieve_pending) {
+ m_condition.wait(lock);
}
- close(accepted_sock);
+ boost::property_tree::json_parser::write_json(answer, m_pt);
+ }
+ else {
+ answer << "Invalid command";
}
-end_serverthread:
- m_fault = true;
- close(m_sock);
-
+ std::string answerstr(answer.str());
+ m_zmq_sock.send(answerstr.c_str(), answerstr.size());
}
catch (std::exception& e) {
- etiLog.level(error) << "Statistics server caught exception: " << e.what();
- m_fault = true;
+ etiLog.level(error) <<
+ "MGMT server caught exception: " <<
+ e.what();
+ }
+}
+
+bool ManagementServer::retrieve_new_ptree(boost::property_tree::ptree& pt)
+{
+ boost::unique_lock<boost::mutex> lock(m_configmutex);
+
+ if (m_retrieve_pending)
+ {
+ pt = m_pt;
+
+ m_retrieve_pending = false;
+ m_condition.notify_one();
+ return true;
+ }
+
+ return false;
+}
+
+void ManagementServer::update_ptree(const boost::property_tree::ptree& pt)
+{
+ if (m_running) {
+ boost::unique_lock<boost::mutex> lock(m_configmutex);
+ m_pt = pt;
+ m_pending = false;
+
+ m_condition.notify_one();
}
}
@@ -318,12 +337,12 @@ end_serverthread:
void InputStat::registerAtServer()
{
- mgmt_server->registerInput(this);
+ get_mgmt_server().registerInput(this);
}
InputStat::~InputStat()
{
- mgmt_server->unregisterInput(m_name);
+ get_mgmt_server().unregisterInput(m_name);
}
std::string InputStat::encodeValuesJSON()
diff --git a/src/ManagementServer.h b/src/ManagementServer.h
index fa3f170..836dee4 100644
--- a/src/ManagementServer.h
+++ b/src/ManagementServer.h
@@ -7,7 +7,7 @@
http://www.opendigitalradio.org
- A TCP Socket server that serves state information and statistics for
+ A server that serves state information and statistics for
monitoring purposes, and also serves the internal configuration
property tree.
@@ -15,7 +15,7 @@
http://munin-monitoring.org/
but is not specific to it.
- The TCP Server responds in JSON, and accepts the commands:
+ The responds in JSON, and accepts the commands:
- config
- values
Inspired by the munin equivalent
@@ -27,6 +27,7 @@
Returns the internal boost property_tree that contains the
multiplexer configuration DB.
+ The server is using REQ/REP ZeroMQ sockets.
*/
/*
This file is part of ODR-DabMux.
@@ -52,15 +53,12 @@
# include "config.h"
#endif
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <pthread.h>
+#include "zmq.hpp"
#include <string>
#include <map>
+#include <atomic>
#include <boost/thread.hpp>
+#include <boost/bind.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <ctime>
@@ -317,33 +315,30 @@ class ManagementServer
{
public:
ManagementServer() :
- m_listenport(0),
+ m_zmq_context(),
+ m_zmq_sock(m_zmq_context, ZMQ_REP),
m_running(false),
m_fault(false),
- m_pending(false)
- { }
-
- ManagementServer(int listen_port) :
- m_listenport(listen_port),
- m_running(false),
- m_fault(false),
- m_thread(&ManagementServer::serverThread, this),
- m_pending(false)
- {
- m_sock = 0;
- }
+ m_pending(false) { }
~ManagementServer()
{
m_running = false;
m_fault = false;
m_pending = false;
- if (m_sock) {
- close(m_sock);
- }
+
+ // TODO notify
m_thread.join();
}
+ void open(int listenport)
+ {
+ m_listenport = listenport;
+ if (m_listenport > 0) {
+ m_thread = boost::thread(&ManagementServer::serverThread, this);
+ }
+ }
+
/* Un-/Register a statistics data source */
void registerInput(InputStat* is);
void unregisterInput(std::string id);
@@ -351,18 +346,16 @@ class ManagementServer
/* Ask if there is a configuration request pending */
bool request_pending() { return m_pending; }
+ /* Load a ptree given by the management server.
+ *
+ * Returns true if the ptree was updated
+ */
+ bool retrieve_new_ptree(boost::property_tree::ptree& pt);
+
/* Update the copy of the configuration property tree and notify the
* update to the internal server thread.
*/
- void update_ptree(const boost::property_tree::ptree& pt) {
- if (m_running) {
- boost::unique_lock<boost::mutex> lock(m_configmutex);
- m_pt = pt;
- m_pending = false;
-
- m_condition.notify_one();
- }
- }
+ void update_ptree(const boost::property_tree::ptree& pt);
bool fault_detected() { return m_fault; }
void restart(void);
@@ -370,24 +363,27 @@ class ManagementServer
private:
void restart_thread(long);
- /******* TCP Socket Server ******/
+ /******* Server ******/
+ zmq::context_t m_zmq_context;
+ zmq::socket_t m_zmq_sock;
+
// no copying (because of the thread)
ManagementServer(const ManagementServer& other);
void serverThread(void);
+ void handle_message(zmq::message_t& zmq_message);
+ bool handle_setptree(zmq::message_t& zmq_message, std::stringstream& answer);
bool isInputRegistered(std::string& id);
int m_listenport;
// serverThread runs in a separate thread
- bool m_running;
- bool m_fault;
+ std::atomic<bool> m_running;
+ std::atomic<bool> m_fault;
boost::thread m_thread;
boost::thread m_restarter_thread;
- int m_sock;
-
/******* Statistics Data ********/
std::map<std::string, InputStat*> m_inputStats;
@@ -415,13 +411,16 @@ class ManagementServer
/******** Configuration Data *******/
bool m_pending;
+ bool m_retrieve_pending;
boost::condition_variable m_condition;
mutable boost::mutex m_configmutex;
boost::property_tree::ptree m_pt;
};
-extern ManagementServer* mgmt_server;
+// If necessary construct the management server singleton and return
+// a reference to it
+ManagementServer& get_mgmt_server();
#endif
diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp
index ac6ee32..3170fe1 100644
--- a/src/MuxElements.cpp
+++ b/src/MuxElements.cpp
@@ -24,6 +24,7 @@
*/
#include <vector>
+#include <algorithm>
#include "MuxElements.h"
#include <boost/algorithm/string.hpp>
@@ -43,29 +44,26 @@ const unsigned short Sub_Channel_SizeTable[64] = {
using namespace std;
-int DabLabel::setLabel(const std::string& text)
+int DabLabel::setLabel(const std::string& label)
{
- int len = text.length();
- if (len > 16)
+ size_t len = label.length();
+ if (len > DABLABEL_LENGTH)
return -3;
- memset(m_text, 0, 17);
- memcpy(m_text, text.c_str(), len);
-
m_flag = 0xFF00; // truncate the label to the eight first characters
+ m_label = label;
+
return 0;
}
-int DabLabel::setLabel(const std::string& text, const std::string& short_label)
+int DabLabel::setLabel(const std::string& label, const std::string& short_label)
{
DabLabel newlabel;
- memset(newlabel.m_text, 0, 17);
- int len = text.length();
- if (len > 16)
- return -3;
- memcpy(newlabel.m_text, text.c_str(), len);
+ int result = newlabel.setLabel(label);
+ if (result < 0)
+ return result;
/* First check if we can actually create the short label */
int flag = newlabel.setShortLabel(short_label);
@@ -73,8 +71,8 @@ int DabLabel::setLabel(const std::string& text, const std::string& short_label)
return flag;
// short label is valid.
- memcpy(this->m_text, newlabel.m_text, 17);
- this->m_flag = flag & 0xFFFF;
+ m_flag = flag & 0xFFFF;
+ m_label = newlabel.m_label;
return 0;
}
@@ -104,8 +102,8 @@ int DabLabel::setShortLabel(const std::string& slabel)
/* Iterate over the label and set the bits in the flag
* according to the characters in the slabel
*/
- for (int i = 0; i < 16; ++i) {
- if (*slab == this->m_text[i]) {
+ for (size_t i = 0; i < m_label.size(); ++i) {
+ if (*slab == m_label[i]) {
flag |= 0x8000 >> i;
if (*(++slab) == '\0') {
break;
@@ -117,7 +115,7 @@ int DabLabel::setShortLabel(const std::string& slabel)
* we went through the whole label, the short label
* cannot be represented
*/
- if (*slab != 0) {
+ if (*slab != '\0') {
return -1;
}
@@ -138,15 +136,22 @@ int DabLabel::setShortLabel(const std::string& slabel)
const string DabLabel::short_label() const
{
stringstream shortlabel;
- for (int i = 0; i < 32; ++i) {
+ for (size_t i = 0; i < m_label.size(); ++i) {
if (m_flag & 0x8000 >> i) {
- shortlabel << m_text[i];
+ shortlabel << m_label[i];
}
}
return shortlabel.str();
}
+void DabLabel::writeLabel(uint8_t* buf) const
+{
+ memset(buf, ' ', DABLABEL_LENGTH);
+ if (m_label.size() <= DABLABEL_LENGTH) {
+ std::copy(m_label.begin(), m_label.end(), (char*)buf);
+ }
+}
vector<dabSubchannel*>::iterator getSubchannel(
vector<dabSubchannel*>& subchannels, int id)
@@ -186,19 +191,19 @@ vector<DabComponent*>::iterator getComponent(
return getComponent(components, serviceId, components.end());
}
-vector<DabService*>::iterator getService(
+std::vector<std::shared_ptr<DabService> >::iterator getService(
DabComponent* component,
- vector<DabService*>& services)
+ std::vector<std::shared_ptr<DabService> >& services)
{
- vector<DabService*>::iterator service;
-
- for (service = services.begin(); service != services.end(); ++service) {
- if ((*service)->id == component->serviceId) {
- break;
+ size_t i = 0;
+ for (auto service : services) {
+ if (service->id == component->serviceId) {
+ return services.begin() + i;
}
+ i++;
}
- return service;
+ throw std::runtime_error("Service not included in any component");
}
bool DabComponent::isPacketComponent(vector<dabSubchannel*>& subchannels)
@@ -224,9 +229,6 @@ bool DabComponent::isPacketComponent(vector<dabSubchannel*>& subchannels)
void DabComponent::set_parameter(const string& parameter,
const string& value)
{
- stringstream ss(value);
- ss.exceptions ( stringstream::failbit | stringstream::badbit );
-
if (parameter == "label") {
vector<string> fields;
boost::split(fields, value, boost::is_any_of(","));
@@ -274,10 +276,7 @@ const string DabComponent::get_parameter(const string& parameter) const
{
stringstream ss;
if (parameter == "label") {
- char l[17];
- l[16] = '\0';
- memcpy(l, label.text(), 16);
- ss << l << "," << label.short_label();
+ ss << label.long_label() << "," << label.short_label();
}
else {
ss << "Parameter '" << parameter <<
@@ -289,7 +288,7 @@ const string DabComponent::get_parameter(const string& parameter) const
}
-unsigned char DabService::getType(dabEnsemble* ensemble)
+unsigned char DabService::getType(boost::shared_ptr<dabEnsemble> ensemble)
{
vector<dabSubchannel*>::iterator subchannel;
vector<DabComponent*>::iterator component =
@@ -322,9 +321,6 @@ unsigned char DabService::nbComponent(vector<DabComponent*>& components)
void DabService::set_parameter(const string& parameter,
const string& value)
{
- stringstream ss(value);
- ss.exceptions ( stringstream::failbit | stringstream::badbit );
-
if (parameter == "label") {
vector<string> fields;
boost::split(fields, value, boost::is_any_of(","));
@@ -372,10 +368,7 @@ const string DabService::get_parameter(const string& parameter) const
{
stringstream ss;
if (parameter == "label") {
- char l[17];
- l[16] = '\0';
- memcpy(l, label.text(), 16);
- ss << l << "," << label.short_label();
+ ss << label.long_label() << "," << label.short_label();
}
else {
ss << "Parameter '" << parameter <<
@@ -388,9 +381,6 @@ const string DabService::get_parameter(const string& parameter) const
void dabEnsemble::set_parameter(const string& parameter, const string& value)
{
- stringstream ss(value);
- ss.exceptions ( stringstream::failbit | stringstream::badbit );
-
if (parameter == "localtimeoffset") {
if (value == "auto") {
lto_auto = true;
diff --git a/src/MuxElements.h b/src/MuxElements.h
index 3653ea4..ebcf708 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2014
+ Copyright (C) 2014, 2015
Matthias P. Braendli, matthias.braendli@mpb.li
This file defines all data structures used in DabMux to represent
@@ -29,6 +29,7 @@
#define _MUX_ELEMENTS
#include <vector>
+#include <memory>
#include <string>
#include <functional>
#include <algorithm>
@@ -51,6 +52,7 @@ struct dabOutput {
DabOutput* output;
};
+#define DABLABEL_LENGTH 16
class DabLabel
{
@@ -61,7 +63,7 @@ class DabLabel
* -2 if the short_label is too long
* -3 if the text is too long
*/
- int setLabel(const std::string& text, const std::string& short_label);
+ int setLabel(const std::string& label, const std::string& short_label);
/* Same as above, but sets the flag to 0xff00, truncating at 8
* characters.
@@ -69,17 +71,28 @@ class DabLabel
* returns: 0 on success
* -3 if the text is too long
*/
- int setLabel(const std::string& text);
+ int setLabel(const std::string& label);
+
+ /* Write the label to the 16-byte buffer given in buf
+ * In the DAB standard, the label is 16 bytes long, and is
+ * padded using spaces.
+ */
+ void writeLabel(uint8_t* buf) const;
- const char* text() const { return m_text; }
uint16_t flag() const { return m_flag; }
+ const std::string long_label() const { return m_label; }
const std::string short_label() const;
private:
- // In the DAB standard, the label is 16 chars.
- // We keep it here zero-terminated
- char m_text[17];
+ /* The flag field selects which label characters make
+ * up the short label
+ */
uint16_t m_flag;
+
+ /* The m_label is not padded in any way */
+ std::string m_label;
+
+ /* Checks and calculates the flag */
int setShortLabel(const std::string& slabel);
};
@@ -87,7 +100,7 @@ class DabLabel
class DabService;
class DabComponent;
-struct dabSubchannel;
+class dabSubchannel;
class dabEnsemble : public RemoteControllable {
public:
dabEnsemble()
@@ -118,7 +131,7 @@ class dabEnsemble : public RemoteControllable {
int international_table;
- std::vector<DabService*> services;
+ std::vector<std::shared_ptr<DabService> > services;
std::vector<DabComponent*> components;
std::vector<dabSubchannel*> subchannels;
};
@@ -166,7 +179,16 @@ enum dab_subchannel_type_t {
Packet = 3
};
-struct dabSubchannel {
+class dabSubchannel
+{
+public:
+ dabSubchannel(std::string& uid) :
+ uid(uid)
+ {
+ }
+
+ std::string uid;
+
std::string inputUri;
DabInputBase* input;
unsigned char id;
@@ -220,12 +242,15 @@ struct dabPacketComponent {
class DabComponent : public RemoteControllable
{
public:
- DabComponent(std::string uid) :
- RemoteControllable(uid)
+ DabComponent(std::string& uid) :
+ RemoteControllable(uid),
+ uid(uid)
{
RC_ADD_PARAMETER(label, "Label and shortlabel [label,short]");
}
+ std::string uid;
+
DabLabel label;
uint32_t serviceId;
uint8_t subchId;
@@ -258,18 +283,21 @@ class DabComponent : public RemoteControllable
class DabService : public RemoteControllable
{
public:
- DabService(std::string uid) :
- RemoteControllable(uid)
+ DabService(std::string& uid) :
+ RemoteControllable(uid),
+ uid(uid)
{
RC_ADD_PARAMETER(label, "Label and shortlabel [label,short]");
}
+ std::string uid;
+
uint32_t id;
unsigned char pty;
unsigned char language;
bool program;
- unsigned char getType(dabEnsemble* ensemble);
+ unsigned char getType(boost::shared_ptr<dabEnsemble> ensemble);
unsigned char nbComponent(std::vector<DabComponent*>& components);
DabLabel label;
@@ -300,9 +328,9 @@ std::vector<DabComponent*>::iterator getComponent(
std::vector<DabComponent*>& components,
uint32_t serviceId);
-std::vector<DabService*>::iterator getService(
+std::vector<std::shared_ptr<DabService> >::iterator getService(
DabComponent* component,
- std::vector<DabService*>& services);
+ std::vector<std::shared_ptr<DabService> >& services);
unsigned short getSizeCu(dabSubchannel* subchannel);
@@ -312,5 +340,5 @@ unsigned short getSizeByte(dabSubchannel* subchannel);
unsigned short getSizeWord(dabSubchannel* subchannel);
-
#endif
+
diff --git a/src/RemoteControl.cpp b/src/RemoteControl.cpp
index e46bc8d..723ba9b 100644
--- a/src/RemoteControl.cpp
+++ b/src/RemoteControl.cpp
@@ -27,6 +27,7 @@
#include <iostream>
#include <string>
#include <boost/asio.hpp>
+#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "Log.h"
@@ -35,6 +36,12 @@
using boost::asio::ip::tcp;
using namespace std;
+RemoteControllerTelnet::~RemoteControllerTelnet()
+{
+ m_running = false;
+ m_io_service.stop();
+ m_child_thread.join();
+}
void RemoteControllerTelnet::restart()
{
@@ -47,85 +54,120 @@ void RemoteControllerTelnet::restart()
// thread.
void RemoteControllerTelnet::restart_thread(long)
{
+ etiLog.level(warn) << "RC: Restart Telnet server";
+
m_running = false;
+ m_io_service.stop();
- if (m_port) {
- m_child_thread.interrupt();
- m_child_thread.join();
- }
+ m_child_thread.join();
m_child_thread = boost::thread(&RemoteControllerTelnet::process, this, 0);
}
-void RemoteControllerTelnet::process(long)
+void RemoteControllerTelnet::handle_accept(
+ const boost::system::error_code& boost_error,
+ boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
+ boost::asio::ip::tcp::acceptor& acceptor)
{
- m_welcome = "ODR-DabMux Remote Control CLI\nWrite 'help' for help.\n**********\n";
- m_prompt = "> ";
+
+ const std::string welcome = "ODR-DabMux Remote Control CLI\n"
+ "Write 'help' for help.\n"
+ "**********\n";
+ const std::string prompt = "> ";
std::string in_message;
size_t length;
- try {
- boost::asio::io_service io_service;
- tcp::acceptor acceptor(io_service, tcp::endpoint(
- boost::asio::ip::address::from_string("127.0.0.1"), m_port) );
-
- while (m_running) {
- in_message = "";
+ if (boost_error)
+ {
+ etiLog.level(error) << "RC: Error accepting connection";
+ return;
+ }
- tcp::socket socket(io_service);
+ try {
+ etiLog.level(info) << "RC: Accepted";
- acceptor.accept(socket);
+ boost::system::error_code ignored_error;
- boost::system::error_code ignored_error;
+ boost::asio::write(*socket, boost::asio::buffer(welcome),
+ boost::asio::transfer_all(),
+ ignored_error);
- boost::asio::write(socket, boost::asio::buffer(m_welcome),
+ while (m_running && in_message != "quit") {
+ boost::asio::write(*socket, boost::asio::buffer(prompt),
boost::asio::transfer_all(),
ignored_error);
- while (m_running && in_message != "quit") {
- boost::asio::write(socket, boost::asio::buffer(m_prompt),
- boost::asio::transfer_all(),
- ignored_error);
-
- in_message = "";
+ in_message = "";
- boost::asio::streambuf buffer;
- length = boost::asio::read_until( socket, buffer, "\n", ignored_error);
+ boost::asio::streambuf buffer;
+ length = boost::asio::read_until(*socket, buffer, "\n", ignored_error);
- std::istream str(&buffer);
- std::getline(str, in_message);
+ std::istream str(&buffer);
+ std::getline(str, in_message);
- if (length == 0) {
- etiLog.level(info) << "RC: Connection terminated";
- break;
- }
+ if (length == 0) {
+ etiLog.level(info) << "RC: Connection terminated";
+ break;
+ }
- while (in_message.length() > 0 &&
- (in_message[in_message.length()-1] == '\r' ||
- in_message[in_message.length()-1] == '\n')) {
- in_message.erase(in_message.length()-1, 1);
- }
+ while (in_message.length() > 0 &&
+ (in_message[in_message.length()-1] == '\r' ||
+ in_message[in_message.length()-1] == '\n')) {
+ in_message.erase(in_message.length()-1, 1);
+ }
- if (in_message.length() == 0) {
- continue;
- }
+ if (in_message.length() == 0) {
+ continue;
+ }
- etiLog.level(info) << "RC: Got message '" << in_message << "'";
+ etiLog.level(info) << "RC: Got message '" << in_message << "'";
- dispatch_command(socket, in_message);
- }
- etiLog.level(info) << "RC: Closing socket";
- socket.close();
+ dispatch_command(*socket, in_message);
}
+ etiLog.level(info) << "RC: Closing socket";
+ socket->close();
}
catch (std::exception& e)
{
etiLog.level(error) << "Remote control caught exception: " << e.what();
- m_fault = true;
}
}
+void RemoteControllerTelnet::process(long)
+{
+ m_running = true;
+
+ while (m_running) {
+ m_io_service.reset();
+
+ tcp::acceptor acceptor(m_io_service, tcp::endpoint(
+ boost::asio::ip::address::from_string("127.0.0.1"), m_port) );
+
+
+ // Add a job to start accepting connections.
+ boost::shared_ptr<tcp::socket> socket(
+ new tcp::socket(acceptor.get_io_service()));
+
+ // Add an accept call to the service. This will prevent io_service::run()
+ // from returning.
+ etiLog.level(warn) << "RC: Waiting on connection";
+ acceptor.async_accept(*socket,
+ boost::bind(&RemoteControllerTelnet::handle_accept,
+ this,
+ boost::asio::placeholders::error,
+ socket,
+ boost::ref(acceptor)));
+
+ // Process event loop.
+ m_io_service.run();
+ }
+
+ etiLog.level(warn) << "RC: Leaving";
+ m_fault = true;
+}
+
+
void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command)
{
vector<string> cmd = tokenise_(command);
diff --git a/src/RemoteControl.h b/src/RemoteControl.h
index 16881b4..46a828f 100644
--- a/src/RemoteControl.h
+++ b/src/RemoteControl.h
@@ -32,6 +32,7 @@
#include <list>
#include <map>
#include <string>
+#include <atomic>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
@@ -104,6 +105,10 @@ class RemoteControllable {
controller.enrol(this);
}
+ virtual void enrol_at(boost::shared_ptr<BaseRemoteController> controller) {
+ controller->enrol(this);
+ }
+
/* Return a list of possible parameters that can be set */
virtual std::list<std::string> get_supported_parameters() const {
std::list<std::string> parameterlist;
@@ -137,25 +142,23 @@ class RemoteControllable {
*/
class RemoteControllerTelnet : public BaseRemoteController {
public:
- RemoteControllerTelnet()
- : m_running(false), m_fault(false),
+ RemoteControllerTelnet() :
+ m_running(false),
+ m_io_service(),
+ m_fault(false),
m_port(0) { }
- RemoteControllerTelnet(int port)
- : m_running(true), m_fault(false),
- m_child_thread(&RemoteControllerTelnet::process, this, 0),
+ RemoteControllerTelnet(int port) :
+ m_running(false),
+ m_io_service(),
+ m_fault(false),
m_port(port)
- { }
-
- ~RemoteControllerTelnet() {
- m_running = false;
- m_fault = false;
- if (m_port) {
- m_child_thread.interrupt();
- m_child_thread.join();
- }
+ {
+ restart();
}
+ ~RemoteControllerTelnet();
+
void enrol(RemoteControllable* controllable) {
m_cohort.push_back(controllable);
}
@@ -174,6 +177,11 @@ class RemoteControllerTelnet : public BaseRemoteController {
void reply(boost::asio::ip::tcp::socket& socket, std::string message);
+ void handle_accept(
+ const boost::system::error_code& boost_error,
+ boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
+ boost::asio::ip::tcp::acceptor& acceptor);
+
RemoteControllerTelnet& operator=(const RemoteControllerTelnet& other);
RemoteControllerTelnet(const RemoteControllerTelnet& other);
@@ -237,10 +245,12 @@ class RemoteControllerTelnet : public BaseRemoteController {
return controllable->set_parameter(param, value);
}
- bool m_running;
+ std::atomic<bool> m_running;
+
+ boost::asio::io_service m_io_service;
/* This is set to true if a fault occurred */
- bool m_fault;
+ std::atomic<bool> m_fault;
boost::thread m_restarter_thread;
boost::thread m_child_thread;
@@ -248,9 +258,6 @@ class RemoteControllerTelnet : public BaseRemoteController {
/* This controller commands the controllables in the cohort */
std::list<RemoteControllable*> m_cohort;
- std::string m_welcome;
- std::string m_prompt;
-
int m_port;
};
diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp
index 89fefd2..75b320f 100644
--- a/src/TcpSocket.cpp
+++ b/src/TcpSocket.cpp
@@ -266,7 +266,7 @@ int TcpSocket::write(const void* data, int size)
// ignore BROKENPIPE signal (we handle it instead)
// void* old_sigpipe = signal ( SIGPIPE, SIG_IGN );
// try to send data
- int ret = send(listenSocket, (char*)data, size, 0 /*MSG_NOSIGNAL*/ );
+ int ret = send(listenSocket, (const char*)data, size, 0 /*MSG_NOSIGNAL*/ );
// restore the BROKENPIPE handling
// signal ( SIGPIPE, (__sighandler_t)old_sigpipe );
if (ret == SOCKET_ERROR) {
diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp
index 6d2728b..74730e9 100644
--- a/src/UdpSocket.cpp
+++ b/src/UdpSocket.cpp
@@ -333,7 +333,7 @@ char *UdpPacket::getData()
* @param data Pointer to the data to add
* @param size Size in bytes of new data
*/
-void UdpPacket::addData(void *data, unsigned size)
+void UdpPacket::addData(const void *data, unsigned size)
{
if (length + size > this->size) {
setSize(this->size << 1);
diff --git a/src/UdpSocket.h b/src/UdpSocket.h
index f1487b3..109732f 100644
--- a/src/UdpSocket.h
+++ b/src/UdpSocket.h
@@ -106,7 +106,7 @@ class UdpPacket {
~UdpPacket();
char *getData();
- void addData(void *data, unsigned size);
+ void addData(const void *data, unsigned size);
unsigned long getLength();
unsigned long getSize();
unsigned long getOffset();
diff --git a/src/crc.c b/src/crc.c
index 2a676d9..cc02473 100644
--- a/src/crc.c
+++ b/src/crc.c
@@ -236,7 +236,7 @@ void init_crc32tab(uint32_t l_code, uint32_t l_init)
uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb)
{
- uint8_t* data = (uint8_t*)lp_data;
+ const uint8_t* data = (const uint8_t*)lp_data;
while (l_nb--) {
l_crc = crc8tab[l_crc ^ *(data++)];
}
@@ -246,7 +246,7 @@ uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb)
uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb)
{
- uint8_t* data = (uint8_t*)lp_data;
+ const uint8_t* data = (const uint8_t*)lp_data;
while (l_nb--) {
l_crc =
(l_crc << 8) ^ crc16tab[(l_crc >> 8) ^ *(data++)];
@@ -257,7 +257,7 @@ uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb)
uint32_t crc32(uint32_t l_crc, const void *lp_data, unsigned l_nb)
{
- uint8_t* data = (uint8_t*)lp_data;
+ const uint8_t* data = (const uint8_t*)lp_data;
while (l_nb--) {
l_crc =
(l_crc << 8) ^ crc32tab[((l_crc >> 24) ^ *(data++)) & 0xff];
diff --git a/src/dabInputZmq.cpp b/src/dabInputZmq.cpp
index 9d39945..5c20baf 100644
--- a/src/dabInputZmq.cpp
+++ b/src/dabInputZmq.cpp
@@ -386,9 +386,9 @@ int DabInputZmqMPEG::readFromSocket(size_t framesize)
}
else if (m_enable_input) {
// copy the input frame blockwise into the frame_buffer
- uint8_t* frame = new uint8_t[framesize];
- memcpy(frame, data, framesize);
- m_frame_buffer.push_back(frame);
+ uint8_t* framedata = new uint8_t[framesize];
+ memcpy(framedata, data, framesize);
+ m_frame_buffer.push_back(framedata);
}
else {
return 0;
@@ -397,7 +397,7 @@ int DabInputZmqMPEG::readFromSocket(size_t framesize)
else {
etiLog.level(error) <<
"inputZMQ " << m_name <<
- " wrong data size: recv'd " << msg.size() <<
+ " wrong data size: recv'd " << msg.size() << " Bytes" <<
", need " << framesize << ".";
messageReceived = false;
}
@@ -498,9 +498,6 @@ int DabInputZmqAAC::readFromSocket(size_t framesize)
void DabInputZmqBase::set_parameter(const string& parameter,
const string& value)
{
- stringstream ss(value);
- ss.exceptions ( stringstream::failbit | stringstream::badbit );
-
if (parameter == "buffer") {
size_t new_limit = atol(value.c_str());
diff --git a/src/dabOutput/dabOutput.h b/src/dabOutput/dabOutput.h
index 18f3848..c8ce9f2 100644
--- a/src/dabOutput/dabOutput.h
+++ b/src/dabOutput/dabOutput.h
@@ -55,6 +55,8 @@
// Configuration for EDI output
struct edi_configuration_t {
+ unsigned chunk_len; // RSk, data length of each chunk
+ unsigned fec; // number of fragments that can be recovered
bool enabled;
unsigned int source_port;
bool dump;
@@ -78,6 +80,8 @@ class DabOutput
virtual int Close() = 0;
virtual ~DabOutput() {}
+
+ virtual std::string get_info() = 0;
};
// ----- used in File and Fifo outputs
@@ -111,7 +115,12 @@ class DabOutputFile : public DabOutput
int Write(void* buffer, int size);
int Close();
+ std::string get_info() {
+ return "file://" + filename_;
+ }
+
protected:
+ std::string filename_;
int file_;
EtiFileType type_;
unsigned long nbFrames_;
@@ -126,6 +135,11 @@ class DabOutputFifo : public DabOutputFile
~DabOutputFifo() {}
int Write(void* buffer, int size);
+
+ std::string get_info() {
+ return "fifo://" + filename_;
+ }
+
};
// -------------- RAW socket -----------
@@ -162,7 +176,12 @@ class DabOutputRaw : public DabOutput
int Open(const char* name);
int Write(void* buffer, int size);
int Close();
+
+ std::string get_info() {
+ return "raw://" + filename_;
+ }
private:
+ std::string filename_;
#ifdef _WIN32
HANDLE socket_;
#else
@@ -197,7 +216,11 @@ class DabOutputUdp : public DabOutput
int Write(void* buffer, int size);
int Close() { return 0; }
+ std::string get_info() {
+ return "udp://" + uri_;
+ }
private:
+ std::string uri_;
UdpSocket* socket_;
UdpPacket* packet_;
};
@@ -230,9 +253,14 @@ class DabOutputTcp : public DabOutput
int Write(void* buffer, int size);
int Close();
+ std::string get_info() {
+ return "tcp://" + uri_;
+ }
+
TcpServer* server;
TcpSocket* client;
private:
+ std::string uri_;
pthread_t thread_;
};
@@ -252,7 +280,12 @@ class DabOutputSimul : public DabOutput
int Open(const char* name);
int Write(void* buffer, int size);
int Close() { return 0; }
+
+ std::string get_info() {
+ return "simul://" + name_;
+ }
private:
+ std::string name_;
#ifdef _WIN32
DWORD startTime_;
#else
@@ -304,6 +337,7 @@ class DabOutputZMQ : public DabOutput
{
public:
DabOutputZMQ() :
+ endpoint_(""),
zmq_proto_(""), zmq_context_(1),
zmq_pub_sock_(zmq_context_, ZMQ_PUB),
zmq_message_ix(0)
@@ -312,6 +346,7 @@ class DabOutputZMQ : public DabOutput
}
DabOutputZMQ(std::string zmq_proto) :
+ endpoint_(""),
zmq_proto_(zmq_proto), zmq_context_(1),
zmq_pub_sock_(zmq_context_, ZMQ_PUB),
zmq_message_ix(0)
@@ -323,7 +358,11 @@ class DabOutputZMQ : public DabOutput
zmq_pub_sock_.close();
}
- int Open(const char* name);
+ std::string get_info() {
+ return "zmq: " + zmq_proto_ + "://" + endpoint_;
+ }
+
+ int Open(const char* endpoint);
int Write(void* buffer, int size);
int Close();
private:
@@ -334,6 +373,7 @@ class DabOutputZMQ : public DabOutput
/* Forbid copy constructor */
}
+ std::string endpoint_;
std::string zmq_proto_;
zmq::context_t zmq_context_; // handle for the zmq context
zmq::socket_t zmq_pub_sock_; // handle for the zmq publisher socket
diff --git a/src/dabOutput/dabOutputFifo.cpp b/src/dabOutput/dabOutputFifo.cpp
index de9579e..6b1c016 100644
--- a/src/dabOutput/dabOutputFifo.cpp
+++ b/src/dabOutput/dabOutputFifo.cpp
@@ -59,6 +59,7 @@ int DabOutputFifo::Write(void* buffer, int size)
if (write(this->file_, padding, 6144 - size) == -1)
goto FIFO_WRITE_ERROR;
break;
+ case ETI_FILE_TYPE_NONE:
default:
etiLog.log(error, "File type is not supported.\n");
return -1;
diff --git a/src/dabOutput/dabOutputFile.cpp b/src/dabOutput/dabOutputFile.cpp
index f26ddfd..d7fd5c7 100644
--- a/src/dabOutput/dabOutputFile.cpp
+++ b/src/dabOutput/dabOutputFile.cpp
@@ -112,6 +112,7 @@ int DabOutputFile::Write(void* buffer, int size)
memset(padding, 0x55, 6144 - size);
if (write(this->file_, padding, 6144 - size) == -1) goto FILE_WRITE_ERROR;
break;
+ case ETI_FILE_TYPE_NONE:
default:
etiLog.log(error, "File type is not supported.\n");
return -1;
diff --git a/src/dabOutput/edi/AFPacket.cpp b/src/dabOutput/edi/AFPacket.cpp
index 3b69f1c..a1d39b9 100644
--- a/src/dabOutput/edi/AFPacket.cpp
+++ b/src/dabOutput/edi/AFPacket.cpp
@@ -39,7 +39,7 @@
#define AFHEADER_PT_TAG 'T'
// AF Packet Major (3 bits) and Minor (4 bits) version
-#define AFHEADER_VERSION 0x8 // MAJ=1, MIN=0
+#define AFHEADER_VERSION 0x10 // MAJ=1, MIN=0
AFPacket AFPacketiser::Assemble(TagPacket tag_packet)
{
@@ -65,6 +65,7 @@ AFPacket AFPacketiser::Assemble(TagPacket tag_packet)
// fill rest of header
packet.push_back(seq >> 8);
packet.push_back(seq & 0xFF);
+ seq++;
packet.push_back((have_crc ? 0x80 : 0) | AFHEADER_VERSION); // ar_cf: CRC=1
packet.push_back(AFHEADER_PT_TAG);
diff --git a/src/dabOutput/edi/AFPacket.h b/src/dabOutput/edi/AFPacket.h
index 5f62456..9b189b8 100644
--- a/src/dabOutput/edi/AFPacket.h
+++ b/src/dabOutput/edi/AFPacket.h
@@ -39,6 +39,8 @@ typedef std::vector<uint8_t> AFPacket;
class AFPacketiser
{
public:
+ AFPacketiser() :
+ m_verbose(false) {};
AFPacketiser(bool verbose) :
m_verbose(verbose) {};
diff --git a/src/dabOutput/edi/PFT.cpp b/src/dabOutput/edi/PFT.cpp
index e1c8249..7f463cc 100644
--- a/src/dabOutput/edi/PFT.cpp
+++ b/src/dabOutput/edi/PFT.cpp
@@ -52,7 +52,7 @@ RSBlock PFT::Protect(AFPacket af_packet)
// number of chunks is ceil(afpacketsize / m_k)
// TS 102 821 7.2.2: c = ceil(l / k_max)
- m_num_chunks = CEIL_DIV(af_packet.size(), 207);
+ m_num_chunks = CEIL_DIV(af_packet.size(), m_k);
if (m_verbose) {
fprintf(stderr, "Protect %zu chunks of size %zu\n",
diff --git a/src/dabOutput/edi/PFT.h b/src/dabOutput/edi/PFT.h
index 4aae817..e17d282 100644
--- a/src/dabOutput/edi/PFT.h
+++ b/src/dabOutput/edi/PFT.h
@@ -49,11 +49,17 @@ class PFT
public:
static const int ParityBytes = 48;
- PFT(unsigned int RSDataWordLength,
- unsigned int NumRecoverableFragments,
- const edi_configuration_t &conf) :
- m_k(RSDataWordLength),
- m_m(NumRecoverableFragments),
+ PFT() :
+ m_k(207),
+ m_m(3),
+ m_dest_port(12000),
+ m_pseq(0),
+ m_verbose(false)
+ { }
+
+ PFT(const edi_configuration_t &conf) :
+ m_k(conf.chunk_len),
+ m_m(conf.fec),
m_dest_port(conf.dest_port),
m_pseq(0),
m_verbose(conf.verbose)
diff --git a/src/dabOutput/edi/TagPacket.cpp b/src/dabOutput/edi/TagPacket.cpp
index fbb0f8b..aa4f23b 100644
--- a/src/dabOutput/edi/TagPacket.cpp
+++ b/src/dabOutput/edi/TagPacket.cpp
@@ -31,6 +31,7 @@
#include <string>
#include <list>
#include <stdint.h>
+#include <cassert>
std::vector<uint8_t> TagPacket::Assemble()
@@ -41,21 +42,17 @@ std::vector<uint8_t> TagPacket::Assemble()
//std::cerr << "Assemble TAGPacket" << std::endl;
- size_t packet_length = 0;
for (tag = tag_items.begin(); tag != tag_items.end(); ++tag) {
std::vector<uint8_t> tag_data = (*tag)->Assemble();
packet.insert(packet.end(), tag_data.begin(), tag_data.end());
- packet_length += tag_data.size();
-
//std::cerr << " Add TAGItem of length " << tag_data.size() << std::endl;
}
// Add padding
- while (packet_length % 8 > 0)
+ while (packet.size() % 8 > 0)
{
packet.push_back(0); // TS 102 821, 5.1, "padding shall be undefined"
- packet_length++;
}
return packet;
diff --git a/src/fig/FIG.h b/src/fig/FIG.h
new file mode 100644
index 0000000..4e6c20f
--- /dev/null
+++ b/src/fig/FIG.h
@@ -0,0 +1,96 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __FIG_H_
+#define __FIG_H_
+
+#include <boost/shared_ptr.hpp>
+#include "MuxElements.h"
+
+class FIGRuntimeInformation {
+ public:
+ FIGRuntimeInformation(boost::shared_ptr<dabEnsemble> e) :
+ currentFrame(0),
+ ensemble(e),
+ factumAnalyzer(false) {}
+
+ unsigned long currentFrame;
+ boost::shared_ptr<dabEnsemble> ensemble;
+ bool factumAnalyzer;
+};
+
+// Recommended FIG rates according to ETSI TR 101 496-2 Table 3.6.1
+enum class FIG_rate {
+ FIG0_0, /* Special repetition rate for FIG0/0, EN 300 401 Clause 6.4
+ In any 96 ms period, the FIG 0/0 should be transmitted in a fixed time
+ position. In transmission mode I, this should be the first FIB (of the three)
+ associated with the first CIF (of the four) in the transmission frame (see
+ clause 5.1). In transmission modes II and III, this should be the first FIB of
+ every fourth transmission frame. In transmission mode IV, this should be the
+ first FIB (of the three) associated with the first CIF (of the two) in every
+ alternate transmission frame (see clause 5.1). */
+ A, // at least 10 times per second
+ B, // once per second
+ C, // once every 10 seconds
+ D, // less than once every 10 seconds
+ E, // all in two minutes
+};
+
+/* Helper function to calculate the deadline for the next transmission, in milliseconds */
+inline int rate_increment_ms(FIG_rate rate)
+{
+ switch (rate) {
+ case FIG_rate::FIG0_0: return 96; // Is a special case
+ case FIG_rate::A: return 100;
+ case FIG_rate::B: return 1000;
+ case FIG_rate::C: return 10000;
+ case FIG_rate::D: return 30000;
+ case FIG_rate::E: return 120000;
+ }
+ return 1000; //some default value, shouldn't be used
+}
+
+class IFIG
+{
+ public:
+ virtual size_t fill(uint8_t *buf, size_t max_size) = 0;
+
+ virtual FIG_rate repetition_rate(void) = 0;
+
+ virtual const int figtype(void) const = 0;
+ virtual const int figextension(void) const = 0;
+
+ virtual const std::string name(void) const
+ {
+ std::stringstream ss;
+ ss << figtype() << "/" << figextension();
+ return ss.str();
+ }
+
+};
+
+#endif // __FIG_H_
+
diff --git a/src/fig/FIG0.cpp b/src/fig/FIG0.cpp
new file mode 100644
index 0000000..458116e
--- /dev/null
+++ b/src/fig/FIG0.cpp
@@ -0,0 +1,483 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Implementation of FIG0
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "fig/FIG0.h"
+#include "DabMultiplexer.h"
+
+#define PACKED __attribute__ ((packed))
+
+//=========== FIG 0/0 ===========
+
+size_t FIG0_0::fill(uint8_t *buf, size_t max_size)
+{
+ if (max_size < 6) {
+ return 0;
+ }
+
+ FIGtype0_0 *fig0_0;
+ fig0_0 = (FIGtype0_0 *)buf;
+
+ fig0_0->FIGtypeNumber = 0;
+ fig0_0->Length = 5;
+ fig0_0->CN = 0;
+ fig0_0->OE = 0;
+ fig0_0->PD = 0;
+ fig0_0->Extension = 0;
+
+ fig0_0->EId = htons(m_rti->ensemble->id);
+ fig0_0->Change = 0;
+ fig0_0->Al = 0;
+ fig0_0->CIFcnt_hight = (m_rti->currentFrame / 250) % 20;
+ fig0_0->CIFcnt_low = (m_rti->currentFrame % 250);
+
+ return 6;
+}
+
+
+//=========== FIG 0/1 ===========
+
+FIG0_1::FIG0_1(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false)
+{
+}
+
+size_t FIG0_1::fill(uint8_t *buf, size_t max_size)
+{
+ size_t remaining = max_size;
+
+ if (not m_initialised) {
+ subchannelFIG0_1 = m_rti->ensemble->subchannels.end();
+ }
+
+ if (max_size < 6) {
+ return 0;
+ }
+
+ auto ensemble = m_rti->ensemble;
+
+ FIGtype0_1 *figtype0_1;
+ figtype0_1 = (FIGtype0_1*)buf;
+
+ figtype0_1->FIGtypeNumber = 0;
+ figtype0_1->Length = 1;
+ figtype0_1->CN = 0;
+ figtype0_1->OE = 0;
+ figtype0_1->PD = 0;
+ figtype0_1->Extension = 1;
+ buf += 2;
+ remaining -= 2;
+
+ // Rotate through the subchannels until there is no more
+ // space in the FIG0/1
+ if (subchannelFIG0_1 == ensemble->subchannels.end()) {
+ subchannelFIG0_1 = ensemble->subchannels.begin();
+ }
+
+ for (; subchannelFIG0_1 != ensemble->subchannels.end();
+ ++subchannelFIG0_1) {
+ dabProtection* protection = &(*subchannelFIG0_1)->protection;
+
+ if ( (protection->form == UEP && remaining < 3) ||
+ (protection->form == EEP && remaining < 4) ) {
+ break;
+ }
+
+ if (protection->form == UEP) {
+ FIG_01_SubChannel_ShortF *fig0_1subchShort =
+ (FIG_01_SubChannel_ShortF*)buf;
+ fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchShort->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchShort->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchShort->Short_Long_form = 0;
+ fig0_1subchShort->TableSwitch = 0;
+ fig0_1subchShort->TableIndex =
+ protection->uep.tableIndex;
+
+ buf += 3;
+ remaining -= 3;
+ figtype0_1->Length += 3;
+ }
+ else if (protection->form == EEP) {
+ FIG_01_SubChannel_LongF *fig0_1subchLong1 =
+ (FIG_01_SubChannel_LongF*)buf;
+ fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchLong1->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchLong1->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchLong1->Short_Long_form = 1;
+ fig0_1subchLong1->Option = protection->eep.GetOption();
+ fig0_1subchLong1->ProtectionLevel =
+ protection->level;
+
+ fig0_1subchLong1->Sub_ChannelSize_high =
+ getSizeCu(*subchannelFIG0_1) / 256;
+ fig0_1subchLong1->Sub_ChannelSize_low =
+ getSizeCu(*subchannelFIG0_1) % 256;
+
+ buf += 4;
+ remaining -= 4;
+ figtype0_1->Length += 4;
+ }
+ }
+
+ return max_size - remaining;
+}
+
+
+//=========== FIG 0/2 ===========
+
+FIG0_2::FIG0_2(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false)
+{
+}
+
+size_t FIG0_2::fill(uint8_t *buf, size_t max_size)
+{
+ FIGtype0_2 *fig0_2 = NULL;
+ int cur = 0;
+ ssize_t remaining = max_size;
+
+ if (not m_initialised) {
+ serviceFIG0_2 = m_rti->ensemble->services.end();
+ }
+
+ auto ensemble = m_rti->ensemble;
+
+ // Rotate through the subchannels until there is no more
+ // space
+ if (serviceFIG0_2 == ensemble->services.end()) {
+ serviceFIG0_2 = ensemble->services.begin();
+ }
+
+ for (; serviceFIG0_2 != ensemble->services.end();
+ ++serviceFIG0_2) {
+
+ // filter out services which have no components
+ if ((*serviceFIG0_2)->nbComponent(ensemble->components) == 0) {
+ continue;
+ }
+
+ // Exclude Fidc type services, TODO why ?
+ auto type = (*serviceFIG0_2)->getType(ensemble);
+ if (type == Fidc) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *)buf;
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = (type == Audio) ? 0 : 1;
+ fig0_2->Extension = 2;
+ buf += 2;
+ remaining -= 2;
+ }
+
+ if (type == Audio and
+ remaining < 3 + 2 *
+ (*serviceFIG0_2)->nbComponent(ensemble->components)) {
+ break;
+ }
+
+ if (type != Audio and
+ remaining < 5 + 2 *
+ (*serviceFIG0_2)->nbComponent(ensemble->components)) {
+ break;
+ }
+
+ if (type == Audio) {
+ auto fig0_2serviceAudio = (FIGtype0_2_Service*)buf;
+
+ fig0_2serviceAudio->SId = htons((*serviceFIG0_2)->id);
+ fig0_2serviceAudio->Local_flag = 0;
+ fig0_2serviceAudio->CAId = 0;
+ fig0_2serviceAudio->NbServiceComp =
+ (*serviceFIG0_2)->nbComponent(ensemble->components);
+ buf += 3;
+ fig0_2->Length += 3;
+ remaining -= 3;
+ }
+ else {
+ auto fig0_2serviceData = (FIGtype0_2_Service_data*)buf;
+
+ fig0_2serviceData->SId = htonl((*serviceFIG0_2)->id);
+ fig0_2serviceData->Local_flag = 0;
+ fig0_2serviceData->CAId = 0;
+ fig0_2serviceData->NbServiceComp =
+ (*serviceFIG0_2)->nbComponent(ensemble->components);
+ buf += 5;
+ fig0_2->Length += 5;
+ remaining -= 5;
+ }
+
+ int curCpnt = 0;
+ for (auto component = getComponent(
+ ensemble->components, (*serviceFIG0_2)->id );
+ component != ensemble->components.end();
+ component = getComponent(
+ ensemble->components,
+ (*serviceFIG0_2)->id,
+ component )
+ ) {
+ auto subchannel = getSubchannel(
+ ensemble->subchannels, (*component)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ {
+ auto audio_description = (FIGtype0_2_audio_component*)buf;
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ }
+ break;
+ case DataDmb:
+ {
+ auto data_description = (FIGtype0_2_data_component*)buf;
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ }
+ break;
+ case Packet:
+ {
+ auto packet_description = (FIGtype0_2_packet_component*)buf;
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ }
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ buf += 2;
+ fig0_2->Length += 2;
+ remaining -= 2;
+ if (remaining < 0) {
+ etiLog.log(error,
+ "Sorry, no space left in FIG 0/2 to insert "
+ "component %i of program service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ return max_size - remaining;
+}
+
+
+//=========== FIG 0/3 ===========
+
+FIG0_3::FIG0_3(FIGRuntimeInformation *rti) :
+ m_rti(rti)
+{
+}
+
+size_t FIG0_3::fill(uint8_t *buf, size_t max_size)
+{
+ ssize_t remaining = max_size;
+ auto ensemble = m_rti->ensemble;
+
+ FIGtype0_3_header *fig0_3_header = NULL;
+ FIGtype0_3_data *fig0_3_data = NULL;
+
+ for (auto& component : ensemble->components) {
+ auto subchannel = getSubchannel(ensemble->subchannels,
+ component->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ component->subchId, component->serviceId);
+ throw MuxInitException();
+ }
+
+ if ((*subchannel)->type != Packet)
+ continue;
+
+ if (fig0_3_header == NULL) {
+ fig0_3_header = (FIGtype0_3_header*)buf;
+ fig0_3_header->FIGtypeNumber = 0;
+ fig0_3_header->Length = 1;
+ fig0_3_header->CN = 0;
+ fig0_3_header->OE = 0;
+ fig0_3_header->PD = 0;
+ fig0_3_header->Extension = 3;
+
+ buf += 2;
+ remaining -= 2;
+ }
+
+ /* Warning: When bit SCCA_flag is unset(0), the multiplexer
+ * R&S does not send the SCCA field. But, in the Factum ETI
+ * analyzer, if this field is not there, it is an error.
+ */
+ fig0_3_data = (FIGtype0_3_data*)buf;
+ fig0_3_data->setSCId(component->packet.id);
+ fig0_3_data->rfa = 0;
+ fig0_3_data->SCCA_flag = 0;
+ // if 0, datagroups are used
+ fig0_3_data->DG_flag = !component->packet.datagroup;
+ fig0_3_data->rfu = 0;
+ fig0_3_data->DSCTy = component->type;
+ fig0_3_data->SubChId = (*subchannel)->id;
+ fig0_3_data->setPacketAddress(component->packet.address);
+ if (m_rti->factumAnalyzer) {
+ fig0_3_data->SCCA = 0;
+ }
+
+ fig0_3_header->Length += 5;
+ buf += 5;
+ remaining -= 5;
+ if (m_rti->factumAnalyzer) {
+ fig0_3_header->Length += 2;
+ buf += 2;
+ remaining -= 2;
+ }
+
+ if (remaining < 0) {
+ etiLog.level(error) <<
+ "can't add FIG 0/3 to FIB, "
+ "too many packet services";
+ throw MuxInitException();
+ }
+ }
+
+ return max_size - remaining;
+}
+
+//=========== FIG 0/17 ===========
+
+FIG0_17::FIG0_17(FIGRuntimeInformation *rti) :
+ m_rti(rti),
+ m_initialised(false)
+{
+}
+
+size_t FIG0_17::fill(uint8_t *buf, size_t max_size)
+{
+ ssize_t remaining = max_size;
+
+ if (not m_initialised) {
+ serviceFIG0_17 = m_rti->ensemble->services.end();
+ }
+
+ auto ensemble = m_rti->ensemble;
+
+ FIGtype0* fig0 = NULL;
+
+ if (serviceFIG0_17 == ensemble->services.end()) {
+ serviceFIG0_17 = ensemble->services.begin();
+ }
+ for (; serviceFIG0_17 != ensemble->services.end();
+ ++serviceFIG0_17) {
+
+ if ( (*serviceFIG0_17)->pty == 0 &&
+ (*serviceFIG0_17)->language == 0) {
+ continue;
+ }
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)buf;
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 17;
+ buf += 2;
+ remaining -= 2;
+ }
+
+ if ((*serviceFIG0_17)->language == 0) {
+ if (remaining < 4) {
+ break;
+ }
+ }
+ else {
+ if (remaining < 5) {
+ break;
+ }
+ }
+
+ auto programme = (FIGtype0_17_programme*)buf;
+ programme->SId = htons((*serviceFIG0_17)->id);
+ programme->SD = 1;
+ programme->PS = 0;
+ programme->L = (*serviceFIG0_17)->language != 0;
+ programme->CC = 0;
+ programme->Rfa = 0;
+ programme->NFC = 0;
+ if ((*serviceFIG0_17)->language == 0) {
+ buf[3] = (*serviceFIG0_17)->pty;
+ fig0->Length += 4;
+ buf += 4;
+ remaining -= 4;
+ }
+ else {
+ buf[3] = (*serviceFIG0_17)->language;
+ buf[4] = (*serviceFIG0_17)->pty;
+ fig0->Length += 5;
+ buf += 5;
+ remaining -= 5;
+ }
+ }
+
+ return max_size - remaining;
+}
+
diff --git a/src/fig/FIG0.h b/src/fig/FIG0.h
new file mode 100644
index 0000000..3f02890
--- /dev/null
+++ b/src/fig/FIG0.h
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __FIG0_H_
+#define __FIG0_H_
+
+#include <cstdint>
+
+#include "fig/FIG.h"
+
+// FIG type 0/0, Multiplex Configuration Info (MCI),
+// Ensemble information
+class FIG0_0 : public IFIG
+{
+ public:
+ FIG0_0(FIGRuntimeInformation* rti) :
+ m_rti(rti) {}
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::FIG0_0; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 0; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+};
+
+// FIG type 0/1, MIC, Sub-Channel Organization,
+// one instance of the part for each subchannel
+class FIG0_1 : public IFIG
+{
+ public:
+ FIG0_1(FIGRuntimeInformation* rti);
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 1; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::vector<dabSubchannel*>::iterator subchannelFIG0_1;
+};
+
+// FIG type 0/2, MCI, Service Organization, one instance of
+// FIGtype0_2_Service for each subchannel
+class FIG0_2 : public IFIG
+{
+ public:
+ FIG0_2(FIGRuntimeInformation* rti);
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 2; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceFIG0_2;
+};
+
+// FIG type 0/3
+// The Extension 3 of FIG type 0 (FIG 0/3) gives additional information about
+// the service component description in packet mode.
+class FIG0_3 : public IFIG
+{
+ public:
+ FIG0_3(FIGRuntimeInformation* rti);
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 3; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+};
+
+// FIG type 0/17
+class FIG0_17 : public IFIG
+{
+ public:
+ FIG0_17(FIGRuntimeInformation* rti);
+ virtual size_t fill(uint8_t *buf, size_t max_size);
+ virtual FIG_rate repetition_rate(void) { return FIG_rate::A; }
+
+ virtual const int figtype(void) const { return 0; }
+ virtual const int figextension(void) const { return 17; }
+
+ private:
+ FIGRuntimeInformation *m_rti;
+ bool m_initialised;
+ std::vector<std::shared_ptr<DabService> >::iterator serviceFIG0_17;
+};
+
+#endif // __FIG0_H_
+
diff --git a/src/fig/FIGCarousel.cpp b/src/fig/FIGCarousel.cpp
new file mode 100644
index 0000000..021dd80
--- /dev/null
+++ b/src/fig/FIGCarousel.cpp
@@ -0,0 +1,199 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Implementation of the FIG carousel to schedule the FIGs into the
+ FIBs.
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "fig/FIGCarousel.h"
+#include <boost/format.hpp>
+#include <iostream>
+#include <deque>
+
+/**************** FIGCarouselElement ****************/
+void FIGCarouselElement::reduce_deadline()
+{
+ deadline -= 24; //ms
+
+ std::cerr << "FIG " << fig->name() <<
+ " deadline decreased to: " << deadline << std::endl;
+
+ if (deadline < 0) {
+ std::cerr << "FIG " << fig->name() <<
+ " has negative scheduling deadline" << std::endl;
+ }
+}
+
+void FIGCarouselElement::increase_deadline()
+{
+ deadline += rate_increment_ms(fig->repetition_rate());
+
+ std::cerr << "FIG " << fig->name() <<
+ " deadline increased to: " << deadline << std::endl;
+}
+
+
+/**************** FIGCarousel *****************/
+
+FIGCarousel::FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble) :
+ m_rti(ensemble),
+ m_fig0_0(&m_rti),
+ m_fig0_1(&m_rti),
+ m_fig0_2(&m_rti),
+ m_fig0_3(&m_rti),
+ m_fig0_17(&m_rti)
+{
+ m_figs_available[std::make_pair(0, 0)] = &m_fig0_0;
+ m_figs_available[std::make_pair(0, 1)] = &m_fig0_1;
+ m_figs_available[std::make_pair(0, 2)] = &m_fig0_2;
+ m_figs_available[std::make_pair(0, 3)] = &m_fig0_3;
+ m_figs_available[std::make_pair(0, 17)] = &m_fig0_17;
+
+ const int fib0 = 0;
+ allocate_fig_to_fib(0, 0, fib0);
+ allocate_fig_to_fib(0, 1, fib0);
+ allocate_fig_to_fib(0, 2, fib0);
+ allocate_fig_to_fib(0, 3, fib0);
+ allocate_fig_to_fib(0, 17, fib0);
+}
+
+void FIGCarousel::set_currentFrame(unsigned long currentFrame)
+{
+ m_rti.currentFrame = currentFrame;
+}
+
+void FIGCarousel::allocate_fig_to_fib(int figtype, int extension, int fib)
+{
+ if (fib < 0 or fib >= 3) {
+ throw std::out_of_range("Invalid FIB");
+ }
+
+ auto fig = m_figs_available.find(std::make_pair(figtype, extension));
+
+ if (fig != m_figs_available.end()) {
+ FIGCarouselElement el;
+ el.fig = fig->second;
+ el.deadline = 0;
+ el.increase_deadline();
+ m_fibs[fib].push_back(el);
+ }
+ else {
+ std::stringstream ss;
+ ss << "No FIG " << figtype << "/" << extension << " available";
+ throw std::runtime_error(ss.str());
+ }
+}
+
+void dumpfib(uint8_t *buf, size_t bufsize) {
+ std::cerr << "FIB ";
+ for (size_t i = 0; i < bufsize; i++) {
+ std::cerr << boost::format("%02x ") % (unsigned int)buf[i];
+ }
+ std::cerr << std::endl;
+}
+
+size_t FIGCarousel::fib0(uint8_t *fib, const size_t bufsize, int framephase) {
+
+ uint8_t *buf = fib;
+
+ std::list<FIGCarouselElement>& figs = m_fibs[0];
+
+ std::cerr << "fib0(framephase=" << framephase << ")" << std::endl;
+
+ std::deque<FIGCarouselElement*> sorted_figs;
+
+ /* Decrement all deadlines */
+ for (auto& fig_el : figs) {
+ fig_el.reduce_deadline();
+
+ sorted_figs.push_back(&fig_el);
+ }
+
+ /* Sort the FIGs in the FIB according to their deadline */
+ std::sort(sorted_figs.begin(), sorted_figs.end(),
+ []( const FIGCarouselElement* left,
+ const FIGCarouselElement* right) {
+ return left->deadline < right->deadline;
+ });
+
+ std::cerr << " Sorted figs:" << std::endl;
+ for (auto& fig_el : sorted_figs) {
+ std::cerr << " " << fig_el->fig->name() <<
+ " d:" << fig_el->deadline << std::endl;
+ }
+
+ /* Data structure to carry FIB */
+ size_t available_size = bufsize;
+
+ /* Take special care for FIG0/0 */
+ auto fig0_0 = find_if(sorted_figs.begin(), sorted_figs.end(),
+ [](const FIGCarouselElement* f) {
+ std::cerr << "Check fig " << f->fig->name() << " " <<
+ rate_increment_ms(f->fig->repetition_rate()) << std::endl;
+ return f->fig->repetition_rate() == FIG_rate::FIG0_0;
+ });
+
+ if (fig0_0 != sorted_figs.end()) {
+ sorted_figs.erase(fig0_0);
+
+ if (framephase == 0) { // TODO check for all TM
+ size_t written = (*fig0_0)->fig->fill(buf, available_size);
+ std::cerr << "Special FIG 0/0 wrote " <<
+ written << " bytes" << std::endl;
+
+ if (written > 0) {
+ available_size -= written;
+ buf += written;
+ (*fig0_0)->increase_deadline();
+ }
+ else {
+ throw std::runtime_error("Failed to write FIG0/0");
+ }
+ }
+ }
+
+
+ /* Fill the FIB with the FIGs, taking the earliest deadline first */
+ while (available_size > 0 and not sorted_figs.empty()) {
+ auto fig_el = sorted_figs[0];
+ size_t written = fig_el->fig->fill(buf, available_size);
+
+ std::cerr << " FIG " << fig_el->fig->name() <<
+ " wrote " << written << " bytes" << std::endl;
+
+ if (written > 0) {
+ available_size -= written;
+ buf += written;
+
+ fig_el->increase_deadline();
+ }
+
+ sorted_figs.pop_front();
+ }
+
+ dumpfib(fib, bufsize);
+
+ return bufsize - available_size;
+}
+
diff --git a/src/fig/FIGCarousel.h b/src/fig/FIGCarousel.h
new file mode 100644
index 0000000..f65bd81
--- /dev/null
+++ b/src/fig/FIGCarousel.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Implementation of the FIG carousel to schedule the FIGs into the
+ FIBs.
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux 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 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux 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 ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __FIG_CAROUSEL_H_
+#define __FIG_CAROUSEL_H_
+
+#include "fig/FIG.h"
+#include "fig/FIG0.h"
+#include <list>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include "MuxElements.h"
+
+struct FIGCarouselElement {
+ IFIG* fig;
+ int deadline; // unit: ms
+
+ void reduce_deadline(void);
+
+ void increase_deadline(void);
+};
+
+class FIGCarousel {
+ public:
+ FIGCarousel(boost::shared_ptr<dabEnsemble> ensemble);
+
+ void set_currentFrame(unsigned long currentFrame);
+
+ void allocate_fig_to_fib(int figtype, int extension, int fib);
+
+ size_t fib0(uint8_t *buf, size_t bufsize, int framephase);
+
+ private:
+ FIGRuntimeInformation m_rti;
+ std::map<std::pair<int, int>, IFIG*> m_figs_available;
+
+ // Each FIB contains a list of carousel elements
+ std::map<int, std::list<FIGCarouselElement> > m_fibs;
+
+ FIG0_0 m_fig0_0;
+ FIG0_1 m_fig0_1;
+ FIG0_2 m_fig0_2;
+ FIG0_3 m_fig0_3;
+ FIG0_17 m_fig0_17;
+};
+
+#endif // __FIG_CAROUSEL_H_
+
diff --git a/src/mpeg.c b/src/mpeg.c
index 16be483..f7aed34 100644
--- a/src/mpeg.c
+++ b/src/mpeg.c
@@ -25,7 +25,7 @@
#include <errno.h>
-const static short bitrateArray[4][4][16] = {
+static const short bitrateArray[4][4][16] = {
{ // MPEG 2.5
{ -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 }, // layer invalid
@@ -69,7 +69,7 @@ const static short bitrateArray[4][4][16] = {
};
-const static int samplingrateArray[4][4] = {
+static const int samplingrateArray[4][4] = {
{ 11025, 12000, 8000, 0 }, // MPEG 2.5
{ -1, -1, -1, -1 }, // MPEG invalid
{ 22050, 24000, 16000, 0 }, // MPEG 2
diff --git a/src/utils.cpp b/src/utils.cpp
index 7c3c516..42937c1 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2013, 2014 Matthias P. Braendli
+ Copyright (C) 2013, 2014, 2015 Matthias P. Braendli
http://mpb.li
*/
/*
@@ -24,6 +24,7 @@
*/
#include <cstring>
#include <iostream>
+#include <boost/shared_ptr.hpp>
#include "DabMux.h"
#include "utils.h"
@@ -73,7 +74,7 @@ void header_message()
fprintf(stderr,
"(Communications Research Centre Canada) All rights reserved.\n\n");
fprintf(stderr,
- "Copyright (C) 2013, 2014 Matthias P. Braendli\n");
+ "Copyright (C) 2013, 2014, 2015 Matthias P. Braendli\n");
fprintf(stderr,
"http://opendigitalradio.org\n\n");
@@ -220,12 +221,12 @@ void printUsage(char *name, FILE* out)
" within the common interleaved frame.\n"
"\n"
" __________________________________________________\n"
- " | CRC-Ensemble | ENSEMBLE\n"
+ " | Ensemble | ENSEMBLE\n"
" |__________________________________________________|\n"
" | | |\n"
" | | |\n"
" _______V______ _______V______ _______V______\n"
- " | CRC-Service1 | | CRC-Service2 | | CRC-Service3 | SERVICES\n"
+ " | Service 1 | | Service 2 | | Service 3 | SERVICES\n"
" |______________| |______________| |______________|\n"
" | | | | |______ |\n"
" | | | | | |\n"
@@ -330,12 +331,12 @@ void printUsage(char *name, FILE* out)
" within the common interleaved frame.\n"
"\n"
" __________________________________________________\n"
- " | CRC-Ensemble | ENSEMBLE\n"
+ " | Ensemble | ENSEMBLE\n"
" |__________________________________________________|\n"
" | | |\n"
" | | |\n"
" _______V______ _______V______ _______V______\n"
- " | CRC-Service1 | | CRC-Service2 | | CRC-Service3 | SERVICES\n"
+ " | Service 1 | | Service 2 | | Service 3 | SERVICES\n"
" |______________| |______________| |______________|\n"
" | | | | |______ |\n"
" | | | | | |\n"
@@ -352,47 +353,40 @@ void printUsage(char *name, FILE* out)
}
#endif
-void printOutputs(vector<dabOutput*>& outputs)
+void printOutputs(vector<boost::shared_ptr<DabOutput> >& outputs)
{
- vector<dabOutput*>::const_iterator output;
int index = 0;
- for (output = outputs.begin(); output != outputs.end(); ++output) {
+ for (auto output : outputs) {
etiLog.log(info, "Output %i", index);
- etiLog.level(info) << " protocol: " <<
- (*output)->outputProto;
-
- etiLog.level(info) << " name: " <<
- (*output)->outputName.c_str();
- // Daboutputfile mangles with outputName, inserting \0 to
- // cut the string in several parts. That doesn't work
- // with stl strings. Thats why the .c_str()
+ etiLog.level(info) << " URI: " <<
+ output->get_info();
++index;
}
}
-void printServices(vector<DabService*>& services)
+void printServices(const vector<shared_ptr<DabService> >& services)
{
- vector<DabService*>::const_iterator current;
int index = 0;
- for (current = services.begin(); current != services.end(); ++current) {
+ for (auto service : services) {
- etiLog.level(info) << "Service " << (*current)->get_rc_name();
- etiLog.level(info) << " label: " << (*current)->label.text();
+ etiLog.level(info) << "Service " << service->get_rc_name();
+ etiLog.level(info) << " label: " <<
+ service->label.long_label();
etiLog.level(info) << " short label: " <<
- (*current)->label.short_label();
+ service->label.short_label();
- etiLog.log(info, " (0x%x)", (*current)->label.flag());
- etiLog.log(info, " id: 0x%lx (%lu)", (*current)->id,
- (*current)->id);
+ etiLog.log(info, " (0x%x)", service->label.flag());
+ etiLog.log(info, " id: 0x%lx (%lu)", service->id,
+ service->id);
- etiLog.log(info, " pty: 0x%x (%u)", (*current)->pty,
- (*current)->pty);
+ etiLog.log(info, " pty: 0x%x (%u)", service->pty,
+ service->pty);
etiLog.log(info, " language: 0x%x (%u)",
- (*current)->language, (*current)->language);
+ service->language, service->language);
++index;
}
}
@@ -413,7 +407,8 @@ void printComponent(DabComponent* component)
{
etiLog.log(info, " service id: %i", component->serviceId);
etiLog.log(info, " subchannel id: %i", component->subchId);
- etiLog.log(info, " label: %s", component->label.text());
+ etiLog.level(info) << " label: " <<
+ component->label.long_label();
etiLog.level(info) << " short label: " <<
component->label.short_label();
@@ -526,12 +521,13 @@ void printSubchannels(vector<dabSubchannel*>& subchannels)
}
}
-void printEnsemble(dabEnsemble* ensemble)
+void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble)
{
etiLog.log(info, "Ensemble");
etiLog.log(info, " id: 0x%lx (%lu)", ensemble->id, ensemble->id);
etiLog.log(info, " ecc: 0x%x (%u)", ensemble->ecc, ensemble->ecc);
- etiLog.log(info, " label: %s", ensemble->label.text());
+ etiLog.level(info) << " label: " <<
+ ensemble->label.long_label();
etiLog.level(info) << " short label: " <<
ensemble->label.short_label();
diff --git a/src/utils.h b/src/utils.h
index ee2340f..1756adf 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -29,6 +29,7 @@
#define _UTILS_H
#include <cstdio>
+#include "MuxElements.h"
time_t getDabTime();
@@ -49,17 +50,18 @@ void printUsageConfigfile(char *name, FILE* out = stderr);
/* The following four utility functions display a
* description of all outputs, services, components
* resp. subchannels*/
-void printOutputs(std::vector<dabOutput*>& outputs);
+void printOutputs(std::vector<boost::shared_ptr<DabOutput> >& outputs);
-void printServices(std::vector<DabService*>& services);
+void printServices(std::vector<std::shared_ptr<DabService> >& services);
void printComponents(std::vector<DabComponent*>& components);
void printSubchannels(std::vector<dabSubchannel*>& subchannels);
/* Print information about the whole ensemble */
-void printEnsemble(dabEnsemble* ensemble);
+void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble);
/* Print detailed component information */
void printComponent(DabComponent* component);
#endif
+
diff --git a/src/zmqinput-keygen.c b/src/zmqinput-keygen.c
index 0169837..8bf61ac 100644
--- a/src/zmqinput-keygen.c
+++ b/src/zmqinput-keygen.c
@@ -58,6 +58,7 @@ int main(int argc, char** argv)
int rc = zmq_curve_keypair(public_key, secret_key);
if (rc != 0) {
fprintf(stderr, "key generation failed\n");
+ return 1;
}
int fdpub = creat(pubkeyfile, S_IRUSR | S_IWUSR);