浏览代码

Merge remote-tracking branch 'origin/master' into httpd

Sebastien 5 年之前
父节点
当前提交
aa71866a17

+ 1 - 1
README.md

@@ -64,7 +64,7 @@ SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,speed=<speed>][,HFlip][,VFlip][dri
 - VFlip and HFlip are optional can be used to change display orientation
 - Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz
 
-Currently 128x32/64 I2C display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported
+Currently 128x32/64 I2C and SPI display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported
 
 The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is
 ```

+ 1 - 1
components/display/SH1106.c

@@ -143,7 +143,7 @@ struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
 	if (!strcasestr(Driver, "SH1106")) return NULL;
 	
 	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
-	*Device = SH1106;	
+	*Device = SH1106;
 	Device->Depth = 1;
 #if !defined SHADOW_BUFFER && defined USE_IRAM	
 	Device->Alloc = GDS_ALLOC_IRAM_SPI;

+ 7 - 7
components/display/SSD1306.c

@@ -42,12 +42,12 @@ static void Update( struct GDS_Device* Device ) {
 #ifdef SHADOW_BUFFER
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
 	// not sure the compiler does not have to redo all calculation in for loops, so local it is
-	int width = Device->Width, rows = Device->Height / 8;
+	int width = Device->Width, pages = Device->Height / 8;
 	uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
-	int CurrentRow = -1, FirstCol = -1, LastCol = -1;
+	int CurrentPage = -1, FirstCol = -1, LastCol = -1;
 	
 	// by row, find first and last columns that have been updated
-	for (int r = 0; r < rows; r++) {
+	for (int p = 0; p < pages; p++) {
 		uint8_t first = 0, last;	
 		for (int c = 0; c < width; c++) {
 			if (*iptr != *optr) {
@@ -70,15 +70,15 @@ static void Update( struct GDS_Device* Device ) {
 			}
 			
 			// Set row only when needed, otherwise let auto-increment work
-			if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 );
-			CurrentRow = r + 1;
+			if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 );
+			CurrentPage = p + 1;
 			
 			// actual write
-			Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
+			Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1);
 		}
 	}	
 #else	
-	// automatic counter and end Page/Column
+	// automatic counter and end Page/Column (we assume Height % 8 == 0)
 	SetColumnAddress( Device, 0, Device->Width - 1);
 	SetPageAddress( Device, 0, Device->Height / 8 - 1);
 	Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );

+ 2 - 6
components/display/SSD132x.c

@@ -162,7 +162,7 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i
         *FBOffset ^= BIT( 7 - XBit );
     } else {
 		// we might be able to save the 7-Xbit using BitRemap (A0 bit 2)
-        *FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit );
+        *FBOffset = ( Color == GDS_COLOR_BLACK ) ?  *FBOffset & ~BIT( 7 - XBit ) : *FBOffset | BIT( 7 - XBit );
     }
 }
 
@@ -181,8 +181,6 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int
 		c += chunk * 8;
 		while (c <= x2) DrawPixel1Fast( Device, c++, r, Color );
 	}
-	
-	Device->Dirty = true;
 }
 
 static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
@@ -193,8 +191,6 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i
 	
 	// just do bitreverse and if BitRemap works, there will be even nothing to do	
 	for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++];
-	
-	// Dirty is set for us
 }
 
 static void SetHFlip( struct GDS_Device* Device, bool On ) { 
@@ -307,7 +303,7 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
 	
 	*Device = SSD132x;	
 	((struct PrivateSpace*) Device->Private)->Model = Model;
-	
+		
 	sscanf(Driver, "%*[^:]:%c", &Device->Depth);
 	
 	if (Model == SSD1326 && Device->Depth == 1) {

+ 252 - 0
components/display/SSD1675.c

@@ -0,0 +1,252 @@
+/**
+ * Copyright (c) 2017-2018 Tara Keeling
+ *				 2020 Philippe G.
+ * 
+ * This software is released under the MIT License.
+ * https://opensource.org/licenses/MIT
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/gpio.h"
+#include <esp_log.h>
+
+#include "gds.h"
+#include "gds_private.h"
+
+static char TAG[] = "SSD1675";
+
+const unsigned char EPD_lut_full_update[] = {
+    0x80,0x60,0x40,0x00,0x00,0x00,0x00,             //LUT0: BB:     VS 0 ~7
+    0x10,0x60,0x20,0x00,0x00,0x00,0x00,             //LUT1: BW:     VS 0 ~7
+    0x80,0x60,0x40,0x00,0x00,0x00,0x00,             //LUT2: WB:     VS 0 ~7
+    0x10,0x60,0x20,0x00,0x00,0x00,0x00,             //LUT3: WW:     VS 0 ~7
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT4: VCOM:   VS 0 ~7
+    0x03,0x03,0x00,0x00,0x02,                       // TP0 A~D RP0
+    0x09,0x09,0x00,0x00,0x02,                       // TP1 A~D RP1
+    0x03,0x03,0x00,0x00,0x02,                       // TP2 A~D RP2
+    0x00,0x00,0x00,0x00,0x00,                       // TP3 A~D RP3
+    0x00,0x00,0x00,0x00,0x00,                       // TP4 A~D RP4
+    0x00,0x00,0x00,0x00,0x00,                       // TP5 A~D RP5
+    0x00,0x00,0x00,0x00,0x00,                       // TP6 A~D RP6
+    0x15,0x41,0xA8,0x32,0x30,0x0A,
+};
+
+const unsigned char EPD_lut_partial_update[]= { //20 bytes
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT0: BB:     VS 0 ~7
+    0x80,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT1: BW:     VS 0 ~7
+    0x40,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT2: WB:     VS 0 ~7
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT3: WW:     VS 0 ~7
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,             //LUT4: VCOM:   VS 0 ~7
+    0x0A,0x00,0x00,0x00,0x00,                       // TP0 A~D RP0
+    0x00,0x00,0x00,0x00,0x00,                       // TP1 A~D RP1
+    0x00,0x00,0x00,0x00,0x00,                       // TP2 A~D RP2
+    0x00,0x00,0x00,0x00,0x00,                       // TP3 A~D RP3
+    0x00,0x00,0x00,0x00,0x00,                       // TP4 A~D RP4
+    0x00,0x00,0x00,0x00,0x00,                       // TP5 A~D RP5
+    0x00,0x00,0x00,0x00,0x00,                       // TP6 A~D RP6
+    0x15,0x41,0xA8,0x32,0x30,0x0A,
+};
+
+struct PrivateSpace {
+	int	ReadyPin;
+	uint16_t Height;
+};
+
+// Functions are not declared to minimize # of lines
+
+void WaitReady( struct GDS_Device* Device) {
+	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
+	if (Private->ReadyPin >= 0) {
+		int count = 4*1000;
+		while (gpio_get_level( Private->ReadyPin ) && count) {
+			vTaskDelay( pdMS_TO_TICKS(100) );
+			count -= 100;
+		}	
+	} else {
+		vTaskDelay( pdMS_TO_TICKS(2000) );
+	}	
+}	
+
+static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
+	Device->WriteData( Device, &Data, 1 );
+}
+
+static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
+	// start might be greater than end if we decrement
+	Device->WriteCommand( Device, 0x44 );
+	Device->WriteData( Device, &Start, 1 );
+	Device->WriteData( Device, &End, 1 );
+	
+	// we obviously want to start ... from the start
+	Device->WriteCommand( Device, 0x4e );	
+	WriteByte( Device, Start );
+}
+static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
+	// start might be greater than end if we decrement
+	Device->WriteCommand( Device, 0x45 );	
+	WriteByte( Device, Start );
+	WriteByte( Device, Start >> 8 );
+	WriteByte( Device, End );
+	WriteByte( Device, End >> 8 );
+	
+	// we obviously want to start ... from the start
+	Device->WriteCommand( Device, 0x4f );	
+	WriteByte( Device, Start );
+	WriteByte( Device, Start >> 8 );
+}
+
+static void Update( struct GDS_Device* Device ) {
+	uint8_t *iptr = Device->Framebuffer;
+	
+	Device->WriteCommand( Device, 0x24 );
+	
+	// this is awfully slow, but e-ink are slow anyway ...
+	for (int i = Device->FramebufferSize; --i >= 0;) {
+		WriteByte( Device, ~*iptr++ );
+	}	
+	
+	Device->WriteCommand( Device, 0x22 ); 
+    WriteByte( Device, 0xC7);
+    Device->WriteCommand( Device, 0X20 );
+
+	WaitReady( Device );
+}
+
+// remember that for these ELD drivers W and H are "inverted"
+static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
+    uint32_t YBit = ( Y & 0x07 );
+    Y>>= 3;
+
+    uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
+    *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7-YBit ) : *FBOffset | BIT( 7-YBit );
+}	
+
+static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
+	for (int r = y1; r <= y2; r++) {
+		for (int c = x1; c <= x2; c++) {
+			DrawPixelFast( Device, c, r, Color );
+		}	
+	}
+}
+
+static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
+	if (!Height) Height = Device->Height;
+	if (!Width) Width = Device->Width;
+	
+	// just do row/column swap
+	for (int r = 0; r < Height; r++) {
+		uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
+		for (int c = Width; --c >= 0;) {
+			*optr++ = *iptr;
+			iptr += Height;
+		}	
+	}
+}
+
+static bool Init( struct GDS_Device* Device ) {
+	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
+	
+	// need to re-adjust framebuffer because these are non % 8 
+	Private->Height = Device->Height;
+	if (Device->Height & 0x07) Device->Height = ((Device->Height >> 3) + 1) << 3;
+	
+	Device->FramebufferSize = Device->Width * Device->Height / 8;
+	Device->Framebuffer = calloc(1, Device->FramebufferSize);
+	NullCheck( Device->Framebuffer, return false );
+	
+	if (Private->ReadyPin >= 0) {
+		gpio_pad_select_gpio( Private->ReadyPin );
+		gpio_set_pull_mode( Private->ReadyPin, GPIO_PULLUP_ONLY);
+		gpio_set_direction( Private->ReadyPin, GPIO_MODE_INPUT );
+	}
+	
+	// soft reset	
+	vTaskDelay(pdMS_TO_TICKS( 2000 ));
+    Device->WriteCommand( Device, 0x12 ); 	
+	WaitReady( Device );
+	
+	Device->WriteCommand( Device, 0x74 ); 			
+    WriteByte( Device, 0x54 );
+	Device->WriteCommand( Device, 0x7e ); 			
+    WriteByte( Device, 0x3B );
+	
+	Device->WriteCommand( Device, 0x3c );	
+    WriteByte( Device, 0x03 );
+	
+	Device->WriteCommand( Device, 0x2c );	
+    WriteByte( Device, 0x55 );
+
+	Device->WriteCommand( Device, 0x03 );	
+	WriteByte( Device, EPD_lut_full_update[70] );
+	
+	Device->WriteCommand( Device, 0x04 );	
+	WriteByte( Device, EPD_lut_full_update[71] );
+	WriteByte( Device, EPD_lut_full_update[72] );
+	WriteByte( Device, EPD_lut_full_update[73] );
+	
+	Device->WriteCommand( Device, 0x3a );	
+	WriteByte( Device, EPD_lut_full_update[74] );
+	Device->WriteCommand( Device, 0x3b );	
+	WriteByte( Device, EPD_lut_full_update[75] );
+	
+	Device->WriteCommand( Device, 0X32 );	
+	for (int i = 0; i < 70; i++) {
+		WriteByte( Device, EPD_lut_full_update[i] );
+	}	
+
+	// now deal with funny X/Y layout (W and H are "inverted")
+	Device->WriteCommand( Device, 0x01 );
+    WriteByte( Device, Device->Width - 1 );
+    WriteByte( Device, (Device->Width - 1) >> 8 );
+    WriteByte( Device, (0 << 0) );		
+
+	/* 
+	 Start from 0, Ymax, incX, decY. Starting from X=Height would be difficult
+	 as we would hit the extra bits added because height % 8 != 0 and they are
+	 not written by the DrawPixel. By starting from X=0 we are aligned and 
+	 doing incY is like a clockwise 90° rotation (vs otherwise we would virtually 
+	 do a counter-clockwise rotation but again have the "border" issue.
+	*/ 
+	Device->WriteCommand( Device, 0x11 ); 			
+	WriteByte( Device, (1 << 2) | (0 << 1) | (1 << 0));
+		
+	// must be in order with increment/decrement i.e start might be > end if we decrement
+	SetColumnAddress( Device, 0x0, (Device->Height >> 3) - 1 );
+	SetRowAddress ( Device, Device->Width - 1, 0 );
+
+	WaitReady( Device );
+	
+	Update( Device );
+	
+	return true;
+}	
+
+static const struct GDS_Device SSD1675 = {
+	.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
+	.DrawPixelFast = DrawPixelFast,
+	.Update = Update, .Init = Init,
+};	
+
+struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) {
+	if (!strcasestr(Driver, "SSD1675")) return NULL;
+	
+	if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
+	*Device = SSD1675;	
+	
+	Device->Depth = 1;
+	Device->Alloc = GDS_ALLOC_NONE;
+	
+	char *p;
+	struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
+	Private->ReadyPin = -1;
+	if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1);
+	
+	ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);
+	
+	return Device;
+}

+ 11 - 4
components/display/core/gds.c

@@ -133,11 +133,18 @@ bool GDS_Reset( struct GDS_Device* Device ) {
 }
 
 bool GDS_Init( struct GDS_Device* Device ) {
-	Device->FramebufferSize = ( Device->Width * Device->Height ) / (8  / Device->Depth);
 	
-	if ((Device->Alloc && GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
-	else Device->Framebuffer = calloc( 1, Device->FramebufferSize );
-	NullCheck( Device->Framebuffer, return false );
+	Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
+	
+	// allocate FB unless explicitely asked not to
+	if (!(Device->Alloc & GDS_ALLOC_NONE)) {
+		if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) {
+			heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
+		} else {
+			Device->Framebuffer = calloc( 1, Device->FramebufferSize );
+		}	
+		NullCheck( Device->Framebuffer, return false );
+	}	
 	
 	bool Res = Device->Init( Device );
 	if (!Res) free(Device->Framebuffer);

+ 3 - 3
components/display/core/gds.h

@@ -13,13 +13,13 @@
  monochrome mode is not such type of screen, SH1106 and SSD1306 are
 */ 
 
-enum { 	GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, 
+enum { 	GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, 
 		GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
 		GDS_COLOR_MAX
 };
 		
-#define GDS_COLOR_BLACK GDS_COLOR_L0
-#define GDS_COLOR_WHITE (GDS_COLOR_MAX - 1)
+#define GDS_COLOR_BLACK (0)
+#define GDS_COLOR_WHITE (-1)
 #define GDS_COLOR_XOR 	(GDS_COLOR_MAX + 1)
 
 struct GDS_Device;

+ 3 - 2
components/display/core/gds_private.h

@@ -7,6 +7,7 @@
 #include "gds.h"
 #include "gds_err.h"
 
+#define GDS_ALLOC_NONE		0x80
 #define GDS_ALLOC_IRAM		0x01
 #define GDS_ALLOC_IRAM_SPI	0x02
 
@@ -158,7 +159,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int
     if ( Color == GDS_COLOR_XOR ) {
         *FBOffset ^= BIT( YBit );
     } else {
-        *FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
+        *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
     }
 }
 
@@ -166,7 +167,7 @@ inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int
 	uint8_t* FBOffset;
 
     FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
-	*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color);
+	*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color  & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
 }
 
 inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {

+ 9 - 6
components/display/display.c

@@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct {
 static void displayer_task(void *args);
 
 struct GDS_Device *display;   
-extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect;
-GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL };
+extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect;
+GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL };
 
 /****************************************************************************************
  * 
@@ -86,15 +86,18 @@ void display_init(char *welcome) {
 			
 	// so far so good
 	if (display && width > 0 && height > 0) {
+		int RST_pin = -1;
+		if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1);
+		
 		// Detect driver interface
 		if (strstr(config, "I2C") && i2c_system_port != -1) {
 			int address = 0x3C;
 				
 			if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
-		
+				
 			init = true;
 			GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
-			GDS_I2CAttachDevice( display, width, height, address, -1 );
+			GDS_I2CAttachDevice( display, width, height, address, RST_pin );
 		
 			ESP_LOGI(TAG, "Display is I2C on port %u", address);
 		} else if (strstr(config, "SPI") && spi_system_host != -1) {
@@ -105,7 +108,7 @@ void display_init(char *welcome) {
 		
 			init = true;
 			GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
-			GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed );
+			GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed );
 				
 			ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
 		} else {
@@ -189,7 +192,7 @@ static void displayer_task(void *args) {
 		
 		// handler elapsed track time
 		if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) {
-			char counter[12];
+			char counter[16];
 			TickType_t tick = xTaskGetTickCount();
 			uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS;
 			

+ 23 - 14
components/raop/raop.c

@@ -57,9 +57,9 @@ typedef struct raop_ctx_s {
 	struct in_addr peer;	// IP of the iDevice (airplay sender)
 	bool running;
 #ifdef WIN32
-	pthread_t thread, search_thread;
+	pthread_t thread;
 #else
-	TaskHandle_t thread, search_thread, joiner;
+	TaskHandle_t thread, joiner;
 	StaticTask_t *xTaskBuffer;
 	StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4)));
 #endif
@@ -81,10 +81,11 @@ typedef struct raop_ctx_s {
 		char				DACPid[32], id[32];
 		struct in_addr		host;
 		u16_t				port;
+		bool running;
 #ifdef WIN32
 		struct mDNShandle_s *handle;
+		pthread_t thread;
 #else
-		bool running;
 		TaskHandle_t thread, joiner;
 		StaticTask_t *xTaskBuffer;
 		StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));;
@@ -218,7 +219,7 @@ void raop_delete(struct raop_ctx_s *ctx) {
 	struct sockaddr addr;
 	socklen_t nlen = sizeof(struct sockaddr);
 #endif
-	
+
 	if (!ctx) return;
 
 #ifdef WIN32
@@ -240,7 +241,7 @@ 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);
+		pthread_join(ctx->active_remote.thread, NULL);
 	}
 
 	// stop broadcasting devices
@@ -515,7 +516,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
 
 #ifdef WIN32	
 		ctx->active_remote.handle = init_mDNS(false, ctx->host);
-		pthread_create(&ctx->search_thread, NULL, &search_remote, ctx);
+		pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx);
 #else
 		ctx->active_remote.running = true;
 		ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex();		
@@ -592,7 +593,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
 		// need to make sure no search is on-going and reclaim pthread memory
 #ifdef WIN32
 		if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle);
-		pthread_join(ctx->search_thread, NULL);
+		pthread_join(ctx->active_remote.thread, NULL);
 #else
 		ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
 		ctx->active_remote.running = false;
@@ -631,18 +632,16 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
 			current = ((current - start) / 44100) * 1000;
 			if (stop) stop = ((stop - start) / 44100) * 1000;
 			else stop = -1;
-			LOG_INFO("[%p]: SET PARAMETER progress %u/%u %s", ctx, current, stop, p);
-			success = ctx->cmd_cb(RAOP_PROGRESS, current, stop);
-		}
-
-		if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) {
+			LOG_INFO("[%p]: SET PARAMETER progress %d/%u %s", ctx, current, stop, p);
+			success = ctx->cmd_cb(RAOP_PROGRESS, max(current, 0), stop);
+		} else 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
 			};
 
-			LOG_INFO("[%p]: received metadata");
+			LOG_INFO("[%p]: received metadata", ctx);
 			settings.ctx = &metadata;
 			memset(&metadata, 0, sizeof(struct metadata_s));
 			if (!dmap_parse(&settings, body, len)) {
@@ -651,6 +650,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
 				success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title);
 				free_metadata(&metadata);
 			}
+		} else {
+			char *dump = kd_dump(headers);
+			LOG_INFO("Unhandled SET PARAMETER\n%s", dump);
+			free(dump);
 		}
 	}
 
@@ -684,9 +687,13 @@ void abort_rtsp(raop_ctx_t *ctx) {
 		rtp_end(ctx->rtp);
 		ctx->rtp = NULL;
 		LOG_INFO("[%p]: RTP thread aborted", ctx);
-	}	
+	}
 
 	if (ctx->active_remote.running) {
+#ifdef WIN32
+		pthread_join(ctx->active_remote.thread, NULL);
+		close_mDNS(ctx->active_remote.handle);
+#else
 		// need to make sure no search is on-going and reclaim task memory
 		ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
 		ctx->active_remote.running = false;
@@ -696,8 +703,10 @@ void abort_rtsp(raop_ctx_t *ctx) {
 		vSemaphoreDelete(ctx->active_remote.thread);
 
 		heap_caps_free(ctx->active_remote.xTaskBuffer);
+#endif
 		memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
 
+
 		LOG_INFO("[%p]: Remote search thread aborted", ctx);
 	}	
 

+ 23 - 18
components/raop/rtp.c

@@ -443,25 +443,23 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
 		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)) {
+		seq_t i;
+		u32_t now;
+
 		// 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;
-			}
+
+		// need to request re-send and adjust timing of gaps
+		rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1);
+		for (now = gettime_ms(), 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;
@@ -519,14 +517,21 @@ static void buffer_push_packet(rtp_t *ctx) {
 			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 (playtime - now <= hold) {
+			if (curframe->ready) {
+				ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime);
+				curframe->ready = 0;
+			} else {
+				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 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;
+		} else {
+			break;
+		}
 
 		ctx->ab_read++;
 		ctx->out_frames++;
@@ -637,7 +642,7 @@ static void *rtp_thread_func(void *arg) {
 				u32_t rtp_now = ntohl(*(u32_t*)(pktp+16));
 				u16_t flags = ntohs(*(u16_t*)(pktp+2));
 				u32_t remote_gap = NTP2MS(remote - ctx->timing.remote);
-				
+
 				// something is wrong and if we are supposed to be NTP synced, better ask for re-sync
 				if (remote_gap > 10000) {
 					if (ctx->synchro.status & NTP_SYNC) rtp_request_timing(ctx);