Skip to content

Commit

Permalink
feat: moved FEC implementation to nanors
Browse files Browse the repository at this point in the history
Fixed audio FEC encoding, Moonlight can now recover audio packets.

fixes #14
  • Loading branch information
ABeltramo committed Feb 4, 2023
1 parent 39e240e commit 7f86a47
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 780 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ build
cmake-build-*
DartConfiguration.tcl
.DS_Store
.run/
3 changes: 2 additions & 1 deletion docs/modules/dev/pages/code-structure.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Here we define core Moonlight functions in order to create the foundation for a
It currently hosts the followings:

* HTTP/S protocol interface: https://github.com/games-on-whales/wolf/blob/HEAD/src/moonlight/moonlight/protocol.hpp[protocol.hpp]
* Reed Solomon FEC implementation https://github.com/games-on-whales/wolf/blob/HEAD/src/moonlight/reedsolomon/rs.h[rs.h]
* Reed Solomon FEC header https://github.com/games-on-whales/wolf/blob/HEAD/src/moonlight/moonlight/fec.hpp[fec.hpp]
** Based on top of https://github.com/sleepybishop/nanors[sleepybishop/nanors]
* RTSP message parser https://github.com/games-on-whales/wolf/blob/HEAD/src/moonlight/rtsp/parser.hpp[parser.hpp]
** Built using https://github.com/yhirose/cpp-peglib[yhirose/cpp-peglib] by writing a formal PEG (Parsing Expression Grammars) definition for all messages

Expand Down
29 changes: 24 additions & 5 deletions src/moonlight/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ project(moonlight)
# Optionally glob, but only for CMake 3.12 or later:
file(GLOB HEADER_LIST CONFIGURE_DEPENDS
moonlight/*.hpp
reedsolomon/rs.h
rtsp/parser.hpp)


Expand All @@ -16,7 +15,6 @@ add_library(wolf::moonlight ALIAS moonlight)
target_sources(moonlight
PRIVATE
moonlight.cpp
reedsolomon/rs.c

PUBLIC
${HEADER_LIST})
Expand All @@ -36,6 +34,29 @@ FetchContent_MakeAvailable(immer)
target_link_libraries(moonlight PUBLIC immer)
unset(FPHSA_NAME_MISMATCHED)

# FEC implementation
FetchContent_Declare(
nanors
GIT_REPOSITORY https://github.com/sleepybishop/nanors.git
GIT_TAG 395e5ada44dd8d5974eaf6bb6b17f23406e3ca72)
FetchContent_GetProperties(nanors)
if (NOT nanors_POPULATED)
FetchContent_Populate(nanors)

add_library(nanors STATIC ${nanors_SOURCE_DIR}/rs.c)
add_library(nanors::nanors ALIAS nanors)
target_include_directories(nanors PUBLIC ${nanors_SOURCE_DIR} ${nanors_SOURCE_DIR}/deps/obl/)

target_sources(nanors
PRIVATE ${nanors_SOURCE_DIR}/rs.c
PUBLIC ${nanors_SOURCE_DIR}/rs.h)

set_source_files_properties(${nanors_SOURCE_DIR}/rs.c
PROPERTIES COMPILE_FLAGS "-include deps/obl/autoshim.h -ftree-vectorize")

target_link_libraries(moonlight PUBLIC nanors::nanors)
endif ()

# Additional algorithms for dealing with containers
FetchContent_Declare(
range
Expand Down Expand Up @@ -65,9 +86,7 @@ set_target_properties(moonlight PROPERTIES PUBLIC_HEADER .)
set_target_properties(moonlight PROPERTIES OUTPUT_NAME "moonlight")

# moonlight-common-c dependencies
target_include_directories(moonlight PUBLIC
./reedsolomon
./rtsp)
target_include_directories(moonlight PUBLIC ./rtsp)

find_package(Boost REQUIRED)

Expand Down
88 changes: 88 additions & 0 deletions src/moonlight/moonlight/fec.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once

#include <memory>

extern "C" {
#include <rs.h>
}

/**
* FEC (Forward Error Correction)
*
* Moonlight uses Reed Solomon ( https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction )
* to encode the payload so that it can be checked on the receiving end for transmission errors
* (and possibly fix them).
*
* This is just a small wrapper on top of the excellent https://github.com/sleepybishop/nanors implementation
*/
namespace moonlight::fec {

/**
* Maximum number of data shards that can be encoded in one go
*/
#define DATA_SHARDS_MAX 255

/**
* One time initialization required by the library
*/
inline void init() {
reed_solomon_init();
}

/**
* A smart pointer to the reed_solomon data structure, it will release the memory when going out of scope
*/
using rs_ptr = std::unique_ptr<reed_solomon, decltype(&reed_solomon_release)>;

/**
* Creates and allocates the required Reed Solomon data structure.
*
* @param data_shards Number of data shards to be encoded
* @param parity_shards Number of parity shards to be created
*
* @return A smart pointer, it will release the memory when going out of scope
*/
inline rs_ptr create(int data_shards, int parity_shards) {
auto rs = reed_solomon_new(data_shards, parity_shards);
return {rs, ::reed_solomon_release};
}

/**
* Encodes the input data shards using Reed Solomon.
* It will read \p nr_shards * \p block_size and then append all the newly created parity shards
* to \p shards.
*
* @warning \p shards MUST be of size: shards[data_shards + parity_shards][block_size]
* @warning The content of \p shards after \p nr_shards will be overwritten
*
* @param rs the reed solomon data structure created with `create()`
* @param shards[in, out] the memory location where data and parity blocks will live
* @param nr_shards the total number of shards ( data_shards + parity_shards )
* @param block_size the size of each block that needs to be encoded
*
* @return zero on success or an error code if failing.
*/
inline int encode(reed_solomon *rs, uint8_t **shards, int nr_shards, int block_size) {
return reed_solomon_encode(rs, shards, nr_shards, block_size);
}

/**
* Decodes back the input data shards using Reed Solomon.
* It will recreate missing blocks based on the \p marks property
*
* @warning \p shards MUST be of size: shards[data_shards + parity_shards][block_size]
* @warning The content of \p shards where blocks are missing will be overwritten
*
* @param rs the reed solomon data structure created with `create()`
* @param shards[in, out] the memory location where data and parity blocks will live
* @param marks an array of size \p nr_shards, if `marks[i] == 1` that blocks will be reconstructed
* @param nr_shards the total number of shards ( data_shards + parity_shards )
* @param block_size the size of each block that needs to be encoded
*
* @return zero on success or an error code if failing
*/
inline int decode(reed_solomon *rs, uint8_t **shards, uint8_t *marks, int nr_shards, int block_size) {
return reed_solomon_decode(rs, shards, marks, nr_shards, block_size);
}

} // namespace moonlight::fec
Loading

0 comments on commit 7f86a47

Please sign in to comment.