Browse Source

Merge remote-tracking branch 'origin/Over_The_Air_Update' into
Over_The_Air_Update

Conflicts:
.cproject
.settings/language.settings.xml
.settings/org.eclipse.cdt.core.prefs

Sebastien 5 years ago
parent
commit
3cb3050257

+ 246 - 0
.cproject

@@ -41,6 +41,7 @@
                             							
                             <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1562292378" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
                             							
+<<<<<<< HEAD
                             <builder arguments="${IDF_PATH}/tools/windows/eclipse_make.py" command="python" id="cdt.managedbuild.builder.gnu.cross.1011968237" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/>
                             							
                             <tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
@@ -198,6 +199,251 @@
                 <buildArguments>${IDF_PATH}/tools/windows/eclipse_make.py -j8 app PROJECT_NAME="recovery.custom"  EXTRA_CFLAGS=" -DRECOVERY_APPLICATION=1"</buildArguments>
                 				
                 <buildTarget>recovery</buildTarget>
+=======
+                            <builder arguments="" command="make" id="cdt.managedbuild.builder.gnu.cross.1011968237" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1614739014" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.c.linker.407309631" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.765822218" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.655344480" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+                                    									
+                                    <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+                                    									
+                                    <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+                                    								
+                                </inputType>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.archiver.1494383819" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.assembler.280698320" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1422333326" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+                                							
+                            </tool>
+                            						
+                        </toolChain>
+                        					
+                    </folderInfo>
+                    				
+                </configuration>
+                			
+            </storageModule>
+            			
+            <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+            		
+        </cconfiguration>
+        		
+        <cconfiguration id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495">
+            			
+            <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495" moduleId="org.eclipse.cdt.core.settings" name="recovery">
+                				
+                <externalSettings/>
+                				
+                <extensions>
+                    					
+                    <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                    					
+                    <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                    					
+                    <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                    					
+                    <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                    					
+                    <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+                    					
+                    <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                    				
+                </extensions>
+                			
+            </storageModule>
+            			
+            <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                				
+                <configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495" name="recovery" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=" parent="org.eclipse.cdt.build.core.emptycfg">
+                    					
+                    <folderInfo id="cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495." name="/" resourcePath="">
+                        						
+                        <toolChain id="cdt.managedbuild.toolchain.gnu.cross.base.1615055647" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.base">
+                            							
+                            <option id="cdt.managedbuild.option.gnu.cross.prefix.1669084138" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
+                            							
+                            <option id="cdt.managedbuild.option.gnu.cross.path.1601842088" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
+                            							
+                            <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.2065316488" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
+                            							
+                            <builder arguments="" command="make" id="cdt.managedbuild.builder.gnu.cross.442472928" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.802362645" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.123064752" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.425997512" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1366677700" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1792747550" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.199471499" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1088734719" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+                                    									
+                                    <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+                                    									
+                                    <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+                                    								
+                                </inputType>
+                                							
+                            </tool>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.archiver.878488427" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
+                            							
+                            <tool id="cdt.managedbuild.tool.gnu.cross.assembler.939007176" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
+                                								
+                                <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1973534280" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+                                							
+                            </tool>
+                            						
+                        </toolChain>
+                        					
+                    </folderInfo>
+                    				
+                </configuration>
+                			
+            </storageModule>
+            			
+            <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+            		
+        </cconfiguration>
+        	
+    </storageModule>
+    	
+    <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+        		
+        <project id="squeezelite-esp32.null.1272501664" name="squeezelite-esp32"/>
+        	
+    </storageModule>
+    	
+    <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+    	
+    <storageModule moduleId="scannerConfiguration">
+        		
+        <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+        		
+        <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1749746745;cdt.managedbuild.tool.gnu.cpp.compiler.input.1914005798">
+            			
+            <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+            		
+        </scannerConfigBuildInfo>
+        		
+        <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.cpp.compiler.254690821;cdt.managedbuild.tool.gnu.cpp.compiler.input.1365876654">
+            			
+            <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+            		
+        </scannerConfigBuildInfo>
+        		
+        <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1943329896;cdt.managedbuild.toolchain.gnu.cross.base.1943329896.30011915;cdt.managedbuild.tool.gnu.cross.c.compiler.2083405506;cdt.managedbuild.tool.gnu.c.compiler.input.404320567">
+            			
+            <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+            		
+        </scannerConfigBuildInfo>
+        		
+        <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.cross.base.1476804786;cdt.managedbuild.toolchain.gnu.cross.base.1476804786.1800826258;cdt.managedbuild.tool.gnu.cross.c.compiler.1502936757;cdt.managedbuild.tool.gnu.c.compiler.input.1614739014">
+            			
+            <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+            		
+        </scannerConfigBuildInfo>
+        	
+    </storageModule>
+    	
+    <storageModule moduleId="refreshScope" versionNumber="2">
+        		
+        <configuration configurationName="Default">
+            			
+            <resource resourceType="PROJECT" workspacePath="/squeezelite-esp32"/>
+            		
+        </configuration>
+        	
+    </storageModule>
+    	
+    <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
+        		
+        <buildTargets>
+            			
+            <target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+                				
+                <buildCommand>make</buildCommand>
+                				
+                <buildArguments>-j8</buildArguments>
+                				
+                <buildTarget>all</buildTarget>
+                				
+                <stopOnError>true</stopOnError>
+                				
+                <useDefaultCommand>false</useDefaultCommand>
+                				
+                <runAllBuilders>true</runAllBuilders>
+                			
+            </target>
+            			
+            <target name="size-components" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+                				
+                <buildCommand>make</buildCommand>
+                				
+                <buildArguments>-j2</buildArguments>
+                				
+                <buildTarget>size-components</buildTarget>
+                				
+                <stopOnError>true</stopOnError>
+                				
+                <useDefaultCommand>true</useDefaultCommand>
+                				
+                <runAllBuilders>true</runAllBuilders>
+                			
+            </target>
+            			
+            <target name="flash" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+                				
+                <buildCommand>make</buildCommand>
+                				
+                <buildArguments>-j2</buildArguments>
+                				
+                <buildTarget>flash</buildTarget>
+                				
+                <stopOnError>true</stopOnError>
+                				
+                <useDefaultCommand>true</useDefaultCommand>
+                				
+                <runAllBuilders>true</runAllBuilders>
+                			
+            </target>
+            			
+            <target name="recovery" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+                				
+                <buildCommand>make</buildCommand>
+                				
+                <buildArguments>-j8 PROJECT_NAME="recovery.custom"    EXTRA_CFLAGS=" -DRECOVERY_APPLICATION=1"</buildArguments>
+                				
+                <buildTarget>app</buildTarget>
+>>>>>>> refs/remotes/origin/Over_The_Air_Update
                 				
                 <stopOnError>true</stopOnError>
                 				

+ 23 - 0
.settings/org.eclipse.cdt.core.prefs

@@ -1,4 +1,5 @@
 eclipse.preferences.version=1
+<<<<<<< HEAD
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/delimiter=;
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/operation=append
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/value=1
@@ -8,5 +9,27 @@ environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PAT
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/delimiter=;
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/operation=replace
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/PATH/value=C\:/msys2/opt/xtensa-esp32-elf/bin;C\:/jdk-12.0.2/bin/server;C\:/jdk-12.0.2/bin;C\:\\Windows\\system32;C\:\\Windows;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C\:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR;C\:\\jdk-12.0.2\\bin;C\:\\Program Files\\PuTTY\\;C\:\\Program Files (x86)\\HP\\IdrsOCR_15.2.10.1114\\;C\:\\eclipse
+=======
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/operation=append
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/BATCH_BUILD/value=1
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/operation=replace
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/EXTRA_CFLAGS/value=-DRECOVERY_APPLICATION\=1
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/operation=replace
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/IDF_PATH/value=/var/opt/esp-idf
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/append=true
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786.212420495/appendContributed=true
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/operation=append
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/BATCH_BUILD/value=1
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/operation=replace
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/EXTRA_CFLAGS/value=
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/delimiter=\:
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/operation=replace
+environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/IDF_PATH/value=/var/opt/esp-idf
+>>>>>>> refs/remotes/origin/Over_The_Air_Update
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/append=true
 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1476804786/appendContributed=true

+ 2 - 0
components/cmd_nvs/component.mk

@@ -8,3 +8,5 @@
 #
 
 COMPONENT_ADD_INCLUDEDIRS := .
+CFLAGS += 	-I$(COMPONENT_PATH)/../squeezelite-ota
+

+ 95 - 95
components/raop/platform.h

@@ -1,4 +1,4 @@
-/*
+/*
  *  platform setting definition
  *
  *  (c) Philippe, philippe_44@outlook.com
@@ -16,112 +16,112 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- */
-
-#ifndef __PLATFORM_H
-#define __PLATFORM_H
-
-#ifdef WIN32
-#define LINUX     0
-#define WIN       1
-#else
-#define LINUX     1
-#define WIN       0
-#endif
-
-#include <stdbool.h>
-#include <signal.h>
-#include <sys/stat.h>
-
-#ifdef WIN32
-
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#include <iphlpapi.h>
-#include <sys/timeb.h>
-
-typedef unsigned __int8  u8_t;
-typedef unsigned __int16 u16_t;
-typedef unsigned __int32 u32_t;
-typedef unsigned __int64 u64_t;
-typedef __int16 s16_t;
-typedef __int32 s32_t;
-typedef __int64 s64_t;
-
-#define inline __inline
-
-int gettimeofday(struct timeval *tv, struct timezone *tz);
-char *strcasestr(const char *haystack, const char *needle);
+ */
+
+#ifndef __PLATFORM_H
+#define __PLATFORM_H
+
+#ifdef WIN32
+#define LINUX     0
+#define WIN       1
+#else
+#define LINUX     1
+#define WIN       0
+#endif
+
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#include <sys/timeb.h>
+
+typedef unsigned __int8  u8_t;
+typedef unsigned __int16 u16_t;
+typedef unsigned __int32 u32_t;
+typedef unsigned __int64 u64_t;
+typedef __int16 s16_t;
+typedef __int32 s32_t;
+typedef __int64 s64_t;
+
+#define inline __inline
+
+int gettimeofday(struct timeval *tv, struct timezone *tz);
+char *strcasestr(const char *haystack, const char *needle);
 
 #define usleep(x) 		Sleep((x)/1000)
-
#define sleep(x) 		Sleep((x)*1000)
-#define last_error() 	WSAGetLastError()
-#define ERROR_WOULDBLOCK WSAEWOULDBLOCK
-#define open 			_open
-#define read 			_read
-#define poll 			WSAPoll
-#define snprintf 		_snprintf
-#define strcasecmp 		stricmp
-#define _random(x) 		random(x)
-#define VALGRIND_MAKE_MEM_DEFINED(x,y)
-#define S_ADDR(X) X.S_un.S_addr
-
-#define in_addr_t 	u32_t
-#define socklen_t 	int
-#define ssize_t 	int
-
-#define RTLD_NOW 0
-
-#else
-
-#include <strings.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-/*
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/time.h>
-#include <netdb.h>
-*/
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <sys/poll.h>
-#include <lwip/inet.h>
-#include <pthread.h>
-#include <errno.h>
-
-#define min(a,b) (((a) < (b)) ? (a) : (b))
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-
-typedef int16_t   s16_t;
+
#define sleep(x) 		Sleep((x)*1000)
+#define last_error() 	WSAGetLastError()
+#define ERROR_WOULDBLOCK WSAEWOULDBLOCK
+#define open 			_open
+#define read 			_read
+#define poll 			WSAPoll
+#define snprintf 		_snprintf
+#define strcasecmp 		stricmp
+#define _random(x) 		random(x)
+#define VALGRIND_MAKE_MEM_DEFINED(x,y)
+#define S_ADDR(X) X.S_un.S_addr
+
+#define in_addr_t 	u32_t
+#define socklen_t 	int
+#define ssize_t 	int
+
+#define RTLD_NOW 0
+
+#else
+
+#include <strings.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+/*
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/time.h>
+#include <netdb.h>
+*/
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <lwip/inet.h>
+#include <pthread.h>
+#include <errno.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+typedef int16_t   s16_t;
 typedef int32_t   s32_t;
 typedef int64_t   s64_t;
 typedef uint8_t   u8_t;
 typedef uint16_t   u16_t;
 typedef uint32_t   u32_t;
 typedef unsigned long long u64_t;
