Преглед на файлове

add vorbis/ogg live metadata (and fix some ogg issues) - release

philippe44 преди 1 година
родител
ревизия
3554af1460
променени са 5 файла, в които са добавени 288 реда и са изтрити 128 реда
  1. 66 53
      components/squeezelite/opus.c
  2. 1 1
      components/squeezelite/slimproto.c
  3. 15 1
      components/squeezelite/squeezelite.h
  4. 125 1
      components/squeezelite/stream.c
  5. 81 72
      components/squeezelite/vorbis.c

+ 66 - 53
components/squeezelite/opus.c

@@ -44,7 +44,7 @@
 #define MAX_OPUS_FRAMES 5760
 
 struct opus {
-	enum {OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER} status;
+	enum { OGG_ID_HEADER, OGG_COMMENT_HEADER } status;
 	ogg_stream_state state;
 	ogg_packet packet;
 	ogg_sync_state sync;
@@ -131,95 +131,108 @@ static opus_uint32 parse_uint32(const unsigned char* _data) {
 		(opus_uint32)_data[2] << 16 | (opus_uint32)_data[3] << 24;
 }
 
-static int get_opus_packet(void) {
+static int get_audio_packet(void) {
 	int status, packet = -1;
 
 	LOCK_S;
 	size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
-	
+
 	while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) {
-        
-        // if sync_pageout (or sync_pageseek) is not called here, sync builds ups
-        while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
+
+		// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
+		while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
 			size_t consumed = min(bytes, 4096);
-			char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
+			char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
 			memcpy(buffer, streambuf->readp, consumed);
-			OG(&gu, sync_wrote, &u->sync, consumed);
+			OG(&go, sync_wrote, &u->sync, consumed);
 
 			_buf_inc_readp(streambuf, consumed);
 			bytes -= consumed;
-        }
+		}
 
-		// if we have a new page, put it in
-		if (status)	OG(&go, stream_pagein, &u->state, &u->page);
-	} 
-    
-    // only return a negative value when true end of streaming is reached
-    if (status > 0) packet = status;
-    else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
+		// if we have a new page, put it in and reset serialno at BoS
+		if (status) {
+			OG(&go, stream_pagein, &u->state, &u->page);
+			if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
+		}
+	}
+
+	/* discard header packets. With no packet, we return a negative value 
+	 * when there is really nothing more to proceed */
+	if (status > 0 && memcmp(u->packet.packet, "OpusHead", 8) && memcmp(u->packet.packet, "OpusTags", 8)) packet = status;
+	else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
 
 	UNLOCK_S;
 	return packet;
 }
 
 static int read_opus_header(void) {
-	int status = 0;
-    bool fetch = true;
+	int done = 0;
+	bool fetch = true;
 
 	LOCK_S;
 	size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
 
-	while (bytes && !status) {
-		// first fetch a page if we need one
-		if (fetch) {
+	while (bytes && !done) {
+		int status;
+
+		// get aligned to a page and ready to bring it in
+		do {
 			size_t consumed = min(bytes, 4096);
-			char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
+
+			char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
 			memcpy(buffer, streambuf->readp, consumed);
-			OG(&gu, sync_wrote, &u->sync, consumed);
+			OG(&go, sync_wrote, &u->sync, consumed);
 
 			_buf_inc_readp(streambuf, consumed);
 			bytes -= consumed;
 
-			if (!OG(&gu, sync_pageseek, &u->sync, &u->page)) continue;
-		}
+			status = fetch ? OG(&go, sync_pageout, &u->sync, &u->page) :
+							 OG(&go, sync_pageseek, &u->sync, &u->page);
+		} while (bytes && status <= 0);
 
-		switch (u->status) {
-		case OGG_SYNC:
-			u->status = OGG_ID_HEADER;
-            OG(&gu, stream_reset_serialno, &u->state, OG(&gu, page_serialno, &u->page));
-            fetch = false;
-			break;
-		case OGG_ID_HEADER:
-			status = OG(&gu, stream_pagein, &u->state, &u->page);
-			if (OG(&gu, stream_packetout, &u->state, &u->packet)) {
-				if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
-					LOG_ERROR("wrong opus header packet (size:%u)", u->packet.bytes);
-					status = -100;
-					break;
-				}
-				u->status = OGG_COMMENT_HEADER;                
+		// nothing has been found and we have no more bytes, come back later
+		if (status <= 0) break;
+
+		// always set stream serialno if we have a new one
+		if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
+
+		// bring new page in if we want it (otherwise we're just skipping)
+		if (fetch) OG(&go, stream_pagein, &u->state, &u->page);
+
+		// no need for a switch...case
+		if (u->status == OGG_ID_HEADER) {
+			// we need the id packet, get more pages if we don't
+			if (OG(&go, stream_packetout, &u->state, &u->packet) <= 0) continue;
+			
+			// make sure this is a valid packet
+			if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
+				LOG_ERROR("wrong header packet (size:%u)", u->packet.bytes);
+				done = -100;
+			} else {
+				u->status = OGG_COMMENT_HEADER;
 				u->channels = u->packet.packet[9];
 				u->pre_skip = parse_uint16(u->packet.packet + 10);
 				u->rate = parse_uint32(u->packet.packet + 12);
 				u->gain = parse_int16(u->packet.packet + 16);
 				u->decoder = OP(&gu, decoder_create, 48000, u->channels, &status);
+				fetch = false;
 				if (!u->decoder || status != OPUS_OK) {
 					LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels);
 				}
+				else {
+					LOG_INFO("codec up and running");
+				}
 			}
-			fetch = true;
-			break;
-		case OGG_COMMENT_HEADER:
-			// skip packets to consume VorbisComment. With opus, header packets align on pages
-			status = OG(&gu, page_packets, &u->page);
-			break;
-		default:
-			break;
+		} else if (u->status == OGG_COMMENT_HEADER) {
+			// don't consume VorbisComment which could be a huge packet, just skip it
+			if (!OG(&go, page_packets, &u->page)) continue;
+			done = 1;
 		}
 	}
 
 	UNLOCK_S;
-	return status;
+	return done;
 }
 
 static decode_state opus_decompress(void) {
@@ -271,7 +284,7 @@ static decode_state opus_decompress(void) {
 		memcpy(write_buf, u->overbuf, u->overframes * BYTES_PER_FRAME);
 		n = u->overframes;
 		u->overframes = 0;
-	} else if ((packet = get_opus_packet()) > 0) {
+	} else if ((packet = get_audio_packet()) > 0) {
 		if (frames < MAX_OPUS_FRAMES) {
 			// don't have enough contiguous space, use the overflow buffer
 			n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) u->overbuf, MAX_OPUS_FRAMES, 0);
@@ -286,7 +299,7 @@ static decode_state opus_decompress(void) {
 			 * outputbuf and streambuf for maybe a long time while we process it all, so don't do that */
 			n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) write_buf, frames, 0);
 		}
