mondain

EFP.h

Jun 12th, 2020
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.00 KB | None | 0 0
  1. // ElasticFrameProtocol
  2. //
  3. // UnitX Edgeware AB 2020
  4. //
  5.  
  6. // Prefixes used
  7. // m class member
  8. // p pointer (*)
  9. // r reference (&)
  10. // h part of header
  11. // l local scope
  12.  
  13. #ifndef EFP_ELASTICFRAMEPROTOCOL_H
  14. #define EFP_ELASTICFRAMEPROTOCOL_H
  15.  
  16. #include <cstdint>
  17. #include <vector>
  18. #include <iostream>
  19. #include <sstream>
  20. #include <climits>
  21. #include <cstring>
  22. #include <cmath>
  23. #include <thread>
  24.  
  25. #ifndef _WIN64
  26.  
  27. #include <unistd.h>
  28.  
  29. #endif
  30.  
  31. #include <functional>
  32. #include <bitset>
  33. #include <mutex>
  34. #include <atomic>
  35. #include <algorithm>
  36. #include <deque>
  37. #include <condition_variable>
  38. #include <chrono>
  39.  
  40. ///Generate the uint32_t 'code' out of 4 characters provided
  41. #define EFP_CODE(c0, c1, c2, c3) (((c0)<<24) | ((c1)<<16) | ((c2)<<8) | (c3))
  42.  
  43. ///Enable or disable the APIs used by the unit tests
  44. #define UNIT_TESTS
  45.  
  46. ///The size of the circular buffer. Must be contiguous set bits defining the size  0b1111111111111 == 8191
  47. #define CIRCULAR_BUFFER_SIZE 0b1111111111111
  48.  
  49. /// Flag defines used py EFP
  50. #define NO_FLAGS 0b00000000
  51. #define INLINE_PAYLOAD 0b00010000
  52. #define UNDEFINED_FLAG1 0b00100000
  53. #define UNDEFINED_FLAG2 0b01000000
  54. #define UNDEFINED_FLAG3 0b10000000
  55.  
  56. #define EFP_MAJOR_VERSION 0
  57. #define EFP_MINOR_VERSION 1
  58.  
  59. //bitwise operations are used on members therefore the namespace is wrapping enum instead of 'enum class'
  60. /// Definition of the data types supported by EFP
  61. namespace ElasticFrameContentNamespace {
  62.     ///Payload data types
  63.     //Payload data defines ----- START ------
  64.     enum ElasticFrameContentDefines : uint8_t {
  65.         unknown,                //Standard                      //code
  66.         privatedata,            //Any user defined format       //USER (not needed)
  67.         adts,                   //Mpeg-4 AAC ADTS framing       //ADTS (not needed)
  68.         mpegts,                 //ITU-T H.222 188byte TS        //TSDT (not needed)
  69.         mpegpes,                //ITU-T H.222 PES packets       //MPES (not needed)
  70.         jpeg2000,               //ITU-T T.800 Annex M           //J2KV (not needed)
  71.         jpeg,                   //ITU-T.81                      //JPEG (not needed)
  72.         jpegxs,                 //ISO/IEC 21122-3               //JPXS (not needed)
  73.         pcmaudio,               //AES-3 framing                 //AES3 (not needed)
  74.         ndi,                    //*TBD*                         //NNDI (not needed)
  75.  
  76.         //Formats defined below (MSB='1') must also use 'code' to define the data format in the superframe
  77.  
  78.         didsdid = 0x80,           //FOURCC format                 //(FOURCC) (Must be the fourcc code for the format used)
  79.         sdi,                    //FOURCC format                 //(FOURCC) (Must be the fourcc code for the format used)
  80.         h264,                   //ITU-T H.264                   //ANXB = Annex B framing / AVCC = AVCC framing
  81.         h265                    //ITU-T H.265                   //ANXB = Annex B framing / AVCC = AVCC framing
  82.     };
  83.  
  84.     ///Embedded data types
  85.     //Embedded data defines ----- START ------
  86.     enum ElasticFrameEmbeddedContentDefines : uint8_t {
  87.         illegal,                //may not be used
  88.         embeddedprivatedata,    //private data
  89.         h222pmt,                //pmt from h222 pids should be truncated to uint8_t leaving the LSB bits only then map to streams
  90.         mp4fragbox,             //All boxes from a mp4 fragment excluding the payload
  91.         lastembeddedcontent = 0x80
  92.         //defines below here do not allow following embedded data.
  93.     };
  94.  
  95.     ///Embedded header define
  96.     //Embedded data header ----- START ------
  97.     struct ElasticEmbeddedHeader {
  98.         uint8_t embeddedFrameType = ElasticFrameEmbeddedContentDefines::illegal;
  99.         uint16_t size = 0;
  100.     };
  101. }
  102. using ElasticFrameContent = ElasticFrameContentNamespace::ElasticFrameContentDefines;
  103. using ElasticEmbeddedFrameContent = ElasticFrameContentNamespace::ElasticFrameEmbeddedContentDefines;
  104.  
  105. // EFP Messages
  106. // Negative numbers are errors
  107. // 0 == No error
  108. // Positive numbers are informative
  109. /// ElasticFrameMessages definitions
  110. enum class ElasticFrameMessages : int16_t {
  111.     tooLargeFrame = -10000,     //The frame is to large for EFP sender to handle
  112.     tooLargeEmbeddedData,       //The embedded data frame is too large.
  113.     unknownFrameType,           //The frame type is unknown by EFP receiver
  114.     frameSizeMismatch,          //The receiver received data less than the header size
  115.     internalCalculationError,   //The sender encountered a condition it can't handle
  116.     endOfPacketError,           //The receiver received a type2 fragment not saying it was the last
  117.     bufferOutOfBounds,          //The receiver circular buffer has wrapped around and all data in the buffer is from now untrusted also data prior to this may have been wrong.
  118.     //This error can be triggered if there is a super high data rate data coming in with a large gap/loss of the incoming fragments in the flow
  119.             bufferOutOfResources,       //This error is indicating there are no more buffer resources. In the unlikely event where all frames miss fragment(s) and the timeout is set high
  120.     //then broken superFrames will be buffered and new incoming data will claim buffers. When there are no more buffers to claim this error will be triggered.
  121.             reservedPTSValue,           //UINT64_MAX is a EFP reserved value
  122.     reservedDTSValue,           //UINT64_MAX is a EFP reserved value
  123.     reservedCodeValue,          //UINT32_MAX is a EFP reserved value
  124.     reservedStreamValue,        //0 is a EFP reserved value for signaling manifests
  125.     memoryAllocationError,      //Failed allocating system memory. This is fatal and results in unknown behaviour.
  126.     illegalEmbeddedData,        //illegal embedded data
  127.     type1And3SizeError,         //Type1 and Type3 must have the same header size
  128.     wrongMode,                  //mode is set to receiver when using the class as sender or the other way around
  129.     receiverNotRunning,         //The EFP receiver is not running
  130.     dtsptsDiffToLarge,          //PTS - DTS > UINT32_MAX
  131.  
  132.     noError = 0,
  133.  
  134.     notImplemented,             //feature/function/level/method/system aso. not implemented.
  135.     duplicatePacketReceived,    //If the underlying infrastructure is handing EFP duplicate segments the second packet of the duplicate will generate this error if the
  136.     //the superFrame is still not delivered to the host system. if it has then tooOldFragment will be returned instead.
  137.             tooOldFragment,             //if the superFrame has been delivered 100% complete or fragments of it due to a timeout and a fragment belonging to the superFrame arrives then it's
  138.     //discarded and the tooOldFragment is triggered.
  139.             receiverAlreadyStarted,     //The EFP receiver is already started no need to start it again. (Stop it and start it again to change parameters)
  140.     failedStoppingReceiver,     //The EFP receiver failed stopping it's resources.
  141.     parameterError,             //When starting the receiver the parameters given where not valid.
  142.     type0Frame                  //Type0 frame
  143. };
  144.  
  145. ///The mode set when constructing the class
  146. enum class ElasticFrameMode : uint8_t {
  147.     unknown,
  148.     sender,
  149.     receiver,
  150. };
  151.  
  152. /**
  153.  * \class ElasticFrameProtocol
  154.  *
  155.  * \brief Class for framing media on top of transport protocols
  156.  *
  157.  * ElasticFrameProtocol can be used to frame elementary streams on top of network protocols such as UDP, TCP, RIST and SRT
  158.  *
  159.  * \author UnitX
  160.  *
  161.  * Contact: bitbucket:andersced
  162.  *
  163.  */
  164. class ElasticFrameProtocol {
  165. public:
  166.     /**
  167.     * \class SuperFrame
  168.     *
  169.     * \brief Reserve frame-data aligned 32-byte addresses in memory
  170.     */
  171.     class SuperFrame {
  172.     public:
  173.         size_t mFrameSize = 0;           // Number of bytes in frame
  174.         uint8_t *pFrameData = nullptr;   // Received frame data
  175.         ElasticFrameContent mDataContent = ElasticFrameContent::unknown; // Superframe type
  176.         bool mBroken = true;
  177.         uint64_t mPts = UINT64_MAX;
  178.         uint64_t mDts = UINT64_MAX; //Should we implement this?
  179.         uint32_t mCode = UINT32_MAX;
  180.         uint8_t mStream = 0;
  181.         uint8_t mSource = 0;
  182.         uint8_t mFlags = NO_FLAGS;
  183.  
  184.         SuperFrame(const SuperFrame &) = delete;
  185.  
  186.         SuperFrame &operator=(const SuperFrame &) = delete;
  187.  
  188.         explicit SuperFrame(size_t memAllocSize) {
  189.  
  190.             int result = 0;
  191.  
  192.             //32 byte memory alignment for AVX2 processing.
  193.  
  194. #ifdef _WIN64
  195.             pFrameData = (uint8_t*)_aligned_malloc(memAllocSize, 32);
  196. #else
  197.             result = posix_memalign((void **) &pFrameData, 32,
  198.                                     memAllocSize);
  199. #endif
  200.  
  201.             if (pFrameData && !result) mFrameSize = memAllocSize;
  202.         }
  203.  
  204.         virtual ~SuperFrame() {
  205.             //Free if allocated
  206.             if (pFrameData)
  207. #ifdef _WIN64
  208.                 _aligned_free(pFrameData);
  209. #else
  210.                 free(pFrameData);
  211. #endif
  212.         }
  213.     };
  214.  
  215.     using pFramePtr = std::unique_ptr<SuperFrame>;
  216.  
  217.     ///Constructor
  218.     explicit ElasticFrameProtocol(uint16_t setMTU = 0, ElasticFrameMode mode = ElasticFrameMode::receiver);
  219.  
  220.     ///Destructor
  221.     virtual ~ElasticFrameProtocol();
  222.  
  223.     ///Return the version of the current implementation
  224.     uint16_t getVersion() { return (EFP_MAJOR_VERSION << 8) | EFP_MINOR_VERSION; }
  225.  
  226.     /**
  227.     * Segments data and calls the send callback
  228.     *
  229.     * @param rPacket The Data to be sent
  230.     * @param dataContent ElasticFrameContent::x where x is the type of data to be sent.
  231.     * @param pts the pts value of the content
  232.     * @param dts the dts value of the content
  233.     * @param code if msb (uint8_t) of ElasticFrameContent is set. Then code is used to further declare the content
  234.     * @param stream The EFP-stream number the data is associated with.
  235.     * @param flags signal what flags are used
  236.     * @return ElasticFrameMessages
  237.     */
  238.     ElasticFrameMessages
  239.     packAndSend(const std::vector<uint8_t> &rPacket, ElasticFrameContent dataContent, uint64_t pts, uint64_t dts,
  240.                 uint32_t code,
  241.                 uint8_t stream, uint8_t flags);
  242.  
  243.  
  244.     /**
  245.     * Send packet callback
  246.     *
  247.     * @param rSubPacket The data to send
  248.     */
  249.     std::function<void(const std::vector<uint8_t> &rSubPacket)> sendCallback = nullptr;
  250.  
  251.     /**
  252.     * Start the receiver worker
  253.     *
  254.     * @param bucketTimeoutMaster The time in bucketTimeoutMaster x 10m to wait for missing fragments
  255.     * @param holTimeoutMaster The time in holTimeoutMaster x 10m to wait for missing superFrames
  256.     * @return ElasticFrameMessages
  257.     */
  258.     ElasticFrameMessages startReceiver(uint32_t bucketTimeoutMaster, uint32_t holTimeoutMaster);
  259.  
  260.     /// Stop the reciever worker
  261.     ElasticFrameMessages stopReceiver();
  262.  
  263.     /**
  264.     * Method to feed the network fragments received
  265.     *
  266.     * @param rSubPacket The data received
  267.     * @param fromSource the unique EFP source id. Provided by the user of the EFP protocol
  268.     * @return ElasticFrameMessages
  269.     */
  270.     ElasticFrameMessages receiveFragment(const std::vector<uint8_t> &rSubPacket, uint8_t fromSource);
  271.  
  272.     /**
  273.     * Recieve data from the EFP worker thread
  274.     *
  275.     * @param rPacket superframe recieved
  276.     * rPacket conatins
  277.     * -> mCcontent ElasticFrameContent::x where x is the type of data to be sent.
  278.     * -> mBbroken if true the data integrety is broken by the underlying protocol.
  279.     * -> mPts the pts value of the content
  280.     * -> mDts the pts value of the content
  281.     * -> mCcode if msb (uint8_t) of ElasticFrameContent is set. Then code is used to further declare the content
  282.     * -> mStream The EFP-stream number the data is associated with.
  283.     * -> mFlags signal what flags are used
  284.     */
  285.     std::function<void(ElasticFrameProtocol::pFramePtr &rPacket)> receiveCallback = nullptr;
  286.  
  287.     ///Delete copy and move constructors and assign operators
  288.     ElasticFrameProtocol(ElasticFrameProtocol const &) = delete;              // Copy construct
  289.     ElasticFrameProtocol(ElasticFrameProtocol &&) = delete;                   // Move construct
  290.     ElasticFrameProtocol &operator=(ElasticFrameProtocol const &) = delete;   // Copy assign
  291.     ElasticFrameProtocol &operator=(ElasticFrameProtocol &&) = delete;        // Move assign
  292.  
  293.     //Help methods ----------- START ----------
  294.     /**
  295.     * Add embedded data infront of a superFrame
  296.     * These helper methods should not be used in production code
  297.     * the embedded data should be embedded prior to filling the payload content
  298.     *
  299.     * @param pPacket pointer to packet (superFrame)
  300.     * @param pPrivateData pointer to the private data
  301.     * @param privateDataSize size of private data
  302.     * @param content what the private data contains
  303.     * @param isLast is the last embedded data
  304.     * @return ElasticFrameMessages
  305.     */
  306.     ElasticFrameMessages addEmbeddedData(std::vector<uint8_t> *pPacket, void *pPrivateData, size_t privateDataSize,
  307.                                          ElasticEmbeddedFrameContent content = ElasticEmbeddedFrameContent::illegal,
  308.                                          bool isLast = false);
  309.  
  310.     /**
  311.     * Add embedded data infront of a superFrame
  312.     * These helper methods should not be used in production code
  313.     * the embedded data should be embedded prior to filling the payload content
  314.     *
  315.     * @param rPacket pointer to packet (superFrame)
  316.     * @param pEmbeddedDataList pointer to the private data 2D array
  317.     * @param pDataContent 1D array of the corresponding type to the extracted data (pEmbeddedDataList)
  318.     * @param pPayloadDataPosition pointer to location of payload relative superFrame start.
  319.     * @return ElasticFrameMessages
  320.     */
  321.     ElasticFrameMessages extractEmbeddedData(pFramePtr &rPacket, std::vector<std::vector<uint8_t>> *pEmbeddedDataList,
  322.                                              std::vector<uint8_t> *pDataContent, size_t *pPayloadDataPosition);
  323.     //Help methods ----------- END ----------
  324.  
  325.     //Used by unitTests ----START-----------------
  326. #ifdef UNIT_TESTS
  327.  
  328.     size_t geType1Size();
  329.  
  330.     size_t geType2Size();
  331.  
  332. #endif
  333.     //Used by unitTests ----END-----------------
  334.  
  335. private:
  336.     //Bucket  ----- START ------
  337.     class Bucket {
  338.     public:
  339.         bool mActive = false;
  340.         ElasticFrameContent mDataContent = ElasticFrameContent::unknown;
  341.         uint16_t mSavedSuperFrameNo = 0; //the SuperFrameNumber using this bucket.
  342.         uint32_t mTimeout = 0;
  343.         uint16_t mFragmentCounter = 0;
  344.         uint16_t mOfFragmentNo = 0;
  345.         uint64_t mDeliveryOrder = UINT64_MAX;
  346.         size_t mFragmentSize = 0;
  347.         uint64_t mPts = UINT64_MAX;
  348.         uint64_t mDts = UINT64_MAX;
  349.         uint32_t mCode = UINT32_MAX;
  350.         uint8_t mStream = 0;
  351.         uint8_t mSource = 0;
  352.         uint8_t mFlags = NO_FLAGS;
  353.         std::bitset<UINT16_MAX> mHaveReceivedPacket;
  354.         pFramePtr mBucketData = nullptr;
  355.     };
  356.     //Bucket ----- END ------
  357.  
  358.     //Stream list ----- START ------
  359.     struct Stream {
  360.         uint32_t code = UINT32_MAX;
  361.         ElasticFrameContent dataContent = ElasticFrameContent::unknown;
  362.     };
  363.     //Stream list ----- END ------
  364.  
  365.     //Private methods ----- START ------
  366.  
  367.     // Dummy callback
  368.     void sendData(const std::vector<uint8_t> &rSubPacket);
  369.  
  370.     // Dummy callback
  371.     void gotData(ElasticFrameProtocol::pFramePtr &rPacket);
  372.  
  373.     // Method dissecting Type1 fragments
  374.     ElasticFrameMessages unpackType1(const std::vector<uint8_t> &rSubPacket, uint8_t fromSource);
  375.  
  376.     // Method dissecting Type2 fragments
  377.     ElasticFrameMessages unpackType2LastFrame(const std::vector<uint8_t> &rSubPacket, uint8_t fromSource);
  378.  
  379.     // Method dissecting Type3 fragments
  380.     ElasticFrameMessages unpackType3(const std::vector<uint8_t> &rSubPacket, uint8_t fromSource);
  381.  
  382.     // The worker thread assembling fragments and delivering the superFrames
  383.     void receiverWorker(uint32_t timeout);
  384.  
  385.     void deliveryWorker();
  386.  
  387.     // Recalculate the 16-bit vector to a 64-bit vector
  388.     uint64_t superFrameRecalculator(uint16_t superFrame);
  389.     // Private methods ----- END ------
  390.  
  391.     // Internal lists and variables ----- START ------
  392.     Stream mStreams[UINT8_MAX]; //EFP-Stream information store
  393.     Bucket mBucketList[
  394.             CIRCULAR_BUFFER_SIZE + 1]; // Internal queue where all fragments are stored and superframes delivered from
  395.     uint32_t mBucketTimeout = 0; // Time out passed to receiver
  396.     uint32_t mHeadOfLineBlockingTimeout = 0; // HOL time out passed to receiver
  397.     std::mutex mNetMtx; //Mutex protecting the bucket queue
  398.     uint32_t mCurrentMTU = 0; //current MTU used by the sender
  399.     // Various counters to keep track of the different frames
  400.     uint16_t mSuperFrameNoGenerator = 0;
  401.     uint16_t mOldSuperFrameNumber = 0;
  402.     uint64_t mSuperFrameRecalc = 0;
  403.     bool mSuperFrameFirstTime = true;
  404.     // Receiver thread management
  405.     std::atomic_bool mIsWorkerThreadActive;
  406.     std::atomic_bool mIsDeliveryThreadActive;
  407.     std::atomic_bool mThreadActive;
  408.     // Mutex for thread safety
  409.     std::mutex mSendMtx; //Mutex protecting the send part
  410.     std::mutex mReceiveMtx; //Mutex protecting the recieve part
  411.     // Current mode
  412.     ElasticFrameMode mCurrentMode = ElasticFrameMode::unknown;
  413.     //
  414.     std::deque<pFramePtr> mSuperFrameQueue;
  415.     std::mutex mSuperFrameMtx;
  416.     std::condition_variable mSuperFrameDeliveryConditionVariable;
  417.     bool mSuperFrameReady = false;
  418.     // Internal lists and variables ----- END ------
  419. };
  420.  
  421. #endif //EFP_ELASTICFRAMEPROTOCOL_H
Add Comment
Please, Sign In to add comment