-
-#define last_error() errno
-#define ERROR_WOULDBLOCK EWOULDBLOCK
-
-char *strlwr(char *str);
-#define _random(x) random()
-#define closesocket(s) close(s)
-#define S_ADDR(X) X.s_addr
-
-#endif
-
-typedef struct ntp_s {
+
+#define last_error() errno
+#define ERROR_WOULDBLOCK EWOULDBLOCK
+
+char *strlwr(char *str);
+#define _random(x) random()
+#define closesocket(s) close(s)
+#define S_ADDR(X) X.s_addr
+
+#endif
+
+typedef struct ntp_s {
 	u32_t seconds;
 	u32_t fraction;
 
} ntp_t;
-
+
 u64_t timeval_to_ntp(struct timeval tv, struct ntp_s *ntp);
-u64_t get_ntp(struct ntp_s *ntp);
-// we expect somebody to provide the ms clock, system-wide
-u32_t _gettime_ms_(void);
+u64_t get_ntp(struct ntp_s *ntp);
+// we expect somebody to provide the ms clock, system-wide
+u32_t _gettime_ms_(void);
 #define gettime_ms _gettime_ms_
 
 #endif     // __PLATFORM

+ 639 - 639
components/raop/raop.c

@@ -1,4 +1,4 @@
-/*
+/*
  *
  *  (c) Philippe 2019, philippe_44@outlook.com
  *
@@ -15,106 +15,106 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- */
-
-#include <stdio.h>
-
-#include "platform.h"
-
-#ifdef WIN32
-#include <openssl/err.h>
+ */
+
+#include <stdio.h>
+
+#include "platform.h"
+
+#ifdef WIN32
+#include <openssl/err.h>
 #include <openssl/rand.h>
 #include <openssl/rsa.h>
 #include <openssl/pem.h>
-#include <openssl/engine.h>
-#include "mdns.h"
-#include "mdnsd.h"
-#include "mdnssd-itf.h"
-#else
-#include "esp_pthread.h"
+#include <openssl/engine.h>
+#include "mdns.h"
+#include "mdnsd.h"
+#include "mdnssd-itf.h"
+#else
+#include "esp_pthread.h"
 #include "mdns.h"
-#include "mbedtls/version.h"
-#include <mbedtls/x509.h>
-#endif
-
-#include "util.h"
-#include "raop.h"
-#include "rtp.h"
-#include "dmap_parser.h"
-#include "log_util.h"
-
-#define RTSP_STACK_SIZE (8*1024)
-
-typedef struct raop_ctx_s {
-#ifdef WIN32
-	struct mdns_service *svc;
-	struct mdnsd *svr;
-#endif
-	struct in_addr host;	// IP of bridge
-	short unsigned port;    // RTSP port for AirPlay
-	int sock;               // socket of the above
-	struct in_addr peer;	// IP of the iDevice (airplay sender)
-	bool running;
-#ifdef WIN32
-	pthread_t thread, search_thread;
-#else
-	TaskHandle_t thread, search_thread, joiner;
-	StaticTask_t *xTaskBuffer;
-    StackType_t *xStack;
-#endif
-	unsigned char mac[6];
-	int latency;
-	struct {
-		char *aesiv, *aeskey;
-		char *fmtp;
-	} rtsp;
-	struct rtp_s *rtp;
-	raop_cmd_cb_t	cmd_cb;
-	raop_data_cb_t	data_cb;
-	/*
-	struct {
-		char				DACPid[32], id[32];
-		struct in_addr		host;
-		u16_t				port;
-		struct mDNShandle_s *handle;
-	} active_remote;
-	*/
-	void *owner;
-} raop_ctx_t;
-
-extern struct mdnsd* glmDNSServer;
-extern log_level	raop_loglevel;
-static log_level 	*loglevel = &raop_loglevel;
-
-static void*	rtsp_thread(void *arg);
-static bool 	handle_rtsp(raop_ctx_t *ctx, int sock);
-
-static char*	rsa_apply(unsigned char *input, int inlen, int *outlen, int mode);
-static int  	base64_pad(char *src, char **padded);
-static int 		base64_encode(const void *data, int size, char **str);
-static int 		base64_decode(const char *str, void *data);
-static void* 	search_remote(void *args);
-
+#include "mbedtls/version.h"
+#include <mbedtls/x509.h>
+#endif
+
+#include "util.h"
+#include "raop.h"
+#include "rtp.h"
+#include "dmap_parser.h"
+#include "log_util.h"
+
+#define RTSP_STACK_SIZE (8*1024)
+
+typedef struct raop_ctx_s {
+#ifdef WIN32
+	struct mdns_service *svc;
+	struct mdnsd *svr;
+#endif
+	struct in_addr host;	// IP of bridge
+	short unsigned port;    // RTSP port for AirPlay
+	int sock;               // socket of the above
+	struct in_addr peer;	// IP of the iDevice (airplay sender)
+	bool running;
+#ifdef WIN32
+	pthread_t thread, search_thread;
+#else
+	TaskHandle_t thread, search_thread, joiner;
+	StaticTask_t *xTaskBuffer;
+    StackType_t *xStack;
+#endif
+	unsigned char mac[6];
+	int latency;
+	struct {
+		char *aesiv, *aeskey;
+		char *fmtp;
+	} rtsp;
+	struct rtp_s *rtp;
+	raop_cmd_cb_t	cmd_cb;
+	raop_data_cb_t	data_cb;
+	/*
+	struct {
+		char				DACPid[32], id[32];
+		struct in_addr		host;
+		u16_t				port;
+		struct mDNShandle_s *handle;
+	} active_remote;
+	*/
+	void *owner;
+} raop_ctx_t;
+
+extern struct mdnsd* glmDNSServer;
+extern log_level	raop_loglevel;
+static log_level 	*loglevel = &raop_loglevel;
+
+static void*	rtsp_thread(void *arg);
+static bool 	handle_rtsp(raop_ctx_t *ctx, int sock);
+
+static char*	rsa_apply(unsigned char *input, int inlen, int *outlen, int mode);
+static int  	base64_pad(char *src, char **padded);
+static int 		base64_encode(const void *data, int size, char **str);
+static int 		base64_decode(const char *str, void *data);
+static void* 	search_remote(void *args);
+
 extern char private_key[];
 
enum { RSA_MODE_KEY, RSA_MODE_AUTH };
-
+
 
static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len);
-
-/*----------------------------------------------------------------------------*/
-struct raop_ctx_s *raop_create(struct in_addr host, char *name,
-						unsigned char mac[6], int latency,
-						raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) {
-	struct raop_ctx_s *ctx = malloc(sizeof(struct raop_ctx_s));
-	struct sockaddr_in addr;
-	char id[64];
+
+/*----------------------------------------------------------------------------*/
+struct raop_ctx_s *raop_create(struct in_addr host, char *name,
+						unsigned char mac[6], int latency,
+						raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) {
+	struct raop_ctx_s *ctx = malloc(sizeof(struct raop_ctx_s));
+	struct sockaddr_in addr;
+	char id[64];
 
#ifdef WIN32
 	socklen_t nlen = sizeof(struct sockaddr);
-	char *txt[] = { "am=airesp32", "tp=UDP", "sm=false", "sv=false", "ek=1",
-					"et=0,1", "md=0,1,2", "cn=0,1", "ch=2",
-					"ss=16", "sr=44100", "vn=3", "txtvers=1",
-					NULL };
-#else
-	mdns_txt_item_t txt[] = {
+	char *txt[] = { "am=airesp32", "tp=UDP", "sm=false", "sv=false", "ek=1",
+					"et=0,1", "md=0,1,2", "cn=0,1", "ch=2",
+					"ss=16", "sr=44100", "vn=3", "txtvers=1",
+					NULL };
+#else
+	mdns_txt_item_t txt[] = {
 		{"am", "airesp32"},
 		{"tp", "UDP"},
 		{"sm","false"},
@@ -129,26 +129,26 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
 		{"vn","3"},
 		{"txtvers","1"},
 	};
-
-#endif
-
-	if (!ctx) return NULL;
-
-	// make sure we have a clean context
-	memset(ctx, 0, sizeof(raop_ctx_t));
-
-#ifdef WIN32
-	ctx->svr = glmDNSServer;
-#endif
-	ctx->host = host;
-	ctx->sock = socket(AF_INET, SOCK_STREAM, 0);
-	ctx->cmd_cb = cmd_cb;
-	ctx->data_cb = data_cb;
-	ctx->latency = min(latency, 88200);
-	if (ctx->sock == -1) {
-		LOG_ERROR("Cannot create listening socket", NULL);
-		free(ctx);
-		return NULL;
+
+#endif
+
+	if (!ctx) return NULL;
+
+	// make sure we have a clean context
+	memset(ctx, 0, sizeof(raop_ctx_t));
+
+#ifdef WIN32
+	ctx->svr = glmDNSServer;
+#endif
+	ctx->host = host;
+	ctx->sock = socket(AF_INET, SOCK_STREAM, 0);
+	ctx->cmd_cb = cmd_cb;
+	ctx->data_cb = data_cb;
+	ctx->latency = min(latency, 88200);
+	if (ctx->sock == -1) {
+		LOG_ERROR("Cannot create listening socket", NULL);
+		free(ctx);
+		return NULL;
 	}
 
 	memset(&addr, 0, sizeof(addr));
@@ -183,16 +183,16 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
 	pthread_create(&ctx->thread, NULL, &rtsp_thread, ctx);
 
#else
 	LOG_INFO("starting mDNS with %s", id);
-	ESP_ERROR_CHECK( mdns_service_add(id, "_raop", "_tcp", ctx->port, txt, sizeof(txt) / sizeof(mdns_txt_item_t)) );
-	
+	ESP_ERROR_CHECK( mdns_service_add(id, "_raop", "_tcp", ctx->port, txt, sizeof(txt) / sizeof(mdns_txt_item_t)) );
+	
 	/*
-	xTaskCreate((TaskFunction_t) rtsp_thread, "RTSP_thread", 8*1024, ctx, ESP_TASK_PRIO_MIN + 1, &ctx->thread);
-	*/
-    ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
-    ctx->xStack = (StackType_t*) malloc(RTSP_STACK_SIZE);
-	ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtsp_thread, "RTSP_thread", RTSP_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->xStack, ctx->xTaskBuffer);
+	xTaskCreate((TaskFunction_t) rtsp_thread, "RTSP_thread", 8*1024, ctx, ESP_TASK_PRIO_MIN + 1, &ctx->thread);
+	*/
+    ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
+    ctx->xStack = (StackType_t*) malloc(RTSP_STACK_SIZE);
+	ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtsp_thread, "RTSP_thread", RTSP_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->xStack, ctx->xTaskBuffer);
 #endif
-
+
 	return ctx;
 }
 
@@ -203,12 +203,12 @@ void raop_delete(struct raop_ctx_s *ctx) {
 	struct sockaddr addr;
 	socklen_t nlen = sizeof(struct sockaddr);
 
-	if (!ctx) return;
-	
-#if !defined WIN32
-	ctx->joiner = xTaskGetCurrentTaskHandle();
-#endif
-
+	if (!ctx) return;
+	
+#if !defined WIN32
+	ctx->joiner = xTaskGetCurrentTaskHandle();
+#endif
+
 	ctx->running = false;
 
 	// wake-up thread by connecting socket, needed for freeBSD
@@ -220,8 +220,8 @@ void raop_delete(struct raop_ctx_s *ctx) {
 #ifdef WIN32
 	pthread_join(ctx->thread, NULL);
 #else
-	xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
-	free(ctx->xStack);
+	xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
+	free(ctx->xStack);
 	heap_caps_free(ctx->xTaskBuffer);
 #endif
 
@@ -237,28 +237,28 @@ void raop_delete(struct raop_ctx_s *ctx) {
 	/*
 	// terminate search, but do not reclaim memory of pthread if never launched
 	if (ctx->active_remote.handle) {
-		close_mDNS(ctx->active_remote.handle);
-		pthread_join(ctx->search_thread, NULL);
+		close_mDNS(ctx->active_remote.handle);
+		pthread_join(ctx->search_thread, NULL);
 	}
 	*/
 
 	NFREE(ctx->rtsp.aeskey);
-	NFREE(ctx->rtsp.aesiv);
-	NFREE(ctx->rtsp.fmtp);
-	
-	// stop broadcasting devices
-#ifdef WIN32
-	mdns_service_remove(ctx->svr, ctx->svc);
-	mdnsd_stop(ctx->svr);
-#else
-	mdns_service_remove("_raop", "_tcp");	
-#endif
-
+	NFREE(ctx->rtsp.aesiv);
+	NFREE(ctx->rtsp.fmtp);
+	
+	// stop broadcasting devices
+#ifdef WIN32
+	mdns_service_remove(ctx->svr, ctx->svc);
+	mdnsd_stop(ctx->svr);
+#else
+	mdns_service_remove("_raop", "_tcp");	
+#endif
+
 	free(ctx);
-}
-
-
-/*----------------------------------------------------------------------------*/
+}
+
+
+/*----------------------------------------------------------------------------*/
 void  raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
 /*
 	struct sockaddr_in addr;
@@ -290,7 +290,7 @@ void  raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
 	if (!command || !ctx->active_remote.port) {
 
		NFREE(command);
 
		return;
-
	}
+
	}
 
 	sock = socket(AF_INET, SOCK_STREAM, 0);
 
@@ -308,269 +308,269 @@ void  raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
 		kd_add(headers, "Active-Remote", ctx->active_remote.id);
 		kd_add(headers, "Connection", "close");
 
-		buf = http_send(sock, method, headers);
-		len = recv(sock, resp, 512, 0);
-		if (len > 0) resp[len-1] = '\0';
-		LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
-
-		NFREE(method);
-		NFREE(buf);
-		kd_free(headers);
-	}
-
-	free(command);
+		buf = http_send(sock, method, headers);
+		len = recv(sock, resp, 512, 0);
+		if (len > 0) resp[len-1] = '\0';
+		LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
+
+		NFREE(method);
+		NFREE(buf);
+		kd_free(headers);
+	}
+
+	free(command);
 
 	closesocket(sock);
 */
 	// then notify local system
 	ctx->cmd_cb(event, param);
-}
-
-/*----------------------------------------------------------------------------*/
-static void *rtsp_thread(void *arg) {
-	raop_ctx_t *ctx = (raop_ctx_t*) arg;
-	int  sock = -1;
-
-	while (ctx->running) {
-		fd_set rfds;
-		struct timeval timeout = {0, 100*1000};
-		int n;
-		bool res = false;
-
-		if (sock == -1) {
-			struct sockaddr_in peer;
-			socklen_t addrlen = sizeof(struct sockaddr_in);
-
-			sock = accept(ctx->sock, (struct sockaddr*) &peer, &addrlen);
-			ctx->peer.s_addr = peer.sin_addr.s_addr;
-
-			if (sock != -1 && ctx->running) {
-				LOG_INFO("got RTSP connection %u", sock);
-			} else continue;
-		}
-
-		FD_ZERO(&rfds);
-		FD_SET(sock, &rfds);
-
-		n = select(sock + 1, &rfds, NULL, NULL, &timeout);
-
-		if (!n) continue;
-
-		if (n > 0) res = handle_rtsp(ctx, sock);
-
-		if (n < 0 || !res) {
-			closesocket(sock);
-			LOG_INFO("RTSP close %u", sock);
-			sock = -1;
-		}
-	}
-
-	if (sock != -1) closesocket(sock);
-
-#ifndef WIN32
-	xTaskNotify(ctx->joiner, 0, eNoAction);
-	vTaskDelete(NULL);
-#endif
-
-	return NULL;
-}
-
-
-/*----------------------------------------------------------------------------*/
-static bool handle_rtsp(raop_ctx_t *ctx, int sock)
-{
-	char *buf = NULL, *body = NULL, method[16] = "";
-	key_data_t headers[16], resp[8] = { {NULL, NULL} };
-	int len;
-	bool success = true;
-	
-	if (!http_parse(sock, method, headers, &body, &len)) {
-		NFREE(body);
-		kd_free(headers);
-		return false;
-	}
-	
-	if (strcmp(method, "OPTIONS")) {
-		LOG_INFO("[%p]: received %s", ctx, method);
-	}
-
-	if ((buf = kd_lookup(headers, "Apple-Challenge")) != NULL) {
-		int n;
-		char *buf_pad, *p, *data_b64 = NULL, data[32];
-
-		LOG_INFO("[%p]: challenge %s", ctx, buf);
-
-		// need to pad the base64 string as apple device don't
-		base64_pad(buf, &buf_pad);
-
-		p = data + min(base64_decode(buf_pad, data), 32-10);
+}
+
+/*----------------------------------------------------------------------------*/
+static void *rtsp_thread(void *arg) {
+	raop_ctx_t *ctx = (raop_ctx_t*) arg;
+	int  sock = -1;
+
+	while (ctx->running) {
+		fd_set rfds;
+		struct timeval timeout = {0, 100*1000};
+		int n;
+		bool res = false;
+
+		if (sock == -1) {
+			struct sockaddr_in peer;
+			socklen_t addrlen = sizeof(struct sockaddr_in);
+
+			sock = accept(ctx->sock, (struct sockaddr*) &peer, &addrlen);
+			ctx->peer.s_addr = peer.sin_addr.s_addr;
+
+			if (sock != -1 && ctx->running) {
+				LOG_INFO("got RTSP connection %u", sock);
+			} else continue;
+		}
+
+		FD_ZERO(&rfds);
+		FD_SET(sock, &rfds);
+
+		n = select(sock + 1, &rfds, NULL, NULL, &timeout);
+
+		if (!n) continue;
+
+		if (n > 0) res = handle_rtsp(ctx, sock);
+
+		if (n < 0 || !res) {
+			closesocket(sock);
+			LOG_INFO("RTSP close %u", sock);
+			sock = -1;
+		}
+	}
+
+	if (sock != -1) closesocket(sock);
+
+#ifndef WIN32
+	xTaskNotify(ctx->joiner, 0, eNoAction);
+	vTaskDelete(NULL);
+#endif
+
+	return NULL;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static bool handle_rtsp(raop_ctx_t *ctx, int sock)
+{
+	char *buf = NULL, *body = NULL, method[16] = "";
+	key_data_t headers[16], resp[8] = { {NULL, NULL} };
+	int len;
+	bool success = true;
+	
+	if (!http_parse(sock, method, headers, &body, &len)) {
+		NFREE(body);
+		kd_free(headers);
+		return false;
+	}
+	
+	if (strcmp(method, "OPTIONS")) {
+		LOG_INFO("[%p]: received %s", ctx, method);
+	}
+
+	if ((buf = kd_lookup(headers, "Apple-Challenge")) != NULL) {
+		int n;
+		char *buf_pad, *p, *data_b64 = NULL, data[32];
+
+		LOG_INFO("[%p]: challenge %s", ctx, buf);
+
+		// need to pad the base64 string as apple device don't
+		base64_pad(buf, &buf_pad);
+
+		p = data + min(base64_decode(buf_pad, data), 32-10);
 		p = (char*) memcpy(p, &S_ADDR(ctx->host), 4) + 4;
-		p = (char*) memcpy(p, ctx->mac, 6) + 6;
-		memset(p, 0, 32 - (p - data));
-		p = rsa_apply((unsigned char*) data, 32, &n, RSA_MODE_AUTH);
-		n = base64_encode(p, n, &data_b64);
-
-		// remove padding as well (seems to be optional now)
-		for (n = strlen(data_b64) - 1; n > 0 && data_b64[n] == '='; data_b64[n--] = '\0');
-
-		kd_add(resp, "Apple-Response", data_b64);
-
-		NFREE(p);
-		NFREE(buf_pad);
-		NFREE(data_b64);
-	}
-	
-	if (!strcmp(method, "OPTIONS")) {
-
-		kd_add(resp, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
-
-	} else if (!strcmp(method, "ANNOUNCE")) {
-		char *padded, *p;
-
-		NFREE(ctx->rtsp.aeskey);
-		NFREE(ctx->rtsp.aesiv);
-		NFREE(ctx->rtsp.fmtp);
-		
-		// LMS might has taken over the player, leaving us with a running RTP session
-		if (ctx->rtp) {
-			LOG_INFO("[%p]: closing unfinished RTP session", ctx);
-			rtp_end(ctx->rtp);
-		}	
-
-		if ((p = strcasestr(body, "rsaaeskey")) != NULL) {
-			unsigned char *aeskey;
-			int len, outlen;
-
-			p = strextract(p, ":", "\r\n");
-			base64_pad(p, &padded);
-			aeskey = malloc(strlen(padded));
-			len = base64_decode(padded, aeskey);
-			ctx->rtsp.aeskey = rsa_apply(aeskey, len, &outlen, RSA_MODE_KEY);
-
-			NFREE(p);
-			NFREE(aeskey);
-			NFREE(padded);
-		}
-
-		if ((p = strcasestr(body, "aesiv")) != NULL) {
-			p = strextract(p, ":", "\r\n");
-			base64_pad(p, &padded);
-			ctx->rtsp.aesiv = malloc(strlen(padded));
-			base64_decode(padded, ctx->rtsp.aesiv);
-
-			NFREE(p);
-			NFREE(padded);
-		}
-
-		if ((p = strcasestr(body, "fmtp")) != NULL) {
-			p = strextract(p, ":", "\r\n");
-			ctx->rtsp.fmtp = strdup(p);
-			NFREE(p);
-		}
-
-		// on announce, search remote
-		/*
-		if ((buf = kd_lookup(headers, "DACP-ID")) != NULL) strcpy(ctx->active_remote.DACPid, buf);
-		if ((buf = kd_lookup(headers, "Active-Remote")) != NULL) strcpy(ctx->active_remote.id, buf);
-
-		ctx->active_remote.handle = init_mDNS(false, ctx->host);
-		pthread_create(&ctx->search_thread, NULL, &search_remote, ctx);
-		*/
-
-	} else if (!strcmp(method, "SETUP") && ((buf = kd_lookup(headers, "Transport")) != NULL)) {
-		char *p;
-		rtp_resp_t rtp = { 0 };
-		short unsigned tport = 0, cport = 0;
-
-		// we are about to stream, do something if needed
-		ctx->cmd_cb(RAOP_SETUP, NULL);
-
-		if ((p = strcasestr(buf, "timing_port")) != NULL) sscanf(p, "%*[^=]=%hu", &tport);
-		if ((p = strcasestr(buf, "control_port")) != NULL) sscanf(p, "%*[^=]=%hu", &cport);
-
-		rtp = rtp_init(ctx->peer, ctx->latency,	ctx->rtsp.aeskey, ctx->rtsp.aesiv,
-					   ctx->rtsp.fmtp, cport, tport, ctx->cmd_cb, ctx->data_cb);
-						
-		ctx->rtp = rtp.ctx;
-		
-		if (cport * tport * rtp.cport * rtp.tport * rtp.aport && rtp.ctx) {
-			char *transport;
-			asprintf(&transport, "RTP/AVP/UDP;unicast;mode=record;control_port=%u;timing_port=%u;server_port=%u", rtp.cport, rtp.tport, rtp.aport);
-			LOG_DEBUG("[%p]: audio=(%hu:%hu), timing=(%hu:%hu), control=(%hu:%hu)", ctx, 0, rtp.aport, tport, rtp.tport, cport, rtp.cport);
-			kd_add(resp, "Transport", transport);
-			kd_add(resp, "Session", "DEADBEEF");
-			free(transport);
-		} else {
-			success = false;
-			LOG_INFO("[%p]: cannot start session, missing ports", ctx);
-		}
-
-	} else if (!strcmp(method, "RECORD")) {
-		unsigned short seqno = 0;
-		unsigned rtptime = 0;
-		char *p;
-
-		if (ctx->latency) {
-			char latency[6];
-			snprintf(latency, 6, "%u", ctx->latency);
-			kd_add(resp, "Audio-Latency", latency);
-		}
-
-		buf = kd_lookup(headers, "RTP-Info");
-		if (buf && (p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
-		if (buf && (p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
-
-		if (ctx->rtp) rtp_record(ctx->rtp, seqno, rtptime);
-
-		ctx->cmd_cb(RAOP_STREAM, NULL);
-
-	}  else if (!strcmp(method, "FLUSH")) {
-		unsigned short seqno = 0;
-		unsigned rtptime = 0;
-		char *p;
-
-		buf = kd_lookup(headers, "RTP-Info");
-		if ((p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
-		if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
-
-		// only send FLUSH if useful (discards frames above buffer head and top)
-		if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime))
-			ctx->cmd_cb(RAOP_FLUSH, NULL);
-
-	}  else if (!strcmp(method, "TEARDOWN")) {
-
-		rtp_end(ctx->rtp);
-
-		ctx->rtp = NULL;
-
-		/*
-		// need to make sure no search is on-going and reclaim pthread memory
-		if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle);
-		pthread_join(ctx->search_thread, NULL);
-		memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
-		*/
-
-		NFREE(ctx->rtsp.aeskey);
-		NFREE(ctx->rtsp.aesiv);
-		NFREE(ctx->rtsp.fmtp);
-
-		ctx->cmd_cb(RAOP_STOP, NULL);
-
-	} if (!strcmp(method, "SET_PARAMETER")) {
-		char *p;
-
-		if (body && (p = strcasestr(body, "volume")) != NULL) {
-			float volume;
-
-			sscanf(p, "%*[^:]:%f", &volume);
-			LOG_INFO("[%p]: SET PARAMETER volume %f", ctx, volume);
-			volume = (volume == -144.0) ? 0 : (1 + volume / 30);
-			ctx->cmd_cb(RAOP_VOLUME, &volume);
-		}
-/*
-		if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) {
-			struct metadata_s metadata;
-			dmap_settings settings = {
+		p = (char*) memcpy(p, ctx->mac, 6) + 6;
+		memset(p, 0, 32 - (p - data));
+		p = rsa_apply((unsigned char*) data, 32, &n, RSA_MODE_AUTH);
+		n = base64_encode(p, n, &data_b64);
+
+		// remove padding as well (seems to be optional now)
+		for (n = strlen(data_b64) - 1; n > 0 && data_b64[n] == '='; data_b64[n--] = '\0');
+
+		kd_add(resp, "Apple-Response", data_b64);
+
+		NFREE(p);
+		NFREE(buf_pad);
+		NFREE(data_b64);
+	}
+	
+	if (!strcmp(method, "OPTIONS")) {
+
+		kd_add(resp, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
+
+	} else if (!strcmp(method, "ANNOUNCE")) {
+		char *padded, *p;
+
+		NFREE(ctx->rtsp.aeskey);
+		NFREE(ctx->rtsp.aesiv);
+		NFREE(ctx->rtsp.fmtp);
+		
+		// LMS might has taken over the player, leaving us with a running RTP session
+		if (ctx->rtp) {
+			LOG_INFO("[%p]: closing unfinished RTP session", ctx);
+			rtp_end(ctx->rtp);
+		}	
+
+		if ((p = strcasestr(body, "rsaaeskey")) != NULL) {
+			unsigned char *aeskey;
+			int len, outlen;
+
+			p = strextract(p, ":", "\r\n");
+			base64_pad(p, &padded);
+			aeskey = malloc(strlen(padded));
+			len = base64_decode(padded, aeskey);
+			ctx->rtsp.aeskey = rsa_apply(aeskey, len, &outlen, RSA_MODE_KEY);
+
+			NFREE(p);
+			NFREE(aeskey);
+			NFREE(padded);
+		}
+
+		if ((p = strcasestr(body, "aesiv")) != NULL) {
+			p = strextract(p, ":", "\r\n");
+			base64_pad(p, &padded);
+			ctx->rtsp.aesiv = malloc(strlen(padded));
+			base64_decode(padded, ctx->rtsp.aesiv);
+
+			NFREE(p);
+			NFREE(padded);
+		}
+
+		if ((p = strcasestr(body, "fmtp")) != NULL) {
+			p = strextract(p, ":", "\r\n");
+			ctx->rtsp.fmtp = strdup(p);
+			NFREE(p);
+		}
+
+		// on announce, search remote
+		/*
+		if ((buf = kd_lookup(headers, "DACP-ID")) != NULL) strcpy(ctx->active_remote.DACPid, buf);
+		if ((buf = kd_lookup(headers, "Active-Remote")) != NULL) strcpy(ctx->active_remote.id, buf);
+
+		ctx->active_remote.handle = init_mDNS(false, ctx->host);
+		pthread_create(&ctx->search_thread, NULL, &search_remote, ctx);
+		*/
+
+	} else if (!strcmp(method, "SETUP") && ((buf = kd_lookup(headers, "Transport")) != NULL)) {
+		char *p;
+		rtp_resp_t rtp = { 0 };
+		short unsigned tport = 0, cport = 0;
+
+		// we are about to stream, do something if needed
+		ctx->cmd_cb(RAOP_SETUP, NULL);
+
+		if ((p = strcasestr(buf, "timing_port")) != NULL) sscanf(p, "%*[^=]=%hu", &tport);
+		if ((p = strcasestr(buf, "control_port")) != NULL) sscanf(p, "%*[^=]=%hu", &cport);
+
+		rtp = rtp_init(ctx->peer, ctx->latency,	ctx->rtsp.aeskey, ctx->rtsp.aesiv,
+					   ctx->rtsp.fmtp, cport, tport, ctx->cmd_cb, ctx->data_cb);
+						
+		ctx->rtp = rtp.ctx;
+		
+		if (cport * tport * rtp.cport * rtp.tport * rtp.aport && rtp.ctx) {
+			char *transport;
+			asprintf(&transport, "RTP/AVP/UDP;unicast;mode=record;control_port=%u;timing_port=%u;server_port=%u", rtp.cport, rtp.tport, rtp.aport);
+			LOG_DEBUG("[%p]: audio=(%hu:%hu), timing=(%hu:%hu), control=(%hu:%hu)", ctx, 0, rtp.aport, tport, rtp.tport, cport, rtp.cport);
+			kd_add(resp, "Transport", transport);
+			kd_add(resp, "Session", "DEADBEEF");
+			free(transport);
+		} else {
+			success = false;
+			LOG_INFO("[%p]: cannot start session, missing ports", ctx);
+		}
+
+	} else if (!strcmp(method, "RECORD")) {
+		unsigned short seqno = 0;
+		unsigned rtptime = 0;
+		char *p;
+
+		if (ctx->latency) {
+			char latency[6];
+			snprintf(latency, 6, "%u", ctx->latency);
+			kd_add(resp, "Audio-Latency", latency);
+		}
+
+		buf = kd_lookup(headers, "RTP-Info");
+		if (buf && (p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
+		if (buf && (p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
+
+		if (ctx->rtp) rtp_record(ctx->rtp, seqno, rtptime);
+
+		ctx->cmd_cb(RAOP_STREAM, NULL);
+
+	}  else if (!strcmp(method, "FLUSH")) {
+		unsigned short seqno = 0;
+		unsigned rtptime = 0;
+		char *p;
+
+		buf = kd_lookup(headers, "RTP-Info");
+		if ((p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
+		if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
+
+		// only send FLUSH if useful (discards frames above buffer head and top)
+		if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime))
+			ctx->cmd_cb(RAOP_FLUSH, NULL);
+
+	}  else if (!strcmp(method, "TEARDOWN")) {
+
+		rtp_end(ctx->rtp);
+
+		ctx->rtp = NULL;
+
+		/*
+		// need to make sure no search is on-going and reclaim pthread memory
+		if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle);
+		pthread_join(ctx->search_thread, NULL);
+		memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
+		*/
+
+		NFREE(ctx->rtsp.aeskey);
+		NFREE(ctx->rtsp.aesiv);
+		NFREE(ctx->rtsp.fmtp);
+
+		ctx->cmd_cb(RAOP_STOP, NULL);
+
+	} if (!strcmp(method, "SET_PARAMETER")) {
+		char *p;
+
+		if (body && (p = strcasestr(body, "volume")) != NULL) {
+			float volume;
+
+			sscanf(p, "%*[^:]:%f", &volume);
+			LOG_INFO("[%p]: SET PARAMETER volume %f", ctx, volume);
+			volume = (volume == -144.0) ? 0 : (1 + volume / 30);
+			ctx->cmd_cb(RAOP_VOLUME, &volume);
+		}
+/*
+		if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) {
+			struct metadata_s metadata;
+			dmap_settings settings = {
 				NULL, NULL, NULL, NULL,	NULL, NULL,	NULL, on_dmap_string, NULL,
 				NULL
 			};
@@ -581,266 +581,266 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
 				LOG_INFO("[%p]: received metadata\n\tartist: %s\n\talbum:  %s\n\ttitle:  %s",
 						 ctx, metadata.artist, metadata.album, metadata.title);
 				free_metadata(&metadata);
-			}
+			}
 		}
 */
 	}
-
-	// don't need to free "buf" because kd_lookup return a pointer, not a strdup
-
-	kd_add(resp, "Audio-Jack-Status", "connected; type=analog");
-	kd_add(resp, "CSeq", kd_lookup(headers, "CSeq"));
-
-	if (success) buf = http_send(sock, "RTSP/1.0 200 OK", resp);
-	else buf = http_send(sock, "RTSP/1.0 500 ERROR", NULL);
-
-	if (strcmp(method, "OPTIONS")) {
-		LOG_INFO("[%p]: responding:\n%s", ctx, buf ? buf : "<void>");
-	}
-
-	NFREE(body);
-	NFREE(buf);
-	kd_free(resp);
-	kd_free(headers);
-
-	return true;
-}
-
-/*----------------------------------------------------------------------------*/
-/*
-bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) {
-	mDNSservice_t *s;
-	raop_ctx_t *ctx = (raop_ctx_t*) cookie;
-
-	// see if we have found an active remote for our ID
+
+	// don't need to free "buf" because kd_lookup return a pointer, not a strdup
+
+	kd_add(resp, "Audio-Jack-Status", "connected; type=analog");
+	kd_add(resp, "CSeq", kd_lookup(headers, "CSeq"));
+
+	if (success) buf = http_send(sock, "RTSP/1.0 200 OK", resp);
+	else buf = http_send(sock, "RTSP/1.0 500 ERROR", NULL);
+
+	if (strcmp(method, "OPTIONS")) {
+		LOG_INFO("[%p]: responding:\n%s", ctx, buf ? buf : "<void>");
+	}
+
+	NFREE(body);
+	NFREE(buf);
+	kd_free(resp);
+	kd_free(headers);
+
+	return true;
+}
+
+/*----------------------------------------------------------------------------*/
+/*
+bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) {
+	mDNSservice_t *s;
+	raop_ctx_t *ctx = (raop_ctx_t*) cookie;
+
+	// see if we have found an active remote for our ID
 	for (s = slist; s; s = s->next) {
 		if (strcasestr(s->name, ctx->active_remote.DACPid)) {
-			ctx->active_remote.host = s->addr;
-			ctx->active_remote.port = s->port;
-			LOG_INFO("[%p]: found ActiveRemote for %s at %s:%u", ctx, ctx->active_remote.DACPid,
-								inet_ntoa(ctx->active_remote.host), ctx->active_remote.port);
-			*stop = true;
-			break;
-		}
-	}
-
-	// let caller clear list
-	return false;
-}
-*/
-
-
-/*----------------------------------------------------------------------------*/
-/*
-static void* search_remote(void *args) {
-	raop_ctx_t *ctx = (raop_ctx_t*) args;
+			ctx->active_remote.host = s->addr;
+			ctx->active_remote.port = s->port;
+			LOG_INFO("[%p]: found ActiveRemote for %s at %s:%u", ctx, ctx->active_remote.DACPid,
+								inet_ntoa(ctx->active_remote.host), ctx->active_remote.port);
+			*stop = true;
+			break;
+		}
+	}
+
+	// let caller clear list
+	return false;
+}
+*/
+
+
+/*----------------------------------------------------------------------------*/
+/*
+static void* search_remote(void *args) {
+	raop_ctx_t *ctx = (raop_ctx_t*) args;
 
 	query_mDNS(ctx->active_remote.handle, "_dacp._tcp.local", 0, 0, &search_remote_cb, (void*) ctx);
 
 	return NULL;
 }
 
*/
-
+
 
/*----------------------------------------------------------------------------*/
-static char *rsa_apply(unsigned char *input, int inlen, int *outlen, int mode)
+static char *rsa_apply(unsigned char *input, int inlen, int *outlen, int mode)
 {
 	static char super_secret_key[] =
-	"-----BEGIN RSA PRIVATE KEY-----\n"
-	"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"
-	"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"
-	"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"
-	"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"
-	"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"
-	"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"
-	"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"
-	"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"
-	"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"
-	"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"
-	"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"
-	"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"
-	"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"
-	"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"
-	"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"
-	"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"
-	"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"
-	"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"
-	"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"
-	"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"
-	"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"
-	"-----END RSA PRIVATE KEY-----";
+	"-----BEGIN RSA PRIVATE KEY-----\n"
+	"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"
+	"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"
+	"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"
+	"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"
+	"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"
+	"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"
+	"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"
+	"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"
+	"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"
+	"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"
+	"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"
+	"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"
+	"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"
+	"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"
+	"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"
+	"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"
+	"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"
+	"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"
+	"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"
+	"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"
+	"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"
+	"-----END RSA PRIVATE KEY-----";
 #ifdef WIN32
 	unsigned char *out;
 	RSA *rsa;
 
-	BIO *bmem = BIO_new_mem_buf(super_secret_key, -1);
-	rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL);
-	BIO_free(bmem);
-
-	out = malloc(RSA_size(rsa));
-	switch (mode) {
-		case RSA_MODE_AUTH:
-			*outlen = RSA_private_encrypt(inlen, input, out, rsa,
-										  RSA_PKCS1_PADDING);
-			break;
-		case RSA_MODE_KEY:
-			*outlen = RSA_private_decrypt(inlen, input, out, rsa,
-										  RSA_PKCS1_OAEP_PADDING);
-			break;
-	}
-
-	RSA_free(rsa);
-
+	BIO *bmem = BIO_new_mem_buf(super_secret_key, -1);
+	rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL);
+	BIO_free(bmem);
+
+	out = malloc(RSA_size(rsa));
+	switch (mode) {
+		case RSA_MODE_AUTH:
+			*outlen = RSA_private_encrypt(inlen, input, out, rsa,
+										  RSA_PKCS1_PADDING);
+			break;
+		case RSA_MODE_KEY:
+			*outlen = RSA_private_decrypt(inlen, input, out, rsa,
+										  RSA_PKCS1_OAEP_PADDING);
+			break;
+	}
+
+	RSA_free(rsa);
+
 	return (char*) out;
 #else
 	mbedtls_pk_context pkctx;
-	mbedtls_rsa_context *trsa;
-	size_t olen;
-	
-	/*
-	we should do entropy initialization & pass a rng function but this
-	consumes a ton of stack and there is no security concern here. Anyway,
-	mbedtls takes a lot of stack, unfortunately ...
-	*/
-
-	mbedtls_pk_init(&pkctx);
-	mbedtls_pk_parse_key(&pkctx, (unsigned char *)super_secret_key,
-		sizeof(super_secret_key), NULL, 0);
-
-	uint8_t *outbuf = NULL;
-	trsa = mbedtls_pk_rsa(pkctx);
-
-	switch (mode) {
-	case RSA_MODE_AUTH:
-		mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
-		outbuf = malloc(trsa->len);
-		mbedtls_rsa_pkcs1_encrypt(trsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, inlen, input, outbuf);
-		*outlen = trsa->len;
-		break;
-	case RSA_MODE_KEY:
-		mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
-		outbuf = malloc(trsa->len);
-		mbedtls_rsa_pkcs1_decrypt(trsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, &olen, input, outbuf, trsa->len);
-		*outlen = olen;
-		break;
-	}
-
-	mbedtls_pk_free(&pkctx);
-
-	return (char*) outbuf;
-#endif
-}
-
-#define DECODE_ERROR 0xffffffff
-
-static char base64_chars[] =
-	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/*----------------------------------------------------------------------------*/
-static int  base64_pad(char *src, char **padded)
-{
-	int n;
-
-	n = strlen(src) + strlen(src) % 4;
-	*padded = malloc(n + 1);
-	memset(*padded, '=', n);
-	memcpy(*padded, src, strlen(src));
-	(*padded)[n] = '\0';
-
-	return strlen(*padded);
-}
-
-/*----------------------------------------------------------------------------*/
-static int pos(char c)
-{
-	char *p;
-	for (p = base64_chars; *p; p++)
-	if (*p == c)
-		return p - base64_chars;
-	return -1;
-}
-
-/*----------------------------------------------------------------------------*/
-static int base64_encode(const void *data, int size, char **str)
-{
-	char *s, *p;
-	int i;
-	int c;
-	const unsigned char *q;
-
-	p = s = (char *) malloc(size * 4 / 3 + 4);
-	if (p == NULL) return -1;
-	q = (const unsigned char *) data;
-	i = 0;
-	for (i = 0; i < size;) {
-		c = q[i++];
-		c *= 256;
-		if (i < size) c += q[i];
-		i++;
-		c *= 256;
-		if (i < size) c += q[i];
-		i++;
-		p[0] = base64_chars[(c & 0x00fc0000) >> 18];
-		p[1] = base64_chars[(c & 0x0003f000) >> 12];
-		p[2] = base64_chars[(c & 0x00000fc0) >> 6];
-		p[3] = base64_chars[(c & 0x0000003f) >> 0];
-		if (i > size) p[3] = '=';
-		if (i > size + 1) p[2] = '=';
-		p += 4;
-	}
-	*p = 0;
-	*str = s;
-	return strlen(s);
-}
-
-/*----------------------------------------------------------------------------*/
-static unsigned int token_decode(const char *token)
-{
-	int i;
-	unsigned int val = 0;
-	int marker = 0;
-	if (strlen(token) < 4)
-	return DECODE_ERROR;
-	for (i = 0; i < 4; i++) {
-	val *= 64;
-	if (token[i] == '=')
-		marker++;
-	else if (marker > 0)
-		return DECODE_ERROR;
-	else
-		val += pos(token[i]);
-	}
-	if (marker > 2)
-	return DECODE_ERROR;
-	return (marker << 24) | val;
-}
-
-/*----------------------------------------------------------------------------*/
-static int base64_decode(const char *str, void *data)
-{
-	const char *p;
-	unsigned char *q;
-
-	q = data;
-	for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
-	unsigned int val = token_decode(p);
-	unsigned int marker = (val >> 24) & 0xff;
-	if (val == DECODE_ERROR)
-		return -1;
-	*q++ = (val >> 16) & 0xff;
-	if (marker < 2)
-		*q++ = (val >> 8) & 0xff;
-	if (marker < 1)
-		*q++ = val & 0xff;
-	}
-	return q - (unsigned char *) data;
-}
-
-/*----------------------------------------------------------------------------*/
-static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len) {
+	mbedtls_rsa_context *trsa;
+	size_t olen;
+	
+	/*
+	we should do entropy initialization & pass a rng function but this
+	consumes a ton of stack and there is no security concern here. Anyway,
+	mbedtls takes a lot of stack, unfortunately ...
+	*/
+
+	mbedtls_pk_init(&pkctx);
+	mbedtls_pk_parse_key(&pkctx, (unsigned char *)super_secret_key,
+		sizeof(super_secret_key), NULL, 0);
+
+	uint8_t *outbuf = NULL;
+	trsa = mbedtls_pk_rsa(pkctx);
+
+	switch (mode) {
+	case RSA_MODE_AUTH:
+		mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_NONE);
+		outbuf = malloc(trsa->len);
+		mbedtls_rsa_pkcs1_encrypt(trsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, inlen, input, outbuf);
+		*outlen = trsa->len;
+		break;
+	case RSA_MODE_KEY:
+		mbedtls_rsa_set_padding(trsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
+		outbuf = malloc(trsa->len);
+		mbedtls_rsa_pkcs1_decrypt(trsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, &olen, input, outbuf, trsa->len);
+		*outlen = olen;
+		break;
+	}
+
+	mbedtls_pk_free(&pkctx);
+
+	return (char*) outbuf;
+#endif
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static char base64_chars[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*----------------------------------------------------------------------------*/
+static int  base64_pad(char *src, char **padded)
+{
+	int n;
+
+	n = strlen(src) + strlen(src) % 4;
+	*padded = malloc(n + 1);
+	memset(*padded, '=', n);
+	memcpy(*padded, src, strlen(src));
+	(*padded)[n] = '\0';
+
+	return strlen(*padded);
+}
+
+/*----------------------------------------------------------------------------*/
+static int pos(char c)
+{
+	char *p;
+	for (p = base64_chars; *p; p++)
+	if (*p == c)
+		return p - base64_chars;
+	return -1;
+}
+
+/*----------------------------------------------------------------------------*/
+static int base64_encode(const void *data, int size, char **str)
+{
+	char *s, *p;
+	int i;
+	int c;
+	const unsigned char *q;
+
+	p = s = (char *) malloc(size * 4 / 3 + 4);
+	if (p == NULL) return -1;
+	q = (const unsigned char *) data;
+	i = 0;
+	for (i = 0; i < size;) {
+		c = q[i++];
+		c *= 256;
+		if (i < size) c += q[i];
+		i++;
+		c *= 256;
+		if (i < size) c += q[i];
+		i++;
+		p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+		p[1] = base64_chars[(c & 0x0003f000) >> 12];
+		p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+		p[3] = base64_chars[(c & 0x0000003f) >> 0];
+		if (i > size) p[3] = '=';
+		if (i > size + 1) p[2] = '=';
+		p += 4;
+	}
+	*p = 0;
+	*str = s;
+	return strlen(s);
+}
+
+/*----------------------------------------------------------------------------*/
+static unsigned int token_decode(const char *token)
+{
+	int i;
+	unsigned int val = 0;
+	int marker = 0;
+	if (strlen(token) < 4)
+	return DECODE_ERROR;
+	for (i = 0; i < 4; i++) {
+	val *= 64;
+	if (token[i] == '=')
+		marker++;
+	else if (marker > 0)
+		return DECODE_ERROR;
+	else
+		val += pos(token[i]);
+	}
+	if (marker > 2)
+	return DECODE_ERROR;
+	return (marker << 24) | val;
+}
+
+/*----------------------------------------------------------------------------*/
+static int base64_decode(const char *str, void *data)
+{
+	const char *p;
+	unsigned char *q;
+
+	q = data;
+	for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+	unsigned int val = token_decode(p);
+	unsigned int marker = (val >> 24) & 0xff;
+	if (val == DECODE_ERROR)
+		return -1;
+	*q++ = (val >> 16) & 0xff;
+	if (marker < 2)
+		*q++ = (val >> 8) & 0xff;
+	if (marker < 1)
+		*q++ = val & 0xff;
+	}
+	return q - (unsigned char *) data;
+}
+
+/*----------------------------------------------------------------------------*/
+static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len) {
 	struct metadata_s *metadata = (struct metadata_s *) ctx;
 
 	if (!strcasecmp(code, "asar")) metadata->artist = strndup(buf, len);
 	else if (!strcasecmp(code, "asal")) metadata->album = strndup(buf, len);
 	else if (!strcasecmp(code, "minm")) metadata->title = strndup(buf, len);
-}
+}
 

+ 768 - 768
components/raop/rtp.c

@@ -1,777 +1,777 @@
-
-/*
- * HairTunes - RAOP packet handler and slave-clocked replay engine
- * Copyright (c) James Laird 2011
- * All rights reserved.
- *
- * Modularisation: philippe_44@outlook.com, 2019
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <pthread.h>
-#include <math.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <stdint.h>
-#include <fcntl.h>
-#include <assert.h>
-
-#include "platform.h"
-#include "rtp.h"
-#include "raop_sink.h"
-#include "log_util.h"
-#include "util.h"
-
-#ifdef WIN32
-#include <openssl/aes.h>
-#include "alac_wrapper.h"
-#else
-#include "esp_pthread.h"
-#include "esp_system.h"
-#include <mbedtls/version.h>
-#include <mbedtls/aes.h>
-#include "alac_wrapper.h"
-#endif
-
-#define NTP2MS(ntp) ((((ntp) >> 10) * 1000L) >> 22)
+
+/*
+ * HairTunes - RAOP packet handler and slave-clocked replay engine
+ * Copyright (c) James Laird 2011
+ * All rights reserved.
+ *
+ * Modularisation: philippe_44@outlook.com, 2019
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "platform.h"
+#include "rtp.h"
+#include "raop_sink.h"
+#include "log_util.h"
+#include "util.h"
+
+#ifdef WIN32
+#include <openssl/aes.h>
+#include "alac_wrapper.h"
+#else
+#include "esp_pthread.h"
+#include "esp_system.h"
+#include <mbedtls/version.h>
+#include <mbedtls/aes.h>
+#include "alac_wrapper.h"
+#endif
+
+#define NTP2MS(ntp) ((((ntp) >> 10) * 1000L) >> 22)
 #define MS2NTP(ms) (((((u64_t) (ms)) << 22) / 1000) << 10)
 #define NTP2TS(ntp, rate) ((((ntp) >> 16) * (rate)) >> 16)
 #define TS2NTP(ts, rate)  (((((u64_t) (ts)) << 16) / (rate)) << 16)
 #define MS2TS(ms, rate) ((((u64_t) (ms)) * (rate)) / 1000)
 #define TS2MS(ts, rate) NTP2MS(TS2NTP(ts,rate))
-
+
 
extern log_level 	raop_loglevel;
-
static log_level 	*loglevel = &raop_loglevel;
-
-//#define __RTP_STORE
-
-// default buffer size
-#define BUFFER_FRAMES 	( (150 * RAOP_SAMPLE_RATE * 2) / (352 * 100) )
-#define MAX_PACKET       1408
-#define MIN_LATENCY		11025
-#define MAX_LATENCY   	( (120 * RAOP_SAMPLE_RATE * 2) / 100 )
-
-#define RTP_STACK_SIZE	(4*1024)
-
-#define RTP_SYNC	(0x01)
-#define NTP_SYNC	(0x02)
-
-#define RESEND_TO	200
-
-enum { DATA = 0, CONTROL, TIMING };
-
-static const u8_t silence_frame[MAX_PACKET] = { 0 };
-
-typedef u16_t seq_t;
-typedef struct audio_buffer_entry {   // decoded audio packets
-	int ready;
-	u32_t rtptime, last_resend;
-	s16_t *data;
-	int len;
-} abuf_t;
-
-typedef struct rtp_s {
-#ifdef __RTP_STORE
-	FILE *rtpIN, *rtpOUT;
-#endif
-	bool running;
-	unsigned char aesiv[16];
-#ifdef WIN32
-	AES_KEY aes;
-#else
-	mbedtls_aes_context aes;
-#endif
-	bool decrypt;
-	u8_t *decrypt_buf;
-	u32_t frame_size, frame_duration;
-	u32_t in_frames, out_frames;
-	struct in_addr host;
-	struct sockaddr_in rtp_host;
-	struct {
-		unsigned short rport, lport;
-		int sock;
-	} rtp_sockets[3]; 					 // data, control, timing
-	struct timing_s {
-		u64_t local, remote;
-	} timing;
-	struct {
-		u32_t 	rtp, time;
-		u8_t  	status;
-	} synchro;
-	struct {
-		u32_t time;
-		seq_t seqno;
-		u32_t rtptime;
-	} record;
-	int latency;			// rtp hold depth in samples
-	u32_t resent_req, resent_rec;	// total resent + recovered frames
-	u32_t silent_frames;	// total silence frames
-	u32_t discarded;
-	abuf_t audio_buffer[BUFFER_FRAMES];
-	seq_t ab_read, ab_write;
-	pthread_mutex_t ab_mutex;
-#ifdef WIN32
-	pthread_t thread;
-#else
-	TaskHandle_t thread, joiner;
-	StaticTask_t *xTaskBuffer;
-    StackType_t *xStack;
-#endif
-
-	struct alac_codec_s *alac_codec;
-	int flush_seqno;
-	bool playing;
-	raop_data_cb_t data_cb;
-	raop_cmd_cb_t cmd_cb;
-} rtp_t;
-
-
-#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)
-static void 	buffer_alloc(abuf_t *audio_buffer, int size);
-static void 	buffer_release(abuf_t *audio_buffer);
-static void 	buffer_reset(abuf_t *audio_buffer);
-static void 	buffer_push_packet(rtp_t *ctx);
-static bool 	rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last);
-static bool 	rtp_request_timing(rtp_t *ctx);
-static void*	rtp_thread_func(void *arg);
-static int	  	seq_order(seq_t a, seq_t b);
-
-/*---------------------------------------------------------------------------*/
-static struct alac_codec_s* alac_init(int fmtp[32]) {
-	struct alac_codec_s *alac;
-	unsigned sample_rate;
-	unsigned char sample_size, channels;
-	struct {
-		uint32_t	frameLength;
-		uint8_t		compatibleVersion;
-		uint8_t		bitDepth;
-		uint8_t		pb;
-		uint8_t		mb;
-		uint8_t		kb;
-		uint8_t		numChannels;
-		uint16_t	maxRun;
-		uint32_t	maxFrameBytes;
-		uint32_t	avgBitRate;
-		uint32_t	sampleRate;
-	} config;
-
-	config.frameLength = htonl(fmtp[1]);
-	config.compatibleVersion = fmtp[2];
-	config.bitDepth = fmtp[3];
-	config.pb = fmtp[4];
-	config.mb = fmtp[5];
-	config.kb = fmtp[6];
-	config.numChannels = fmtp[7];
-	config.maxRun = htons(fmtp[8]);
-	config.maxFrameBytes = htonl(fmtp[9]);
-	config.avgBitRate = htonl(fmtp[10]);
-	config.sampleRate = htonl(fmtp[11]);
-
-	alac = alac_create_decoder(sizeof(config), (unsigned char*) &config, &sample_size, &sample_rate, &channels);
-	if (!alac) {
-		LOG_ERROR("cannot create alac codec", NULL);
-		return NULL;
-	}
-
-	return alac;
-}
-
-/*---------------------------------------------------------------------------*/
-rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr,
-								short unsigned pCtrlPort, short unsigned pTimingPort,
-								raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb)
-{
-	int i = 0;
-	char *arg;
-	int fmtp[12];
-	bool rc = true;
-	rtp_t *ctx = calloc(1, sizeof(rtp_t));
-	rtp_resp_t resp = { 0, 0, 0, NULL };
-
-	if (!ctx) return resp;
-	
-	ctx->host = host;
-	ctx->decrypt = false;
-	ctx->cmd_cb = cmd_cb;
-	ctx->data_cb = data_cb;
-	ctx->rtp_host.sin_family = AF_INET;
-	ctx->rtp_host.sin_addr.s_addr = INADDR_ANY;
-	pthread_mutex_init(&ctx->ab_mutex, 0);
-	ctx->flush_seqno = -1;
-	ctx->latency = latency;
-	ctx->ab_read = ctx->ab_write;
-
-#ifdef __RTP_STORE
-	ctx->rtpIN = fopen("airplay.rtpin", "wb");
-	ctx->rtpOUT = fopen("airplay.rtpout", "wb");
-#endif
-
-	ctx->rtp_sockets[CONTROL].rport = pCtrlPort;
-	ctx->rtp_sockets[TIMING].rport = pTimingPort;
-
-	if (aesiv && aeskey) {
-		memcpy(ctx->aesiv, aesiv, 16);
-#ifdef WIN32
-		AES_set_decrypt_key((unsigned char*) aeskey, 128, &ctx->aes);
-#else
-		memset(&ctx->aes, 0, sizeof(mbedtls_aes_context));
-		mbedtls_aes_setkey_dec(&ctx->aes, (unsigned char*) aeskey, 128);
-#endif
-		ctx->decrypt = true;
-		ctx->decrypt_buf = malloc(MAX_PACKET);
-	}
-
-	memset(fmtp, 0, sizeof(fmtp));
-	while ((arg = strsep(&fmtpstr, " \t")) != NULL) fmtp[i++] = atoi(arg);
-
-	ctx->frame_size = fmtp[1];
-	ctx->frame_duration = (ctx->frame_size * 1000) / RAOP_SAMPLE_RATE;
-
-	// alac decoder
-	ctx->alac_codec = alac_init(fmtp);
-	rc &= ctx->alac_codec != NULL;
-
-	buffer_alloc(ctx->audio_buffer, ctx->frame_size*4);
-
-	// create rtp ports
-	for (i = 0; i < 3; i++) {
-		ctx->rtp_sockets[i].sock = bind_socket(&ctx->rtp_sockets[i].lport, SOCK_DGRAM);
-		rc &= ctx->rtp_sockets[i].sock > 0;
-	}
-
-	// create http port and start listening
-	resp.cport = ctx->rtp_sockets[CONTROL].lport;
-	resp.tport = ctx->rtp_sockets[TIMING].lport;
-	resp.aport = ctx->rtp_sockets[DATA].lport;
-	
-	if (rc) {
-		ctx->running = true;
-#ifdef WIN32
-		pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx);
-#else
-		// xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx,  CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread);
-		ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
-		ctx->xStack = (StackType_t*) malloc(RTP_STACK_SIZE);
-		ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, 
-										 CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer );
-#endif
-	} else {
-		rtp_end(ctx);
-		ctx = NULL;
-	}
-
-	resp.ctx = ctx;
-
-	return resp;
-}
-
-/*---------------------------------------------------------------------------*/
-void rtp_end(rtp_t *ctx)
-{
-	int i;
-
-	if (!ctx) return;
-
-	if (ctx->running) {
-#if !defined WIN32		
-		ctx->joiner = xTaskGetCurrentTaskHandle();
-#endif
-		ctx->running = false;
-#ifdef WIN32
-		pthread_join(ctx->thread, NULL);
-#else
-		xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
-		free(ctx->xStack);
-		heap_caps_free(ctx->xTaskBuffer);
-#endif
-	}
-	
-	for (i = 0; i < 3; i++) closesocket(ctx->rtp_sockets[i].sock);
-
-	if (ctx->alac_codec) alac_delete_decoder(ctx->alac_codec);
-	if (ctx->decrypt_buf) free(ctx->decrypt_buf);
-	
-	pthread_mutex_destroy(&ctx->ab_mutex);
-	buffer_release(ctx->audio_buffer);
-	
-	free(ctx);
-
-#ifdef __RTP_STORE
-	fclose(ctx->rtpIN);
-	fclose(ctx->rtpOUT);
-#endif
-}
-
-/*---------------------------------------------------------------------------*/
-bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime)
-{
-	bool rc = true;
-	u32_t now = gettime_ms();
-
-	if (now < ctx->record.time + 250 || (ctx->record.seqno == seqno && ctx->record.rtptime == rtptime)) {
-		rc = false;
-		LOG_ERROR("[%p]: FLUSH ignored as same as RECORD (%hu - %u)", ctx, seqno, rtptime);
-	} else {
-		pthread_mutex_lock(&ctx->ab_mutex);
-		buffer_reset(ctx->audio_buffer);
-		ctx->playing = false;
-		ctx->flush_seqno = seqno;
-		pthread_mutex_unlock(&ctx->ab_mutex);
-	}
-
-	LOG_INFO("[%p]: flush %hu %u", ctx, seqno, rtptime);
-
-	return rc;
-}
-
-/*---------------------------------------------------------------------------*/
-void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime)
-{
-	ctx->record.seqno = seqno;
-	ctx->record.rtptime = rtptime;
-	ctx->record.time = gettime_ms();
-
-	LOG_INFO("[%p]: record %hu %u", ctx, seqno, rtptime);
-}
-
-/*---------------------------------------------------------------------------*/
-static void buffer_alloc(abuf_t *audio_buffer, int size) {
-	int i;
-	for (i = 0; i < BUFFER_FRAMES; i++) {
-		audio_buffer[i].data = malloc(size);
-		audio_buffer[i].ready = 0;
-	}
-}
-
-/*---------------------------------------------------------------------------*/
-static void buffer_release(abuf_t *audio_buffer) {
-	int i;
-	for (i = 0; i < BUFFER_FRAMES; i++) {
-		free(audio_buffer[i].data);
-	}
-}
-
-/*---------------------------------------------------------------------------*/
-static void buffer_reset(abuf_t *audio_buffer) {
-	int i;
-	for (i = 0; i < BUFFER_FRAMES; i++) audio_buffer[i].ready = 0;
-}
-
-/*---------------------------------------------------------------------------*/
-// the sequence numbers will wrap pretty often.
-// this returns true if the second arg is after the first
-static int seq_order(seq_t a, seq_t b) {
-	s16_t d = b - a;
-	return d > 0;
-}
-
-/*---------------------------------------------------------------------------*/
-static void alac_decode(rtp_t *ctx, s16_t *dest, char *buf, int len, int *outsize) {
-	unsigned char iv[16];
-	int aeslen;
-	assert(len<=MAX_PACKET);
-
-	if (ctx->decrypt) {
-		aeslen = len & ~0xf;
-		memcpy(iv, ctx->aesiv, sizeof(iv));
-#ifdef WIN32
-		AES_cbc_encrypt((unsigned char*)buf, ctx->decrypt_buf, aeslen, &ctx->aes, iv, AES_DECRYPT);
-#else
-		mbedtls_aes_crypt_cbc(&ctx->aes, MBEDTLS_AES_DECRYPT, aeslen, iv, (unsigned char*) buf, ctx->decrypt_buf);
-#endif
-		memcpy(ctx->decrypt_buf+aeslen, buf+aeslen, len-aeslen);
-		alac_to_pcm(ctx->alac_codec, (unsigned char*) ctx->decrypt_buf, (unsigned char*) dest, 2, (unsigned int*) outsize);
-	} else {
-		alac_to_pcm(ctx->alac_codec, (unsigned char*) buf, (unsigned char*) dest, 2, (unsigned int*) outsize);
-	}	
-	
-	*outsize *= 4;
-}
-
-
-/*---------------------------------------------------------------------------*/
-static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool first, char *data, int len) {
-	abuf_t *abuf = NULL;
-	u32_t playtime;
-
-	pthread_mutex_lock(&ctx->ab_mutex);
-
-	if (!ctx->playing) {
-		if ((ctx->flush_seqno == -1 || seq_order(ctx->flush_seqno, seqno)) &&
-		   (ctx->synchro.status & RTP_SYNC) && (ctx->synchro.status & NTP_SYNC)) {
-			ctx->ab_write = seqno-1;
-			ctx->ab_read = seqno;
-			ctx->flush_seqno = -1;
-			ctx->playing = true;
-			ctx->resent_req = ctx->resent_rec = ctx->silent_frames = ctx->discarded = 0;
-			playtime = ctx->synchro.time + (((s32_t)(rtptime - ctx->synchro.rtp)) * 1000) / RAOP_SAMPLE_RATE;
-			ctx->cmd_cb(RAOP_PLAY, &playtime);
-		} else {
-			pthread_mutex_unlock(&ctx->ab_mutex);
-			return;
-		}
-	}
-
-	if (seqno == (u16_t) (ctx->ab_write+1)) {
-		// expected packet
-		abuf = ctx->audio_buffer + BUFIDX(seqno);
-		ctx->ab_write = seqno;
-		LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read);
-
-	} else if (seq_order(ctx->ab_write, seqno)) {
-		// newer than expected
-		if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) {
-			// only get rtp latency-1 frames back (last one is seqno)
-			LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read);
-			ctx->ab_write = seqno - ctx->latency / ctx->frame_size;
-		}
-		if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_read)) {
-			// if ab_read is lagging more than http latency, advance it
-			LOG_WARN("[%p] on hold for too long %hu (W:%hu R:%hu)", ctx, seqno - ctx->ab_read + 1, ctx->ab_write, ctx->ab_read);
-			ctx->ab_read = seqno - ctx->latency / ctx->frame_size + 1;
-		}
-		if (rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1)) {
-			seq_t i;
-			u32_t now = gettime_ms();
-			for (i = ctx->ab_write + 1; seq_order(i, seqno); i++) {
-				ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size;
-				ctx->audio_buffer[BUFIDX(i)].last_resend = now;
-			}
-		}
-		LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
-		abuf = ctx->audio_buffer + BUFIDX(seqno);
-		ctx->ab_write = seqno;
-	} else if (seq_order(ctx->ab_read, seqno + 1)) {
-		// recovered packet, not yet sent
-		abuf = ctx->audio_buffer + BUFIDX(seqno);
-		ctx->resent_rec++;
-		LOG_DEBUG("[%p]: packet recovered seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
-	} else {
-		// too late
-		LOG_DEBUG("[%p]: packet too late seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
-	}
-
-	if (ctx->in_frames++ > 1000) {
-		LOG_INFO("[%p]: fill [level:%hu rec:%u] [W:%hu R:%hu]", ctx, ctx->ab_write - ctx->ab_read, ctx->resent_rec, ctx->ab_write, ctx->ab_read);
-		ctx->in_frames = 0;
-	}
-
-	if (abuf) {
-		alac_decode(ctx, abuf->data, data, len, &abuf->len);
-		abuf->ready = 1;
-		// this is the local rtptime when this frame is expected to play
-		abuf->rtptime = rtptime;
-		buffer_push_packet(ctx);
-
-#ifdef __RTP_STORE
-		fwrite(data, len, 1, ctx->rtpIN);
-		fwrite(abuf->data, abuf->len, 1, ctx->rtpOUT);
-#endif
-	}
-
-	pthread_mutex_unlock(&ctx->ab_mutex);
-}
-
-/*---------------------------------------------------------------------------*/
-// push as many frames as possible through callback
-static void buffer_push_packet(rtp_t *ctx) {
-	abuf_t *curframe = NULL;
-	u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
-	int i;
-
-	// not ready to play yet
-	if (!ctx->playing ||  ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
-
-	// maybe re-evaluate time in loop in case data callback blocks ...
-	now = gettime_ms();
-
-	// there is always at least one frame in the buffer
-	do {
-
-		curframe = ctx->audio_buffer + BUFIDX(ctx->ab_read);
-		playtime = ctx->synchro.time + (((s32_t)(curframe->rtptime - ctx->synchro.rtp)) * 1000) / RAOP_SAMPLE_RATE;
-
-		if (now > playtime) {
-			LOG_DEBUG("[%p]: discarded frame now:%u missed by:%d (W:%hu R:%hu)", ctx, now, now - playtime, ctx->ab_write, ctx->ab_read);
-			ctx->discarded++;
-			curframe->ready = 0;
-		} else if (curframe->ready) {
-			ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime);
-			curframe->ready = 0;
-		} else if (playtime - now <= hold) {
-			LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read);
-			ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime);
-			ctx->silent_frames++;
-		} else break;
-
-		ctx->ab_read++;
-		ctx->out_frames++;
-
-	} while (seq_order(ctx->ab_read, ctx->ab_write));
-
-	if (ctx->out_frames > 1000) {
-		LOG_INFO("[%p]: drain [level:%hd head:%d ms] [W:%hu R:%hu] [req:%u sil:%u dis:%u]",
-				ctx, ctx->ab_write - ctx->ab_read, playtime - now, ctx->ab_write, ctx->ab_read,
-				ctx->resent_req, ctx->silent_frames, ctx->discarded);
-		ctx->out_frames = 0;
-	}
-
-	LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
-
-	// each missing packet will be requested up to (latency_frames / 16) times
-	for (i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
-		abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
-		if (!frame->ready && now - frame->last_resend > RESEND_TO) {
-			rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i);
+
static log_level 	*loglevel = &raop_loglevel;
+
+//#define __RTP_STORE
+
+// default buffer size
+#define BUFFER_FRAMES 	( (150 * RAOP_SAMPLE_RATE * 2) / (352 * 100) )
+#define MAX_PACKET       1408
+#define MIN_LATENCY		11025
+#define MAX_LATENCY   	( (120 * RAOP_SAMPLE_RATE * 2) / 100 )
+
+#define RTP_STACK_SIZE	(4*1024)
+
+#define RTP_SYNC	(0x01)
+#define NTP_SYNC	(0x02)
+
+#define RESEND_TO	200
+
+enum { DATA = 0, CONTROL, TIMING };
+
+static const u8_t silence_frame[MAX_PACKET] = { 0 };
+
+typedef u16_t seq_t;
+typedef struct audio_buffer_entry {   // decoded audio packets
+	int ready;
+	u32_t rtptime, last_resend;
+	s16_t *data;
+	int len;
+} abuf_t;
+
+typedef struct rtp_s {
+#ifdef __RTP_STORE
+	FILE *rtpIN, *rtpOUT;
+#endif
+	bool running;
+	unsigned char aesiv[16];
+#ifdef WIN32
+	AES_KEY aes;
+#else
+	mbedtls_aes_context aes;
+#endif
+	bool decrypt;
+	u8_t *decrypt_buf;
+	u32_t frame_size, frame_duration;
+	u32_t in_frames, out_frames;
+	struct in_addr host;
+	struct sockaddr_in rtp_host;
+	struct {
+		unsigned short rport, lport;
+		int sock;
+	} rtp_sockets[3]; 					 // data, control, timing
+	struct timing_s {
+		u64_t local, remote;
+	} timing;
+	struct {
+		u32_t 	rtp, time;
+		u8_t  	status;
+	} synchro;
+	struct {
+		u32_t time;
+		seq_t seqno;
+		u32_t rtptime;
+	} record;
+	int latency;			// rtp hold depth in samples
+	u32_t resent_req, resent_rec;	// total resent + recovered frames
+	u32_t silent_frames;	// total silence frames
+	u32_t discarded;
+	abuf_t audio_buffer[BUFFER_FRAMES];
+	seq_t ab_read, ab_write;
+	pthread_mutex_t ab_mutex;
+#ifdef WIN32
+	pthread_t thread;
+#else
+	TaskHandle_t thread, joiner;
+	StaticTask_t *xTaskBuffer;
+    StackType_t *xStack;
+#endif
+
+	struct alac_codec_s *alac_codec;
+	int flush_seqno;
+	bool playing;
+	raop_data_cb_t data_cb;
+	raop_cmd_cb_t cmd_cb;
+} rtp_t;
+
+
+#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)
+static void 	buffer_alloc(abuf_t *audio_buffer, int size);
+static void 	buffer_release(abuf_t *audio_buffer);
+static void 	buffer_reset(abuf_t *audio_buffer);
+static void 	buffer_push_packet(rtp_t *ctx);
+static bool 	rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last);
+static bool 	rtp_request_timing(rtp_t *ctx);
+static void*	rtp_thread_func(void *arg);
+static int	  	seq_order(seq_t a, seq_t b);
+
+/*---------------------------------------------------------------------------*/
+static struct alac_codec_s* alac_init(int fmtp[32]) {
+	struct alac_codec_s *alac;
+	unsigned sample_rate;
+	unsigned char sample_size, channels;
+	struct {
+		uint32_t	frameLength;
+		uint8_t		compatibleVersion;
+		uint8_t		bitDepth;
+		uint8_t		pb;
+		uint8_t		mb;
+		uint8_t		kb;
+		uint8_t		numChannels;
+		uint16_t	maxRun;
+		uint32_t	maxFrameBytes;
+		uint32_t	avgBitRate;
+		uint32_t	sampleRate;
+	} config;
+
+	config.frameLength = htonl(fmtp[1]);
+	config.compatibleVersion = fmtp[2];
+	config.bitDepth = fmtp[3];
+	config.pb = fmtp[4];
+	config.mb = fmtp[5];
+	config.kb = fmtp[6];
+	config.numChannels = fmtp[7];
+	config.maxRun = htons(fmtp[8]);
+	config.maxFrameBytes = htonl(fmtp[9]);
+	config.avgBitRate = htonl(fmtp[10]);
+	config.sampleRate = htonl(fmtp[11]);
+
+	alac = alac_create_decoder(sizeof(config), (unsigned char*) &config, &sample_size, &sample_rate, &channels);
+	if (!alac) {
+		LOG_ERROR("cannot create alac codec", NULL);
+		return NULL;
+	}
+
+	return alac;
+}
+
+/*---------------------------------------------------------------------------*/
+rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, char *fmtpstr,
+								short unsigned pCtrlPort, short unsigned pTimingPort,
+								raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb)
+{
+	int i = 0;
+	char *arg;
+	int fmtp[12];
+	bool rc = true;
+	rtp_t *ctx = calloc(1, sizeof(rtp_t));
+	rtp_resp_t resp = { 0, 0, 0, NULL };
+
+	if (!ctx) return resp;
+	
+	ctx->host = host;
+	ctx->decrypt = false;
+	ctx->cmd_cb = cmd_cb;
+	ctx->data_cb = data_cb;
+	ctx->rtp_host.sin_family = AF_INET;
+	ctx->rtp_host.sin_addr.s_addr = INADDR_ANY;
+	pthread_mutex_init(&ctx->ab_mutex, 0);
+	ctx->flush_seqno = -1;
+	ctx->latency = latency;
+	ctx->ab_read = ctx->ab_write;
+
+#ifdef __RTP_STORE
+	ctx->rtpIN = fopen("airplay.rtpin", "wb");
+	ctx->rtpOUT = fopen("airplay.rtpout", "wb");
+#endif
+
+	ctx->rtp_sockets[CONTROL].rport = pCtrlPort;
+	ctx->rtp_sockets[TIMING].rport = pTimingPort;
+
+	if (aesiv && aeskey) {
+		memcpy(ctx->aesiv, aesiv, 16);
+#ifdef WIN32
+		AES_set_decrypt_key((unsigned char*) aeskey, 128, &ctx->aes);
+#else
+		memset(&ctx->aes, 0, sizeof(mbedtls_aes_context));
+		mbedtls_aes_setkey_dec(&ctx->aes, (unsigned char*) aeskey, 128);
+#endif
+		ctx->decrypt = true;
+		ctx->decrypt_buf = malloc(MAX_PACKET);
+	}
+
+	memset(fmtp, 0, sizeof(fmtp));
+	while ((arg = strsep(&fmtpstr, " \t")) != NULL) fmtp[i++] = atoi(arg);
+
+	ctx->frame_size = fmtp[1];
+	ctx->frame_duration = (ctx->frame_size * 1000) / RAOP_SAMPLE_RATE;
+
+	// alac decoder
+	ctx->alac_codec = alac_init(fmtp);
+	rc &= ctx->alac_codec != NULL;
+
+	buffer_alloc(ctx->audio_buffer, ctx->frame_size*4);
+
+	// create rtp ports
+	for (i = 0; i < 3; i++) {
+		ctx->rtp_sockets[i].sock = bind_socket(&ctx->rtp_sockets[i].lport, SOCK_DGRAM);
+		rc &= ctx->rtp_sockets[i].sock > 0;
+	}
+
+	// create http port and start listening
+	resp.cport = ctx->rtp_sockets[CONTROL].lport;
+	resp.tport = ctx->rtp_sockets[TIMING].lport;
+	resp.aport = ctx->rtp_sockets[DATA].lport;
+	
+	if (rc) {
+		ctx->running = true;
+#ifdef WIN32
+		pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx);
+#else
+		// xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx,  CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread);
+		ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
+		ctx->xStack = (StackType_t*) malloc(RTP_STACK_SIZE);
+		ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, 
+										 CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer );
+#endif
+	} else {
+		rtp_end(ctx);
+		ctx = NULL;
+	}
+
+	resp.ctx = ctx;
+
+	return resp;
+}
+
+/*---------------------------------------------------------------------------*/
+void rtp_end(rtp_t *ctx)
+{
+	int i;
+
+	if (!ctx) return;
+
+	if (ctx->running) {
+#if !defined WIN32		
+		ctx->joiner = xTaskGetCurrentTaskHandle();
+#endif
+		ctx->running = false;
+#ifdef WIN32
+		pthread_join(ctx->thread, NULL);
+#else
+		xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
+		free(ctx->xStack);
+		heap_caps_free(ctx->xTaskBuffer);
+#endif
+	}
+	
+	for (i = 0; i < 3; i++) closesocket(ctx->rtp_sockets[i].sock);
+
+	if (ctx->alac_codec) alac_delete_decoder(ctx->alac_codec);
+	if (ctx->decrypt_buf) free(ctx->decrypt_buf);
+	
+	pthread_mutex_destroy(&ctx->ab_mutex);
+	buffer_release(ctx->audio_buffer);
+	
+	free(ctx);
+
+#ifdef __RTP_STORE
+	fclose(ctx->rtpIN);
+	fclose(ctx->rtpOUT);
+#endif
+}
+
+/*---------------------------------------------------------------------------*/
+bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime)
+{
+	bool rc = true;
+	u32_t now = gettime_ms();
+
+	if (now < ctx->record.time + 250 || (ctx->record.seqno == seqno && ctx->record.rtptime == rtptime)) {
+		rc = false;
+		LOG_ERROR("[%p]: FLUSH ignored as same as RECORD (%hu - %u)", ctx, seqno, rtptime);
+	} else {
+		pthread_mutex_lock(&ctx->ab_mutex);
+		buffer_reset(ctx->audio_buffer);
+		ctx->playing = false;
+		ctx->flush_seqno = seqno;
+		pthread_mutex_unlock(&ctx->ab_mutex);
+	}
+
+	LOG_INFO("[%p]: flush %hu %u", ctx, seqno, rtptime);
+
+	return rc;
+}
+
+/*---------------------------------------------------------------------------*/
+void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime)
+{
+	ctx->record.seqno = seqno;
+	ctx->record.rtptime = rtptime;
+	ctx->record.time = gettime_ms();
+
+	LOG_INFO("[%p]: record %hu %u", ctx, seqno, rtptime);
+}
+
+/*---------------------------------------------------------------------------*/
+static void buffer_alloc(abuf_t *audio_buffer, int size) {
+	int i;
+	for (i = 0; i < BUFFER_FRAMES; i++) {
+		audio_buffer[i].data = malloc(size);
+		audio_buffer[i].ready = 0;
+	}
+}
+
+/*---------------------------------------------------------------------------*/
+static void buffer_release(abuf_t *audio_buffer) {
+	int i;
+	for (i = 0; i < BUFFER_FRAMES; i++) {
+		free(audio_buffer[i].data);
+	}
+}
+
+/*---------------------------------------------------------------------------*/
+static void buffer_reset(abuf_t *audio_buffer) {
+	int i;
+	for (i = 0; i < BUFFER_FRAMES; i++) audio_buffer[i].ready = 0;
+}
+
+/*---------------------------------------------------------------------------*/
+// the sequence numbers will wrap pretty often.
+// this returns true if the second arg is after the first
+static int seq_order(seq_t a, seq_t b) {
+	s16_t d = b - a;
+	return d > 0;
+}
+
+/*---------------------------------------------------------------------------*/
+static void alac_decode(rtp_t *ctx, s16_t *dest, char *buf, int len, int *outsize) {
+	unsigned char iv[16];
+	int aeslen;
+	assert(len<=MAX_PACKET);
+
+	if (ctx->decrypt) {
+		aeslen = len & ~0xf;
+		memcpy(iv, ctx->aesiv, sizeof(iv));
+#ifdef WIN32
+		AES_cbc_encrypt((unsigned char*)buf, ctx->decrypt_buf, aeslen, &ctx->aes, iv, AES_DECRYPT);
+#else
+		mbedtls_aes_crypt_cbc(&ctx->aes, MBEDTLS_AES_DECRYPT, aeslen, iv, (unsigned char*) buf, ctx->decrypt_buf);
+#endif
+		memcpy(ctx->decrypt_buf+aeslen, buf+aeslen, len-aeslen);
+		alac_to_pcm(ctx->alac_codec, (unsigned char*) ctx->decrypt_buf, (unsigned char*) dest, 2, (unsigned int*) outsize);
+	} else {
+		alac_to_pcm(ctx->alac_codec, (unsigned char*) buf, (unsigned char*) dest, 2, (unsigned int*) outsize);
+	}	
+	
+	*outsize *= 4;
+}
+
+
+/*---------------------------------------------------------------------------*/
+static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool first, char *data, int len) {
+	abuf_t *abuf = NULL;
+	u32_t playtime;
+
+	pthread_mutex_lock(&ctx->ab_mutex);
+
+	if (!ctx->playing) {
+		if ((ctx->flush_seqno == -1 || seq_order(ctx->flush_seqno, seqno)) &&
+		   (ctx->synchro.status & RTP_SYNC) && (ctx->synchro.status & NTP_SYNC)) {
+			ctx->ab_write = seqno-1;
+			ctx->ab_read = seqno;
+			ctx->flush_seqno = -1;
+			ctx->playing = true;
+			ctx->resent_req = ctx->resent_rec = ctx->silent_frames = ctx->discarded = 0;
+			playtime = ctx->synchro.time + (((s32_t)(rtptime - ctx->synchro.rtp)) * 1000) / RAOP_SAMPLE_RATE;
+			ctx->cmd_cb(RAOP_PLAY, &playtime);
+		} else {
+			pthread_mutex_unlock(&ctx->ab_mutex);
+			return;
+		}
+	}
+
+	if (seqno == (u16_t) (ctx->ab_write+1)) {
+		// expected packet
+		abuf = ctx->audio_buffer + BUFIDX(seqno);
+		ctx->ab_write = seqno;
+		LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read);
+
+	} else if (seq_order(ctx->ab_write, seqno)) {
+		// newer than expected
+		if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) {
+			// only get rtp latency-1 frames back (last one is seqno)
+			LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read);
+			ctx->ab_write = seqno - ctx->latency / ctx->frame_size;
+		}
+		if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_read)) {
+			// if ab_read is lagging more than http latency, advance it
+			LOG_WARN("[%p] on hold for too long %hu (W:%hu R:%hu)", ctx, seqno - ctx->ab_read + 1, ctx->ab_write, ctx->ab_read);
+			ctx->ab_read = seqno - ctx->latency / ctx->frame_size + 1;
+		}
+		if (rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1)) {
+			seq_t i;
+			u32_t now = gettime_ms();
+			for (i = ctx->ab_write + 1; seq_order(i, seqno); i++) {
+				ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size;
+				ctx->audio_buffer[BUFIDX(i)].last_resend = now;
+			}
+		}
+		LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
+		abuf = ctx->audio_buffer + BUFIDX(seqno);
+		ctx->ab_write = seqno;
+	} else if (seq_order(ctx->ab_read, seqno + 1)) {
+		// recovered packet, not yet sent
+		abuf = ctx->audio_buffer + BUFIDX(seqno);
+		ctx->resent_rec++;
+		LOG_DEBUG("[%p]: packet recovered seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
+	} else {
+		// too late
+		LOG_DEBUG("[%p]: packet too late seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
+	}
+
+	if (ctx->in_frames++ > 1000) {
+		LOG_INFO("[%p]: fill [level:%hu rec:%u] [W:%hu R:%hu]", ctx, ctx->ab_write - ctx->ab_read, ctx->resent_rec, ctx->ab_write, ctx->ab_read);
+		ctx->in_frames = 0;
+	}
+
+	if (abuf) {
+		alac_decode(ctx, abuf->data, data, len, &abuf->len);
+		abuf->ready = 1;
+		// this is the local rtptime when this frame is expected to play
+		abuf->rtptime = rtptime;
+		buffer_push_packet(ctx);
+
+#ifdef __RTP_STORE
+		fwrite(data, len, 1, ctx->rtpIN);
+		fwrite(abuf->data, abuf->len, 1, ctx->rtpOUT);
+#endif
+	}
+
+	pthread_mutex_unlock(&ctx->ab_mutex);
+}
+
+/*---------------------------------------------------------------------------*/
+// push as many frames as possible through callback
+static void buffer_push_packet(rtp_t *ctx) {
+	abuf_t *curframe = NULL;
+	u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
+	int i;
+
+	// not ready to play yet
+	if (!ctx->playing ||  ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
+
+	// maybe re-evaluate time in loop in case data callback blocks ...
+	now = gettime_ms();
+
+	// there is always at least one frame in the buffer
+	do {
+
+		curframe = ctx->audio_buffer + BUFIDX(ctx->ab_read);
+		playtime = ctx->synchro.time + (((s32_t)(curframe->rtptime - ctx->synchro.rtp)) * 1000) / RAOP_SAMPLE_RATE;
+
+		if (now > playtime) {
+			LOG_DEBUG("[%p]: discarded frame now:%u missed by:%d (W:%hu R:%hu)", ctx, now, now - playtime, ctx->ab_write, ctx->ab_read);
+			ctx->discarded++;
+			curframe->ready = 0;
+		} else if (curframe->ready) {
+			ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime);
+			curframe->ready = 0;
+		} else if (playtime - now <= hold) {
+			LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read);
+			ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime);
+			ctx->silent_frames++;
+		} else break;
+
+		ctx->ab_read++;
+		ctx->out_frames++;
+
+	} while (seq_order(ctx->ab_read, ctx->ab_write));
+
+	if (ctx->out_frames > 1000) {
+		LOG_INFO("[%p]: drain [level:%hd head:%d ms] [W:%hu R:%hu] [req:%u sil:%u dis:%u]",
+				ctx, ctx->ab_write - ctx->ab_read, playtime - now, ctx->ab_write, ctx->ab_read,
+				ctx->resent_req, ctx->silent_frames, ctx->discarded);
+		ctx->out_frames = 0;
+	}
+
+	LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
+
+	// each missing packet will be requested up to (latency_frames / 16) times
+	for (i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
+		abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
+		if (!frame->ready && now - frame->last_resend > RESEND_TO) {
+			rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i);
 			frame->last_resend = now;
 		}