-	} else if (!packet && !OG(&go, page_eos, &u->page)) {
+	} else if (!packet) {
 		UNLOCK_O_direct;
 		return DECODE_RUNNING;
 	}
@@ -342,7 +355,7 @@ static decode_state opus_decompress(void) {
 
 	} else {
 
-		LOG_INFO("opus decode error: %d", n);
+		LOG_INFO("decode error: %d", n);
 		UNLOCK_O_direct;
 		return DECODE_COMPLETE;
 	}
@@ -357,7 +370,7 @@ static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
     
 	if (!u->overbuf) u->overbuf = malloc(MAX_OPUS_FRAMES * BYTES_PER_FRAME);
     
-    u->status = OGG_SYNC;
+    u->status = OGG_ID_HEADER;
 	u->overframes = 0;
 
     OG(&go, stream_clear, &u->state);	

+ 1 - 1
components/squeezelite/slimproto.c

@@ -393,7 +393,7 @@ static void process_strm(u8_t *pkt, int len) {
 				stream_file(header, header_len, strm->threshold * 1024);
 				autostart -= 2;
 			} else {
-				stream_sock(ip, port, header, header_len, strm->threshold * 1024, autostart >= 2);
+				stream_sock(ip, port, strm->format, header, header_len, strm->threshold * 1024, autostart >= 2);
 			}
 			sendSTAT("STMc", 0);
 			sentSTMu = sentSTMo = sentSTMl = false;

