|  | @@ -62,6 +62,7 @@ static sockfd fd;
 | 
	
		
			
				|  |  |  struct EXT_RAM_ATTR streamstate stream;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static EXT_RAM_ATTR struct {
 | 
	
		
			
				|  |  | +    bool flac;
 | 
	
		
			
				|  |  |  	enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
 | 
	
		
			
				|  |  |  	size_t want, miss, match;
 | 
	
		
			
				|  |  |      u64_t granule;
 | 
	
	
		
			
				|  | @@ -183,6 +184,10 @@ static size_t memfind(const u8_t* haystack, size_t n, const char* needle, size_t
 | 
	
		
			
				|  |  |  	return i;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* https://xiph.org/ogg/doc/framing.html 
 | 
	
		
			
				|  |  | + * https://xiph.org/flac/ogg_mapping.html
 | 
	
		
			
				|  |  | + * https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 */
 | 
	
		
			
				|  |  | + 
 | 
	
		
			
				|  |  |  static void stream_ogg(size_t n) {
 | 
	
		
			
				|  |  |  	if (ogg.state == OGG_OFF) return;
 | 
	
		
			
				|  |  |  	u8_t* p = streambuf->writep;
 | 
	
	
		
			
				|  | @@ -212,9 +217,8 @@ static void stream_ogg(size_t n) {
 | 
	
		
			
				|  |  |  				ogg.data = (u8_t*) &ogg.header;
 | 
	
		
			
				|  |  |  				ogg.match = 0;
 | 
	
		
			
				|  |  |  			} else {
 | 
	
		
			
				|  |  | -                if (!ogg.match) {
 | 
	
		
			
				|  |  | -                    LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                if (!ogg.match) LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
 | 
	
		
			
				|  |  | +                LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
 | 
	
		
			
				|  |  |  				return;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			break;
 | 
	
	
		
			
				|  | @@ -248,15 +252,19 @@ static void stream_ogg(size_t n) {
 | 
	
		
			
				|  |  |              if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;            
 | 
	
		
			
				|  |  |  			break;
 | 
	
		
			
				|  |  |  		case OGG_PAGE: {
 | 
	
		
			
				|  |  | -			size_t offset = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			// try to find one of valid Ogg pattern (vorbis, opus)
 | 
	
		
			
				|  |  | -			for (char** tag = (char*[]) { "\x3vorbis", "OpusTags", NULL }; *tag; tag++, offset = 0) {
 | 
	
		
			
				|  |  | -				size_t pos = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &offset);
 | 
	
		
			
				|  |  | -				if (offset != strlen(*tag)) continue;
 | 
	
		
			
				|  |  | -				
 | 
	
		
			
				|  |  | +			char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL };
 | 
	
		
			
				|  |  | +			size_t ofs = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			/* with OggFlac, we need the next page (packet) - VorbisComment is wrapped into a FLAC_METADATA
 | 
	
		
			
				|  |  | +			 * and except with vorbis, comment packet starts a new page but even in vorbis, it won't span
 | 
	
		
			
				|  |  | +			 * accross multiple pages */
 | 
	
		
			
				|  |  | +			if (ogg.flac) ofs = 4;
 | 
	
		
			
				|  |  | +			else if (!memcmp(ogg.data, "\x7f""FLAC", 5)) ogg.flac = true;
 | 
	
		
			
				|  |  | +			else for (size_t n = 0; *tag; tag++, ofs = 0) if ((ofs = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &n)) && n == strlen(*tag)) break;
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +			if (ofs) {			
 | 
	
		
			
				|  |  |  				// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
 | 
	
		
			
				|  |  | -				char* p = (char*) ogg.data + pos;
 | 
	
		
			
				|  |  | +				char* p = (char*) ogg.data + ofs;
 | 
	
		
			
				|  |  |  				p += *p + 4;
 | 
	
		
			
				|  |  |  				u32_t count = *p;
 | 
	
		
			
				|  |  |  				p += 4;
 | 
	
	
		
			
				|  | @@ -283,7 +291,6 @@ static void stream_ogg(size_t n) {
 | 
	
		
			
				|  |  |  				stream.meta_send = true;
 | 
	
		
			
				|  |  |  				wake_controller();
 | 
	
		
			
				|  |  |  				LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			free(ogg.data);
 | 
	
		
			
				|  |  |              ogg.data = NULL;
 | 
	
	
		
			
				|  | @@ -625,7 +632,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
 | 
	
		
			
				|  |  |  	UNLOCK;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
 | 
	
		
			
				|  |  | +void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
 | 
	
		
			
				|  |  |  	struct sockaddr_in addr;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #if EMBEDDED
 | 
	
	
		
			
				|  | @@ -726,7 +733,8 @@ void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t he
 | 
	
		
			
				|  |  |  	stream.threshold = threshold;
 | 
	
		
			
				|  |  |      
 | 
	
		
			
				|  |  |      ogg.miss = ogg.match = 0;
 | 
	
		
			
				|  |  | -	ogg.state = (codec == 'o' || codec == 'p') ? OGG_SYNC : OGG_OFF;
 | 
	
		
			
				|  |  | +	ogg.state = use_ogg ? OGG_SYNC : OGG_OFF;
 | 
	
		
			
				|  |  | +    ogg.flac = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	UNLOCK;
 | 
	
		
			
				|  |  |  }
 |