-	}
+	}
 
}
-

-
-/*---------------------------------------------------------------------------*/
-static void *rtp_thread_func(void *arg) {
-	fd_set fds;
-	int i, sock = -1;
-	int count = 0;
-	bool ntp_sent;
-	char *packet = malloc(MAX_PACKET);
-	rtp_t *ctx = (rtp_t*) arg;
-
-	for (i = 0; i < 3; i++) {
-		if (ctx->rtp_sockets[i].sock > sock) sock = ctx->rtp_sockets[i].sock;
-		// send synchro requets 3 times
-		ntp_sent = rtp_request_timing(ctx);
-	}
-
-	while (ctx->running) {
-		ssize_t plen;
-		char type;
-		socklen_t rtp_client_len = sizeof(struct sockaddr_storage);
-		int idx = 0;
-		char *pktp = packet;
-		struct timeval timeout = {0, 100*1000};
-
-		FD_ZERO(&fds);
-		for (i = 0; i < 3; i++)	{ FD_SET(ctx->rtp_sockets[i].sock, &fds); }
-
-		if (select(sock + 1, &fds, NULL, NULL, &timeout) <= 0) continue;
-
-		for (i = 0; i < 3; i++)
-			if (FD_ISSET(ctx->rtp_sockets[i].sock, &fds)) idx = i;
-
-		plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, 0, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len);
-
-		if (!ntp_sent) {
-			LOG_WARN("[%p]: NTP request not send yet", ctx);
-			ntp_sent = rtp_request_timing(ctx);
-		}
-
-		if (plen < 0) continue;
-		assert(plen <= MAX_PACKET);
-
-		type = packet[1] & ~0x80;
-		pktp = packet;
-
-		switch (type) {
-			seq_t seqno;
-			unsigned rtptime;
-
-			// re-sent packet
-			case 0x56: {
-				pktp += 4;
-				plen -= 4;
-			}
-
-			// data packet
-			case 0x60: {
-				seqno = ntohs(*(u16_t*)(pktp+2));
-				rtptime = ntohl(*(u32_t*)(pktp+4));
-
-				// adjust pointer and length
-				pktp += 12;
-				plen -= 12;
-
-				LOG_SDEBUG("[%p]: seqno:%hu rtp:%u (type: %x, first: %u)", ctx, seqno, rtptime, type, packet[1] & 0x80);
-
-				// check if packet contains enough content to be reasonable
-				if (plen < 16) break;
-
-				if ((packet[1] & 0x80) && (type != 0x56)) {
-					LOG_INFO("[%p]: 1st audio packet received", ctx);
-				}
-
-				buffer_put_packet(ctx, seqno, rtptime, packet[1] & 0x80, pktp, plen);
-
-				break;
-			}
-
-			// sync packet
-			case 0x54: {
-				u32_t rtp_now_latency = ntohl(*(u32_t*)(pktp+4));
-				u64_t remote = (((u64_t) ntohl(*(u32_t*)(pktp+8))) << 32) + ntohl(*(u32_t*)(pktp+12));
-				u32_t rtp_now = ntohl(*(u32_t*)(pktp+16));
-				u16_t flags = ntohs(*(u16_t*)(pktp+2));
-
-				pthread_mutex_lock(&ctx->ab_mutex);
-
-				// re-align timestamp and expected local playback time (and magic 11025 latency)
-				ctx->latency = rtp_now - rtp_now_latency;
-				if (flags == 7 || flags == 4) ctx->latency += 11025;
-				if (ctx->latency < MIN_LATENCY) ctx->latency = MIN_LATENCY;
-				else if (ctx->latency > MAX_LATENCY) ctx->latency = MAX_LATENCY;
-				ctx->synchro.rtp = rtp_now - ctx->latency;
-				ctx->synchro.time = ctx->timing.local + (u32_t) NTP2MS(remote - ctx->timing.remote);
-
-				// now we are synced on RTP frames
-				ctx->synchro.status |= RTP_SYNC;
-
-				// 1st sync packet received (signals a restart of playback)
-				if (packet[0] & 0x10) {
-					LOG_INFO("[%p]: 1st sync packet received", ctx);
-				}
-
-				pthread_mutex_unlock(&ctx->ab_mutex);
-
-				LOG_DEBUG("[%p]: sync packet latency:%d rtp_latency:%u rtp:%u remote ntp:%llx, local time:%u local rtp:%u (now:%u)",
-						  ctx, ctx->latency, rtp_now_latency, rtp_now, remote, ctx->synchro.time, ctx->synchro.rtp, gettime_ms());
-
-				if (!count--) {
-					rtp_request_timing(ctx);
-					count = 3;
-				}
-
-				if ((ctx->synchro.status & RTP_SYNC) && (ctx->synchro.status & NTP_SYNC)) ctx->cmd_cb(RAOP_TIMING, NULL);
-
-				break;
-			}
-
-			// NTP timing packet
-			case 0x53: {
-				u64_t expected;
-				u32_t reference   = ntohl(*(u32_t*)(pktp+12)); // only low 32 bits in our case
-				u64_t remote 	  =(((u64_t) ntohl(*(u32_t*)(pktp+16))) << 32) + ntohl(*(u32_t*)(pktp+20));
-				u32_t roundtrip   = gettime_ms() - reference;
-
-				// better discard sync packets when roundtrip is suspicious
-				if (roundtrip > 100) {
-					LOG_WARN("[%p]: discarding NTP roundtrip of %u ms", ctx, roundtrip);
-					break;
-				}
-
-				/*
-				  The expected elapsed remote time should be exactly the same as
-				  elapsed local time between the two request, corrected by the
-				  drifting
-				*/
-				expected = ctx->timing.remote + MS2NTP(reference - ctx->timing.local);
-
-				ctx->timing.remote = remote;
-				ctx->timing.local = reference;
-
-				// now we are synced on NTP (mutex not needed)
-				ctx->synchro.status |= NTP_SYNC;
-
-				LOG_DEBUG("[%p]: Timing references local:%llu, remote:%llx (delta:%lld, sum:%lld, adjust:%lld, gaps:%d)",
-						  ctx, ctx->timing.local, ctx->timing.remote);
-
-				break;
-			}
-		}
-	}
-
-	free(packet);
-	LOG_INFO("[%p]: terminating", ctx);
-
-#ifndef WIN32
-	xTaskNotify(ctx->joiner, 0, eNoAction);
-	vTaskDelete(NULL);
-#endif
-
-	return NULL;
-}
-
-/*---------------------------------------------------------------------------*/
-static bool rtp_request_timing(rtp_t *ctx) {
-	unsigned char req[32];
-	u32_t now = gettime_ms();
-	int i;
-	struct sockaddr_in host;
-
-	LOG_DEBUG("[%p]: timing request now:%u (port: %hu)", ctx, now, ctx->rtp_sockets[TIMING].rport);
-
-	req[0] = 0x80;
-	req[1] = 0x52|0x80;
-	*(u16_t*)(req+2) = htons(7);
-	*(u32_t*)(req+4) = htonl(0);  // dummy
-	for (i = 0; i < 16; i++) req[i+8] = 0;
-	*(u32_t*)(req+24) = 0;
-	*(u32_t*)(req+28) = htonl(now); // this is not a real NTP, but a 32 ms counter in the low part of the NTP
-
-	if (ctx->host.s_addr != INADDR_ANY) {
-		host.sin_family = AF_INET;
-		host.sin_addr =	ctx->host;
-	} else host = ctx->rtp_host;
-
-	// no address from sender, need to wait for 1st packet to be received
-	if (host.sin_addr.s_addr == INADDR_ANY) return false;
-
-	host.sin_port = htons(ctx->rtp_sockets[TIMING].rport);
-
-	if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), 0, (struct sockaddr*) &host, sizeof(host))) {
-		LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno));
-	}
-
-	return true;
-}
-
-/*---------------------------------------------------------------------------*/
-static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) {
-	unsigned char req[8];    // *not* a standard RTCP NACK
-
-	// do not request silly ranges (happens in case of network large blackouts)
-	if (seq_order(last, first) || last - first > BUFFER_FRAMES / 2) return false;
-	
-	ctx->resent_req += last - first + 1;
-
-	LOG_DEBUG("resend request [W:%hu R:%hu first=%hu last=%hu]", ctx->ab_write, ctx->ab_read, first, last);
-
-	req[0] = 0x80;
-	req[1] = 0x55|0x80;  // Apple 'resend'
-	*(u16_t*)(req+2) = htons(1);  // our seqnum
-	*(u16_t*)(req+4) = htons(first);  // missed seqnum
-	*(u16_t*)(req+6) = htons(last-first+1);  // count
-
-	ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport);
-
-	if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), 0, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) {
-		LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno));
-	}
-
-	return true;
-}
-
+
+
+/*---------------------------------------------------------------------------*/
+static void *rtp_thread_func(void *arg) {
+	fd_set fds;
+	int i, sock = -1;
+	int count = 0;
+	bool ntp_sent;
+	char *packet = malloc(MAX_PACKET);
+	rtp_t *ctx = (rtp_t*) arg;
+
+	for (i = 0; i < 3; i++) {
+		if (ctx->rtp_sockets[i].sock > sock) sock = ctx->rtp_sockets[i].sock;
+		// send synchro requets 3 times
+		ntp_sent = rtp_request_timing(ctx);
+	}
+
+	while (ctx->running) {
+		ssize_t plen;
+		char type;
+		socklen_t rtp_client_len = sizeof(struct sockaddr_storage);
+		int idx = 0;
+		char *pktp = packet;
+		struct timeval timeout = {0, 100*1000};
+
+		FD_ZERO(&fds);
+		for (i = 0; i < 3; i++)	{ FD_SET(ctx->rtp_sockets[i].sock, &fds); }
+
+		if (select(sock + 1, &fds, NULL, NULL, &timeout) <= 0) continue;
+
+		for (i = 0; i < 3; i++)
+			if (FD_ISSET(ctx->rtp_sockets[i].sock, &fds)) idx = i;
+
+		plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, 0, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len);
+
+		if (!ntp_sent) {
+			LOG_WARN("[%p]: NTP request not send yet", ctx);
+			ntp_sent = rtp_request_timing(ctx);
+		}
+
+		if (plen < 0) continue;
+		assert(plen <= MAX_PACKET);
+
+		type = packet[1] & ~0x80;
+		pktp = packet;
+
+		switch (type) {
+			seq_t seqno;
+			unsigned rtptime;
+
+			// re-sent packet
+			case 0x56: {
+				pktp += 4;
+				plen -= 4;
+			}
+
+			// data packet
+			case 0x60: {
+				seqno = ntohs(*(u16_t*)(pktp+2));
+				rtptime = ntohl(*(u32_t*)(pktp+4));
+
+				// adjust pointer and length
+				pktp += 12;
+				plen -= 12;
+
+				LOG_SDEBUG("[%p]: seqno:%hu rtp:%u (type: %x, first: %u)", ctx, seqno, rtptime, type, packet[1] & 0x80);
+
+				// check if packet contains enough content to be reasonable
+				if (plen < 16) break;
+
+				if ((packet[1] & 0x80) && (type != 0x56)) {
+					LOG_INFO("[%p]: 1st audio packet received", ctx);
+				}
+
+				buffer_put_packet(ctx, seqno, rtptime, packet[1] & 0x80, pktp, plen);
+
+				break;
+			}
+
+			// sync packet
+			case 0x54: {
+				u32_t rtp_now_latency = ntohl(*(u32_t*)(pktp+4));
+				u64_t remote = (((u64_t) ntohl(*(u32_t*)(pktp+8))) << 32) + ntohl(*(u32_t*)(pktp+12));
+				u32_t rtp_now = ntohl(*(u32_t*)(pktp+16));
+				u16_t flags = ntohs(*(u16_t*)(pktp+2));
+
+				pthread_mutex_lock(&ctx->ab_mutex);
+
+				// re-align timestamp and expected local playback time (and magic 11025 latency)
+				ctx->latency = rtp_now - rtp_now_latency;
+				if (flags == 7 || flags == 4) ctx->latency += 11025;
+				if (ctx->latency < MIN_LATENCY) ctx->latency = MIN_LATENCY;
+				else if (ctx->latency > MAX_LATENCY) ctx->latency = MAX_LATENCY;
+				ctx->synchro.rtp = rtp_now - ctx->latency;
+				ctx->synchro.time = ctx->timing.local + (u32_t) NTP2MS(remote - ctx->timing.remote);
+
+				// now we are synced on RTP frames
+				ctx->synchro.status |= RTP_SYNC;
+
+				// 1st sync packet received (signals a restart of playback)
+				if (packet[0] & 0x10) {
+					LOG_INFO("[%p]: 1st sync packet received", ctx);
+				}
+
+				pthread_mutex_unlock(&ctx->ab_mutex);
+
+				LOG_DEBUG("[%p]: sync packet latency:%d rtp_latency:%u rtp:%u remote ntp:%llx, local time:%u local rtp:%u (now:%u)",
+						  ctx, ctx->latency, rtp_now_latency, rtp_now, remote, ctx->synchro.time, ctx->synchro.rtp, gettime_ms());
+
+				if (!count--) {
+					rtp_request_timing(ctx);
+					count = 3;
+				}
+
+				if ((ctx->synchro.status & RTP_SYNC) && (ctx->synchro.status & NTP_SYNC)) ctx->cmd_cb(RAOP_TIMING, NULL);
+
+				break;
+			}
+
+			// NTP timing packet
+			case 0x53: {
+				u64_t expected;
+				u32_t reference   = ntohl(*(u32_t*)(pktp+12)); // only low 32 bits in our case
+				u64_t remote 	  =(((u64_t) ntohl(*(u32_t*)(pktp+16))) << 32) + ntohl(*(u32_t*)(pktp+20));
+				u32_t roundtrip   = gettime_ms() - reference;
+
+				// better discard sync packets when roundtrip is suspicious
+				if (roundtrip > 100) {
+					LOG_WARN("[%p]: discarding NTP roundtrip of %u ms", ctx, roundtrip);
+					break;
+				}
+
+				/*
+				  The expected elapsed remote time should be exactly the same as
+				  elapsed local time between the two request, corrected by the
+				  drifting
+				*/
+				expected = ctx->timing.remote + MS2NTP(reference - ctx->timing.local);
+
+				ctx->timing.remote = remote;
+				ctx->timing.local = reference;
+
+				// now we are synced on NTP (mutex not needed)
+				ctx->synchro.status |= NTP_SYNC;
+
+				LOG_DEBUG("[%p]: Timing references local:%llu, remote:%llx (delta:%lld, sum:%lld, adjust:%lld, gaps:%d)",
+						  ctx, ctx->timing.local, ctx->timing.remote);
+
+				break;
+			}
+		}
+	}
+
+	free(packet);
+	LOG_INFO("[%p]: terminating", ctx);
+
+#ifndef WIN32
+	xTaskNotify(ctx->joiner, 0, eNoAction);
+	vTaskDelete(NULL);
+#endif
+
+	return NULL;
+}
+
+/*---------------------------------------------------------------------------*/
+static bool rtp_request_timing(rtp_t *ctx) {
+	unsigned char req[32];
+	u32_t now = gettime_ms();
+	int i;
+	struct sockaddr_in host;
+
+	LOG_DEBUG("[%p]: timing request now:%u (port: %hu)", ctx, now, ctx->rtp_sockets[TIMING].rport);
+
+	req[0] = 0x80;
+	req[1] = 0x52|0x80;
+	*(u16_t*)(req+2) = htons(7);
+	*(u32_t*)(req+4) = htonl(0);  // dummy
+	for (i = 0; i < 16; i++) req[i+8] = 0;
+	*(u32_t*)(req+24) = 0;
+	*(u32_t*)(req+28) = htonl(now); // this is not a real NTP, but a 32 ms counter in the low part of the NTP
+
+	if (ctx->host.s_addr != INADDR_ANY) {
+		host.sin_family = AF_INET;
+		host.sin_addr =	ctx->host;
+	} else host = ctx->rtp_host;
+
+	// no address from sender, need to wait for 1st packet to be received
+	if (host.sin_addr.s_addr == INADDR_ANY) return false;
+
+	host.sin_port = htons(ctx->rtp_sockets[TIMING].rport);
+
+	if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), 0, (struct sockaddr*) &host, sizeof(host))) {
+		LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno));
+	}
+
+	return true;
+}
+
+/*---------------------------------------------------------------------------*/
+static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) {
+	unsigned char req[8];    // *not* a standard RTCP NACK
+
+	// do not request silly ranges (happens in case of network large blackouts)
+	if (seq_order(last, first) || last - first > BUFFER_FRAMES / 2) return false;
+	
+	ctx->resent_req += last - first + 1;
+
+	LOG_DEBUG("resend request [W:%hu R:%hu first=%hu last=%hu]", ctx->ab_write, ctx->ab_read, first, last);
+
+	req[0] = 0x80;
+	req[1] = 0x55|0x80;  // Apple 'resend'
+	*(u16_t*)(req+2) = htons(1);  // our seqnum
+	*(u16_t*)(req+4) = htons(first);  // missed seqnum
+	*(u16_t*)(req+6) = htons(last-first+1);  // count
+
+	ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport);
+
+	if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), 0, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) {
+		LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno));
+	}
+
+	return true;
+}
+

