aboutsummaryrefslogtreecommitdiffstats
path: root/src/AVTEDIInput.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/AVTEDIInput.cpp')
-rw-r--r--src/AVTEDIInput.cpp738
1 files changed, 0 insertions, 738 deletions
diff --git a/src/AVTEDIInput.cpp b/src/AVTEDIInput.cpp
deleted file mode 100644
index f8a9e60..0000000
--- a/src/AVTEDIInput.cpp
+++ /dev/null
@@ -1,738 +0,0 @@
-/* ------------------------------------------------------------------
- * Copyright (C) 2017 AVT GmbH - Fabien Vercasson
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- * -------------------------------------------------------------------
- */
-
-#include "AVTEDIInput.h"
-#include <cstring>
-#include <cstdio>
-#include <stdint.h>
-#include <limits.h>
-
-#include "crc.h"
-#include "OrderedQueue.h"
-
-extern "C" {
-#include <fec.h>
-}
-
-#define SUBCH_QUEUE_SIZE (50) /* In 24ms frames. Intermediate buffer */
-
-#define RS_DECODE 1 /* Set to 0 to disable rs decoding */
-#define RS_TEST1 0 /* Remove one fragment on each PFT */
-#define RS_TEST2 0 /* Remove regularily fragments */
-#define RS_TEST2_NBDROP 3 /* For RS_TEST2, nb packet remove on each time */
-
-#define PRINTF(fmt, A...) fprintf(stderr, fmt, ##A)
-//#define PRINTF(x ...)
-#define INFO(fmt, A...) fprintf(stderr, "AVT EDI: " fmt, ##A)
-//#define DEBUG(fmt, A...) fprintf(stderr, "AVT EDI: " fmt, ##A)
-#define DEBUG(X...)
-#define ERROR(fmt, A...) fprintf(stderr, "AVT EDI: ERROR " fmt, ##A)
-
-static int hideFirstPFTErrors = 30; /* Hide the errors that can occurs on */
- /* the first PFT, as they are likely incomplete */
-
-#define TAG_NAME_DETI (('d'<<24)|('e'<<16)|('t'<<8)|('i'))
-#define TAG_NAME_EST (('e'<<24)|('s'<<16)|('t'<<8))
-
-/* ------------------------------------------------------------------
-static void _dump(const uint8_t* buf, int size)
-{
- for( int i = 0 ; i < size ; i ++)
- {
- PRINTF("%02X ", buf[i]);
- if( (i+1) % 16 == 0 ) PRINTF("\n");
- }
- if( size % 16 != 0 ) PRINTF("\n");
-}
-*/
-
-/* ------------------------------------------------------------------
- *
- */
-static uint32_t unpack2(const uint8_t* buf)
-{
- return( buf[0] << 8 |
- buf[1]);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-static uint32_t unpack3(const uint8_t* buf)
-{
- return( buf[0] << 16 |
- buf[1] << 8 |
- buf[2]);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-static uint32_t unpack4(const uint8_t* buf)
-{
- return( buf[0] << 24 |
- buf[1] << 16 |
- buf[2] << 8 |
- buf[3]);
-}
-
-/* ------------------------------------------------------------------
- * bitpos 0 : left most bit.
- *
- */
-static uint32_t unpack1bit(uint8_t byte, int bitpos)
-{
- return (byte & 1 << (7-bitpos)) > (7-bitpos);
-}
-
-
-/* ------------------------------------------------------------------
- *
- */
-static bool _checkCRC(uint8_t* buf, size_t length)
-{
- if (length <= 2) return false;
-
- uint16_t CRC = unpack2(buf+length-2);
-
- uint16_t crc = 0xffff;
- crc = crc16(crc, buf, length-2);
- crc ^= 0xffff;
-
- return (CRC == crc);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-AVTEDIInput::AVTEDIInput(uint32_t fragmentTimeoutMs)
- : _fragmentTimeoutMs(fragmentTimeoutMs)
-{
- _subChannelQueue = new OrderedQueue(5000, SUBCH_QUEUE_SIZE);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-AVTEDIInput::~AVTEDIInput()
-{
- PFTIterator it = _pft.begin();
- while (it != _pft.end()) {
- delete it->second;
- it++;
- }
- delete _subChannelQueue;
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool AVTEDIInput::pushData(uint8_t* buf, size_t length)
-{
- bool identified = false;
-
- if (length >= 12 && buf[0] == 'P' && buf[1] == 'F')
- {
-
-#if RS_TEST2
- static int count=0;
- if (++count%1421<RS_TEST2_NBDROP)
- identified = true;
- else
-#endif // RS_TEST2
- identified = _pushPFTFrag(buf, length);
-
- }
- else if (length >= 10 && buf[0] == 'A' && buf[1] == 'F')
- {
- identified = _pushAF(buf, length, false);
- }
- return identified;
-}
-
-/* ------------------------------------------------------------------
- *
- */
-size_t AVTEDIInput::popFrame(std::vector<uint8_t>& data, int32_t& frameNumber)
-{
- return _subChannelQueue->pop(data, &frameNumber);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool AVTEDIInput::_pushPFTFrag(uint8_t* buf, size_t length)
-{
- PFTFrag* frag = new PFTFrag(buf, length);
- bool isValid = frag->isValid();
- if (!isValid) {
- delete frag;
- } else {
- // Find PFT
- PFT* pft = NULL;
- PFTIterator it = _pft.find(frag->Pseq());
- if (it != _pft.end()) {
- pft = it->second;
- } else {
- // create PFT is new
- pft = new PFT(frag->Pseq(), frag->Fcount());
- if (_pft.insert(std::make_pair(frag->Pseq(), pft)).second == false)
- {
- // Not inserted
- delete pft;
- pft = NULL;
- }
- it = _pft.find(frag->Pseq());
- }
-
- if (pft) {
- // Add frag to PFT
- pft->pushPFTFrag(frag);
-
- // If the PFT is complete, extract the AF
- if (pft->complete()) {
- std::vector<uint8_t> af;
- bool ok = pft->extractAF(af);
-
- if (ok) {
- _pushAF(af.data(), af.size(), ok);
- } else {
- ERROR("AF Frame Corrupted, Size=%zu\n", af.size());
- //_dump(af.data(), 10);
- }
-
- _pft.erase(it);
- delete pft;
- }
- }
- }
-
- // Check old incomplete PFT to either try to extract AF or discard it
- // TODO
- const auto now = std::chrono::steady_clock::now();
- const auto timeout_duration = std::chrono::milliseconds(_fragmentTimeoutMs);
-
- PFTIterator it = _pft.begin();
- while (it != _pft.end()) {
- PFT* pft = it->second;
- bool erased = false;
- if (pft) {
- const auto creation = pft->creation();
- const auto diff = now - creation;
- if (diff > timeout_duration) {
- //DEBUG("PFT timeout\n");
- std::vector<uint8_t> af;
- bool ok = pft->extractAF(af);
- if (ok) {
- _pushAF(af.data(), af.size(), ok);
- } else {
- //ERROR("AF Frame CorruptedSize=%zu\n", af.size());
- //_dump(af.data(), 10);
- }
-
- it = _pft.erase(it);
- delete pft;
- erased = true;
- }
- }
- if (!erased) ++it;
- }
-
- return isValid;
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool AVTEDIInput::_pushAF(uint8_t* buf, size_t length, bool checked)
-{
- bool ok = checked;
-
- // Check the AF integrity
- if (!ok) {
- // EDI specific, must have a CRC.
- if (length >= 12) {
- ok = (buf[0] == 'A' && buf[1] == 'F');
- ok &= _checkCRC(buf, length);
- }
- }
-
- int index = 0;
-
- index += 2;
- uint32_t LEN = unpack4(buf+index); index += 4;
- ok = (LEN == length-12);
- //uint32_t SEQ = unpack2(buf+index); index += 2;
-
- if (ok) {
- uint32_t CF = unpack1bit(buf[index], 0);
- uint32_t MAJ = (buf[index]&0x70) >> 4;
- uint32_t MIN = (buf[index]&0x0F);
- index += 1;
- uint32_t PT = buf[index]; index += 1;
-
- // EDI specific
- ok = (CF == 1 && PT == 'T' && MAJ == 1 && MIN == 0);
-
-// DEBUG("AF Header: LEN=%u SEQ=%u CF=%u MAJ=%u MIN=%u PT=%c ok=%d\n",
-// LEN, SEQ, CF, MAJ, MIN, PT, ok);
- }
-
- if (ok) {
- // Extract the first stream and FrameCount from AF
- int tagIndex = index;
- uint32_t frameCount = 0;
- bool frameCountFound = false;
- int est0Index = 0;
- size_t est0Length = 0;
- // Iterate through tags
- while (tagIndex < (ssize_t)length - 2/*CRC*/ - 8/*Min tag length*/ && (!frameCountFound || est0Index==0) )
- {
- uint32_t tagName = unpack4(buf+tagIndex); tagIndex += 4;
- uint32_t tagLen = unpack4(buf+tagIndex); tagIndex += 4;
- uint32_t tagLenByte = (tagLen+7)/8;
-// DEBUG("TAG %c%c%c%c size %u bits %u bytes\n",
-// tagName>>24&0xFF, tagName>>16&0xFF, tagName>>8&0xFF, tagName&0xFF,
-// tagLen, tagLenByte);
-
- if (tagName == TAG_NAME_DETI) {
- uint32_t FCTH = buf[tagIndex] & 0x1F;
- uint32_t FCT = buf[tagIndex+1];
- frameCount = FCTH * 250 + FCT;
- frameCountFound = true;
-// DEBUG("frameCount=%u\n", frameCount);
- } else if ((tagName & 0xFFFFFF00) == TAG_NAME_EST) {
- est0Index = tagIndex+3 /*3 bytes SSTC*/;
- est0Length = tagLenByte-3;
-// DEBUG("Stream found at index %u, size=%zu\n", est0Index, est0Length);
- }
-
- tagIndex += tagLenByte;
- }
- if (frameCountFound && est0Index !=0) {
- _subChannelQueue->push(frameCount, buf+est0Index, est0Length);
- } else {
- ok = false;
- }
- }
-
- return ok;
-}
-
-/* ------------------------------------------------------------------
- * ------------------------------------------------------------------
- * ------------------------------------------------------------------
- * ------------------------------------------------------------------
- */
-
-/* ------------------------------------------------------------------
- *
- */
-//static int nbPFTFrag = 0;
-PFTFrag::PFTFrag(uint8_t* buf, size_t length)
-{
- //DEBUG("+ PFTFrag %d\n", ++nbPFTFrag);
- _valid = _parse(buf, length);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-PFTFrag::~PFTFrag()
-{
- //DEBUG("- PFTFrag %d\n", --nbPFTFrag);
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool PFTFrag::_parse(uint8_t* buf, size_t length)
-{
- int index = 0;
-
- // Parse PFT Fragment Header (ETSI TS 102 821 V1.4.1 ch7.1)
- index += 2; // Psync
-
- _Pseq = unpack2(buf+index); index += 2;
- _Findex = unpack3(buf+index); index += 3;
- _Fcount = unpack3(buf+index); index += 3;
- _FEC = unpack1bit(buf[index], 0);
- _Addr = unpack1bit(buf[index], 1);
- _Plen = unpack2(buf+index) & 0x3FFF; index += 2;
-
- // Optional RS Header
- _RSk = 0;
- _RSz = 0;
- if (_FEC) {
- _RSk = buf[index]; index += 1;
- _RSz = buf[index]; index += 1;
- }
-
- // Optional transport header
- _Source = 0;
- _Dest = 0;
- if (_Addr) {
- _Source = unpack2(buf+index); index += 2;
- _Dest = unpack2(buf+index); index += 2;
- }
-
- index += 2;
- bool isValid = (_FEC==0) || _checkCRC(buf, index);
- isValid &= length == index + _Plen;
-
- if (!isValid) {
-// DEBUG("PFT isValid=%d Pseq=%u Findex=%u Fcount=%u FEC=%u "
-// "Addr=%u Plen=%u",
-// isValid, _Pseq, _Findex, _Fcount, _FEC,
-// _Addr, _Plen);
- if (_FEC) PRINTF(" RSk=%u RSz=%u", _RSk, _RSz);
- if (_Addr) PRINTF(" Source=%u Dest=%u", _Source, _Dest);
- PRINTF("\n");
- }
-
- if (isValid) {
- _payload.resize(_Plen);
- memcpy(_payload.data(), buf+index, _Plen);
- }
-
- return isValid;
-}
-
-/* ------------------------------------------------------------------
- * ------------------------------------------------------------------
- * ------------------------------------------------------------------
- * ------------------------------------------------------------------
- */
-void* PFT::_rs_handler = NULL;
-
-/* ------------------------------------------------------------------
- *
- */
-//static int nbPFT = 0;
-PFT::PFT(uint32_t Pseq, uint32_t Fcount)
- : _frags(NULL)
- , _Pseq(Pseq)
- , _Fcount(Fcount)
- , _Plen(0)
- , _nbFrag(0)
- , _RSk(0)
- , _RSz(0)
- , _cmax(0)
- , _rxmin(0)
- , _creation(std::chrono::steady_clock::now())
-{
-// DEBUG("+ PFT %d\n", ++nbPFT);
- if (Fcount > 0) {
- _frags = new PFTFrag* [Fcount];
- memset(_frags, 0, Fcount*sizeof(PFTFrag*));
- }
-}
-
-/* ------------------------------------------------------------------
- *
- */
-PFT::~PFT()
-{
-// DEBUG("- PFT %d\n", --nbPFT);
- if (_frags) {
- for (size_t i = 0 ; i < _Fcount ; i++) {
- delete _frags[i];
- }
- delete [] _frags;
- }
-}
-
-/* ------------------------------------------------------------------
- * static
- */
-void PFT::_initRSDecoder()
-{
-#if RS_DECODE
- if (!_rs_handler) {
- // From ODR-DabMux: PFT.h/cpp and ReedSolomon.h/cpp
-
- // Create the RS(k+p,k) encoder
- const int firstRoot = 1; // Discovered by analysing EDI dump
- const int gfPoly = 0x11d;
-
- // The encoding has to be 255, 207 always, because the chunk has to
- // be padded at the end, and not at the beginning as libfec would
- // do
- const int N = 255;
- const int K = 207;
- const int primElem = 1;
- const int symsize = 8;
- const int nroots = N - K; // For EDI PFT, this must be 48
- const int pad = ((1 << symsize) - 1) - N; // is 255-N
-
- _rs_handler = init_rs_char(symsize, gfPoly, firstRoot, primElem, nroots, pad);
-
-
-/* TEST RS CODE */
-#if 0
-
- // Populate data
- uint8_t data[255];
- memset(data, 0x00, 255);
- for (int i=0;i<207;i++) data[i] = i%10;
-
- // Add RS Code
- encode_rs_char(_rs_handler, data, data+207);
- _dump(data, 255);
-
- // Disturb data
- for (int i=50; i<50+24; i++) data[i]+=0x50;
-
- // Correct data
- int nbErr = decode_rs_char(_rs_handler, data, NULL, 0);
- printf("nbErr=%d\n", nbErr);
- _dump(data, 255);
-
- // Check data
- for (int i=0;i<207;i++) {
- if (data[i] != i%10) {
- printf("Error position %d %hhu != %d\n", i, data[i], i%10);
- }
- }
-
- // STOP (sorry :-| )
- int* i=0;
- *i = 9;
-#endif // 0
- }
-#endif
-}
-
-/* ------------------------------------------------------------------
- *
- */
-void PFT::pushPFTFrag(PFTFrag* frag)
-{
- uint32_t Findex = frag->Findex();
-#if RS_TEST1
- if (Findex != 0 && _frags[Findex] == NULL) /* TEST */
-#else
- if (_frags[Findex] == NULL)
-#endif
- {
- _frags[Findex] = frag;
- _nbFrag++;
-
- // Calculate the minimum number of fragment necessary to apply FEC
- // This can't be done with the last fragment that does may have a smaller size
- // ETSI TS 102 821 V1.4.1 ch 7.4.4
- if (_Plen == 0 && (Findex == 0 || Findex < (_Fcount-1)))
- {
- _Plen = frag->Plen();
- }
-
- if (_cmax == 0 && frag->FEC() && (Findex == 0 || Findex < (_Fcount-1)) && _Plen>0)
- {
- _RSk = frag->RSk();
- _RSz = frag->RSz();
- _cmax = (_Fcount*_Plen) / (_RSk+48);
- _rxmin = _Fcount - (_cmax*48)/_Plen;
- }
- } else {
- // Already received, delete the fragment
- delete frag;
- }
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool PFT::complete()
-{
-#if RS_TEST1
- return _nbFrag == _Fcount-1;
-#else
- return _nbFrag == _Fcount;
-#endif
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool PFT::_canAttemptToDecode()
-{
- if (complete()) return true;
-
- if (_cmax>0 && _nbFrag >= _rxmin) return true;
-
- return false;
-}
-
-/* ------------------------------------------------------------------
- *
- */
-bool PFT::extractAF(std::vector<uint8_t>& afdata)
-{
- bool ok = false;
-// DEBUG("extractAF from PFT %u. Fcount=%u nbFrag=%u Plen=%u cmax=%u rxmin=%u RSk=%u RSz=%u\n",
-// _Pseq, _Fcount, _nbFrag, _Plen, _cmax, _rxmin, _RSk, _RSz);
-
- if (_canAttemptToDecode()) {
- int totCorrectedErr = 0;
-
- if (_cmax > 0) // FEC present.
- {
- uint8_t* p_data_w;
- uint8_t* p_data_r;
- size_t data_len = 0;
-
- // Re-assemble RS block
- uint8_t rs_block[_Plen*_Fcount];
- int eras_pos[_cmax][/*48*/255]; /* 48 theoritically but ... */
- int no_eras[_cmax];
- memset(no_eras, 0, sizeof(no_eras));
-
- p_data_w = rs_block;
- for (size_t j = 0; j < _Fcount; ++j) {
- if (!_frags[j]) // fill with zeros if fragment is missing
- {
- for (size_t k = 0; k < _Plen; k++) {
- size_t pos = k * _Fcount;
- p_data_w[pos] = 0x00;
- size_t chunk = pos / (_RSk+48);
- size_t chunkpos = (pos) % (_RSk+48);
- if (chunkpos > _RSk) {
- chunkpos += (207-_RSk);
- }
- eras_pos[chunk][no_eras[chunk]] = chunkpos;
- no_eras[chunk]++;
- }
- } else {
- uint8_t* p_data_r = _frags[j]->payload();
- for (size_t k = 0; k < _frags[j]->Plen(); k++)
- p_data_w[k * _Fcount] = *p_data_r++;
- for (size_t k = _frags[j]->Plen(); k < _Plen; k++)
- p_data_w[k * _Fcount] = 0x00;
- }
- p_data_w++;
- }
-
- // Apply RS Code
-#if RS_DECODE
- uint8_t rs_chunks[255 * _cmax];
- _initRSDecoder();
- if (_rs_handler) {
- size_t k = _RSk;
- memset(rs_chunks, 0, sizeof(rs_chunks));
- p_data_w = rs_chunks;
- p_data_r = rs_block;
- for (size_t j = 0; j < _cmax; j++) {
- memcpy(p_data_w, p_data_r, k);
- p_data_w += k;
- p_data_r += k;
- if (k < 207)
- memset(p_data_w, 0, 207 - k);
- p_data_w += 207 - k;
- memcpy(p_data_w, p_data_r, 48);
- p_data_w += 48;
- p_data_r += 48;
- }
-
- p_data_r = rs_chunks;
- for (size_t j = 0 ; j < _cmax && totCorrectedErr != -1 ; j++) {
-#if RS_TEST1 || RS_TEST2
- if (no_eras[j]>0) {
- DEBUG("RS Chuck %d: %d errors\n", j, no_eras[j]);
- }
-#endif
- int nbErr = decode_rs_char(_rs_handler, p_data_r, eras_pos[j], no_eras[j]);
-// int nbErr = decode_rs_char(_rs_handler, p_data_r, NULL, 0);
- if (nbErr >= 0) {
-#if RS_TEST1 || RS_TEST2
- if (nbErr > 0) DEBUG("RS Chuck %d: %d corrections\n", j, nbErr);
-#endif
- totCorrectedErr += nbErr;
- } else {
-#if RS_TEST1 || RS_TEST2
- DEBUG("RS Chuck %d: too many errors\n", j);
-#endif
- totCorrectedErr = -1;
- }
- p_data_r += 255;
- }
-#if RS_TEST1 || RS_TEST2
- if (totCorrectedErr>0) {
- DEBUG("RS corrected %d errors in %d chunks\n", totCorrectedErr, _cmax);
- }
-#endif
- }
-#endif // RS_DECODE
- // Assemble AF frame from rs code
- /* --- re-assemble packet from Reed-Solomon block ----------- */
- afdata.resize(_Plen*_Fcount);
- p_data_w = afdata.data();
-#if RS_DECODE
- p_data_r = rs_chunks;
- for (size_t j = 0; j < _cmax; j++) {
- memcpy(p_data_w, p_data_r, _RSk);
- p_data_w += _RSk;
- p_data_r += 255;
- data_len += _RSk;
- }
-#else
- p_data_r = rs_block;
- for (size_t j = 0; j < _cmax; j++) {
- memcpy(p_data_w, p_data_r, _RSk);
- p_data_w += _RSk;
- p_data_r += _RSk + 48;
- data_len += _RSk;
- }
-#endif // RS_DECODE
- data_len -= _RSz;
- afdata.resize(data_len);
- } else { // No Fec Just assemble packets
- afdata.resize(0);
- for (size_t j = 0; j < _Fcount; ++j) {
- if (_frags[j])
- {
- afdata.insert(afdata.end(),
- _frags[j]->payloadVector().begin(), _frags[j]->payloadVector().end());
- }
- }
- }
-
- // EDI specific, must have a CRC.
- if( afdata.size()>=12 ) {
- ok = _checkCRC(afdata.data(), afdata.size());
- if (ok && totCorrectedErr > 0) {
- if (hideFirstPFTErrors==0) {
- INFO("AF reconstructed from %u/%u PFT fragments\n", _nbFrag, _Fcount);
- }
- }
- if (!ok && totCorrectedErr == -1) {
- if (hideFirstPFTErrors==0) {
- ERROR("Too many errors to reconstruct AF from %u/%u PFT fragments\n", _nbFrag, _Fcount);
- }
- }
- }
- }
- else {
- if (hideFirstPFTErrors==0) {
- ERROR("Not enough fragments to reconstruct AF from %u/%u PFT fragments (min=%u)\n", _nbFrag, _Fcount, _rxmin);
- }
- }
-
- if( hideFirstPFTErrors > 0 ) hideFirstPFTErrors--;
-
- return ok;
-}