diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ConfigParser.cpp | 216 | ||||
| -rw-r--r-- | src/DabMux.cpp | 18 | ||||
| -rw-r--r-- | src/DabMux.h | 7 | ||||
| -rw-r--r-- | src/Makefile.am | 22 | ||||
| -rw-r--r-- | src/MuxElements.h | 4 | ||||
| -rw-r--r-- | src/UdpSocket.cpp | 18 | ||||
| -rw-r--r-- | src/UdpSocket.h | 10 | ||||
| -rw-r--r-- | src/dabInput.h | 17 | ||||
| -rw-r--r-- | src/dabInputMpegFile.cpp | 31 | ||||
| -rw-r--r-- | src/input/File.cpp | 443 | ||||
| -rw-r--r-- | src/input/File.h | 88 | ||||
| -rw-r--r-- | src/input/Prbs.cpp (renamed from src/dabInputPrbs.cpp) | 33 | ||||
| -rw-r--r-- | src/input/Prbs.h (renamed from src/dabInputPrbs.h) | 14 | ||||
| -rw-r--r-- | src/input/Udp.cpp | 134 | ||||
| -rw-r--r-- | src/input/Udp.h | 52 | ||||
| -rw-r--r-- | src/input/Zmq.cpp (renamed from src/dabInputZmq.cpp) | 53 | ||||
| -rw-r--r-- | src/input/Zmq.h (renamed from src/dabInputZmq.h) | 48 | ||||
| -rw-r--r-- | src/input/inputs.h | 52 | ||||
| -rw-r--r-- | src/mpeg.c | 26 | ||||
| -rw-r--r-- | src/mpeg.h | 7 | 
20 files changed, 1103 insertions, 190 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 770daa6..1ed1bac 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -48,27 +48,16 @@  #include <map>  #include <cstring>  #include "dabOutput/dabOutput.h" -#include "dabInput.h" +#include "input/inputs.h"  #include "utils.h" -#include "dabInputFile.h" -#include "dabInputFifo.h" -#include "dabInputMpegFile.h" -#include "dabInputMpegFifo.h" -#include "dabInputDabplusFile.h" -#include "dabInputDabplusFifo.h" -#include "dabInputPacketFile.h" -#include "dabInputEnhancedPacketFile.h" -#include "dabInputEnhancedFifo.h" -#include "dabInputUdp.h" -#include "dabInputPrbs.h" -#include "dabInputRawFile.h" -#include "dabInputRawFifo.h" -#include "dabInputDmbFile.h" -#include "dabInputDmbUdp.h" -#include "dabInputZmq.h"  #include "DabMux.h"  #include "ManagementServer.h" +#include "input/Prbs.h" +#include "input/Zmq.h" +#include "input/File.h" +#include "input/Udp.h" +  #ifdef _WIN32  #   pragma warning ( disable : 4103 ) @@ -541,13 +530,13 @@ void parse_ptree(      }  } -static dab_input_zmq_config_t setup_zmq_input( +static Inputs::dab_input_zmq_config_t setup_zmq_input(          const boost::property_tree::ptree &pt,          const std::string& subchanuid)  {      using boost::property_tree::ptree_error; -    dab_input_zmq_config_t zmqconfig; +    Inputs::dab_input_zmq_config_t zmqconfig;      try {          zmqconfig.buffer_size = pt.get<int>("zmq-buffer"); @@ -621,6 +610,7 @@ static void setup_subchannel_from_ptree(DabSubchannel* subchan,      subchan->inputUri = inputUri; +#if OLD_INPUTS // {{{      /* The input is of the old_style type,       * with the struct of function pointers,       * and needs to be a DabInputCompatible @@ -714,7 +704,7 @@ static void setup_subchannel_from_ptree(DabSubchannel* subchan,      } else if (type == "data" and proto == "prbs") {          input_is_old_style = false; -        subchan->input = new DabInputPrbs(); +        subchan->input = make_shared<Inputs::Prbs>();          subchan->type = subchannel_type_t::DataDmb;          subchan->bitrate = DEFAULT_DATA_BITRATE;      } else if (type == "data") { @@ -928,5 +918,191 @@ static void setup_subchannel_from_ptree(DabSubchannel* subchan,          subchan->input = new DabInputCompatible(operations);      }      // else { it's already been created! } +#endif // 0 }}} + +    dabProtection* protection = &subchan->protection; + +    const bool nonblock = pt.get("nonblock", false); +    if (nonblock) { +        etiLog.level(warn) << "The nonblock option is not supported"; +    } + +    if (type == "dabplus" or type == "audio") { +        subchan->type = subchannel_type_t::Audio; +        subchan->bitrate = 0; + +        if (proto == "file") { +            if (type == "audio") { +                subchan->input = make_shared<Inputs::MPEGFile>(); +            } +            else if (type == "dabplus") { +                subchan->input = make_shared<Inputs::RawFile>(); +            } +            else { +                throw logic_error("Incomplete handling of file input"); +            } +        } +        else if (proto == "tcp"  || +                 proto == "epgm" || +                 proto == "ipc") { + +            auto zmqconfig = setup_zmq_input(pt, subchanuid); + +            if (type == "audio") { +                auto inzmq = make_shared<Inputs::ZmqMPEG>(subchanuid, zmqconfig); +                rcs.enrol(inzmq.get()); +                subchan->input = inzmq; +            } +            else if (type == "dabplus") { +                auto inzmq = make_shared<Inputs::ZmqAAC>(subchanuid, zmqconfig); +                rcs.enrol(inzmq.get()); +                subchan->input = inzmq; +            } + +            if (proto == "epgm") { +                etiLog.level(warn) << "Using untested epgm:// zeromq input"; +            } +            else if (proto == "ipc") { +                etiLog.level(warn) << "Using untested ipc:// zeromq input"; +            } +        } +        else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Invalid protocol for " << type << " input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } +    } +    else if (type == "data" and proto == "prbs") { +        subchan->input = make_shared<Inputs::Prbs>(); +        subchan->type = subchannel_type_t::DataDmb; +        subchan->bitrate = DEFAULT_DATA_BITRATE; +    } +    else if (type == "data" or type == "dmb") { +        if (proto == "udp") { +            subchan->input = make_shared<Inputs::Udp>(); +        } else if (proto == "file" or proto == "fifo") { +            subchan->input = make_shared<Inputs::RawFile>(); +        } else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Invalid protocol for data input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } + +        subchan->type = subchannel_type_t::DataDmb; +        subchan->bitrate = DEFAULT_DATA_BITRATE; + +        if (type == "dmb") { +            /* The old dmb input took care of interleaving and Reed-Solomon encoding. This +             * code is unported. +             * See dabInputDmbFile.cpp +             */ +            etiLog.level(warn) << "uid " << subchanuid << " of type Dmb uses RAW input"; +        } +    } +    else if (type == "packet" or type == "enhancedpacket") { +        subchan->type = subchannel_type_t::Packet; +        subchan->bitrate = DEFAULT_PACKET_BITRATE; + +        bool enhanced = (type == "enhancedpacket"); +        subchan->input = make_shared<Inputs::PacketFile>(enhanced); +    } +    else { +        stringstream ss; +        ss << "Subchannel with uid " << subchanuid << " has unknown type!"; +        throw runtime_error(ss.str()); +    } +    subchan->startAddress = 0; + +    if (type == "audio") { +        protection->form = UEP; +        protection->level = 2; +        protection->uep.tableIndex = 0; +    } +    else { +        protection->level = 2; +        protection->form = EEP; +        protection->eep.profile = EEP_A; +    } + +    /* Get bitrate */ +    try { +        subchan->bitrate = pt.get<int>("bitrate"); +        if ((subchan->bitrate & 0x7) != 0) { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Bitrate (" << subchan->bitrate << " not a multiple of 8!"; +            throw runtime_error(ss.str()); +        } +    } +    catch (ptree_error &e) { +        stringstream ss; +        ss << "Error, no bitrate defined for subchannel " << subchanuid; +        throw runtime_error(ss.str()); +    } + +    /* Get id */ +    try { +        subchan->id = hexparse(pt.get<std::string>("id")); +    } +    catch (ptree_error &e) { +        for (int i = 0; i < 64; ++i) { // Find first free subchannel +            vector<DabSubchannel*>::iterator subchannel = getSubchannel(ensemble->subchannels, i); +            if (subchannel == ensemble->subchannels.end()) { +                subchannel = ensemble->subchannels.end() - 1; +                subchan->id = i; +                break; +            } +        } +    } + +    /* Get optional protection profile */ +    string profile = pt.get("protection-profile", ""); + +    if (profile == "EEP_A") { +        protection->form = EEP; +        protection->eep.profile = EEP_A; +    } +    else if (profile == "EEP_B") { +        protection->form = EEP; +        protection->eep.profile = EEP_B; +    } +    else if (profile == "UEP") { +        protection->form = UEP; +    } + +    /* Get protection level */ +    try { +        int level = pt.get<int>("protection"); + +        if (protection->form == UEP) { +            if ((level < 1) || (level > 5)) { +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << +                    ": protection level must be between " +                    "1 to 5 inclusively (current = " << level << " )"; +                throw runtime_error(ss.str()); +            } +        } +        else if (protection->form == EEP) { +            if ((level < 1) || (level > 4)) { +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << +                    ": protection level must be between " +                    "1 to 4 inclusively (current = " << level << " )"; +                throw runtime_error(ss.str()); +            } +        } +        protection->level = level - 1; +    } +    catch (ptree_error &e) { +        stringstream ss; +        ss << "Subchannel with uid " << subchanuid << +            ": protection level undefined!"; +        throw runtime_error(ss.str()); +    }  } diff --git a/src/DabMux.cpp b/src/DabMux.cpp index 32ddb39..3927420 100644 --- a/src/DabMux.cpp +++ b/src/DabMux.cpp @@ -94,22 +94,8 @@ typedef DWORD32 uint32_t;  #   include "Eti.h"  #endif -#include "dabInputFile.h" -#include "dabInputFifo.h" -#include "dabInputMpegFile.h" -#include "dabInputMpegFifo.h" -#include "dabInputDabplusFile.h" -#include "dabInputDabplusFifo.h" -#include "dabInputPacketFile.h" -#include "dabInputEnhancedPacketFile.h" -#include "dabInputEnhancedFifo.h" -#include "dabInputUdp.h" -#include "dabInputPrbs.h" -#include "dabInputRawFile.h" -#include "dabInputRawFifo.h" -#include "dabInputDmbFile.h" -#include "dabInputDmbUdp.h" - +#include "input/Prbs.h" +#include "input/Zmq.h"  #include "dabOutput/dabOutput.h"  #include "dabOutput/edi/TagItems.h" diff --git a/src/DabMux.h b/src/DabMux.h index 5dda759..80b4881 100644 --- a/src/DabMux.h +++ b/src/DabMux.h @@ -25,8 +25,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/>.  */ -#ifndef _DABMUX_H -#define _DABMUX_H +#pragma once  #include <stdint.h>  #include <string> @@ -34,7 +33,7 @@  #include "DabMultiplexer.h"  #include "RemoteControl.h"  #include "dabOutput/dabOutput.h" -#include "dabInput.h" +#include "input/inputs.h"  #include "Eti.h"  #include "MuxElements.h" @@ -44,5 +43,3 @@  #   include <sys/time.h>  #endif -#endif - diff --git a/src/Makefile.am b/src/Makefile.am index 408c86e..084cf7b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,23 +47,11 @@ odr_dabmux_LDADD    =$(ZMQ_LIBS) $(CURL_LIBS) \  odr_dabmux_SOURCES  =DabMux.cpp DabMux.h \  					 DabMultiplexer.cpp DabMultiplexer.h \ -					 dabInput.h dabInput.cpp \ -					 dabInputDabplusFifo.h dabInputDabplusFifo.cpp \ -					 dabInputDabplusFile.h dabInputDabplusFile.cpp \ -					 dabInputDmbFile.h dabInputDmbFile.cpp \ -					 dabInputDmbUdp.h dabInputDmbUdp.cpp \ -					 dabInputEnhancedFifo.h dabInputEnhancedFifo.cpp \ -					 dabInputEnhancedPacketFile.h dabInputEnhancedPacketFile.cpp \ -					 dabInputFifo.h dabInputFifo.cpp \ -					 dabInputFile.h dabInputFile.cpp \ -					 dabInputMpegFifo.h dabInputMpegFifo.cpp \ -					 dabInputMpegFile.h dabInputMpegFile.cpp \ -					 dabInputPacketFile.h dabInputPacketFile.cpp \ -					 dabInputPrbs.h dabInputPrbs.cpp \ -					 dabInputRawFile.h dabInputRawFile.cpp \ -					 dabInputRawFifo.h dabInputRawFifo.cpp \ -					 dabInputUdp.h dabInputUdp.cpp \ -					 dabInputZmq.h dabInputZmq.cpp \ +					 input/inputs.h \ +					 input/Prbs.cpp input/Prbs.h \ +					 input/Zmq.cpp input/Zmq.h \ +					 input/File.cpp input/File.h \ +					 input/Udp.cpp input/Udp.h \  					 dabOutput/dabOutput.h \  					 dabOutput/dabOutputFile.cpp \  					 dabOutput/dabOutputFifo.cpp \ diff --git a/src/MuxElements.h b/src/MuxElements.h index 7056121..7324cdc 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -40,7 +40,7 @@  #include <boost/optional.hpp>  #include <stdint.h>  #include "dabOutput/dabOutput.h" -#include "dabInput.h" +#include "input/inputs.h"  #include "RemoteControl.h"  #include "Eti.h" @@ -295,7 +295,7 @@ public:      std::string uid;      std::string inputUri; -    DabInputBase* input; +    std::shared_ptr<Inputs::InputBase> input;      unsigned char id;      subchannel_type_t type;      uint16_t startAddress; diff --git a/src/UdpSocket.cpp b/src/UdpSocket.cpp index 020e3f5..ccdd7ed 100644 --- a/src/UdpSocket.cpp +++ b/src/UdpSocket.cpp @@ -37,19 +37,19 @@ using namespace std;  UdpSocket::UdpSocket() :      listenSocket(INVALID_SOCKET)  { -    init_sock(0, ""); +    reinit(0, "");  }  UdpSocket::UdpSocket(int port) :      listenSocket(INVALID_SOCKET)  { -    init_sock(port, ""); +    reinit(port, "");  }  UdpSocket::UdpSocket(int port, const std::string& name) :      listenSocket(INVALID_SOCKET)  { -    init_sock(port, name); +    reinit(port, name);  } @@ -67,7 +67,7 @@ int UdpSocket::setBlocking(bool block)      return 0;  } -int UdpSocket::init_sock(int port, const std::string& name) +int UdpSocket::reinit(int port, const std::string& name)  {      if (listenSocket != INVALID_SOCKET) {          ::close(listenSocket); @@ -98,6 +98,16 @@ int UdpSocket::init_sock(int port, const std::string& name)      return 0;  } +int UdpSocket::close() +{ +    if (listenSocket != INVALID_SOCKET) { +        ::close(listenSocket); +    } + +    listenSocket = INVALID_SOCKET; + +    return 0; +}  UdpSocket::~UdpSocket()  { diff --git a/src/UdpSocket.h b/src/UdpSocket.h index 535499e..dfeaac1 100644 --- a/src/UdpSocket.h +++ b/src/UdpSocket.h @@ -80,6 +80,15 @@ class UdpSocket          UdpSocket(const UdpSocket& other) = delete;          const UdpSocket& operator=(const UdpSocket& other) = delete; +        /** reinitialise socket. Close the already open socket, and +         * create a new one +         */ +        int reinit(int port, const std::string& name); + +        /** Close the socket +         */ +        int close(void); +          /** Send an UDP packet.           *  @param packet The UDP packet to be sent. It includes the data and the           *                destination address @@ -111,7 +120,6 @@ class UdpSocket          int setBlocking(bool block);      protected: -        int init_sock(int port, const std::string& name);          /// The address on which the socket is bound.          InetAddress address; diff --git a/src/dabInput.h b/src/dabInput.h index d5444cd..d2c5f49 100644 --- a/src/dabInput.h +++ b/src/dabInput.h @@ -29,8 +29,6 @@  #include "RemoteControl.h"  #include <string> -extern Logger etiLog; -  // TODO replace usage of dabInputOperations by a  // class hierarchy  struct dabInputOperations { @@ -48,19 +46,6 @@ struct dabInputOperations {      bool operator==(const dabInputOperations&);  }; -/* New input object base */ -class DabInputBase { -    public: -        virtual int open(const std::string& name) = 0; -        virtual int readFrame(void* buffer, int size) = 0; -        virtual int setBitrate(int bitrate) = 0; -        virtual int close() = 0; - -        virtual ~DabInputBase() {} -    protected: -        DabInputBase() {} -}; -  /* Wrapper class for old-style dabInputOperations inputs */  class DabInputCompatible : public DabInputBase {      public: @@ -77,7 +62,7 @@ class DabInputCompatible : public DabInputBase {          virtual int setbuf(int size)          { return m_ops.setbuf(args, size); } -        virtual int readFrame(void* buffer, int size) +        virtual int readFrame(uint8_t* buffer, size_t size)          {              if (m_ops.lock) {                  m_ops.lock(args); diff --git a/src/dabInputMpegFile.cpp b/src/dabInputMpegFile.cpp index 804ea29..6f24f32 100644 --- a/src/dabInputMpegFile.cpp +++ b/src/dabInputMpegFile.cpp @@ -47,37 +47,6 @@ struct dabInputOperations dabInputMpegFileOperations = {  }; -#define MPEG_FREQUENCY      -2 -#define MPEG_PADDING        -3 -#define MPEG_COPYRIGHT      -4 -#define MPEG_ORIGINAL       -5 -#define MPEG_EMPHASIS       -6 -int checkDabMpegFrame(void* data) { -    mpegHeader* header = (mpegHeader*)data; -    unsigned long* headerData = (unsigned long*)data; -    if ((*headerData & 0x0f0ffcff) == 0x0004fcff) return 0; -    if ((*headerData & 0x0f0ffcff) == 0x0004f4ff) return 0; -    if (getMpegFrequency(header) != 48000) { -        if (getMpegFrequency(header) != 24000) { -            return MPEG_FREQUENCY; -        } -    } -    if (header->padding != 0) { -        return MPEG_PADDING; -    } -    if (header->copyright != 0) { -        return MPEG_COPYRIGHT; -    } -    if (header->original != 0) { -        return MPEG_ORIGINAL; -    } -    if (header->emphasis != 0) { -        return MPEG_EMPHASIS; -    } -    return -1; -} - -  int dabInputMpegFileRead(dabInputOperations* ops, void* args, void* buffer, int size)  {      dabInputFileData* data = (dabInputFileData*)args; diff --git a/src/input/File.cpp b/src/input/File.cpp new file mode 100644 index 0000000..5c61fd4 --- /dev/null +++ b/src/input/File.cpp @@ -0,0 +1,443 @@ +/* +   Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2016 Matthias P. Braendli +    http://www.opendigitalradio.org + +   */ +/* +   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 <sstream> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#ifndef _WIN32 +#   define O_BINARY 0 +#endif + +#include "input/File.h" +#include "mpeg.h" +#include "ReedSolomon.h" + +namespace Inputs { + +#ifdef _WIN32 +#   pragma pack(push, 1) +#endif +struct packetHeader { +    unsigned char addressHigh:2; +    unsigned char last:1; +    unsigned char first:1; +    unsigned char continuityIndex:2; +    unsigned char packetLength:2; +    unsigned char addressLow; +    unsigned char dataLength:7; +    unsigned char command; +} +#ifdef _WIN32 +#   pragma pack(pop) +#else +__attribute((packed)) +#endif +; + + +int FileBase::open(const std::string& name) +{ +    m_fd = ::open(name.c_str(), O_RDONLY | O_BINARY); +    if (m_fd == -1) { +        std::stringstream ss; +        ss << "Could not open input file " << name << ": " << +            strerror(errno); +        throw std::runtime_error(ss.str()); +    } +    return 0; +} + +int FileBase::setBitrate(int bitrate) +{ +    if (bitrate <= 0) { +        etiLog.log(error, "Invalid bitrate (%i)\n", bitrate); +        return -1; +    } + +    return bitrate; +} + + +int FileBase::close() +{ +    if (m_fd != -1) { +        ::close(m_fd); +        m_fd = -1; +    } +    return 0; +} + +int FileBase::rewind() +{ +    return ::lseek(m_fd, 0, SEEK_SET); +} + +ssize_t FileBase::readFromFile(uint8_t* buffer, size_t size) +{ +    ssize_t ret = read(m_fd, buffer, size); + +    if (ret == -1) { +        etiLog.log(alert, "ERROR: Can't read file\n"); +        perror(""); +        return -1; +    } + +    if (ret < (ssize_t)size) { +        ssize_t sizeOut = ret; +        etiLog.log(info, "reach end of file -> rewinding\n"); +        if (rewind() == -1) { +            etiLog.log(alert, "ERROR: Can't rewind file\n"); +            return -1; +        } + +        ret = read(m_fd, buffer + sizeOut, size - sizeOut); +        if (ret == -1) { +            etiLog.log(alert, "ERROR: Can't read file\n"); +            perror(""); +            return -1; +        } + +        if (ret < (ssize_t)size) { +            etiLog.log(alert, "ERROR: Not enough data in file\n"); +            return -1; +        } +    } + +    return size; +} + +int MPEGFile::readFrame(uint8_t* buffer, size_t size) +{ +    int result; +    bool do_rewind = false; +READ_SUBCHANNEL: +    if (m_parity) { +        result = readData(m_fd, buffer, size, 2); +        m_parity = false; +        return 0; +    } else { +        result = readMpegHeader(m_fd, buffer, size); +        if (result > 0) { +            result = readMpegFrame(m_fd, buffer, size); +            if (result < 0 && getMpegFrequency(buffer) == 24000) { +                m_parity = true; +                result = size; +            } +        } +    } +    switch (result) { +    case MPEG_BUFFER_UNDERFLOW: +        etiLog.log(warn, "data underflow -> frame muted\n"); +        goto MUTE_SUBCHANNEL; +    case MPEG_BUFFER_OVERFLOW: +        etiLog.log(warn, "bitrate too high -> frame muted\n"); +        goto MUTE_SUBCHANNEL; +    case MPEG_FILE_EMPTY: +        if (do_rewind) { +            etiLog.log(error, "file rewinded and still empty " +                    "-> frame muted\n"); +            goto MUTE_SUBCHANNEL; +        } +        else { +            etiLog.log(info, "reach end of file -> rewinding\n"); +            rewind(); +            goto READ_SUBCHANNEL; +        } +    case MPEG_FILE_ERROR: +        etiLog.log(alert, "can't read file (%i) -> frame muted\n", errno); +        perror(""); +        goto MUTE_SUBCHANNEL; +    case MPEG_SYNC_NOT_FOUND: +        etiLog.log(alert, "mpeg sync not found, maybe is not a valid file " +                "-> frame muted\n"); +        goto MUTE_SUBCHANNEL; +    case MPEG_INVALID_FRAME: +        etiLog.log(alert, "file is not a valid mpeg file " +                "-> frame muted\n"); +        goto MUTE_SUBCHANNEL; +    default: +        if (result < 0) { +            etiLog.log(alert, +                    "unknown error (code = %i) -> frame muted\n", +                    result); +MUTE_SUBCHANNEL: +            memset(buffer, 0, size); +        } +        else { +            if (result < (ssize_t)size) { +                etiLog.log(warn, "bitrate too low from file " +                        "-> frame padded\n"); +                memset((char*)buffer + result, 0, size - result); +            } + +            result = checkDabMpegFrame(buffer); +            switch (result) { +            case MPEG_FREQUENCY: +                etiLog.log(error, "file has a frame with an invalid " +                        "frequency: %i, should be 48000 or 24000\n", +                        getMpegFrequency(buffer)); +                break; +            case MPEG_PADDING: +                etiLog.log(warn, +                        "file has a frame with padding bit set\n"); +                break; +            case MPEG_COPYRIGHT: +                result = 0; +                break; +            case MPEG_ORIGINAL: +                result = 0; +                break; +            case MPEG_EMPHASIS: +                etiLog.log(warn, +                        "file has a frame with emphasis bits set\n"); +                break; +            default: +                if (result < 0) { +                    etiLog.log(alert, "mpeg file has an invalid DAB " +                            "mpeg frame (unknown reason: %i)\n", result); +                } +                break; +            } +        } +    } +    return result; +} + +int MPEGFile::setBitrate(int bitrate) +{ +    if (bitrate == 0) { +        uint8_t buffer[4]; + +        if (readFrame(buffer, 4) == 0) { +            bitrate = getMpegBitrate(buffer); +        } +        else { +            bitrate = -1; +        } +        rewind(); +    } +    return bitrate; +} + +int RawFile::readFrame(uint8_t* buffer, size_t size) +{ +    return readFromFile(buffer, size); +} + +PacketFile::PacketFile(bool enhancedPacketMode) +{ +    m_enhancedPacketEnabled = enhancedPacketMode; +} + +int PacketFile::readFrame(uint8_t* buffer, size_t size) +{ +    size_t written = 0; +    int length; +    packetHeader* header; +    int indexRow; +    int indexCol; + +    while (written < size) { +        if (m_enhancedPacketWaiting > 0) { +            *buffer = 192 - m_enhancedPacketWaiting; +            *buffer /= 22; +            *buffer <<= 2; +            *(buffer++) |= 0x03; +            *(buffer++) = 0xfe; +            indexCol = 188; +            indexCol += (192 - m_enhancedPacketWaiting) / 12; +            indexRow = 0; +            indexRow += (192 - m_enhancedPacketWaiting) % 12; +            for (int j = 0; j < 22; ++j) { +                if (m_enhancedPacketWaiting == 0) { +                    *(buffer++) = 0; +                } +                else { +                    *(buffer++) = m_enhancedPacketData[indexRow][indexCol]; +                    if (++indexRow == 12) { +                        indexRow = 0; +                        ++indexCol; +                    } +                    --m_enhancedPacketWaiting; +                } +            } +            written += 24; +            if (m_enhancedPacketWaiting == 0) { +                m_enhancedPacketLength = 0; +            } +        } +        else if (m_packetLength != 0) { +            header = (packetHeader*)(&m_packetData[0]); +            if (written + m_packetLength > (unsigned)size) { +                memset(buffer, 0, 22); +                buffer[22] = 0x60; +                buffer[23] = 0x4b; +                length = 24; +            } +            else if (m_enhancedPacketEnabled) { +                if (m_enhancedPacketLength + m_packetLength > (12 * 188)) { +                    memset(buffer, 0, 22); +                    buffer[22] = 0x60; +                    buffer[23] = 0x4b; +                    length = 24; +                } +                else { +                    std::copy(m_packetData.begin(), +                            m_packetData.begin() + m_packetLength, +                            buffer); +                    length = m_packetLength; +                    m_packetLength = 0; +                } +            } +            else { +                std::copy(m_packetData.begin(), +                        m_packetData.begin() + m_packetLength, +                        buffer); +                length = m_packetLength; +                m_packetLength = 0; +            } + +            if (m_enhancedPacketEnabled) { +                indexCol = m_enhancedPacketLength / 12; +                indexRow = m_enhancedPacketLength % 12;     // TODO Check if always 0 +                for (int j = 0; j < length; ++j) { +                    m_enhancedPacketData[indexRow][indexCol] = buffer[j]; +                    if (++indexRow == 12) { +                        indexRow = 0; +                        ++indexCol; +                    } +                } +                m_enhancedPacketLength += length; +                if (m_enhancedPacketLength >= (12 * 188)) { +                    m_enhancedPacketLength = (12 * 188); +                    ReedSolomon encoder(204, 188); +                    for (int j = 0; j < 12; ++j) { +                        encoder.encode(&m_enhancedPacketData[j][0], 188); +                    } +                    m_enhancedPacketWaiting = 192; +                } +            } +            written += length; +            buffer += length; +        } +        else { +            int nbBytes = readFromFile(buffer, 3); +            header = (packetHeader*)buffer; +            if (nbBytes == -1) { +                if (errno == EAGAIN) goto END_PACKET; +                perror("Packet file"); +                return -1; +            } +            else if (nbBytes == 0) { +                if (rewind() == -1) { +                    goto END_PACKET; +                } +                continue; +            } +            else if (nbBytes < 3) { +                etiLog.log(error, +                        "Error while reading file for packet header; " +                        "read %i out of 3 bytes\n", nbBytes); +                break; +            } + +            length = header->packetLength * 24 + 24; +            if (written + length > size) { +                memcpy(&m_packetData[0], header, 3); +                readFromFile(&m_packetData[3], length - 3); +                m_packetLength = length; +                continue; +            } + +            if (m_enhancedPacketEnabled) { +                if (m_enhancedPacketLength + length > (12 * 188)) { +                    memcpy(&m_packetData[0], header, 3); +                    readFromFile(&m_packetData[3], length - 3); +                    m_packetLength = length; +                    continue; +                } +            } + +            nbBytes = readFromFile(buffer + 3, length - 3); +            if (nbBytes == -1) { +                perror("Packet file"); +                return -1; +            } +            else if (nbBytes == 0) { +                etiLog.log(info, +                        "Packet header read, but no data!\n"); +                if (rewind() == -1) { +                    goto END_PACKET; +                } +                continue; +            } +            else if (nbBytes < length - 3) { +                etiLog.log(error, "Error while reading packet file; " +                        "read %i out of %i bytes\n", nbBytes, length - 3); +                break; +            } + +            if (m_enhancedPacketEnabled) { +                indexCol = m_enhancedPacketLength / 12; +                indexRow = m_enhancedPacketLength % 12;     // TODO Check if always 0 +                for (int j = 0; j < length; ++j) { +                    m_enhancedPacketData[indexRow][indexCol] = buffer[j]; +                    if (++indexRow == 12) { +                        indexRow = 0; +                        ++indexCol; +                    } +                } +                m_enhancedPacketLength += length; +                if (m_enhancedPacketLength >= (12 * 188)) { +                    if (m_enhancedPacketLength > (12 * 188)) { +                        etiLog.log(error, +                                "Error, too much enhanced packet data!\n"); +                    } +                    ReedSolomon encoder(204, 188); +                    for (int j = 0; j < 12; ++j) { +                        encoder.encode(&m_enhancedPacketData[j][0], 188); +                    } +                    m_enhancedPacketWaiting = 192; +                } +            } +            written += length; +            buffer += length; +        } +    } +END_PACKET: +    while (written < size) { +        memset(buffer, 0, 22); +        buffer[22] = 0x60; +        buffer[23] = 0x4b; +        buffer += 24; +        written += 24; +    } +    return written; +} + +}; diff --git a/src/input/File.h b/src/input/File.h new file mode 100644 index 0000000..bf99748 --- /dev/null +++ b/src/input/File.h @@ -0,0 +1,88 @@ +/* +   Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2016 Matthias P. Braendli +    http://www.opendigitalradio.org + +   */ +/* +   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/>. +   */ + +#pragma once + +#include <vector> +#include <array> +#include <string> +#include <stdint.h> +#include "input/inputs.h" +#include "ManagementServer.h" + +namespace Inputs { + +class FileBase : public InputBase { +    public: +        virtual int open(const std::string& name); +        virtual int readFrame(uint8_t* buffer, size_t size) = 0; +        virtual int setBitrate(int bitrate); +        virtual int close(); + +        /* Rewind the file +         * Returns -1 on failure, 0 on success +         */ +        virtual int rewind(); +    protected: +        /* Read len bytes from the file into buf, and return +         * the number of bytes read, or -1 in case of error. +         */ +        virtual ssize_t readFromFile(uint8_t* buf, size_t len); + +        // We use unix open() instead of fopen() because +        // we might want to do non-blocking I/O in the future +        int m_fd = -1; +}; + +class MPEGFile : public FileBase { +    public: +        virtual int readFrame(uint8_t* buffer, size_t size); +        virtual int setBitrate(int bitrate); + +    private: +        bool m_parity = false; +}; + +class RawFile : public FileBase { +    public: +        virtual int readFrame(uint8_t* buffer, size_t size); +}; + +class PacketFile : public FileBase { +    public: +        PacketFile(bool enhancedPacketMode); +        virtual int readFrame(uint8_t* buffer, size_t size); + +    protected: +        std::array<uint8_t,96> m_packetData; +        size_t m_packetLength; + +        bool m_enhancedPacketEnabled = false; +        std::array<std::array<uint8_t, 204>,12> m_enhancedPacketData; +        size_t m_enhancedPacketWaiting; +        size_t m_enhancedPacketLength; +}; + +}; diff --git a/src/dabInputPrbs.cpp b/src/input/Prbs.cpp index 2678668..607ce9f 100644 --- a/src/dabInputPrbs.cpp +++ b/src/input/Prbs.cpp @@ -26,7 +26,7 @@     along with ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>.     */ -#include "dabInputPrbs.h" +#include "input/Prbs.h"  #include <stdexcept>  #include <sstream> @@ -38,23 +38,31 @@  using namespace std; +namespace Inputs { +  // ETS 300 799 Clause G.2.1  // Preferred polynomial is G(x) = x^20 + x^17 + 1  const uint32_t PRBS_DEFAULT_POLY = (1 << 19) | (1 << 16) | 1; -int DabInputPrbs::open(const string& name) +int Prbs::open(const string& name)  { -    if (name.empty()) { +    if (name.substr(0, 7) != "prbs://") { +        throw logic_error("Invalid PRBS name"); +    } + +    const string& url_polynomial = name.substr(7); + +    if (url_polynomial.empty()) {          m_prbs.setup(PRBS_DEFAULT_POLY);      }      else { -        if (name[0] != ':') { +        if (url_polynomial[0] != ':') {              throw invalid_argument(                      "Invalid PRBS address format. "                      "Must be prbs://:polynomial.");          } -        const string poly_str = name.substr(1); +        const string poly_str = url_polynomial.substr(1);          long polynomial = hexparse(poly_str); @@ -69,30 +77,29 @@ int DabInputPrbs::open(const string& name)      return 0;  } -int DabInputPrbs::readFrame(void* buffer, int size) +int Prbs::readFrame(uint8_t* buffer, size_t size)  { -    unsigned char* cbuffer = reinterpret_cast<unsigned char*>(buffer); - -    for (int i = 0; i < size; ++i) { -        cbuffer[i] = m_prbs.step(); +    for (size_t i = 0; i < size; ++i) { +        buffer[i] = m_prbs.step();      }      return size;  } -int DabInputPrbs::setBitrate(int bitrate) +int Prbs::setBitrate(int bitrate)  {      return bitrate;  } -int DabInputPrbs::close() +int Prbs::close()  {      return 0;  } -int DabInputPrbs::rewind() +int Prbs::rewind()  {      m_prbs.rewind();      return 0;  } +}; diff --git a/src/dabInputPrbs.h b/src/input/Prbs.h index 95c5e25..3b2b7d4 100644 --- a/src/dabInputPrbs.h +++ b/src/input/Prbs.h @@ -28,19 +28,17 @@  #pragma once -#ifdef HAVE_CONFIG_H -#  include "config.h" -#endif -  #include <string> -#include "dabInput.h" +#include "input/inputs.h"  #include "prbs.h" -class DabInputPrbs : public DabInputBase { +namespace Inputs { + +class Prbs : public InputBase {      public:          virtual int open(const std::string& name); -        virtual int readFrame(void* buffer, int size); +        virtual int readFrame(uint8_t* buffer, size_t size);          virtual int setBitrate(int bitrate);          virtual int close(); @@ -50,3 +48,5 @@ class DabInputPrbs : public DabInputBase {          PrbsGenerator m_prbs;  }; +}; + diff --git a/src/input/Udp.cpp b/src/input/Udp.cpp new file mode 100644 index 0000000..a238d9b --- /dev/null +++ b/src/input/Udp.cpp @@ -0,0 +1,134 @@ +/* +   Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2016 +   Matthias P. Braendli, matthias.braendli@mpb.li + +    http://www.opendigitalradio.org +   */ +/* +   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 "input/Udp.h" + +#include <stdexcept> +#include <sstream> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <errno.h> +#include "utils.h" + +using namespace std; + +namespace Inputs { + +int Udp::open(const std::string& name) +{ +    // Skip the udp:// part if it is present +    const string endpoint = (name.substr(0, 6) == "udp://") ? +        name.substr(6) : name; + +    // The endpoint should be address:port +    const auto colon_pos = endpoint.find_first_of(":"); +    if (colon_pos == string::npos) { +        stringstream ss; +        ss << "'" << name << +                " is an invalid format for udp address: " +                "expected [udp://]address:port"; +        throw invalid_argument(ss.str()); +    } + +    const auto address = endpoint.substr(0, colon_pos); +    const auto port_str = endpoint.substr(colon_pos + 1); + +    const long port = strtol(port_str.c_str(), nullptr, 10); + +    if ((port == LONG_MIN or port == LONG_MAX) and errno == ERANGE) { +        throw out_of_range("udp input: port out of range"); +    } +    else if (port == 0 and errno != 0) { +        stringstream ss; +        ss << "udp input port parse error: " << strerror(errno); +        throw invalid_argument(ss.str()); +    } + +    if (port == 0) { +        throw out_of_range("can't use port number 0 in udp address"); +    } + +    if (m_sock.reinit(port, address) == -1) { +        stringstream ss; +        ss << "Could not init UDP socket: " << inetErrMsg; +        throw runtime_error(ss.str()); +    } + +    if (m_sock.setBlocking(false) == -1) { +        stringstream ss; +        ss << "Could not set non-blocking UDP socket: " << inetErrMsg; +        throw runtime_error(ss.str()); +    } + +    return 0; +} + +int Udp::readFrame(uint8_t* buffer, size_t size) +{ +    uint8_t* data = reinterpret_cast<uint8_t*>(buffer); + +    // Regardless of buffer contents, try receiving data. +    UdpPacket packet; +    int ret = m_sock.receive(packet); + +    if (ret == -1) { +        stringstream ss; +        ss << "Could not read from UDP socket: " << inetErrMsg; +        throw runtime_error(ss.str()); +    } + +    std::copy(packet.getData(), packet.getData() + packet.getSize(), +            back_inserter(m_buffer)); + +    // Take data from the buffer if it contains enough data, +    // in any case write the buffer +    if (m_buffer.size() >= (size_t)size) { +        std::copy(m_buffer.begin(), m_buffer.begin() + size, data); +    } +    else { +        memset(data, 0x0, size); +    } + +    return size; +} + +int Udp::setBitrate(int bitrate) +{ +    if (bitrate <= 0) { +        etiLog.log(error, "Invalid bitrate (%i)\n", bitrate); +        return -1; +    } + +    return bitrate; +} + +int Udp::close() +{ +    return m_sock.close(); +} + +}; diff --git a/src/input/Udp.h b/src/input/Udp.h new file mode 100644 index 0000000..379dbf3 --- /dev/null +++ b/src/input/Udp.h @@ -0,0 +1,52 @@ +/* +   Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2016 +   Matthias P. Braendli, matthias.braendli@mpb.li + +    http://www.opendigitalradio.org +   */ +/* +   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/>. +   */ + +#pragma once + +#include <string> +#include <vector> +#include "input/inputs.h" +#include "UdpSocket.h" + +namespace Inputs { + +class Udp : public InputBase { +    public: +        virtual int open(const std::string& name); +        virtual int readFrame(uint8_t* buffer, size_t size); +        virtual int setBitrate(int bitrate); +        virtual int close(); + +    private: +        UdpSocket m_sock; + +        // The content of the UDP packets gets written into the +        // buffer, and the UDP packet boundaries disappear there. +        std::vector<uint8_t> m_buffer; +}; + +}; + diff --git a/src/dabInputZmq.cpp b/src/input/Zmq.cpp index 93f1ea3..a5601fa 100644 --- a/src/dabInputZmq.cpp +++ b/src/input/Zmq.cpp @@ -2,7 +2,7 @@     Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2013, 2014 Matthias P. Braendli +   Copyright (C) 2016 Matthias P. Braendli      http://www.opendigitalradio.org     ZeroMQ input. see www.zeromq.org for more info @@ -39,17 +39,8 @@     along with ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>.     */ -#include "dabInput.h" -#include "dabInputZmq.h" -#include "PcDebug.h" - -#ifdef HAVE_CONFIG_H -#   include "config.h" -#endif +#include "input/Zmq.h" -#ifdef HAVE_INPUT_ZEROMQ - -#include "zmq.hpp"  #include <cstdio>  #include <cstdlib>  #include <list> @@ -58,22 +49,28 @@  #include <string>  #include <sstream>  #include <limits.h> +#include "PcDebug.h" +#include "Log.h"  #ifdef __MINGW32__  #   define bzero(s, n) memset(s, 0, n)  #endif +namespace Inputs { +  using namespace std;  int readkey(string& keyfile, char* key)  { -    int fd = open(keyfile.c_str(), O_RDONLY); -    if (fd < 0) -        return fd; -    int ret = read(fd, key, CURVE_KEYLEN); -    close(fd); -    if (ret < 0) { -        return ret; +    FILE* fd = fopen(keyfile.c_str(), "r"); +    if (fd == nullptr) { +        return -1; +    } + +    int ret = fread(key, CURVE_KEYLEN, 1, fd); +    fclose(fd); +    if (ret == 0) { +        return -1;      }      /* It needs to be zero-terminated */ @@ -89,7 +86,7 @@ int readkey(string& keyfile, char* key)   * keys to the socket, and finally bind the socket   * to the new address   */ -void DabInputZmqBase::rebind() +void ZmqBase::rebind()  {      if (! m_zmq_sock_bound_to.empty()) {          try { @@ -223,7 +220,7 @@ void DabInputZmqBase::rebind()      }  } -int DabInputZmqBase::open(const std::string& inputUri) +int ZmqBase::open(const std::string& inputUri)  {      m_inputUri = inputUri; @@ -236,20 +233,20 @@ int DabInputZmqBase::open(const std::string& inputUri)      return 0;  } -int DabInputZmqBase::close() +int ZmqBase::close()  {      m_zmq_sock.close();      return 0;  } -int DabInputZmqBase::setBitrate(int bitrate) +int ZmqBase::setBitrate(int bitrate)  {      m_bitrate = bitrate;      return bitrate; // TODO do a nice check here  }  // size corresponds to a frame size. It is constant for a given bitrate -int DabInputZmqBase::readFrame(void* buffer, int size) +int ZmqBase::readFrame(uint8_t* buffer, size_t size)  {      int rc; @@ -340,7 +337,7 @@ int DabInputZmqBase::readFrame(void* buffer, int size)  /******** MPEG input *******/  // Read a MPEG frame from the socket, and push to list -int DabInputZmqMPEG::readFromSocket(size_t framesize) +int ZmqMPEG::readFromSocket(size_t framesize)  {      bool messageReceived = false;      zmq::message_t msg; @@ -410,7 +407,7 @@ int DabInputZmqMPEG::readFromSocket(size_t framesize)  // Read a AAC+ superframe from the socket, cut it into five frames,  // and push to list -int DabInputZmqAAC::readFromSocket(size_t framesize) +int ZmqAAC::readFromSocket(size_t framesize)  {      bool messageReceived;      zmq::message_t msg; @@ -496,7 +493,7 @@ int DabInputZmqAAC::readFromSocket(size_t framesize)  /********* REMOTE CONTROL ***********/ -void DabInputZmqBase::set_parameter(const string& parameter, +void ZmqBase::set_parameter(const string& parameter,          const string& value)  {      if (parameter == "buffer") { @@ -576,7 +573,7 @@ void DabInputZmqBase::set_parameter(const string& parameter,      }  } -const string DabInputZmqBase::get_parameter(const string& parameter) const +const string ZmqBase::get_parameter(const string& parameter) const  {      stringstream ss;      if (parameter == "buffer") { @@ -615,5 +612,5 @@ const string DabInputZmqBase::get_parameter(const string& parameter) const  } -#endif +}; diff --git a/src/dabInputZmq.h b/src/input/Zmq.h index 351fb07..8d729e0 100644 --- a/src/dabInputZmq.h +++ b/src/input/Zmq.h @@ -2,7 +2,7 @@     Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2013, 2014 Matthias P. Braendli +   Copyright (C) 2016 Matthias P. Braendli      http://www.opendigitalradio.org     ZeroMQ input. see www.zeromq.org for more info @@ -41,48 +41,37 @@     along with ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>.     */ -#ifndef DAB_INPUT_ZMQ_H -#define DAB_INPUT_ZMQ_H - -#ifdef HAVE_CONFIG_H -#   include "config.h" -#endif - -#ifdef HAVE_INPUT_ZEROMQ +#pragma once  #include <list>  #include <string>  #include <stdint.h>  #include "zmq.hpp" -#include "dabInput.h" +#include "input/inputs.h"  #include "ManagementServer.h" +namespace Inputs { +  /* The frame_buffer contains DAB logical frames as defined in   * TS 102 563, section 6.   * Five elements of this buffer make one AAC superframe (120ms audio)   */ -// Number of elements to prebuffer before starting the pipeline -#define INPUT_ZMQ_DEF_PREBUFFERING (5*4) // 480ms - -// Default frame_buffer size in number of elements -#define INPUT_ZMQ_DEF_BUFFER_SIZE (5*8) // 960ms -  // Minimum frame_buffer size in number of elements  // This is one AAC superframe, but you probably don't want to  // go that low anyway. -#define INPUT_ZMQ_MIN_BUFFER_SIZE (5*1) // 120ms +const size_t INPUT_ZMQ_MIN_BUFFER_SIZE = 5*1; // 120ms  // Maximum frame_buffer size in number of elements  // One minute is clearly way over what everybody would  // want. -#define INPUT_ZMQ_MAX_BUFFER_SIZE (5*500) // 60s +const size_t INPUT_ZMQ_MAX_BUFFER_SIZE = 5*500; // 60s  /* The ZeroMQ Curve key is 40 bytes long in Z85 representation   *   * But we need to store it as zero-terminated string.   */ -#define CURVE_KEYLEN 40 +const size_t CURVE_KEYLEN = 40;  /* helper to invalidate a key */  #define INVALIDATE_KEY(k) memset(k, 0, CURVE_KEYLEN+1) @@ -156,9 +145,9 @@ struct zmq_frame_header_t  #define ZMQ_FRAME_DATA(f) ( ((uint8_t*)f)+sizeof(zmq_frame_header_t) ) -class DabInputZmqBase : public DabInputBase, public RemoteControllable { +class ZmqBase : public InputBase, public RemoteControllable {      public: -        DabInputZmqBase(const std::string name, +        ZmqBase(const std::string name,                  dab_input_zmq_config_t config)              : RemoteControllable(name),              m_zmq_context(1), @@ -192,7 +181,7 @@ class DabInputZmqBase : public DabInputBase, public RemoteControllable {              }          virtual int open(const std::string& inputUri); -        virtual int readFrame(void* buffer, int size); +        virtual int readFrame(uint8_t* buffer, size_t size);          virtual int setBitrate(int bitrate);          virtual int close(); @@ -238,11 +227,11 @@ class DabInputZmqBase : public DabInputBase, public RemoteControllable {          size_t m_prebuf_current;  }; -class DabInputZmqMPEG : public DabInputZmqBase { +class ZmqMPEG : public ZmqBase {      public: -        DabInputZmqMPEG(const std::string name, +        ZmqMPEG(const std::string name,                  dab_input_zmq_config_t config) -            : DabInputZmqBase(name, config) { +            : ZmqBase(name, config) {                  RC_ADD_PARAMETER(buffer,                          "Size of the input buffer [mpeg frames]"); @@ -254,11 +243,11 @@ class DabInputZmqMPEG : public DabInputZmqBase {          virtual int readFromSocket(size_t framesize);  }; -class DabInputZmqAAC : public DabInputZmqBase { +class ZmqAAC : public ZmqBase {      public: -        DabInputZmqAAC(const std::string name, +        ZmqAAC(const std::string name,                  dab_input_zmq_config_t config) -            : DabInputZmqBase(name, config) { +            : ZmqBase(name, config) {                  RC_ADD_PARAMETER(buffer,                          "Size of the input buffer [aac superframes]"); @@ -270,7 +259,6 @@ class DabInputZmqAAC : public DabInputZmqBase {          virtual int readFromSocket(size_t framesize);  }; -#endif // HAVE_INPUT_ZMQ +}; -#endif // DAB_INPUT_ZMQ_H diff --git a/src/input/inputs.h b/src/input/inputs.h new file mode 100644 index 0000000..bfb1fb6 --- /dev/null +++ b/src/input/inputs.h @@ -0,0 +1,52 @@ +/* +   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Her Majesty the Queen in +   Right of Canada (Communications Research Center Canada) + +   Copyright (C) 2016 +   Matthias P. Braendli, matthias.braendli@mpb.li + +    http://www.opendigitalradio.org +   */ +/* +   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/>. +   */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#   include "config.h" +#endif +#include "Log.h" +#include "RemoteControl.h" +#include <string> + +namespace Inputs { + +/* New input object base */ +class InputBase { +    public: +        virtual int open(const std::string& name) = 0; +        virtual int readFrame(uint8_t* buffer, size_t size) = 0; +        virtual int setBitrate(int bitrate) = 0; +        virtual int close() = 0; + +        virtual ~InputBase() {} +    protected: +        InputBase() {} +}; + +}; + @@ -219,3 +219,29 @@ int readMpegFrame(int file, void* data, int size)      }      return framelength;  } + +int checkDabMpegFrame(void* data) { +    mpegHeader* header = (mpegHeader*)data; +    unsigned long* headerData = (unsigned long*)data; +    if ((*headerData & 0x0f0ffcff) == 0x0004fcff) return 0; +    if ((*headerData & 0x0f0ffcff) == 0x0004f4ff) return 0; +    if (getMpegFrequency(header) != 48000) { +        if (getMpegFrequency(header) != 24000) { +            return MPEG_FREQUENCY; +        } +    } +    if (header->padding != 0) { +        return MPEG_PADDING; +    } +    if (header->emphasis != 0) { +        return MPEG_EMPHASIS; +    } +    if (header->copyright != 0) { +        return MPEG_COPYRIGHT; +    } +    if (header->original != 0) { +        return MPEG_ORIGINAL; +    } +    return -1; +} + @@ -75,6 +75,13 @@ ssize_t readData(int file, void* data, size_t size, unsigned int tries);  int readMpegHeader(int file, void* data, int size);  int readMpegFrame(int file, void* data, int size); +#define MPEG_FREQUENCY      -2 +#define MPEG_PADDING        -3 +#define MPEG_COPYRIGHT      -4 +#define MPEG_ORIGINAL       -5 +#define MPEG_EMPHASIS       -6 +int checkDabMpegFrame(void* data); +  #ifdef __cplusplus  }  #endif  | 