+ 286 - 286
components/raop/util.c

@@ -1,4 +1,4 @@
-/*
+/*
  *  AirConnect: Chromecast & UPnP to AirPlay
  *
  *  (c) Philippe 2016-2017, philippe_44@outlook.com
@@ -16,11 +16,11 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- */
-
-#include "platform.h"
-
-#ifdef WIN32
+ */
+
+#include "platform.h"
+
+#ifdef WIN32
 #include <iphlpapi.h>
 #else
 /*
@@ -34,63 +34,63 @@
 
 #include <stdarg.h>
 
-#include "pthread.h"
-#include "util.h"
-#include "log_util.h"
-
-/*----------------------------------------------------------------------------*/
-/* globals */
-/*----------------------------------------------------------------------------*/
-
-extern log_level	util_loglevel;
-
-/*----------------------------------------------------------------------------*/
-/* locals */
-/*----------------------------------------------------------------------------*/
-static log_level 		*loglevel = &util_loglevel;
-
-static char *ltrim(char *s);
+#include "pthread.h"
+#include "util.h"
+#include "log_util.h"
+
+/*----------------------------------------------------------------------------*/
+/* globals */
+/*----------------------------------------------------------------------------*/
+
+extern log_level	util_loglevel;
+
+/*----------------------------------------------------------------------------*/
+/* locals */
+/*----------------------------------------------------------------------------*/
+static log_level 		*loglevel = &util_loglevel;
+
+static char *ltrim(char *s);
 static int read_line(int fd, char *line, int maxlen, int timeout);
