diff --git a/ambedtest/cambe.cpp b/ambedtest/cambe.cpp
new file mode 100644
index 0000000..5809822
--- /dev/null
+++ b/ambedtest/cambe.cpp
@@ -0,0 +1,45 @@
+//
+// cambe.cpp
+// ambedtest
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 16/05/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include
+#include "cambe.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CAmbe::CAmbe()
+{
+ ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe));
+}
+
+CAmbe::CAmbe(uint8 *ambe)
+{
+ ::memcpy(m_uiAmbe, ambe, sizeof(m_uiAmbe));
+}
+
diff --git a/ambedtest/cambe.h b/ambedtest/cambe.h
new file mode 100644
index 0000000..404ac8c
--- /dev/null
+++ b/ambedtest/cambe.h
@@ -0,0 +1,59 @@
+//
+// cambe.h
+// ambedtest
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 16/05/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef cambe_h
+#define cambe_h
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// frame sizes
+#define AMBE_SIZE 9
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CAmbe
+{
+public:
+ // constructor
+ CAmbe();
+ CAmbe(uint8 *);
+
+ // destructor
+ virtual ~CAmbe() {}
+
+ // get
+ const uint8 *GetData(void) const { return m_uiAmbe; }
+
+protected:
+ // data
+ uint8 m_uiAmbe[AMBE_SIZE];
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* cambe_h */
diff --git a/ambedtest/cbuffer.cpp b/ambedtest/cbuffer.cpp
new file mode 100644
index 0000000..d935939
--- /dev/null
+++ b/ambedtest/cbuffer.cpp
@@ -0,0 +1,200 @@
+//
+// cbuffer.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include
+#include "cbuffer.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CBuffer::CBuffer(uint8 *buffer, int len)
+{
+ resize(len);
+ ::memcpy(data(), buffer, len);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// set
+
+void CBuffer::Set(uint8 *buffer, int len)
+{
+ resize(len);
+ ::memcpy(data(), buffer, len);
+}
+
+void CBuffer::Set(const char *sz)
+{
+ resize(::strlen(sz)+1);
+ ::strcpy((char *)data(), sz);
+}
+
+void CBuffer::Append(uint8 *buffer, int len)
+{
+ int n = (int)size();
+ resize(n+len);
+ ::memcpy(&(data()[n]), buffer, len);
+}
+
+void CBuffer::Append(uint8 ui, int len)
+{
+ int n = (int)size();
+ resize(n+len);
+ ::memset(&(data()[n]), ui, len);
+}
+
+void CBuffer::Append(uint8 ui)
+{
+ int n = (int)size();
+ resize(n+sizeof(uint8));
+ ::memcpy(&(data()[n]), &ui, sizeof(uint8));
+}
+
+void CBuffer::Append(uint16 ui)
+{
+ int n = (int)size();
+ resize(n+sizeof(uint16));
+ ::memcpy(&(data()[n]), &ui, sizeof(uint16));
+}
+
+void CBuffer::Append(uint32 ui)
+{
+ int n = (int)size();
+ resize(n+sizeof(uint32));
+ ::memcpy(&(data()[n]), &ui, sizeof(uint32));
+}
+
+void CBuffer::Append(const char *sz)
+{
+ Append((uint8 *)sz, (int)strlen(sz));
+ Append((uint8)0x00);
+}
+
+void CBuffer::ReplaceAt(int i, uint8 ui)
+{
+ if ( size() < (i+sizeof(uint8)) )
+ {
+ resize(i+sizeof(uint8));
+ }
+ *(uint8 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, uint16 ui)
+{
+ if ( size() < (i+sizeof(uint16)) )
+ {
+ resize(i+sizeof(uint16));
+ }
+ *(uint16 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, uint32 ui)
+{
+ if ( size() < (i+sizeof(uint32)) )
+ {
+ resize(i+sizeof(uint32));
+ }
+ *(uint32 *)(&(data()[i])) = ui;
+}
+
+void CBuffer::ReplaceAt(int i, const uint8 *ptr, int len)
+{
+ if ( size() < (i+len) )
+ {
+ resize(i+len);
+ }
+ ::memcpy(&(data()[i]), ptr, len);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+int CBuffer::Compare(uint8 *buffer, int len) const
+{
+ int result = -1;
+ if ( size() >= len )
+ {
+ result = ::memcmp(data(), buffer, len);
+ }
+ return result;
+}
+
+int CBuffer::Compare(uint8 *buffer, int off, int len) const
+{
+ int result = -1;
+ if ( size() >= (off+len) )
+ {
+ result = ::memcmp(&(data()[off]), buffer, len);
+ }
+ return result;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operator
+
+bool CBuffer::operator ==(const CBuffer &Buffer) const
+{
+ if ( size() == Buffer.size() )
+ {
+ return (::memcmp((const char *)data(), (const char *)Buffer.data(), size()) == 0);
+ }
+ return false;
+}
+
+bool CBuffer::operator ==(const char *sz) const
+{
+ if ( size() == ::strlen(sz) )
+ {
+ return (::memcmp((const char *)data(), sz, size()) == 0);
+ }
+ return false;
+}
+
+CBuffer::operator const char *() const
+{
+ return (const char *)data();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// debug
+
+void CBuffer::DebugDump(std::ofstream &debugout) const
+{
+ for ( int i = 0; i < size(); i++ )
+ {
+ char sz[16];
+ sprintf(sz, "%02X", data()[i]);
+ debugout << sz;
+ if ( i == size()-1 )
+ {
+ debugout << std::endl;
+ }
+ else
+ {
+ debugout << ',';
+ }
+ }
+}
+
diff --git a/ambedtest/cbuffer.h b/ambedtest/cbuffer.h
new file mode 100644
index 0000000..95bbae9
--- /dev/null
+++ b/ambedtest/cbuffer.h
@@ -0,0 +1,68 @@
+//
+// cbuffer.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef cbuffer_h
+#define cbuffer_h
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+class CBuffer : public std::vector
+{
+public:
+ // constructor
+ CBuffer() {};
+ CBuffer(uint8 *, int);
+
+ // destructor
+ virtual ~CBuffer() {};
+
+ // set
+ void Set(uint8 *, int);
+ void Set(const char *);
+ void Append(uint8 *, int);
+ void Append(uint8, int);
+ void Append(uint8);
+ void Append(uint16);
+ void Append(uint32);
+ void Append(const char *);
+ void ReplaceAt(int, uint8);
+ void ReplaceAt(int, uint16);
+ void ReplaceAt(int, uint32);
+ void ReplaceAt(int, const uint8 *, int);
+
+ // operation
+ int Compare(uint8 *, int) const;
+ int Compare(uint8 *, int, int) const;
+
+ // operator
+ bool operator ==(const CBuffer &) const;
+ bool operator ==(const char *) const;
+ operator const char *() const;
+
+ // debug
+ void DebugDump(std::ofstream &) const;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* cbuffer_h */
diff --git a/ambedtest/ccodecstream.cpp b/ambedtest/ccodecstream.cpp
new file mode 100644
index 0000000..5910f8d
--- /dev/null
+++ b/ambedtest/ccodecstream.cpp
@@ -0,0 +1,297 @@
+//
+// ccodecstream.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include
+#include "ccodecstream.h"
+#include "samples.h"
+#include "ctranscoder.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CCodecStream::CCodecStream(uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut)
+{
+ m_bStopThread = false;
+ m_pThread = NULL;
+ m_uiStreamId = uiId;
+ m_uiPid = 0;
+ m_uiCodecIn = uiCodecIn;
+ m_uiCodecOut = uiCodecOut;
+ m_bConnected = false;
+ m_iAmbeSrcPtr = 0;
+ m_iAmbeDestPtr = 0;
+ m_uiNbPacketSent = 0;
+ m_uiNbPacketReceived = 0;
+ m_uiNbPacketBad = 0;
+ m_uiNbPacketTimeout = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// destructor
+
+CCodecStream::~CCodecStream()
+{
+ // close socket
+ m_Socket.Close();
+
+ // kill threads
+ m_bStopThread = true;
+ if ( m_pThread != NULL )
+ {
+ m_pThread->join();
+ delete m_pThread;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// initialization
+
+bool CCodecStream::Init(uint16 uiPort)
+{
+ bool ok;
+
+ // reset stop flag
+ m_bStopThread = false;
+
+ // copy our test data
+ if ( m_uiCodecIn == CODEC_AMBE2PLUS )
+ {
+ // DMR -> DSTAR
+ for ( int i = 0; i < sizeof(g_uiDmrSample); i += AMBE_SIZE )
+ {
+ CAmbe *ambe = new CAmbe(&(g_uiDmrSample[i]));
+ m_AmbeSrc.push_back(ambe);
+ }
+ for ( int i = 0; i < sizeof(g_uiDstarSample); i += AMBE_SIZE )
+ {
+ CAmbe *ambe = new CAmbe(&(g_uiDstarSample[i]));
+ m_AmbeDest.push_back(ambe);
+ }
+ }
+ else
+ {
+ // DSTAR -> DMR
+ for ( int i = 0; i < sizeof(g_uiDstarSample); i += AMBE_SIZE )
+ {
+ CAmbe *ambe = new CAmbe(&(g_uiDstarSample[i]));
+ m_AmbeSrc.push_back(ambe);
+ }
+ for ( int i = 0; i < sizeof(g_uiDmrSample); i += AMBE_SIZE )
+ {
+ CAmbe *ambe = new CAmbe(&(g_uiDmrSample[i]));
+ m_AmbeDest.push_back(ambe);
+ }
+ }
+
+ // create server's IP
+ m_Ip = g_Transcoder.GetAmbedIp();
+ m_uiPort = uiPort;
+
+ // create our socket
+ ok = m_Socket.Open(uiPort);
+ if ( ok )
+ {
+ // start thread;
+ m_pThread = new std::thread(CCodecStream::Thread, this);
+ m_bConnected = true;
+ m_FrameTimer.Now();
+ ResetStats();
+ }
+ else
+ {
+ std::cout << "Error opening socket on port UDP" << uiPort << " on ip " << m_Ip << std::endl;
+ m_bConnected = false;
+ }
+
+ // done
+ return ok;
+}
+
+void CCodecStream::Close(void)
+{
+ // close socket
+ m_bConnected = false;
+ m_Socket.Close();
+
+ // kill threads
+ m_bStopThread = true;
+ if ( m_pThread != NULL )
+ {
+ m_pThread->join();
+ delete m_pThread;
+ m_pThread = NULL;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// thread
+
+void CCodecStream::Thread(CCodecStream *This)
+{
+ while ( !This->m_bStopThread )
+ {
+ This->Task();
+ }
+}
+
+void CCodecStream::Task(void)
+{
+ CBuffer Buffer;
+ CIp Ip;
+ uint8 Ambe[AMBE_SIZE];
+
+ // connected ?
+ if ( m_bConnected )
+ {
+ // time to send next packet to be transcoded ?
+ /*if ( m_FrameTimer.DurationSinceNow() >= 0.020 )
+ {
+ // yes
+ m_FrameTimer.Now();
+
+ // encode packet @ send it
+ EncodeAmbePacket(&Buffer, m_AmbeSrc[m_iAmbeSrcPtr]->GetData());
+ m_Socket.Send(Buffer, m_Ip, m_uiPort);
+
+ // and increment pointer
+ m_iAmbeSrcPtr = (m_iAmbeSrcPtr + 1) % m_AmbeSrc.size();
+ m_uiNbPacketSent++;
+ }*/
+
+ // any packt to send to trancoder ?
+ uint32 uiNbPacketToSend = (uint32)(m_FrameTimer.DurationSinceNow() * 50.0) - m_uiNbPacketSent;
+ if ( uiNbPacketToSend > 0 )
+ {
+ for ( int i = 0; i < uiNbPacketToSend; i++ )
+ {
+ // encode packet @ send it
+ EncodeAmbePacket(&Buffer, m_AmbeSrc[m_iAmbeSrcPtr]->GetData());
+ m_Socket.Send(Buffer, m_Ip, m_uiPort);
+
+ // and increment pointer
+ m_iAmbeSrcPtr = (m_iAmbeSrcPtr + 1) % m_AmbeSrc.size();
+ m_uiNbPacketSent++;
+ }
+ }
+
+ // any packet from transcoder
+ if ( m_Socket.Receive(&Buffer, &Ip, 1) != -1 )
+ {
+ // crack
+ if ( IsValidAmbePacket(Buffer, Ambe) )
+ {
+ m_TimeoutTimer.Now();
+
+ // check the PID
+ // check the transcoded packet
+ /*if ( ::memcmp(Ambe, m_AmbeDest[m_iAmbeDestPtr]->GetData(), AMBE_SIZE) != 0 )
+ {
+ m_uiNbPacketBad++;
+ ::memcpy((void *)m_AmbeDest[m_iAmbeDestPtr]->GetData(), Ambe, AMBE_SIZE);
+ }*/
+
+ // and increment pointer
+ m_iAmbeDestPtr = (m_iAmbeDestPtr + 1) % m_AmbeDest.size();
+ m_uiNbPacketReceived++;
+
+ }
+ }
+ }
+
+ // display stats
+ if ( m_DisplayStatsTimer.DurationSinceNow() >= 2.0 )
+ {
+ m_DisplayStatsTimer.Now();
+ DisplayStats();
+ }
+
+ // handle timeout
+ if ( m_TimeoutTimer.DurationSinceNow() >= (TRANSCODER_AMBEPACKET_TIMEOUT/1000.0f) )
+ {
+ //std::cout << "ambed packet timeout" << std::endl;
+ m_uiNbPacketTimeout++;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// packet decoding helpers
+
+bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe)
+{
+ bool valid = false;
+
+ if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) )
+ {
+ ::memcpy(Ambe, &(Buffer.data()[2]), 9);
+ valid = true;
+ }
+ return valid;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// packet encoding helpers
+
+void CCodecStream::EncodeAmbePacket(CBuffer *Buffer, const uint8 *Ambe)
+{
+ Buffer->clear();
+ Buffer->Append(m_uiCodecIn);
+ Buffer->Append(m_uiPid);
+ Buffer->Append((uint8 *)Ambe, 9);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// stats helpers
+
+void CCodecStream::ResetStats(void)
+{
+ m_StatsTimer.Now();
+ m_DisplayStatsTimer.Now();
+ m_TimeoutTimer.Now();
+ m_uiNbPacketSent = 0;
+ m_uiNbPacketReceived = 0;
+ m_uiNbPacketBad = 0;
+ m_uiNbPacketTimeout = 0;
+
+}
+
+void CCodecStream::DisplayStats(void)
+{
+ //double fps = (double)m_uiNbPacketSent / m_StatsTimer.DurationSinceNow();
+ double fps = (double)m_uiNbPacketReceived / m_StatsTimer.DurationSinceNow();
+
+ std::cout << "Stream " << (int)m_uiStreamId << " (" << (int)m_uiCodecIn << "->" << (int)m_uiCodecOut << ") : ";
+ std::cout << m_uiNbPacketSent << " / " << m_uiNbPacketReceived << " / " << m_uiNbPacketTimeout;
+ //std::cout << " / " << m_uiNbPacketBad;
+ std::cout << " ; " << fps << " fps";
+
+ std::cout << std::endl;
+
+ m_uiNbPacketBad = 0;
+}
diff --git a/ambedtest/ccodecstream.h b/ambedtest/ccodecstream.h
new file mode 100644
index 0000000..568e466
--- /dev/null
+++ b/ambedtest/ccodecstream.h
@@ -0,0 +1,114 @@
+//
+// ccodecstream.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef ccodecstream_h
+#define ccodecstream_h
+
+#include "csemaphore.h"
+#include "cudpsocket.h"
+#include "ctimepoint.h"
+#include "cambe.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// frame sizes
+#define AMBE_SIZE 9
+#define AMBEPLUS_SIZE 9
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CCodecStream
+{
+public:
+ // constructor
+ CCodecStream(uint16, uint8, uint8);
+
+ // destructor
+ virtual ~CCodecStream();
+
+ // initialization
+ bool Init(uint16);
+ void Close(void);
+
+ // get
+ bool IsConnected(void) const { return m_bConnected; }
+ uint16 GetStreamId(void) const { return m_uiStreamId; }
+
+ // task
+ static void Thread(CCodecStream *);
+ void Task(void);
+
+
+protected:
+ // packet decoding helpers
+ bool IsValidAmbePacket(const CBuffer &, uint8 *);
+
+ // packet encoding helpers
+ void EncodeAmbePacket(CBuffer *, const uint8 *);
+
+ // stats helpers
+ void ResetStats(void);
+ void DisplayStats(void);
+
+protected:
+ // test data
+ std::vector m_AmbeSrc;
+ int m_iAmbeSrcPtr;
+ std::vector m_AmbeDest;
+ int m_iAmbeDestPtr;
+
+ // data
+ uint16 m_uiStreamId;
+ uint16 m_uiPort;
+ uint8 m_uiPid;
+ uint8 m_uiCodecIn;
+ uint8 m_uiCodecOut;
+
+ // socket
+ CIp m_Ip;
+ CUdpSocket m_Socket;
+ bool m_bConnected;
+
+ // thread
+ bool m_bStopThread;
+ std::thread *m_pThread;
+ CTimePoint m_TimeoutTimer;
+ CTimePoint m_FrameTimer;
+
+ // stats
+ CTimePoint m_StatsTimer;
+ CTimePoint m_DisplayStatsTimer;
+ uint32 m_uiNbPacketSent;
+ uint32 m_uiNbPacketReceived;
+ uint32 m_uiNbPacketBad;
+ uint32 m_uiNbPacketTimeout;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* ccodecstream_h */
diff --git a/ambedtest/cip.cpp b/ambedtest/cip.cpp
new file mode 100644
index 0000000..5cec9b5
--- /dev/null
+++ b/ambedtest/cip.cpp
@@ -0,0 +1,91 @@
+//
+// cip.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include
+#include "cip.h"
+
+#include
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructors
+
+CIp::CIp()
+{
+ ::memset(&m_Addr, 0, sizeof(m_Addr));
+ m_Addr.sin_family = AF_INET;
+}
+
+CIp::CIp(const char *sz)
+{
+ ::memset(&m_Addr, 0, sizeof(m_Addr));
+ m_Addr.sin_family = AF_INET;
+ // try xxx.xxx.xxx.xxxx first
+ m_Addr.sin_addr.s_addr = inet_addr(sz);
+ if ( m_Addr.sin_addr.s_addr == INADDR_NONE )
+ {
+ // otherwise try to resolve via dns
+ hostent *record = gethostbyname(sz);
+ if( record != NULL )
+ {
+ m_Addr.sin_addr.s_addr = ((in_addr * )record->h_addr)->s_addr;
+ }
+ }
+}
+
+CIp::CIp(const struct sockaddr_in *sa)
+{
+ ::memcpy(&m_Addr, sa, sizeof(m_Addr));
+}
+
+
+CIp::CIp(const CIp &ip)
+{
+ ::memcpy(&m_Addr, &ip.m_Addr, sizeof(m_Addr));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// set
+
+void CIp::SetSockAddr(struct sockaddr_in *sa)
+{
+ ::memcpy(&m_Addr, sa, sizeof(m_Addr));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operator
+
+bool CIp::operator ==(const CIp &ip) const
+{
+ return ( (ip.m_Addr.sin_family == m_Addr.sin_family) &&
+ (ip.m_Addr.sin_addr.s_addr == m_Addr.sin_addr.s_addr) &&
+ (ip.m_Addr.sin_port == m_Addr.sin_port)) ;
+}
+
+CIp::operator const char *() const
+{
+ return ::inet_ntoa(m_Addr.sin_addr);
+}
+
+
diff --git a/ambedtest/cip.h b/ambedtest/cip.h
new file mode 100644
index 0000000..3a65fd6
--- /dev/null
+++ b/ambedtest/cip.h
@@ -0,0 +1,59 @@
+//
+// cip.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef cip_h
+#define cip_h
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CIp
+{
+public:
+ // constructors
+ CIp();
+ //CIp(uint8, uint8, uint8, uint8);
+ CIp(const struct sockaddr_in *);
+ CIp(const char *);
+ CIp(const CIp &);
+
+ // destructor
+ virtual ~CIp() {};
+
+ // sockaddr
+ void SetSockAddr(struct sockaddr_in *);
+ struct sockaddr_in *GetSockAddr(void) { return &m_Addr; }
+
+ // operator
+ bool operator ==(const CIp &) const;
+ operator const char *() const;
+
+protected:
+ // data
+ struct sockaddr_in m_Addr;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* cip_h */
diff --git a/ambedtest/csemaphore.cpp b/ambedtest/csemaphore.cpp
new file mode 100644
index 0000000..3b91e82
--- /dev/null
+++ b/ambedtest/csemaphore.cpp
@@ -0,0 +1,72 @@
+//
+// csemaphore.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "csemaphore.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CSemaphore::CSemaphore()
+{
+ // Initialized as locked.
+ m_Count = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+void CSemaphore::Reset(void)
+{
+ std::unique_lock lock(m_Mutex);
+ m_Count = 0;
+}
+
+void CSemaphore::Notify(void)
+{
+ std::unique_lock lock(m_Mutex);
+ m_Count++;
+ m_Condition.notify_one();
+}
+
+void CSemaphore::Wait(void)
+{
+ std::unique_lock lock(m_Mutex);
+ m_Condition.wait(lock, [&]{ return m_Count > 0; });
+ m_Count--;
+}
+
+bool CSemaphore::WaitFor(uint ms)
+{
+ std::chrono::milliseconds timespan(ms);
+ std::unique_lock lock(m_Mutex);
+ auto ok = m_Condition.wait_for(lock, timespan, [&]{ return m_Count > 0; });
+ if ( ok )
+ {
+ m_Count--;
+ }
+ return ok;
+
+}
+
diff --git a/ambedtest/csemaphore.h b/ambedtest/csemaphore.h
new file mode 100644
index 0000000..0729512
--- /dev/null
+++ b/ambedtest/csemaphore.h
@@ -0,0 +1,56 @@
+//
+// csemaphore.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef csemaphore_h
+#define csemaphore_h
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CSemaphore
+{
+public:
+ // constructor
+ CSemaphore();
+
+ // destructor
+ virtual ~CSemaphore() {};
+
+ // operation
+ void Reset(void);
+ void Notify(void);
+ void Wait(void);
+ bool WaitFor(uint);
+
+protected:
+ // data
+ std::mutex m_Mutex;
+ std::condition_variable m_Condition;
+ size_t m_Count;
+
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* csemaphore_h */
diff --git a/ambedtest/ctimepoint.cpp b/ambedtest/ctimepoint.cpp
new file mode 100644
index 0000000..41b59f0
--- /dev/null
+++ b/ambedtest/ctimepoint.cpp
@@ -0,0 +1,53 @@
+//
+// ctimepoint.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 05/11/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "ctimepoint.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CTimePoint::CTimePoint()
+{
+ m_TimePoint = std::chrono::steady_clock::now();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// operation
+
+double CTimePoint::DurationSinceNow(void) const
+{
+ std::chrono::steady_clock::time_point Now = std::chrono::steady_clock::now();
+ std::chrono::steady_clock::duration time_span = (Now - m_TimePoint);
+ return double(time_span.count()) * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// task
+
+void CTimePoint::TaskSleepFor(uint ms)
+{
+ std::chrono::milliseconds timespan(ms);
+ std::this_thread::sleep_for(timespan);
+}
diff --git a/ambedtest/ctimepoint.h b/ambedtest/ctimepoint.h
new file mode 100644
index 0000000..209e8c7
--- /dev/null
+++ b/ambedtest/ctimepoint.h
@@ -0,0 +1,55 @@
+//
+// ctimepoint.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 05/11/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef ctimepoint_h
+#define ctimepoint_h
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CTimePoint : public std::chrono::steady_clock::time_point
+{
+public:
+ // constructor
+ CTimePoint();
+
+ // destructor
+ virtual ~CTimePoint() {}
+
+ // operation
+ void Now(void) { m_TimePoint = std::chrono::steady_clock::now(); }
+ double DurationSinceNow(void) const;
+
+ // task
+ static void TaskSleepFor(uint);
+
+protected:
+ // data
+ std::chrono::steady_clock::time_point m_TimePoint;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* ctimepoint_h */
diff --git a/ambedtest/ctranscoder.cpp b/ambedtest/ctranscoder.cpp
new file mode 100644
index 0000000..5d7805b
--- /dev/null
+++ b/ambedtest/ctranscoder.cpp
@@ -0,0 +1,388 @@
+//
+// ctranscoder.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "ctranscoder.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+// status
+#define STATUS_IDLE 0
+#define STATUS_LOGGED 1
+
+// timeout
+#define AMBED_OPENSTREAM_TIMEOUT 200 // in ms
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+CTranscoder g_Transcoder;
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CTranscoder::CTranscoder()
+{
+ m_bStopThread = false;
+ m_pThread = NULL;
+ m_Streams.reserve(12);
+ m_bConnected = false;
+ m_LastKeepaliveTime.Now();
+ m_LastActivityTime.Now();
+ m_bStreamOpened = false;
+ m_StreamidOpenStream = 0;
+ m_PortOpenStream = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// destructor
+
+CTranscoder::~CTranscoder()
+{
+ // close all streams
+ m_Mutex.lock();
+ {
+ for ( int i = 0; i < m_Streams.size(); i++ )
+ {
+ delete m_Streams[i];
+ }
+ m_Streams.clear();
+
+ }
+ m_Mutex.unlock();
+
+ // kill threads
+ m_bStopThread = true;
+ if ( m_pThread != NULL )
+ {
+ m_pThread->join();
+ delete m_pThread;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// initialization
+
+bool CTranscoder::Init(const CIp &ListenIp, const CIp &AmbedIp)
+{
+ bool ok;
+
+ // reset stop flag
+ m_bStopThread = false;
+
+ // create server's IP
+ m_ListenIp = ListenIp;
+ m_AmbedIp = AmbedIp;
+
+ // create our socket
+ ok = m_Socket.Open(TRANSCODER_PORT);
+ if ( ok )
+ {
+ // start thread;
+ m_pThread = new std::thread(CTranscoder::Thread, this);
+ }
+ else
+ {
+ std::cout << "Error opening socket on port UDP" << TRANSCODER_PORT << " on ip " << m_AmbedIp << std::endl;
+ }
+
+ // done
+ return ok;
+}
+
+void CTranscoder::Close(void)
+{
+ // close socket
+ m_Socket.Close();
+
+ // close all streams
+ m_Mutex.lock();
+ {
+ for ( int i = 0; i < m_Streams.size(); i++ )
+ {
+ delete m_Streams[i];
+ }
+ m_Streams.clear();
+
+ }
+ m_Mutex.unlock();
+
+ // kill threads
+ m_bStopThread = true;
+ if ( m_pThread != NULL )
+ {
+ m_pThread->join();
+ delete m_pThread;
+ m_pThread = NULL;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// thread
+
+void CTranscoder::Thread(CTranscoder *This)
+{
+ while ( !This->m_bStopThread )
+ {
+ This->Task();
+ }
+}
+
+void CTranscoder::Task(void)
+{
+ CBuffer Buffer;
+ CIp Ip;
+ uint16 StreamId;
+ uint16 Port;
+
+ // anything coming in from codec server ?
+ //if ( (m_Socket.Receive(&Buffer, &Ip, 20) != -1) && (Ip == m_Ip) )
+ if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
+ {
+ m_LastActivityTime.Now();
+
+ // crack packet
+ if ( IsValidStreamDescrPacket(Buffer, &StreamId, &Port) )
+ {
+ //std::cout << "Transcoder stream " << (int) StreamId << " descr packet " << std::endl;
+ m_bStreamOpened = true;
+ m_StreamidOpenStream = StreamId;
+ m_PortOpenStream = Port;
+ m_SemaphoreOpenStream.Notify();
+ }
+ else if ( IsValidNoStreamAvailablePacket(Buffer) )
+ {
+ m_bStreamOpened = false;
+ m_SemaphoreOpenStream.Notify();
+ }
+ else if ( IsValidKeepAlivePacket(Buffer) )
+ {
+ if ( !m_bConnected )
+ {
+ std::cout << "Transcoder connected at " << Ip << std::endl;
+ }
+ m_bConnected = true;
+ }
+
+ }
+
+ // handle end of streaming timeout
+ //CheckStreamsTimeout();
+
+ // handle queue from reflector
+ //HandleQueue();
+
+ // keep client alive
+ if ( m_LastKeepaliveTime.DurationSinceNow() > TRANSCODER_KEEPALIVE_PERIOD )
+ {
+ //
+ HandleKeepalives();
+
+ // update time
+ m_LastKeepaliveTime.Now();
+ }
+ }
+
+////////////////////////////////////////////////////////////////////////////////////////
+// manage streams
+
+CCodecStream *CTranscoder::GetStream(uint8 uiCodecIn)
+{
+ CBuffer Buffer;
+
+ CCodecStream *stream = NULL;
+
+ // do we need transcoding
+ if ( uiCodecIn != CODEC_NONE )
+ {
+ // are we connected to server
+ if ( m_bConnected )
+ {
+ // yes, post openstream request
+ EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
+ m_Socket.Send(Buffer, m_AmbedIp, TRANSCODER_PORT);
+
+ // wait relpy here
+ if ( m_SemaphoreOpenStream.WaitFor(AMBED_OPENSTREAM_TIMEOUT) )
+ {
+ if ( m_bStreamOpened )
+ {
+ std::cout << "ambed openstream(" << m_StreamidOpenStream << ") ok" << std::endl;
+
+ // create stream object
+ stream = new CCodecStream(m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
+
+ // init it
+ if ( stream->Init(m_PortOpenStream) )
+ {
+ // and append to list
+ Lock();
+ m_Streams.push_back(stream);
+ Unlock();
+ }
+ else
+ {
+ // send close packet
+ EncodeClosestreamPacket(&Buffer, stream->GetStreamId());
+ m_Socket.Send(Buffer, m_AmbedIp, TRANSCODER_PORT);
+ // and delete
+ delete stream;
+ stream = NULL;
+ }
+ }
+ else
+ {
+ std::cout << "ambed openstream failed (no suitable channel available)" << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "ambed openstream timeout" << std::endl;
+ }
+
+ }
+ }
+ return stream;
+}
+
+void CTranscoder::ReleaseStream(CCodecStream *stream)
+{
+ CBuffer Buffer;
+
+ if ( stream != NULL )
+ {
+ // look for the stream
+ bool found = false;
+ Lock();
+ {
+ for ( int i = 0; (i < m_Streams.size()) && !found; i++ )
+ {
+ // compare object pointers
+ if ( (m_Streams[i]) == stream )
+ {
+ // send close packet
+ EncodeClosestreamPacket(&Buffer, m_Streams[i]->GetStreamId());
+ m_Socket.Send(Buffer, m_AmbedIp, TRANSCODER_PORT);
+
+ // and close it
+ m_Streams[i]->Close();
+ delete m_Streams[i];
+ m_Streams.erase(m_Streams.begin()+i);
+ found = true;
+ }
+ }
+ }
+ Unlock();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// keepalive helpers
+
+void CTranscoder::HandleKeepalives(void)
+{
+ CBuffer keepalive;
+
+ // send keepalive
+ EncodeKeepAlivePacket(&keepalive);
+ m_Socket.Send(keepalive, m_AmbedIp, TRANSCODER_PORT);
+
+ // check if still with us
+ if ( m_bConnected && (m_LastActivityTime.DurationSinceNow() >= TRANSCODER_KEEPALIVE_TIMEOUT) )
+ {
+ // no, disconnect
+ m_bConnected = false;
+ std::cout << "Transcoder keepalive timeout" << std::endl;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet decoding helpers
+
+bool CTranscoder::IsValidKeepAlivePacket(const CBuffer &Buffer)
+{
+ uint8 tag[] = { 'A','M','B','E','D','P','O','N','G' };
+
+ bool valid = false;
+ if ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+ {
+ valid = true;
+ }
+ return valid;
+}
+
+bool CTranscoder::IsValidStreamDescrPacket(const CBuffer &Buffer, uint16 *Id, uint16 *Port)
+{
+ uint8 tag[] = { 'A','M','B','E','D','S','T','D' };
+
+ bool valid = false;
+ if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
+ {
+ *Id = *(uint16 *)(&Buffer.data()[8]);
+ *Port = *(uint16 *)(&Buffer.data()[10]);
+ // uint8 CodecIn = Buffer.data()[12];
+ // uint8 CodecOut = Buffer.data()[13];
+ valid = true;
+ }
+ return valid;
+}
+
+bool CTranscoder::IsValidNoStreamAvailablePacket(const CBuffer&Buffer)
+{
+ uint8 tag[] = { 'A','M','B','E','D','B','U','S','Y' };
+
+ return ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) );
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// packet encoding helpers
+
+void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer)
+{
+ uint8 tag[] = { 'A','M','B','E','D','P','I','N','G' };
+
+ Buffer->Set(tag, sizeof(tag));
+ Buffer->Append((uint8 *)(const char *)"XLX000 ", 8);
+}
+
+void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut)
+{
+ uint8 tag[] = { 'A','M','B','E','D','O','S' };
+
+ Buffer->Set(tag, sizeof(tag));
+ Buffer->Append((uint8 *)(const char *)"XLX000 ", 8);
+ Buffer->Append((uint8)uiCodecIn);
+ Buffer->Append((uint8)uiCodecOut);
+}
+
+void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId)
+{
+ uint8 tag[] = { 'A','M','B','E','D','C','S' };
+
+ Buffer->Set(tag, sizeof(tag));
+ Buffer->Append((uint16)uiStreamId);
+}
+
diff --git a/ambedtest/ctranscoder.h b/ambedtest/ctranscoder.h
new file mode 100644
index 0000000..2df670d
--- /dev/null
+++ b/ambedtest/ctranscoder.h
@@ -0,0 +1,113 @@
+//
+// ctranscoder.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef ctranscoder_h
+#define ctranscoder_h
+
+#include "csemaphore.h"
+#include "ccodecstream.h"
+#include "cudpsocket.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CTranscoder
+{
+public:
+ // constructor
+ CTranscoder();
+
+ // destructor
+ virtual ~CTranscoder();
+
+ // initialization
+ bool Init(const CIp &, const CIp &);
+ void Close(void);
+
+ // locks
+ void Lock(void) { m_Mutex.lock(); }
+ void Unlock(void) { m_Mutex.unlock(); }
+
+ // get
+ const CIp &GetListenIp(void) const { return m_ListenIp; }
+ const CIp &GetAmbedIp(void) const { return m_AmbedIp; }
+ bool IsAmbedConnected(void) const { return m_bConnected; }
+
+ // manage streams
+ CCodecStream *GetStream(uint8);
+ void ReleaseStream(CCodecStream *);
+
+ // task
+ static void Thread(CTranscoder *);
+ void Task(void);
+
+protected:
+ // keepalive helpers
+ void HandleKeepalives(void);
+
+ // packet decoding helpers
+ bool IsValidKeepAlivePacket(const CBuffer &);
+ bool IsValidStreamDescrPacket(const CBuffer &, uint16 *, uint16 *);
+ bool IsValidNoStreamAvailablePacket(const CBuffer&);
+
+ // packet encoding helpers
+ void EncodeKeepAlivePacket(CBuffer *);
+ void EncodeOpenstreamPacket(CBuffer *, uint8, uint8);
+ void EncodeClosestreamPacket(CBuffer *, uint16);
+
+protected:
+ // IP's
+ CIp m_ListenIp;
+ CIp m_AmbedIp;
+
+ // streams
+ std::mutex m_Mutex;
+ std::vector m_Streams;
+
+ // sync objects for Openstream
+ CSemaphore m_SemaphoreOpenStream;
+ bool m_bStreamOpened;
+ uint16 m_StreamidOpenStream;
+ uint16 m_PortOpenStream;
+
+ // thread
+ bool m_bStopThread;
+ std::thread *m_pThread;
+
+ // socket
+ CUdpSocket m_Socket;
+ bool m_bConnected;
+
+ // time
+ CTimePoint m_LastKeepaliveTime;
+ CTimePoint m_LastActivityTime;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* ctranscoder_h */
diff --git a/ambedtest/cudpsocket.cpp b/ambedtest/cudpsocket.cpp
new file mode 100644
index 0000000..e108614
--- /dev/null
+++ b/ambedtest/cudpsocket.cpp
@@ -0,0 +1,173 @@
+//
+// cudpsocket.cpp
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include
+#include "cudpsocket.h"
+#include "ctranscoder.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// constructor
+
+CUdpSocket::CUdpSocket()
+{
+ m_Socket = -1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// destructor
+
+CUdpSocket::~CUdpSocket()
+{
+ if ( m_Socket != -1 )
+ {
+ Close();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// open & close
+
+bool CUdpSocket::Open(uint16 uiPort)
+{
+ bool open = false;
+
+ // create socket
+ m_Socket = socket(PF_INET,SOCK_DGRAM,0);
+ if ( m_Socket != -1 )
+ {
+ // initialize sockaddr struct
+ ::memset(&m_SocketAddr, 0, sizeof(struct sockaddr_in));
+ m_SocketAddr.sin_family = AF_INET;
+ m_SocketAddr.sin_port = htons(uiPort);
+ m_SocketAddr.sin_addr.s_addr = inet_addr(g_Transcoder.GetListenIp());
+
+ if ( bind(m_Socket, (struct sockaddr *)&m_SocketAddr, sizeof(struct sockaddr_in)) == 0 )
+ {
+ fcntl(m_Socket, F_SETFL, O_NONBLOCK);
+ open = true;
+ }
+ else
+ {
+ close(m_Socket);
+ m_Socket = -1;
+ }
+ }
+
+ // done
+ return open;
+}
+
+void CUdpSocket::Close(void)
+{
+ if ( m_Socket != -1 )
+ {
+ close(m_Socket);
+ m_Socket = -1;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// read
+
+int CUdpSocket::Receive(CBuffer *Buffer, CIp *Ip, int timeout)
+{
+ struct sockaddr_in Sin;
+ fd_set FdSet;
+ unsigned int uiFromLen = sizeof(struct sockaddr_in);
+ int iRecvLen = -1;
+ struct timeval tv;
+
+ // socket valid ?
+ if ( m_Socket != -1 )
+ {
+ // control socket
+ FD_ZERO(&FdSet);
+ FD_SET(m_Socket, &FdSet);
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ select(m_Socket + 1, &FdSet, 0, 0, &tv);
+
+ // allocate buffer
+ Buffer->resize(UDP_BUFFER_LENMAX);
+
+ // read
+ iRecvLen = (int)recvfrom(m_Socket,
+ (void *)Buffer->data(), UDP_BUFFER_LENMAX,
+ 0, (struct sockaddr *)&Sin, &uiFromLen);
+
+ // handle
+ if ( iRecvLen != -1 )
+ {
+ // adjust buffer size
+ Buffer->resize(iRecvLen);
+
+ // get IP
+ Ip->SetSockAddr(&Sin);
+ }
+ }
+
+ // done
+ return iRecvLen;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// write
+
+int CUdpSocket::Send(const CBuffer &Buffer, const CIp &Ip)
+{
+ CIp temp(Ip);
+ return (int)::sendto(m_Socket,
+ (void *)Buffer.data(), Buffer.size(),
+ 0, (struct sockaddr *)temp.GetSockAddr(), sizeof(struct sockaddr_in));
+}
+
+int CUdpSocket::Send(const char *Buffer, const CIp &Ip)
+{
+ CIp temp(Ip);
+ return (int)::sendto(m_Socket,
+ (void *)Buffer, ::strlen(Buffer),
+ 0, (struct sockaddr *)temp.GetSockAddr(), sizeof(struct sockaddr_in));
+}
+
+int CUdpSocket::Send(const CBuffer &Buffer, const CIp &Ip, uint16 destport)
+{
+ CIp temp(Ip);
+ temp.GetSockAddr()->sin_port = htons(destport);
+ return (int)::sendto(m_Socket,
+ (void *)Buffer.data(), Buffer.size(),
+ 0, (struct sockaddr *)temp.GetSockAddr(), sizeof(struct sockaddr_in));
+}
+
+int CUdpSocket::Send(const char *Buffer, const CIp &Ip, uint16 destport)
+{
+ CIp temp(Ip);
+ temp.GetSockAddr()->sin_port = htons(destport);
+ return (int)::sendto(m_Socket,
+ (void *)Buffer, ::strlen(Buffer),
+ 0, (struct sockaddr *)temp.GetSockAddr(), sizeof(struct sockaddr_in));
+}
+
+
diff --git a/ambedtest/cudpsocket.h b/ambedtest/cudpsocket.h
new file mode 100644
index 0000000..e91ed80
--- /dev/null
+++ b/ambedtest/cudpsocket.h
@@ -0,0 +1,78 @@
+//
+// cudpsocket.h
+// xlxd
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef cudpsocket_h
+#define cudpsocket_h
+
+#include
+//#include
+#include
+#include
+#include
+#include
+#include
+
+#include "cip.h"
+#include "cbuffer.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// define
+
+#define UDP_BUFFER_LENMAX 1024
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// class
+
+class CUdpSocket
+{
+public:
+ // constructor
+ CUdpSocket();
+
+ // destructor
+ ~CUdpSocket();
+
+ // open & close
+ bool Open(uint16);
+ void Close(void);
+ int GetSocket(void) { return m_Socket; }
+
+ // read
+ int Receive(CBuffer *, CIp *, int);
+
+ // write
+ int Send(const CBuffer &, const CIp &);
+ int Send(const CBuffer &, const CIp &, uint16);
+ int Send(const char *, const CIp &);
+ int Send(const char *, const CIp &, uint16);
+
+protected:
+ // data
+ int m_Socket;
+ struct sockaddr_in m_SocketAddr;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+#endif /* cudpsocket_h */
diff --git a/ambedtest/main.cpp b/ambedtest/main.cpp
new file mode 100644
index 0000000..c0d975a
--- /dev/null
+++ b/ambedtest/main.cpp
@@ -0,0 +1,77 @@
+//
+// main.cpp
+// ambedtest
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 12/05/2017.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#include "main.h"
+#include "ctranscoder.h"
+
+#define NB_STREAM 1
+
+int main(int argc, const char * argv[])
+{
+ std::vector Streams;
+
+ // check args
+ if ( argc != 5 )
+ {
+ std::cout << "Usage: ambedtest myip ambedip nbdmrstreams nbdstarstreams" << std::endl;
+ std::cout << "example: ambed 192.168.178.212 127.0.0.1 2 2" << std::endl;
+ return 1;
+ }
+
+ // init the transcoder
+ std::cout << "Connecting to ambed server " << std::endl;
+ g_Transcoder.Init(CIp(argv[1]), CIp(argv[2]));
+ while ( !g_Transcoder.IsAmbedConnected() );
+ std::cout << "Press enter to start test" << std::endl;
+ std::cin.get();
+
+ // create streams
+ int nDmr = atoi(argv[3]);
+ int nDstar = atoi(argv[4]);
+
+ for ( int i = 0; i < nDmr; i++ )
+ {
+ CTimePoint::TaskSleepFor(300);
+ Streams.push_back(g_Transcoder.GetStream(CODEC_AMBE2PLUS));
+ }
+
+ for ( int i = 0; i < nDstar; i++ )
+ {
+ CTimePoint::TaskSleepFor(300);
+ Streams.push_back(g_Transcoder.GetStream(CODEC_AMBEPLUS));
+ }
+
+ // and loop wait
+ std::cin.get();
+
+ // close
+ for ( int i = 0; i < Streams.size(); i++ )
+ {
+ g_Transcoder.ReleaseStream(Streams[i]);
+ }
+ g_Transcoder.Close();
+
+ // done
+ return 0;
+}
diff --git a/ambedtest/main.h b/ambedtest/main.h
new file mode 100644
index 0000000..b6bc7ed
--- /dev/null
+++ b/ambedtest/main.h
@@ -0,0 +1,103 @@
+//
+// main.h
+// ambedtest
+//
+// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
+// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
+//
+// ----------------------------------------------------------------------------
+// This file is part of xlxd.
+//
+// xlxd 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.
+//
+// xlxd 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 Foobar. If not, see .
+// ----------------------------------------------------------------------------
+
+#ifndef main_h
+#define main_h
+
+#include
+#include
+#include