+ 15 - 1
components/squeezelite/squeezelite.h

@@ -580,12 +580,26 @@ struct streamstate {
 	u32_t meta_next;
 	u32_t meta_left;
 	bool  meta_send;
+    struct {
+		enum { STREAM_OGG_OFF, STREAM_OGG_SYNC, STREAM_OGG_HEADER, STREAM_OGG_SEGMENTS, STREAM_OGG_PAGE } state;
+		u32_t want, miss, match;
+		u8_t* data;
+#pragma pack(push, 1)
+		struct {
+			char pattern[4];
+			u8_t version, type;
+			u64_t granule;
+			u32_t serial, page, checksum;
+			u8_t count;
+		} header;
+    } ogg;
+#pragma pack(pop)
 };
 
 void stream_init(log_level level, unsigned stream_buf_size);
 void stream_close(void);
 void stream_file(const char *header, size_t header_len, unsigned threshold);
-void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
+void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
 bool stream_disconnect(void);
 
 // decode.c

+ 125 - 1
components/squeezelite/stream.c

@@ -148,6 +148,8 @@ static bool running = true;
 static void _disconnect(stream_state state, disconnect_code disconnect) {
 	stream.state = state;
 	stream.disconnect = disconnect;
+    if (stream.ogg.state == STREAM_OGG_HEADER && stream.ogg.data) free(stream.ogg.data);
+	stream.ogg.data = NULL;
 #if USE_SSL
 	if (ssl) {
 		SSL_shutdown(ssl);
@@ -160,6 +162,122 @@ static void _disconnect(stream_state state, disconnect_code disconnect) {
 	wake_controller();
 }
 
+static u32_t memfind(const u8_t* haystack, u32_t n, const char* needle, u32_t len, u32_t *offset) {
+	int i;
+	for (i = 0; i < n && *offset != len; i++) *offset = (haystack[i] == needle[*offset]) ? *offset + 1 : 0;
+	return i;
+}
+
+static void stream_ogg(size_t n) {
+	if (stream.ogg.state == STREAM_OGG_OFF) return;
+	u8_t* p = streambuf->writep;
+
+	while (n) {
+		size_t consumed = min(stream.ogg.miss, n);
+
+		// copy as many bytes as possible and come back later if we do'nt have enough
+		if (stream.ogg.data) {
+			memcpy(stream.ogg.data + stream.ogg.want - stream.ogg.miss, p, consumed);
+			stream.ogg.miss -= consumed;
+			if (stream.ogg.miss) return;
+		}
+
+		// we have what we want, let's parse
+		switch (stream.ogg.state) {
+		case STREAM_OGG_SYNC: {
+			stream.ogg.miss -= consumed;
+			if (consumed) break;
+
+			// we have to memorize position in case any of last 3 bytes match...
+			int pos = memfind(p, n, "OggS", 4, &stream.ogg.match);
+			if (stream.ogg.match == 4) {
+				consumed = pos - stream.ogg.match;
+				stream.ogg.state = STREAM_OGG_HEADER;
+				stream.ogg.miss = stream.ogg.want = sizeof(stream.ogg.header);
+				stream.ogg.data = (u8_t*) &stream.ogg.header;
+				stream.ogg.match = 0;
+			} else {
+				LOG_INFO("OggS not at expected position");
+				return;
+			}
+			break;
+		}
+		case STREAM_OGG_HEADER:
+			if (!memcmp(stream.ogg.header.pattern, "OggS", 4)) {
+				stream.ogg.miss = stream.ogg.want = stream.ogg.header.count;
+				stream.ogg.data = malloc(stream.ogg.miss);
+				stream.ogg.state = STREAM_OGG_SEGMENTS;
+			} else {
+				stream.ogg.state = STREAM_OGG_SYNC;
+				stream.ogg.data = NULL;
+			}
+			break;
+		case STREAM_OGG_SEGMENTS:
+			// calculate size of page using lacing values
+			for (int i = 0; i < stream.ogg.want; i++) stream.ogg.miss += stream.ogg.data[i];
+			stream.ogg.want = stream.ogg.miss;
+
+			if (stream.ogg.header.granule == 0) {
+				// granule 0 means a new stream, so let's look into it
+				stream.ogg.state = STREAM_OGG_PAGE;
+				stream.ogg.data = realloc(stream.ogg.data, stream.ogg.want);
+			} else {
+				// otherwise, jump over data
+				stream.ogg.state = STREAM_OGG_SYNC;
+				free(stream.ogg.data);
+				stream.ogg.data = NULL;
+			}
+			break;
+		case STREAM_OGG_PAGE: {
+			u32_t offset = 0;
+
+			// try to find one of valid Ogg pattern (vorbis, opus)
+			for (char** tag = (char*[]) { "\x3vorbis", "OpusTags", NULL }; *tag; tag++, offset = 0) {
+				u32_t pos = memfind(stream.ogg.data, stream.ogg.want, *tag, strlen(*tag), &offset);
+				if (offset != strlen(*tag)) continue;
+				
+				// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
+				char* p = (char*) stream.ogg.data + pos;
+				p += *p + 4;
+				u32_t count = *p;
+				p += 4;
+
+				// LMS metadata format for Ogg is "Ogg", N x (u16:len,char[]:comment)
+				memcpy(stream.header, "Ogg", 3);
+				stream.header_len = 3;
+
+				for (u32_t len; count--; p += len) {
+					len = *p;
+					p += 4;
+
+					// only report what we use and don't overflow (network byte order)
+					if (!strncasecmp(p, "TITLE=", 6) || !strncasecmp(p, "ARTIST=", 7) || !strncasecmp(p, "ALBUM=", 6)) {
+						if (stream.header_len + len > MAX_HEADER) break;
+						stream.header[stream.header_len++] = len >> 8;
+						stream.header[stream.header_len++] = len;
+						memcpy(stream.header + stream.header_len, p, len);
+						stream.header_len += len;
+					}
+				}
+
+				stream.meta_send = true;
+				wake_controller();
+				LOG_INFO("Ogg meta len: %u", stream.header_len);
+				break;
+			}
+			free(stream.ogg.data);
+			stream.ogg.state = STREAM_OGG_SYNC;
+			break;
+		}
+        default: 
+            break;
+		}
+
+		p += consumed;
+		n -= consumed;
+	}
+}
+
 static void *stream_thread() {
 
 	while (running) {
@@ -343,6 +461,7 @@ static void *stream_thread() {
 					}
 					
 					if (n > 0) {
+                        stream_ogg(n);
 						_buf_inc_writep(streambuf, n);
 						stream.bytes += n;
 						if (stream.meta_interval) {
@@ -485,7 +604,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
 	UNLOCK;
 }
 
-void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
+void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
 	struct sockaddr_in addr;
 
 #if EMBEDDED
@@ -584,6 +703,9 @@ void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, un
 	stream.sent_headers = false;
 	stream.bytes = 0;
 	stream.threshold = threshold;
+    
+    stream.ogg.miss = stream.ogg.match = 0;
+	stream.ogg.state = (codec == 'o' || codec == 'p') ? STREAM_OGG_SYNC : STREAM_OGG_OFF;
 
 	UNLOCK;
 }
@@ -604,6 +726,8 @@ bool stream_disconnect(void) {
 		disc = true;
 	}
 	stream.state = STOPPED;
+    if (stream.ogg.state == STREAM_OGG_HEADER && stream.ogg.data) free(stream.ogg.data);
+	stream.ogg.data = NULL;
 	UNLOCK;
 	return disc;
 }

+ 81 - 72
components/squeezelite/vorbis.c

@@ -50,7 +50,7 @@ static inline int32_t clip15(int32_t x) {
 
 struct vorbis {
 	bool opened;
-	enum { OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
+	enum { OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
 	struct {
 		ogg_stream_state state;
 		ogg_packet packet;
@@ -138,49 +138,55 @@ extern struct processstate process;
 #define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
 #endif
 
-static int get_ogg_packet(void) {
+static int get_audio_packet(void) {
 	int status, packet = -1;
 
 	LOCK_S;
 	size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
 
 	while (!(status = OG(&go, stream_packetout, &v->state, &v->packet)) && bytes) {
-        
-        // if sync_pageout (or sync_pageseek) is not called first, sync buffers build ups
-        while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
+		
+		// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
+		while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
 			size_t consumed = min(bytes, 4096);
-			char* buffer = OG(&gv, sync_buffer, &v->sync, consumed);
+			char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
 			memcpy(buffer, streambuf->readp, consumed);
-			OG(&gv, sync_wrote, &v->sync, consumed);
+			OG(&go, sync_wrote, &v->sync, consumed);
 
 			_buf_inc_readp(streambuf, consumed);
 			bytes -= consumed;
-        }
+		}
 
-		// if we have a new page, put it in
-		if (status)	OG(&go, stream_pagein, &v->state, &v->page);
+		// if we have a new page, put it in and reset serialno at BoS
+		if (status) {
+			OG(&go, stream_pagein, &v->state, &v->page);
+			if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));	
+		}
 	}
-    
-    // only return a negative value when true end of streaming is reached
-    if (status > 0) packet = status;
-    else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
+
+	/* odd packets are not audio and should be discarded. With no packet, we
+	 * return a negative value when there is really nothing more to proceed */
+	if (status > 0 && (v->packet.packet[0] & 0x01) == 0) packet = status;
+	else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
 
 	UNLOCK_S;
 	return packet;
 }
 
 static int read_vorbis_header(void) {
-	int status = 0;
+	int done = 0;
 	bool fetch = true;
 
 	LOCK_S;
-
 	size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
 
-	while (bytes && !status) {
-		// first fetch a page if we need one
-		if (fetch) {
+	while (bytes && !done) {
+		int status;
+
+		// get aligned to a page and ready to bring it in
+		do {
 			size_t consumed = min(bytes, 4096);
+
 			char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
 			memcpy(buffer, streambuf->readp, consumed);
 			OG(&go, sync_wrote, &v->sync, consumed);
@@ -188,80 +194,83 @@ static int read_vorbis_header(void) {
 			_buf_inc_readp(streambuf, consumed);
 			bytes -= consumed;
 
-			if (!OG(&go, sync_pageseek, &v->sync, &v->page)) continue;
-		}
+			status = fetch ? OG(&go, sync_pageout, &v->sync, &v->page) :
+		 					 OG(&go, sync_pageseek, &v->sync, &v->page);
+		} while (bytes && status <= 0);
+
+		// nothing has been found and we have no more bytes, come back later
+		if (status <= 0) break;
+
+		// always set stream serialno if we have a new one
+		if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
+
+		// bring new page in if we want it (otherwise we're just skipping)
+		if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
+
+		// not a switch...case b/c we might have multiple packets in a page in vorbis
+		if (v->status == OGG_ID_HEADER) {
+			// we need the id packet, get more pages if we don't
+			if (!OG(&go, stream_packetout, &v->state, &v->packet)) continue;
 
-		switch (v->status) {
-		case OGG_SYNC:
-			v->status = OGG_ID_HEADER;
-			OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
-			fetch = false;
-			break;
-		case OGG_ID_HEADER:
-			status = OG(&go, stream_pagein, &v->state, &v->page);
-			if (!OG(&go, stream_packetout, &v->state, &v->packet)) break;
-		
 			OV(&gv, info_init, &v->info);
 			status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet);
 
 			if (status) {
-				LOG_ERROR("vorbis id header packet error %d", status);
-				status = -1;
+				LOG_ERROR("id header packet error %d", status);
+				done = -1;
 			} else {
 				v->channels = v->info.channels;
 				v->rate = v->info.rate;
 				v->status = OGG_COMMENT_HEADER;
-
-				// only fetch if no other packet already in (they should not)
-				fetch = OG(&go, page_packets, &v->page) <= 1;
-				if (!fetch) LOG_INFO("id packet should terminate page");
+				fetch = false;
 				LOG_INFO("id acquired");
+				// we should only have one packet, so get next pages
+				if (OG(&go, page_packets, &v->page) == 1) continue;
 			}
-			break;
-		case OGG_SETUP_HEADER:
-			// header packets don't align with pages on Vorbis (contrary to Opus)
-			if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
+		} 
+		
+		if (v->status == OGG_COMMENT_HEADER) {
+			// don't consume VorbisComment which could be a huge packet, just skip it
+			int packets = OG(&go, page_packets, &v->page);
+			if (!packets) continue;
+
+			// we have a "fake" comment packet that is just has the last page...
+			v->status = OGG_SETUP_HEADER;
+			OG(&go, stream_pagein, &v->state, &v->page);
+			OG(&go, stream_packetout, &v->state, &v->packet);
+
+			OV(&gv, comment_init, &v->comment);
+			v->comment.vendor = "N/A";
+			fetch = true;
+			LOG_INFO("comment skipped succesfully");
+
+			// because of lack of page alignment, we might have the setup page already fully in
+			if (packets == 1) continue;
+		}
+
+		if (v->status == OGG_SETUP_HEADER) {
+			// we need the setup packet, get more pages if we don't
+			if (OG(&go, stream_packetout, &v->state, &v->packet) <= 0) continue;
 
 			// finally build a codec if we have the packet
-			status = OG(&go, stream_packetout, &v->state, &v->packet);
-			if (status && ((status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet)) ||
-				(status = OV(&gv, synthesis_init, &v->decoder, &v->info)))) {
-				LOG_ERROR("vorbis setup header packet error %d", status);
+			if (OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet) ||
+				OV(&gv, synthesis_init, &v->decoder, &v->info)) {
+				LOG_ERROR("setup header packet error %d", status);
 				// no need to free comment, it's fake
 				OV(&gv, info_clear, &v->info);
-				status = -1;
+				done = -1;
 			} else {
 				OV(&gv, block_init, &v->decoder, &v->block);
 				v->opened = true;
-				LOG_INFO("codec up and running (rate: %d, channels:%d)", v->rate, v->channels);
-				status = 1;
-			}
-			//@FIXME: can we have audio on that page as well?
-			break;
-		case OGG_COMMENT_HEADER: {
-			// don't consume VorbisComment, just skip it
-			int packets = OG(&go, page_packets, &v->page);
-			if (packets) {
-				v->status = OGG_SETUP_HEADER;
-				OG(&go, stream_pagein, &v->state, &v->page);
-				OG(&go, stream_packetout, &v->state, &v->packet);
-
-				OV(&gv, comment_init, &v->comment);
-				v->comment.vendor = "N/A";
-
-				// because of lack of page alignment, we might have the setup page already fully in
-				if (packets > 1) fetch = false;
-				LOG_INFO("comment skipped succesfully");
+				LOG_INFO("codec up and running");
+				done = 1;
 			}
 			break;
 		}
-		default:
-			break;
-		}
 	}
 
 	UNLOCK_S;
-	return status;
+	return done;
 }
 
 inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) {
@@ -317,12 +326,12 @@ static decode_state vorbis_decode(void) {
     if (v->overflow) {
         n = pcm_out(&v->decoder, &pcm);
         v->overflow = n - min(n, frames);                
-    } else if ((packet = get_ogg_packet()) > 0) {
+    } else if ((packet = get_audio_packet()) > 0) {
 		n = OV(&gv, synthesis, &v->block, &v->packet);
 		if (n == 0) n = OV(&gv, synthesis_blockin, &v->decoder, &v->block);
         if (n == 0) n = pcm_out(&v->decoder, &pcm);
         v->overflow = n - min(n, frames);
-	} else if (!packet && !OG(&go, page_eos, &v->page)) {
+	} else if (!packet) {
 		UNLOCK_O_direct;
 		return DECODE_RUNNING;
 	}
@@ -410,7 +419,7 @@ static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
 	}
     
     v->opened = false;
-	v->status = OGG_SYNC;
+	v->status = OGG_ID_HEADER;
     v->overflow = 0;
 
     OG(&go, stream_clear, &v->state);