-
-/*----------------------------------------------------------------------------*/
-/* 																			  */
-/* NETWORKING utils															  */
-/* 																			  */
-/*----------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------*/
-#define MAX_INTERFACES 256
-#define DEFAULT_INTERFACE 1
-#if !defined(WIN32)
-#define INVALID_SOCKET (-1)
-#endif
-in_addr_t get_localhost(char **name)
-{
-#ifdef WIN32
-	char buf[256];
-	struct hostent *h = NULL;
-	struct sockaddr_in LocalAddr;
-
-	memset(&LocalAddr, 0, sizeof(LocalAddr));
-
-	gethostname(buf, 256);
-	h = gethostbyname(buf);
-
-	if (name) *name = strdup(buf);
-
-	if (h != NULL) {
-		memcpy(&LocalAddr.sin_addr, h->h_addr_list[0], 4);
-		return LocalAddr.sin_addr.s_addr;
-	}
-	else return INADDR_ANY;
-#else
-	// missing platform here ...
-	return INADDR_ANY;
-#endif
-}
-
-
-/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/* 																			  */
+/* NETWORKING utils															  */
+/* 																			  */
+/*----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+#define MAX_INTERFACES 256
+#define DEFAULT_INTERFACE 1
+#if !defined(WIN32)
+#define INVALID_SOCKET (-1)
+#endif
+in_addr_t get_localhost(char **name)
+{
+#ifdef WIN32
+	char buf[256];
+	struct hostent *h = NULL;
+	struct sockaddr_in LocalAddr;
+
+	memset(&LocalAddr, 0, sizeof(LocalAddr));
+
+	gethostname(buf, 256);
+	h = gethostbyname(buf);
+
+	if (name) *name = strdup(buf);
+
+	if (h != NULL) {
+		memcpy(&LocalAddr.sin_addr, h->h_addr_list[0], 4);
+		return LocalAddr.sin_addr.s_addr;
+	}
+	else return INADDR_ANY;
+#else
+	// missing platform here ...
+	return INADDR_ANY;
+#endif
+}
+
+
+/*----------------------------------------------------------------------------*/
 #ifdef WIN32
 void winsock_init(void) {
 	WSADATA wsaData;
@@ -108,92 +108,92 @@ void winsock_close(void) {
 }
 #endif
 
-
+
+/*----------------------------------------------------------------------------*/
+int shutdown_socket(int sd)
+{
+	if (sd <= 0) return -1;
+
+#ifdef WIN32
+	shutdown(sd, SD_BOTH);
+#else
+	shutdown(sd, SHUT_RDWR);
+#endif
+
+	LOG_DEBUG("closed socket %d", sd);
+
+	return closesocket(sd);
+}
+
+
+/*----------------------------------------------------------------------------*/
+int bind_socket(unsigned short *port, int mode)
+{
+	int sock;
+	socklen_t len = sizeof(struct sockaddr);
+	struct sockaddr_in addr;
+
+	if ((sock = socket(AF_INET, mode, 0)) < 0) {
+		LOG_ERROR("cannot create socket %d", sock);
+		return sock;
+	}
+
+	/*  Populate socket address structure  */
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family      = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_ANY);
+	addr.sin_port        = htons(*port);
+#ifdef SIN_LEN
+	si.sin_len = sizeof(si);
+#endif
+
+	if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
+		closesocket(sock);
+		LOG_ERROR("cannot bind socket %d", sock);
+		return -1;
+	}
+
+	if (!*port) {
+		getsockname(sock, (struct sockaddr *) &addr, &len);
+		*port = ntohs(addr.sin_port);
+	}
+
+	LOG_DEBUG("socket binding %d on port %d", sock, *port);
+
+	return sock;
+}
+
+
+/*----------------------------------------------------------------------------*/
+int conn_socket(unsigned short port)
+{
+	struct sockaddr_in addr;
+	int sd;
+
+	sd = socket(AF_INET, SOCK_STREAM, 0);
+
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	addr.sin_port = htons(port);
+
+	if (sd < 0 || connect(sd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(sd);
+		return -1;
+	}
+
+	LOG_DEBUG("created socket %d", sd);
+
+	return sd;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/* 																			  */
+/* SYSTEM utils															 	  */
+/* 																			  */
 /*----------------------------------------------------------------------------*/
-int shutdown_socket(int sd)
-{
-	if (sd <= 0) return -1;
-
-#ifdef WIN32
-	shutdown(sd, SD_BOTH);
-#else
-	shutdown(sd, SHUT_RDWR);
-#endif
-
-	LOG_DEBUG("closed socket %d", sd);
-
-	return closesocket(sd);
-}
-
-
-/*----------------------------------------------------------------------------*/
-int bind_socket(unsigned short *port, int mode)
-{
-	int sock;
-	socklen_t len = sizeof(struct sockaddr);
-	struct sockaddr_in addr;
-
-	if ((sock = socket(AF_INET, mode, 0)) < 0) {
-		LOG_ERROR("cannot create socket %d", sock);
-		return sock;
-	}
-
-	/*  Populate socket address structure  */
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family      = AF_INET;
-	addr.sin_addr.s_addr = htonl(INADDR_ANY);
-	addr.sin_port        = htons(*port);
-#ifdef SIN_LEN
-	si.sin_len = sizeof(si);
-#endif
-
-	if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
-		closesocket(sock);
-		LOG_ERROR("cannot bind socket %d", sock);
-		return -1;
-	}
-
-	if (!*port) {
-		getsockname(sock, (struct sockaddr *) &addr, &len);
-		*port = ntohs(addr.sin_port);
-	}
-
-	LOG_DEBUG("socket binding %d on port %d", sock, *port);
-
-	return sock;
-}
-
-
-/*----------------------------------------------------------------------------*/
-int conn_socket(unsigned short port)
-{
-	struct sockaddr_in addr;
-	int sd;
-
-	sd = socket(AF_INET, SOCK_STREAM, 0);
-
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-	addr.sin_port = htons(port);
-
-	if (sd < 0 || connect(sd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		close(sd);
-		return -1;
-	}
-
-	LOG_DEBUG("created socket %d", sd);
-
-	return sd;
-}
-
-
-
-/*----------------------------------------------------------------------------*/
-/* 																			  */
-/* SYSTEM utils															 	  */
-/* 																			  */
-/*----------------------------------------------------------------------------*/
-
+
 #ifdef WIN32
 /*----------------------------------------------------------------------------*/
 void *dlopen(const char *filename, int flag) {
@@ -222,14 +222,14 @@ char *dlerror(void) {
 
 
 /*----------------------------------------------------------------------------*/
-/* 																			  */
-/* STDLIB extensions													 	  */
-/* 																			  */
-/*----------------------------------------------------------------------------*/
-
-#ifdef WIN32
-/*---------------------------------------------------------------------------*/
-char *strcasestr(const char *haystack, const char *needle) {
+/* 																			  */
+/* STDLIB extensions													 	  */
+/* 																			  */
+/*----------------------------------------------------------------------------*/
+
+#ifdef WIN32
+/*---------------------------------------------------------------------------*/
+char *strcasestr(const char *haystack, const char *needle) {
 	size_t length_needle;
 	size_t length_haystack;
 	size_t i;
@@ -242,7 +242,7 @@ char *strcasestr(const char *haystack, const char *needle) {
 
 	if (length_haystack < length_needle) return NULL;
 
-	length_haystack -= length_needle - 1;
+	length_haystack -= length_needle - 1;
 
 	for (i = 0; i < length_haystack; i++)
 	{
@@ -264,92 +264,92 @@ char *strcasestr(const char *haystack, const char *needle) {
 	}
 
 	return NULL;
-}
-
-/*---------------------------------------------------------------------------*/
-char* strsep(char** stringp, const char* delim)
-{
-  char* start = *stringp;
-  char* p;
-
-  p = (start != NULL) ? strpbrk(start, delim) : NULL;
-
-  if (p == NULL)  {
-	*stringp = NULL;
-  } else {
-	*p = '\0';
-	*stringp = p + 1;
-  }
-
-  return start;
-}
-
-/*---------------------------------------------------------------------------*/
-char *strndup(const char *s, size_t n) {
-	char *p = malloc(n + 1);
-	strncpy(p, s, n);
-	p[n] = '\0';
-
-	return p;
-}
-#endif
-
-
-/*----------------------------------------------------------------------------*/
-char* strextract(char *s1, char *beg, char *end)
-{
-	char *p1, *p2, *res;
-
-	p1 = strcasestr(s1, beg);
-	if (!p1) return NULL;
-
-	p1 += strlen(beg);
-	p2 = strcasestr(p1, end);
-	if (!p2) return strdup(p1);
-
-	res = malloc(p2 - p1 + 1);
-	memcpy(res, p1, p2 - p1);
-	res[p2 - p1] = '\0';
-
-	return res;
-}
-
-
-#ifdef WIN32
-/*----------------------------------------------------------------------------*/
-int asprintf(char **strp, const char *fmt, ...)
-{
-	va_list args, cp;
-	int len, ret = 0;
-
-	va_start(args, fmt);
-	len = vsnprintf(NULL, 0, fmt, args);
-	*strp = malloc(len + 1);
-
-	if (*strp) ret = vsprintf(*strp, fmt, args);
-
-	va_end(args);
-
-	return ret;
-}
-#endif
-
-/*---------------------------------------------------------------------------*/
-static char *ltrim(char *s)
+}
+
+/*---------------------------------------------------------------------------*/
+char* strsep(char** stringp, const char* delim)
+{
+  char* start = *stringp;
+  char* p;
+
+  p = (start != NULL) ? strpbrk(start, delim) : NULL;
+
+  if (p == NULL)  {
+	*stringp = NULL;
+  } else {
+	*p = '\0';
+	*stringp = p + 1;
+  }
+
+  return start;
+}
+
+/*---------------------------------------------------------------------------*/
+char *strndup(const char *s, size_t n) {
+	char *p = malloc(n + 1);
+	strncpy(p, s, n);
+	p[n] = '\0';
+
+	return p;
+}
+#endif
+
+
+/*----------------------------------------------------------------------------*/
+char* strextract(char *s1, char *beg, char *end)
+{
+	char *p1, *p2, *res;
+
+	p1 = strcasestr(s1, beg);
+	if (!p1) return NULL;
+
+	p1 += strlen(beg);
+	p2 = strcasestr(p1, end);
+	if (!p2) return strdup(p1);
+
+	res = malloc(p2 - p1 + 1);
+	memcpy(res, p1, p2 - p1);
+	res[p2 - p1] = '\0';
+
+	return res;
+}
+
+
+#ifdef WIN32
+/*----------------------------------------------------------------------------*/
+int asprintf(char **strp, const char *fmt, ...)
+{
+	va_list args, cp;
+	int len, ret = 0;
+
+	va_start(args, fmt);
+	len = vsnprintf(NULL, 0, fmt, args);
+	*strp = malloc(len + 1);
+
+	if (*strp) ret = vsprintf(*strp, fmt, args);
+
+	va_end(args);
+
+	return ret;
+}
+#endif
+
+/*---------------------------------------------------------------------------*/
+static char *ltrim(char *s)
 {
 	while(isspace((int) *s)) s++;
 	return s;
 }
 
 /*----------------------------------------------------------------------------*/
-/* 																			  */
-/* HTTP management														 	  */
-/* 																			  */
-/*----------------------------------------------------------------------------*/
-
-/*----------------------------------------------------------------------------*/
-bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len)
-{
+/* 																			  */
+/* HTTP management														 	  */
+/* 																			  */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len)
+{
 	char line[256], *dp;
 	unsigned j;
 	int i, timeout = 100;
@@ -357,14 +357,14 @@ bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len)
 	rkd[0].key = NULL;
 
 	if ((i = read_line(sock, line, sizeof(line), timeout)) <= 0) {
-		if (i < 0) {
-			LOG_ERROR("cannot read method", NULL);
-		}
+		if (i < 0) {
+			LOG_ERROR("cannot read method", NULL);
+		}
 		return false;
 	}
 
 	if (!sscanf(line, "%s", method)) {
-		LOG_ERROR("missing method", NULL);
+		LOG_ERROR("missing method", NULL);
 		return false;
 	}
 
@@ -377,7 +377,7 @@ bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len)
 		// line folding should be deprecated
 		if (i && rkd[i].key && (line[0] == ' ' || line[0] == '\t')) {
 			for(j = 0; j < strlen(line); j++) if (line[j] != ' ' && line[j] != '\t') break;
-			rkd[i].data = realloc(rkd[i].data, strlen(rkd[i].data) + strlen(line + j) + 1);
+			rkd[i].data = realloc(rkd[i].data, strlen(rkd[i].data) + strlen(line + j) + 1);
 			strcat(rkd[i].data, line + j);
 			continue;
 		}
@@ -403,7 +403,7 @@ bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len)
 	if (*len) {
 		int size = 0;
 
-		*body = malloc(*len + 1);
+		*body = malloc(*len + 1);
 		while (*body && size < *len) {
 			int bytes = recv(sock, *body + size, *len - size, 0);
 			if (bytes <= 0) break;
@@ -467,23 +467,23 @@ static int read_line(int fd, char *line, int maxlen, int timeout)
 
 /*----------------------------------------------------------------------------*/
 char *http_send(int sock, char *method, key_data_t *rkd)
-{
-	unsigned sent, len;
-	char *resp = kd_dump(rkd);
-	char *data = malloc(strlen(method) + 2 + strlen(resp) + 2 + 1);
-
-	len = sprintf(data, "%s\r\n%s\r\n", method, resp);
-	NFREE(resp);
-
-	sent = send(sock, data, len, 0);
-
-	if (sent != len) {
-		LOG_ERROR("HTTP send() error:%s %u (strlen=%u)", data, sent, len);
-		NFREE(data);
-	}
-
-	return data;
-}
+{
+	unsigned sent, len;
+	char *resp = kd_dump(rkd);
+	char *data = malloc(strlen(method) + 2 + strlen(resp) + 2 + 1);
+
+	len = sprintf(data, "%s\r\n%s\r\n", method, resp);
+	NFREE(resp);
+
+	sent = send(sock, data, len, 0);
+
+	if (sent != len) {
+		LOG_ERROR("HTTP send() error:%s %u (strlen=%u)", data, sent, len);
+		NFREE(data);
+	}
+
+	return data;
+}
 
 
 /*----------------------------------------------------------------------------*/
@@ -534,7 +534,7 @@ char *kd_dump(key_data_t *kd)
 	char *str = NULL;
 
 	if (!kd || !kd[0].key) return strdup("\r\n");
-
+
 	while (kd && kd[i].key) {
 		char *buf;
 		int len;
@@ -545,8 +545,8 @@ char *kd_dump(key_data_t *kd)
 			void *p = realloc(str, size + 1024);
 			size += 1024;
 			if (!p) {
-				free(str);
-				return NULL;
+				free(str);
+				return NULL;
 			}
 			str = p;
 		}
@@ -574,28 +574,28 @@ void free_metadata(struct metadata_s *metadata)
 	NFREE(metadata->artwork);
 	NFREE(metadata->remote_title);
 }
-
+
 
/*----------------------------------------------------------------------------*/
-

-int _fprintf(FILE *file, ...)
-{
-	va_list args;
-	char *fmt;
-	int n;
-
-	va_start(args, file);
-	fmt = va_arg(args, char*);
-
-	n = vfprintf(file, fmt, args);
-	va_end(args);
-	return n;
-}
-
-
-
-
-
-
-
-
-
+
+int _fprintf(FILE *file, ...)
+{
+	va_list args;
+	char *fmt;
+	int n;
+
+	va_start(args, file);
+	fmt = va_arg(args, char*);
+
+	n = vfprintf(file, fmt, args);
+	va_end(args);
+	return n;
+}
+
+
+
+
+
+
+
+
+

+ 44 - 44
components/raop/util.h

@@ -1,4 +1,4 @@
-/*
+/*
  *  Misc utilities
  *
  *  (c) Adrian Smith 2012-2014, triode1@btinternet.com
@@ -17,43 +17,43 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- */
-
-#ifndef __UTIL_H
-#define __UTIL_H
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "platform.h"
-#include "pthread.h"
-
+ */
+
+#ifndef __UTIL_H
+#define __UTIL_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "platform.h"
+#include "pthread.h"
+
 #define NFREE(p) if (p) { free(p); p = NULL; }
 
 typedef struct metadata_s {
-	char *artist;
-	char *album;
-	char *title;
-	char *genre;
-	char *path;
-	char *artwork;
-	char *remote_title;
-	u32_t track;
-	u32_t duration;
-	u32_t track_hash;
-	u32_t sample_rate;
-	u8_t  sample_size;
-	u8_t  channels;
-} metadata_t;
-
-/*
-void 		free_metadata(struct metadata_s *metadata);
-void 		dup_metadata(struct metadata_s *dst, struct metadata_s *src);
-*/
+	char *artist;
+	char *album;
+	char *title;
+	char *genre;
+	char *path;
+	char *artwork;
+	char *remote_title;
+	u32_t track;
+	u32_t duration;
+	u32_t track_hash;
+	u32_t sample_rate;
+	u8_t  sample_size;
+	u8_t  channels;
+} metadata_t;
+
+/*
+void 		free_metadata(struct metadata_s *metadata);
+void 		dup_metadata(struct metadata_s *dst, struct metadata_s *src);
+*/
 
 
u32_t 		gettime_ms(void);
-
+
 #ifdef WIN32
 char* 		strsep(char** stringp, const char* delim);
 char 		*strndup(const char *s, size_t n);
@@ -64,22 +64,22 @@ void 		winsock_close(void);
 char 		*strlwr(char *str);
 #endif
 char* 		strextract(char *s1, char *beg, char *end);
-in_addr_t 	get_localhost(char **name);
-void 		get_mac(u8_t mac[]);
-int 		shutdown_socket(int sd);
-int 		bind_socket(short unsigned *port, int mode);
-int 		conn_socket(unsigned short port);

typedef struct {
+in_addr_t 	get_localhost(char **name);
+void 		get_mac(u8_t mac[]);
+int 		shutdown_socket(int sd);
+int 		bind_socket(short unsigned *port, int mode);
+int 		conn_socket(unsigned short port);
typedef struct {
 
	char *key;
 	char *data;
-} key_data_t;
-
+} key_data_t;
+
 bool 		http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len);
char*		http_send(int sock, char *method, key_data_t *rkd);
 
-char*		kd_lookup(key_data_t *kd, char *key);
-bool 		kd_add(key_data_t *kd, char *key, char *value);
-char* 		kd_dump(key_data_t *kd);
+char*		kd_lookup(key_data_t *kd, char *key);
+bool 		kd_add(key_data_t *kd, char *key, char *value);
+char* 		kd_dump(key_data_t *kd);
 void 		kd_free(key_data_t *kd);
 
 int 		_fprintf(FILE *file, ...);
 
#endif
-
+

+ 27 - 2
components/squeezelite-ota/cmd_ota.c

@@ -27,21 +27,46 @@
 #include "sdkconfig.h"
 
 static const char * TAG = "platform_esp32";
-
+extern void start_ota(const char * bin_url);
+static struct {
+    struct arg_str *url;
+    struct arg_end *end;
+} ota_args;
 /* 'heap' command prints minumum heap size */
 static int perform_ota_update(int argc, char **argv)
 {
+    int nerrors = arg_parse(argc, argv, (void **) &ota_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, ota_args.end, argv[0]);
+        return 1;
+    }
+
+    const char *url = ota_args.url->sval[0];
+
+    esp_err_t err=ESP_OK;
+    start_ota(url);
+
+
+
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "%s", esp_err_to_name(err));
+        return 1;
+    }
 
     return 0;
 }
 
