diff --git a/CMakeLists.txt b/CMakeLists.txt index cbd02f45..5f0208d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ add_custom_target(test-verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose) add_subdirectory(src/basop/test_framework) add_subdirectory(src/basop/flc) add_subdirectory(src/bs1770demo) +add_subdirectory(src/dlyerr_2_errpat) add_subdirectory(src/eid) add_subdirectory(src/esdru) add_subdirectory(src/fir) diff --git a/doc/manual/eid.tex b/doc/manual/eid.tex index 8769dbe8..7b7594c4 100644 --- a/doc/manual/eid.tex +++ b/doc/manual/eid.tex @@ -1341,3 +1341,110 @@ \subsubsection{EID-EV G.192 Output frame examples } \label{fig:G192outputFrameLayer034ErrorIndividual} \end{center} \end{figure} + +%-------------------------------------- +\section {Delay/error profile to erasure pattern (dlyerr\_2\_errpat)} \label{DLYERR} +%-------------------------------------- + +\subsection{Description} + +The {\tt dlyerr\_2\_errpat} tool converts a delay-and-error profile into a +frame-erasure error pattern suitable for use with {\tt eid-xor}. This enables +simulation of jitter buffer management (JBM) behavior: packets that arrive +too late for playout are treated as lost, in addition to packets lost in +the network. + +The input profile contains one integer per line representing either: +\begin{itemize} +\item A network delay in milliseconds (0--1999), or +\item The value $-1$, indicating the packet was lost in the network. +\end{itemize} + +Two JBM emulation modes are supported: +\begin{itemize} +\item {\bf Constant-delay mode} ({\tt -d}): a fixed JBM delay in milliseconds. + Any packet with delay exceeding this threshold is marked as lost. +\item {\bf Bounded-loss-rate mode} ({\tt -l}): the tool automatically selects + the minimum JBM delay such that the combined late-loss rate stays + below the specified percentage. +\end{itemize} + +\subsection{Algorithm} + +Processing is performed in two passes over the input profile: + +\begin{enumerate} +\item {\bf Histogram pass}: reads the profile and builds a delay histogram + (counts per millisecond bin). In bounded-loss-rate mode, this histogram + is used to determine the JBM delay threshold by accumulating from the + highest delay downward until the target late-loss rate is exceeded. + +\item {\bf Output pass}: re-reads the profile and classifies each packet: + \begin{itemize} + \item delay $= -1$ (network loss) $\rightarrow$ frame erased + \item delay $>$ JBM threshold $\rightarrow$ frame erased (late loss) + \item otherwise $\rightarrow$ frame received (good) + \end{itemize} +\end{enumerate} + +If the profile is shorter than the requested output length ({\tt -L}), the +file is rewound and reused from the beginning. + +When {\tt -f~2} is specified, each packet decision produces two consecutive +frame-erasure flags in the output (for codecs sending two frames per packet). + +\subsection{Usage} + +{\tt\small +\begin{verbatim} +dlyerr_2_errpat [options] + + -i delay/error profile (required) + -o error pattern output (required) + -d constant-JBM-delay mode + -l bounded-loss-rate mode + -L output length (default: 7500) + -s skip initial frames in profile + -f <1|2> frames per packet (default: 1) + -w word-oriented G.192 output + -b byte-oriented G.192 output + -c text with LF (one entry per line) +\end{verbatim} +} + +Exactly one of {\tt -d} or {\tt -l} must be supplied. + +\subsection{Output formats} + +\begin{itemize} +\item {\bf Default text}: concatenated ASCII {\tt 0} (good) / {\tt 1} (erased) +\item {\bf Text with LF} ({\tt -c}): one digit per line +\item {\bf Byte G.192} ({\tt -b}): binary bytes 0x21 (good) / 0x20 (erased) +\item {\bf Word G.192} ({\tt -w}): binary 16-bit words 0x6B21 / 0x6B20 +\end{itemize} + +\subsection{Example} + +Apply MTSI delay/error profile with a fixed 200\,ms JBM delay, word-oriented +G.192 output, random offset into the profile: + +{\tt\small +\begin{verbatim} +dlyerr_2_errpat -d 200 -f 1 -w -s 1234 \ + -i dly_err_profile_1.dat -o ep1.g192 +eid-xor -fer -ep g192 bitstream.g192 ep1.g192 output.g192 +\end{verbatim} +} + +Bounded loss rate of 1\%, two frames per packet: + +{\tt\small +\begin{verbatim} +dlyerr_2_errpat -l 1 -f 2 -w -i dly_err_profile_5.dat -o ep5.g192 +\end{verbatim} +} + +\subsection{Origin} + +Provided by Fraunhofer IIS via 3GPP Tdoc S4-121077 (TSGS4\#70, Chicago, +13--17 Aug 2012), in support of the EVS reference codec processing plan. diff --git a/src/dlyerr_2_errpat/CMakeLists.txt b/src/dlyerr_2_errpat/CMakeLists.txt new file mode 100755 index 00000000..d3c6f0a6 --- /dev/null +++ b/src/dlyerr_2_errpat/CMakeLists.txt @@ -0,0 +1,25 @@ +add_executable(dlyerr_2_errpat dlyerr_2_errpat.c) + +# default text format, constant delay +add_test(dlyerr_2_errpat1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t1_text.proc -L 30 -d 40) +add_test(dlyerr_2_errpat1-verify ${CMAKE_COMMAND} -E compare_files + test_data/t1_text.ref test_data/t1_text.proc) + +# LF text format +add_test(dlyerr_2_errpat2 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t2_lf.proc -L 30 -d 40 -c) +add_test(dlyerr_2_errpat2-verify ${CMAKE_COMMAND} -E compare_files + test_data/t2_lf.ref test_data/t2_lf.proc) + +# word G.192 ASCII format with frame aggregation +add_test(dlyerr_2_errpat3 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t3_word.proc -L 30 -d 40 -w -f 2) +add_test(dlyerr_2_errpat3-verify ${CMAKE_COMMAND} -E compare_files + test_data/t3_word.ref test_data/t3_word.proc) + +# late-loss-rate driven threshold +add_test(dlyerr_2_errpat4 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t4_lateloss.proc -L 30 -l 5.0) +add_test(dlyerr_2_errpat4-verify ${CMAKE_COMMAND} -E compare_files + test_data/t4_lateloss.ref test_data/t4_lateloss.proc) diff --git a/src/dlyerr_2_errpat/README.md b/src/dlyerr_2_errpat/README.md new file mode 100644 index 00000000..0780a12c --- /dev/null +++ b/src/dlyerr_2_errpat/README.md @@ -0,0 +1,59 @@ + ============================================================= + COPYRIGHT NOTE: This source code, and all of its derivations, + is subject to the "ITU-T General Public License". Please have + it read in the distribution disk, or in the ITU-T + Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO + CODING STANDARDS". + ============================================================= + +# dlyerr_2_errpat + +Delay-and-error profile to frame-erasure pattern conversion tool. Reads a +delay/error profile (one entry per packet, giving network delay in ms and a +loss flag) and emits an error pattern suitable for use with the STL +`eid-xor` tool. Supports both fixed-delay and bounded-loss-rate jitter +buffer management emulation modes. + +Provided by Fraunhofer IIS via 3GPP Tdoc S4-121077 (TSGS4#70, Chicago, +13–17 Aug 2012), in support of the EVS reference codec processing plan. + +## Source files + + dlyerr_2_errpat.c: Tool source and main(); single-file program. + +## Usage + + dlyerr_2_errpat [options] + + -i delay/error profile (required) + -o error pattern (required) + -L output length in frames + -s shift/offset in frames into the profile + -f 1 or 2 + -l bounded-loss-rate mode, percent + -d constant-JBM-delay mode, milliseconds + -b byte-oriented G.192 format (0x21 ok, 0x20 lost) + -w word-oriented G.192 (0x6b21 ok, 0x6b20 lost) + -c append LF in text format (one entry per line; + was the default in V1.0) + +Exactly one of `-l` or `-d` must be supplied. + +## Examples (from S4-121077) + +Apply MTSI delay/error profile 1, 2, or 3 (fixed JBM delay) to a G.192 +bitstream: + + dlyerr_2_errpat -d 200 -f 1 -w -s YYY -i dly_err_profile_XXX.dat -o epXXX.g192 + eid-xor -fer g192bsin epXXX.g192 g192bsout + +Profiles 4 or 6 (bounded loss rate, 1 frame/packet): + + dlyerr_2_errpat -l 1 -f 1 -w -s YYY -i dly_err_profile_XXX.dat -o epXXX.g192 + +Profile 5 (bounded loss rate, 2 frames/packet): + + dlyerr_2_errpat -l 1 -f 2 -w -s YYY -i dly_err_profile_5.dat -o ep5.g192 + +`YYY` is a random offset into the profile (see the STL `random` tool for +generating reproducible offsets). diff --git a/src/dlyerr_2_errpat/dlyerr_2_errpat.c b/src/dlyerr_2_errpat/dlyerr_2_errpat.c new file mode 100644 index 00000000..f2729714 --- /dev/null +++ b/src/dlyerr_2_errpat/dlyerr_2_errpat.c @@ -0,0 +1,266 @@ +/*---------------------------------------------------------------------------* + * Delay-and-error profile to FER pattern conversion tool, V1.1 * + * ------------------------------------------ * + * (C) 2012 Fraunhofer IIS. All rights reserved. * + * * + * =============================================================== * + * COPYRIGHT NOTE: This source code, and all of its derivations, * + * is subject to the "ITU-T General Public License". Please have * + * it read in the distribution disk, or in the ITU-T Recommendation * + * G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". * + * See LICENSE.md in the top-level directory for terms. * + * =============================================================== * + * * + * Fraunhofer IIS makes no representation nor warranty in regard to * + * the accuracy, completeness or sufficiency of The Software, nor * + * shall Fraunhofer IIS be held liable for any damages whatsoever * + * relating to use of said Software. * + *---------------------------------------------------------------------------*/ + + +#include +#include +#include +#include + +static void usage() +{ + fprintf( stdout,"\nConvert a delay and error profile to an error pattern\n" ); + fprintf( stdout,"\nValid commandline switches:\n" ); + fprintf( stdout,"-i \n" ); + fprintf( stdout,"-o \n" ); + fprintf( stdout,"-L \n" ); + fprintf( stdout,"-s \n" ); + fprintf( stdout,"-f [1;2]\n" ); + fprintf( stdout,"-l \n" ); + fprintf( stdout,"-b use byte-oriented G.192 format (0x21 okay, 0x20 lost)\n" ); + fprintf( stdout,"-w use word-oriented G.192 format (0x6b21 okay, 0x6b20 lost)\n" ); + fprintf( stdout,"-c use LF for text format to have one entry per line - was default in V1.0\n" ); + fprintf( stdout,"-d \n" ); + fprintf( stdout," either -l or -d parameter must be supplied (not both!) and a valid inputfile\n"); +} + +int main( int argc, char** argv ) +{ + int i; + char *infilename = NULL; + char *outfilename = NULL; + float late_loss_rate = 0.0f; + unsigned int constant_delay_ms = 0; + int shift = 0; + int length = 7500; + unsigned int framesPerPacket = 1; /* no aggregation by default */ + int useG192 = 0; + int useG192WordOriented = 0; + int useLF = 0; + + char line [64] = {0}; + unsigned int delayCount [2001] = {0}; /* [-1;1999] ms */ + unsigned int line_cnt = 0; + + FILE * infile = NULL; + FILE * outfile = NULL; + + while (argc > 1 && argv[1][0] == '-') + if (strcmp (argv[1], "-i") == 0) { + infilename = argv[2]; + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-o") == 0) { + outfilename = argv[2]; + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-l") == 0) { + late_loss_rate = atof (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-d") == 0) { + constant_delay_ms = atoi (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-s") == 0) { + shift = atoi (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-L") == 0) { + length = atoi (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-f") == 0) { + framesPerPacket = atoi (argv[2]); + argc -= 2; + argv += 2; + } else if (strcmp (argv[1], "-b") == 0) { + useG192 = 1; + argc--; + argv++; + } else if (strcmp (argv[1], "-w") == 0) { + useG192 = 1; + useG192WordOriented = 1; + argc--; + argv++; + } else if (strcmp (argv[1], "-c") == 0) { + useLF = 1; + argc--; + argv++; + } else if (strcmp (argv[1], "-?") == 0 || strstr (argv[1], "-help")) { + usage(); + return 0; + } else { + fprintf (stderr, "ERROR! Invalid option \"%s\" in command line\n\n", argv[1]); + usage(); + return -1; + } + + if((infilename == NULL) || ((late_loss_rate == 0) && (constant_delay_ms == 0)) || + framesPerPacket == 0U || framesPerPacket > 2U) + { + usage(); + return -1; + } + + infile = fopen(infilename, "r"); + + if(!infile) + { + fprintf(stderr, "unable to open %s\n", infilename); + return -2; + } + + if(outfilename) + { + outfile = fopen(outfilename, "w"); + + if(!outfile) + { + fprintf(stderr, "unable to open %s\n", outfilename); + goto cleanup; + } + } + + /* read offset to Nirvana */ + for(i=0; i 1999)) + { + fprintf(stderr, "value in line %i out of range - aborting\n", line_cnt); + abort(); + } + if(delay_ms == -1) + delayCount[2000]++; + else + delayCount[delay_ms]++; + } + + if(late_loss_rate) + { + unsigned int late_loss_cnt = 0; + for(i=1999; i>=0; i--) + { + float current_late_loss_rate; + late_loss_cnt += delayCount[i]; + current_late_loss_rate = ((float)late_loss_cnt * 100.0f) / (float)line_cnt; + if(current_late_loss_rate > late_loss_rate) + { + constant_delay_ms = i; + fprintf(stdout, "selected %ims to stay below %f late loss\n", i, late_loss_rate); + break; + } + } + } + + /**** emulate constant delay JBM with given delay ****/ + fseek(infile, 0, SEEK_SET); + + /* read offset to nirvana, again */ + for(i=0; i constant_delay_ms) + { + late_loss_cnt++; + if(outfile) + { + for( iFramePerPacket = 0; iFramePerPacket != framesPerPacket; ++iFramePerPacket ) + fprintf(outfile, "%s", useG192 ? useG192WordOriented ? " k" : " " : useLF ? "1\n" : "1"); + } + } + else + { + if(outfile) + { + for( iFramePerPacket = 0; iFramePerPacket != framesPerPacket; ++iFramePerPacket ) + fprintf(outfile, "%s", useG192 ? useG192WordOriented ? "!k" : "!" : useLF ? "0\n" : "0"); + } + } + } + + fprintf(stdout, "#processed delay and error values: %i\n", line_cnt); + fprintf(stdout, "network loss rate: %.3f%%\n", ((float)network_loss_cnt * 100.0f) / (float)line_cnt); + fprintf(stdout, "late loss rate: %.3f%%\n", ((float)late_loss_cnt * 100.0f) / (float)line_cnt); + fprintf(stdout, "distorted frames: %d\n", framesPerPacket * (network_loss_cnt+late_loss_cnt) ); + } + +cleanup: + fclose(infile); + if(outfile) + fclose(outfile); + + return 0; +} diff --git a/src/dlyerr_2_errpat/test_data/profile.dat b/src/dlyerr_2_errpat/test_data/profile.dat new file mode 100755 index 00000000..22030ae8 --- /dev/null +++ b/src/dlyerr_2_errpat/test_data/profile.dat @@ -0,0 +1,30 @@ +10 +20 +5 +0 +30 +-1 +10 +40 +20 +5 +80 +10 +-1 +30 +20 +10 +5 +0 +60 +20 +10 +30 +-1 +40 +10 +50 +5 +20 +10 +10 diff --git a/src/dlyerr_2_errpat/test_data/t1_text.ref b/src/dlyerr_2_errpat/test_data/t1_text.ref new file mode 100755 index 00000000..58e58f24 --- /dev/null +++ b/src/dlyerr_2_errpat/test_data/t1_text.ref @@ -0,0 +1 @@ +000001000010100000100010010000 \ No newline at end of file diff --git a/src/dlyerr_2_errpat/test_data/t2_lf.ref b/src/dlyerr_2_errpat/test_data/t2_lf.ref new file mode 100755 index 00000000..24460fff --- /dev/null +++ b/src/dlyerr_2_errpat/test_data/t2_lf.ref @@ -0,0 +1,30 @@ +0 +0 +0 +0 +0 +1 +0 +0 +0 +0 +1 +0 +1 +0 +0 +0 +0 +0 +1 +0 +0 +0 +1 +0 +0 +1 +0 +0 +0 +0 diff --git a/src/dlyerr_2_errpat/test_data/t3_word.ref b/src/dlyerr_2_errpat/test_data/t3_word.ref new file mode 100755 index 00000000..31848379 --- /dev/null +++ b/src/dlyerr_2_errpat/test_data/t3_word.ref @@ -0,0 +1 @@ +!k!k!k!k!k!k!k!k!k!k k k!k!k!k!k!k!k!k!k k k!k!k k k!k!k!k!k!k!k!k!k!k!k k k!k!k!k!k!k!k k k!k!k!k!k k k!k!k!k!k!k!k!k!k \ No newline at end of file diff --git a/src/dlyerr_2_errpat/test_data/t4_lateloss.ref b/src/dlyerr_2_errpat/test_data/t4_lateloss.ref new file mode 100755 index 00000000..c49265e8 --- /dev/null +++ b/src/dlyerr_2_errpat/test_data/t4_lateloss.ref @@ -0,0 +1 @@ +000001000010100000000010000000 \ No newline at end of file diff --git a/src/eid/CMakeLists.txt b/src/eid/CMakeLists.txt index a83eb223..a0eb28e0 100644 --- a/src/eid/CMakeLists.txt +++ b/src/eid/CMakeLists.txt @@ -103,3 +103,31 @@ add_test(eid-xor ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/eid-xor -fer -bs bit -ep g192 add_test(eid-xor ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/eid-xor -fer -bs bit -ep byte test_data/zero.src test_data/epf05g10.byt test_data/z_f05g10.bby) add_test(eid-xor ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/eid-xor -fer -bs bit -ep bit test_data/zero.src test_data/epf05g10.bit test_data/z_f05g10.bbi) + +#--- dlyerr_2_errpat: delay/error profile to frame-erasure pattern (from 3GPP S4-121077) +add_executable(dlyerr_2_errpat dlyerr_2_errpat.c softbit.c) +target_link_libraries(dlyerr_2_errpat ${M_LIBRARY}) + +# default text format, constant delay +add_test(dlyerr_2_errpat1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t1_text.proc -L 30 -d 40) +add_test(dlyerr_2_errpat1-verify ${CMAKE_COMMAND} -E compare_files --ignore-eol + test_data/t1_text.ref test_data/t1_text.proc) + +# LF text format +add_test(dlyerr_2_errpat2 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t2_lf.proc -L 30 -d 40 -c) +add_test(dlyerr_2_errpat2-verify ${CMAKE_COMMAND} -E compare_files --ignore-eol + test_data/t2_lf.ref test_data/t2_lf.proc) + +# word G.192 format with frame aggregation +add_test(dlyerr_2_errpat3 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t3_word.proc -L 30 -d 40 -w -f 2) +add_test(dlyerr_2_errpat3-verify ${CMAKE_COMMAND} -E compare_files + test_data/t3_word.ref test_data/t3_word.proc) + +# late-loss-rate driven threshold +add_test(dlyerr_2_errpat4 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/dlyerr_2_errpat + -i test_data/profile.dat -o test_data/t4_lateloss.proc -L 30 -l 5.0) +add_test(dlyerr_2_errpat4-verify ${CMAKE_COMMAND} -E compare_files --ignore-eol + test_data/t4_lateloss.ref test_data/t4_lateloss.proc) diff --git a/src/eid/README.md b/src/eid/README.md index 6918115c..10a01f10 100644 --- a/src/eid/README.md +++ b/src/eid/README.md @@ -20,6 +20,7 @@ The "Error Insertion Device" (EID) module is built of the following files: eid-int.c: .... Interpolates error patterns from a master EP eid-xor.c: .... Disturbs bits or erases frames based on error patterns + dlyerr_2_errpat.c: Delay/error profile to frame-erasure pattern converter ep-stats.c: ... Assesses and prints statistics about an error pattern file gen-patt.c: ... Generates error pattern files softbit.c: .... Library with softbit file I/O and format check @@ -263,5 +264,46 @@ little-endian computers (PC/VAX/Alpha) are: Has not been implemented yet. + +## dlyerr_2_errpat: Delay/error profile to frame-erasure pattern + +Converts a delay-and-error profile (one integer per line: network delay in ms, +or -1 for network loss) into a frame-erasure error pattern for use with +`eid-xor`. Supports two jitter buffer management emulation modes. + +Usage: + + dlyerr_2_errpat [options] + + -i delay/error profile (required) + -o error pattern output (required) + -d constant-JBM-delay mode (milliseconds) + -l bounded-loss-rate mode (percent) + -L output length in frames (default: 7500) + -s skip offset frames into the profile + -f <1|2> frames per packet (default: 1) + -w word-oriented G.192 output + -b byte-oriented G.192 output + -c text mode with LF (one entry per line) + +Exactly one of `-d` or `-l` must be supplied. + +Input format: one integer per line. Values 0--1999 represent network delay in +milliseconds; -1 indicates network loss (packet never arrived). If the input +file is shorter than `-L` frames, it wraps around. + +Output: `0` = good frame, `1` = lost frame (text mode); or G.192 binary +equivalent with `-w` or `-b` flags. + +Example (fixed JBM delay of 200ms, word G.192 output): + + dlyerr_2_errpat -d 200 -f 1 -w -s 100 -i profile.dat -o ep.g192 + eid-xor -fer -ep g192 bitstream.g192 ep.g192 output.g192 + +Originally provided by Fraunhofer IIS via 3GPP Tdoc S4-121077 +(TSGS4#70, Chicago, 13--17 Aug 2012), in support of the EVS codec +processing plan. + + Good luck! -- diff --git a/src/eid/dlyerr_2_errpat.c b/src/eid/dlyerr_2_errpat.c new file mode 100644 index 00000000..e139e36e --- /dev/null +++ b/src/eid/dlyerr_2_errpat.c @@ -0,0 +1,253 @@ +/*---------------------------------------------------------------------------* + * Delay-and-error profile to FER pattern conversion tool, V1.2 * + * ------------------------------------------ * + * (C) 2012 Fraunhofer IIS. * + * * + * =============================================================== * + * COPYRIGHT NOTE: This source code, and all of its derivations, * + * is subject to the "ITU-T General Public License". Please have * + * it read in the distribution disk, or in the ITU-T Recommendation * + * G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". * + * See LICENSE.md in the top-level directory for terms. * + * =============================================================== * + * * + * Fraunhofer IIS makes no representation nor warranty in regard to * + * the accuracy, completeness or sufficiency of The Software, nor * + * shall Fraunhofer IIS be held liable for any damages whatsoever * + * relating to use of said Software. * + *---------------------------------------------------------------------------*/ + +#include +#include +#include +#include "ugstdemo.h" +#include "softbit.h" + +static void usage() { + fprintf(stdout, "\nConvert a delay and error profile to an error pattern\n"); + fprintf(stdout, "\nValid commandline switches:\n"); + fprintf(stdout, "-i \n"); + fprintf(stdout, "-o \n"); + fprintf(stdout, "-L \n"); + fprintf(stdout, "-s \n"); + fprintf(stdout, "-f [1;2]\n"); + fprintf(stdout, "-l \n"); + fprintf(stdout, "-b use byte-oriented G.192 format (0x21 okay, 0x20 lost)\n"); + fprintf(stdout, "-w use word-oriented G.192 format (0x6b21 okay, 0x6b20 lost)\n"); + fprintf(stdout, "-c use LF for text format to have one entry per line - was default in V1.0\n"); + fprintf(stdout, "-d \n"); + fprintf(stdout, " either -l or -d parameter must be supplied (not both!) and a valid inputfile\n"); +} + +/* Write one frame erasure flag to output */ +static void write_flag(short flag, int useG192, int useG192WordOriented, int useLF, FILE *outfile) { + if (useG192) { + if (useG192WordOriented) + save_g192(&flag, 1, outfile); + else + save_byte(&flag, 1, outfile); + } else { + /* Text mode: 0 = good, 1 = erased */ + if (useLF) + fprintf(outfile, "%c\n", flag == G192_FER ? '1' : '0'); + else + fprintf(outfile, "%c", flag == G192_FER ? '1' : '0'); + } +} + +int main(int argc, char **argv) { + int i; + char *infilename = NULL; + char *outfilename = NULL; + float late_loss_rate = 0.0f; + unsigned int constant_delay_ms = 0; + int shift = 0; + int length = 7500; + unsigned int framesPerPacket = 1; /* no aggregation by default */ + int useG192 = 0; + int useG192WordOriented = 0; + int useLF = 0; + int retval = 0; + + char line[64] = {0}; + unsigned int delayCount[2001] = {0}; /* [-1;1999] ms */ + unsigned int line_cnt = 0; + + FILE *infile = NULL; + FILE *outfile = NULL; + + while (argc > 1 && argv[1][0] == '-') { + if (strcmp(argv[1], "-i") == 0) { + if (argc < 3) { usage(); return -1; } + infilename = argv[2]; + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-o") == 0) { + if (argc < 3) { usage(); return -1; } + outfilename = argv[2]; + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-l") == 0) { + if (argc < 3) { usage(); return -1; } + late_loss_rate = (float)atof(argv[2]); + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-d") == 0) { + if (argc < 3) { usage(); return -1; } + constant_delay_ms = atoi(argv[2]); + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-s") == 0) { + if (argc < 3) { usage(); return -1; } + shift = atoi(argv[2]); + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-L") == 0) { + if (argc < 3) { usage(); return -1; } + length = atoi(argv[2]); + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-f") == 0) { + if (argc < 3) { usage(); return -1; } + framesPerPacket = atoi(argv[2]); + argc -= 2; argv += 2; + } else if (strcmp(argv[1], "-b") == 0) { + useG192 = 1; + argc--; argv++; + } else if (strcmp(argv[1], "-w") == 0) { + useG192 = 1; + useG192WordOriented = 1; + argc--; argv++; + } else if (strcmp(argv[1], "-c") == 0) { + useLF = 1; + argc--; argv++; + } else if (strcmp(argv[1], "-?") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0) { + usage(); + return 0; + } else { + fprintf(stderr, "ERROR! Invalid option \"%s\" in command line\n\n", argv[1]); + usage(); + return -1; + } + } + + if ((infilename == NULL) || ((late_loss_rate == 0) && (constant_delay_ms == 0)) || + framesPerPacket == 0U || framesPerPacket > 2U) { + usage(); + return -1; + } + + infile = fopen(infilename, "r"); + if (!infile) { + fprintf(stderr, "unable to open %s\n", infilename); + return -2; + } + + if (outfilename) { + outfile = fopen(outfilename, useG192 ? WB : "w"); + if (!outfile) { + fprintf(stderr, "unable to open %s\n", outfilename); + retval = -2; + goto cleanup; + } + } + + /* read offset to Nirvana */ + for (i = 0; i < shift; i++) { + if (!fgets(line, 63, infile)) { + fprintf(stderr, "shift out of range\n"); + retval = -3; + goto cleanup; + } + } + + /* create delay histogram */ + for (i = 0; i < length; i++) { + int delay_ms; + if (!fgets(line, 63, infile)) { + rewind(infile); + if (!fgets(line, 63, infile)) { + fprintf(stderr, "unable to rewind and read file\n"); + retval = -4; + goto cleanup; + } + } + line_cnt++; + delay_ms = atoi(line); + if ((delay_ms < -1) || (delay_ms > 1999)) { + fprintf(stderr, "value in line %u out of range - aborting\n", line_cnt); + retval = -5; + goto cleanup; + } + if (delay_ms == -1) + delayCount[2000]++; + else + delayCount[delay_ms]++; + } + + if (late_loss_rate) { + unsigned int late_loss_cnt = 0; + for (i = 1999; i >= 0; i--) { + float current_late_loss_rate; + late_loss_cnt += delayCount[i]; + current_late_loss_rate = ((float)late_loss_cnt * 100.0f) / (float)line_cnt; + if (current_late_loss_rate > late_loss_rate) { + constant_delay_ms = i; + fprintf(stdout, "selected %ims to stay below %f late loss\n", i, late_loss_rate); + break; + } + } + } + + /**** emulate constant delay JBM with given delay ****/ + fseek(infile, 0, SEEK_SET); + + /* read offset to nirvana, again */ + for (i = 0; i < shift; i++) { + if (!fgets(line, 63, infile)) { + fprintf(stderr, "shift out of range\n"); + retval = -3; + goto cleanup; + } + } + + if (constant_delay_ms) { + unsigned int late_loss_cnt = 0; + unsigned int network_loss_cnt = 0; + unsigned int iFramePerPacket; + for (i = 0; i < length; i++) { + int delay_ms; + short flag; + if (!fgets(line, 63, infile)) { + rewind(infile); + if (!fgets(line, 63, infile)) { + fprintf(stderr, "unable to rewind and read file\n"); + retval = -4; + goto cleanup; + } + } + delay_ms = atoi(line); + + if (delay_ms == -1) { + network_loss_cnt++; + flag = G192_FER; + } else if ((unsigned int)delay_ms > constant_delay_ms) { + late_loss_cnt++; + flag = G192_FER; + } else { + flag = G192_SYNC; + } + + if (outfile) { + for (iFramePerPacket = 0; iFramePerPacket != framesPerPacket; ++iFramePerPacket) + write_flag(flag, useG192, useG192WordOriented, useLF, outfile); + } + } + + fprintf(stdout, "#processed delay and error values: %u\n", line_cnt); + fprintf(stdout, "network loss rate: %.3f%%\n", ((float)network_loss_cnt * 100.0f) / (float)line_cnt); + fprintf(stdout, "late loss rate: %.3f%%\n", ((float)late_loss_cnt * 100.0f) / (float)line_cnt); + fprintf(stdout, "distorted frames: %u\n", framesPerPacket * (network_loss_cnt + late_loss_cnt)); + } + +cleanup: + fclose(infile); + if (outfile) + fclose(outfile); + + return retval; +} diff --git a/src/eid/test_data/profile.dat b/src/eid/test_data/profile.dat new file mode 100644 index 00000000..22030ae8 --- /dev/null +++ b/src/eid/test_data/profile.dat @@ -0,0 +1,30 @@ +10 +20 +5 +0 +30 +-1 +10 +40 +20 +5 +80 +10 +-1 +30 +20 +10 +5 +0 +60 +20 +10 +30 +-1 +40 +10 +50 +5 +20 +10 +10 diff --git a/src/eid/test_data/t1_text.ref b/src/eid/test_data/t1_text.ref new file mode 100644 index 00000000..58e58f24 --- /dev/null +++ b/src/eid/test_data/t1_text.ref @@ -0,0 +1 @@ +000001000010100000100010010000 \ No newline at end of file diff --git a/src/eid/test_data/t2_lf.ref b/src/eid/test_data/t2_lf.ref new file mode 100644 index 00000000..24460fff --- /dev/null +++ b/src/eid/test_data/t2_lf.ref @@ -0,0 +1,30 @@ +0 +0 +0 +0 +0 +1 +0 +0 +0 +0 +1 +0 +1 +0 +0 +0 +0 +0 +1 +0 +0 +0 +1 +0 +0 +1 +0 +0 +0 +0 diff --git a/src/eid/test_data/t3_word.ref b/src/eid/test_data/t3_word.ref new file mode 100644 index 00000000..31848379 --- /dev/null +++ b/src/eid/test_data/t3_word.ref @@ -0,0 +1 @@ +!k!k!k!k!k!k!k!k!k!k k k!k!k!k!k!k!k!k!k k k!k!k k k!k!k!k!k!k!k!k!k!k!k k k!k!k!k!k!k!k k k!k!k!k!k k k!k!k!k!k!k!k!k!k \ No newline at end of file diff --git a/src/eid/test_data/t4_lateloss.ref b/src/eid/test_data/t4_lateloss.ref new file mode 100644 index 00000000..c49265e8 --- /dev/null +++ b/src/eid/test_data/t4_lateloss.ref @@ -0,0 +1 @@ +000001000010100000000010000000 \ No newline at end of file