-static void register_ota_cmd()
+ void register_ota_cmd()
 {
+	 ota_args.url= arg_str1(NULL, NULL, "<url>", "url of the binary app file");
+	 ota_args.end = arg_end(2);
+
     const esp_console_cmd_t cmd = {
         .command = "ota_update",
         .help = "Updates the application binary from the provided URL",
         .hint = NULL,
         .func = &perform_ota_update,
+        .argtable = &ota_args
     };
     ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
 }

+ 1 - 1
components/squeezelite-ota/cmd_ota.h

@@ -13,7 +13,7 @@ extern "C" {
 #endif
 
 // Register system functions
-static void register_ota_cmd();
+ void register_ota_cmd();
 #ifdef __cplusplus
 }
 #endif

+ 5 - 1
components/squeezelite-ota/squeezelite-ota.c

@@ -41,12 +41,15 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
     switch (evt->event_id) {
     case HTTP_EVENT_ERROR:
         ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
+        strncpy(ota_status,sizeof(ota_status)-1,"HTTP_EVENT_ERROR");
         break;
     case HTTP_EVENT_ON_CONNECTED:
         ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
-        break;
+        strncpy(ota_status,sizeof(ota_status)-1,"HTTP_EVENT_ON_CONNECTED");
+                break;
     case HTTP_EVENT_HEADER_SENT:
         ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+        strncpy(ota_status,sizeof(ota_status)-1,"HTTP_EVENT_HEADER_SENT");
         break;
     case HTTP_EVENT_ON_HEADER:
         ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
@@ -59,6 +62,7 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
         break;
     case HTTP_EVENT_DISCONNECTED:
         ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+        strncpy(ota_status,sizeof(ota_status)-1,"HTTP_EVENT_DISCONNECTED");
         break;
     }
     return ESP_OK;

+ 3 - 3
main/console.c

@@ -240,11 +240,11 @@ void console_start() {
 	esp_console_register_help_command();
 	register_system();
 	register_nvs();
-#if !RECOVERY_APPLICATION
-#pragma message "compiling for squeezelite""
+#if ! RECOVERY_APPLICATION
+#warning  "compiling for squeezelite"
 	register_squeezelite();
 #else
-#pragma message "compiling for recovery"
+#warning "compiling for recovery"
 	register_ota_cmd();
 #endif
 	register_i2ctools();