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

This commit is contained in:
philippe44
2023-12-31 18:50:25 -08:00
parent 9679c5c104
commit 3554af1460
5 changed files with 306 additions and 146 deletions

View File

@@ -44,7 +44,7 @@
#define MAX_OPUS_FRAMES 5760 #define MAX_OPUS_FRAMES 5760
struct opus { 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_stream_state state;
ogg_packet packet; ogg_packet packet;
ogg_sync_state sync; 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; (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; int status, packet = -1;
LOCK_S; LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) { while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) {
// if sync_pageout (or sync_pageseek) is not called here, sync builds ups // 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) { while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
size_t consumed = min(bytes, 4096); 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); memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed); OG(&go, sync_wrote, &u->sync, consumed);
_buf_inc_readp(streambuf, consumed); _buf_inc_readp(streambuf, consumed);
bytes -= consumed; bytes -= consumed;
} }
// if we have a new page, put it in // 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 (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));
// 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;
/* 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; UNLOCK_S;
return packet; return packet;
} }
static int read_opus_header(void) { static int read_opus_header(void) {
int status = 0; int done = 0;
bool fetch = true; bool fetch = true;
LOCK_S; LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (bytes && !status) { while (bytes && !done) {
// first fetch a page if we need one int status;
if (fetch) {
// get aligned to a page and ready to bring it in
do {
size_t consumed = min(bytes, 4096); 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); memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed); OG(&go, sync_wrote, &u->sync, consumed);
_buf_inc_readp(streambuf, consumed); _buf_inc_readp(streambuf, consumed);
bytes -= 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) { // nothing has been found and we have no more bytes, come back later
case OGG_SYNC: if (status <= 0) break;
u->status = OGG_ID_HEADER;
OG(&gu, stream_reset_serialno, &u->state, OG(&gu, page_serialno, &u->page)); // always set stream serialno if we have a new one
fetch = false; if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
break;
case OGG_ID_HEADER: // bring new page in if we want it (otherwise we're just skipping)
status = OG(&gu, stream_pagein, &u->state, &u->page); if (fetch) OG(&go, 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)) { // no need for a switch...case
LOG_ERROR("wrong opus header packet (size:%u)", u->packet.bytes); if (u->status == OGG_ID_HEADER) {
status = -100; // we need the id packet, get more pages if we don't
break; if (OG(&go, stream_packetout, &u->state, &u->packet) <= 0) continue;
}
u->status = OGG_COMMENT_HEADER; // 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->channels = u->packet.packet[9];
u->pre_skip = parse_uint16(u->packet.packet + 10); u->pre_skip = parse_uint16(u->packet.packet + 10);
u->rate = parse_uint32(u->packet.packet + 12); u->rate = parse_uint32(u->packet.packet + 12);
u->gain = parse_int16(u->packet.packet + 16); u->gain = parse_int16(u->packet.packet + 16);
u->decoder = OP(&gu, decoder_create, 48000, u->channels, &status); u->decoder = OP(&gu, decoder_create, 48000, u->channels, &status);
fetch = false;
if (!u->decoder || status != OPUS_OK) { if (!u->decoder || status != OPUS_OK) {
LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels); LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels);
} }
else {
LOG_INFO("codec up and running");
}
} }
fetch = true; } else if (u->status == OGG_COMMENT_HEADER) {
break; // don't consume VorbisComment which could be a huge packet, just skip it
case OGG_COMMENT_HEADER: if (!OG(&go, page_packets, &u->page)) continue;
// skip packets to consume VorbisComment. With opus, header packets align on pages done = 1;
status = OG(&gu, page_packets, &u->page);
break;
default:
break;
} }
} }
UNLOCK_S; UNLOCK_S;
return status; return done;
} }
static decode_state opus_decompress(void) { 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); memcpy(write_buf, u->overbuf, u->overframes * BYTES_PER_FRAME);
n = u->overframes; n = u->overframes;
u->overframes = 0; u->overframes = 0;
} else if ((packet = get_opus_packet()) > 0) { } else if ((packet = get_audio_packet()) > 0) {
if (frames < MAX_OPUS_FRAMES) { if (frames < MAX_OPUS_FRAMES) {
// don't have enough contiguous space, use the overflow buffer // 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); 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 */ * 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); 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; UNLOCK_O_direct;
return DECODE_RUNNING; return DECODE_RUNNING;
} }
@@ -342,7 +355,7 @@ static decode_state opus_decompress(void) {
} else { } else {
LOG_INFO("opus decode error: %d", n); LOG_INFO("decode error: %d", n);
UNLOCK_O_direct; UNLOCK_O_direct;
return DECODE_COMPLETE; 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); if (!u->overbuf) u->overbuf = malloc(MAX_OPUS_FRAMES * BYTES_PER_FRAME);
u->status = OGG_SYNC; u->status = OGG_ID_HEADER;
u->overframes = 0; u->overframes = 0;
OG(&go, stream_clear, &u->state); OG(&go, stream_clear, &u->state);

View File

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

View File

@@ -580,12 +580,26 @@ struct streamstate {
u32_t meta_next; u32_t meta_next;
u32_t meta_left; u32_t meta_left;
bool meta_send; 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_init(log_level level, unsigned stream_buf_size);
void stream_close(void); void stream_close(void);
void stream_file(const char *header, size_t header_len, unsigned threshold); 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); bool stream_disconnect(void);
// decode.c // decode.c

View File

@@ -148,6 +148,8 @@ static bool running = true;
static void _disconnect(stream_state state, disconnect_code disconnect) { static void _disconnect(stream_state state, disconnect_code disconnect) {
stream.state = state; stream.state = state;
stream.disconnect = disconnect; 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 USE_SSL
if (ssl) { if (ssl) {
SSL_shutdown(ssl); SSL_shutdown(ssl);
@@ -160,6 +162,122 @@ static void _disconnect(stream_state state, disconnect_code disconnect) {
wake_controller(); 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() { static void *stream_thread() {
while (running) { while (running) {
@@ -343,6 +461,7 @@ static void *stream_thread() {
} }
if (n > 0) { if (n > 0) {
stream_ogg(n);
_buf_inc_writep(streambuf, n); _buf_inc_writep(streambuf, n);
stream.bytes += n; stream.bytes += n;
if (stream.meta_interval) { if (stream.meta_interval) {
@@ -485,7 +604,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
UNLOCK; 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; struct sockaddr_in addr;
#if EMBEDDED #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.sent_headers = false;
stream.bytes = 0; stream.bytes = 0;
stream.threshold = threshold; stream.threshold = threshold;
stream.ogg.miss = stream.ogg.match = 0;
stream.ogg.state = (codec == 'o' || codec == 'p') ? STREAM_OGG_SYNC : STREAM_OGG_OFF;
UNLOCK; UNLOCK;
} }
@@ -604,6 +726,8 @@ bool stream_disconnect(void) {
disc = true; disc = true;
} }
stream.state = STOPPED; stream.state = STOPPED;
if (stream.ogg.state == STREAM_OGG_HEADER && stream.ogg.data) free(stream.ogg.data);
stream.ogg.data = NULL;
UNLOCK; UNLOCK;
return disc; return disc;
} }

View File

@@ -50,7 +50,7 @@ static inline int32_t clip15(int32_t x) {
struct vorbis { struct vorbis {
bool opened; 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 { struct {
ogg_stream_state state; ogg_stream_state state;
ogg_packet packet; ogg_packet packet;
@@ -138,48 +138,16 @@ extern struct processstate process;
#define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__) #define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
#endif #endif
static int get_ogg_packet(void) { static int get_audio_packet(void) {
int status, packet = -1; int status, packet = -1;
LOCK_S; LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (!(status = OG(&go, stream_packetout, &v->state, &v->packet)) && bytes) { 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 // 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) { 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);
memcpy(buffer, streambuf->readp, consumed);
OG(&gv, 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);
}
// 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;
UNLOCK_S;
return packet;
}
static int read_vorbis_header(void) {
int status = 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) {
size_t consumed = min(bytes, 4096); size_t consumed = min(bytes, 4096);
char* buffer = OG(&go, sync_buffer, &v->sync, consumed); char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
memcpy(buffer, streambuf->readp, consumed); memcpy(buffer, streambuf->readp, consumed);
@@ -187,81 +155,122 @@ static int read_vorbis_header(void) {
_buf_inc_readp(streambuf, consumed); _buf_inc_readp(streambuf, consumed);
bytes -= consumed; bytes -= consumed;
if (!OG(&go, sync_pageseek, &v->sync, &v->page)) continue;
} }
switch (v->status) { // if we have a new page, put it in and reset serialno at BoS
case OGG_SYNC: if (status) {
v->status = OGG_ID_HEADER; OG(&go, stream_pagein, &v->state, &v->page);
OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page)); if (OG(&go, page_bos, &v->page)) 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); /* odd packets are not audio and should be discarded. With no packet, we
if (!OG(&go, stream_packetout, &v->state, &v->packet)) break; * 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 done = 0;
bool fetch = true;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
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);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
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;
OV(&gv, info_init, &v->info); OV(&gv, info_init, &v->info);
status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet); status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet);
if (status) { if (status) {
LOG_ERROR("vorbis id header packet error %d", status); LOG_ERROR("id header packet error %d", status);
status = -1; done = -1;
} else { } else {
v->channels = v->info.channels; v->channels = v->info.channels;
v->rate = v->info.rate; v->rate = v->info.rate;
v->status = OGG_COMMENT_HEADER; v->status = OGG_COMMENT_HEADER;
fetch = false;
// 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");
LOG_INFO("id acquired"); 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 (v->status == OGG_COMMENT_HEADER) {
if (fetch) OG(&go, stream_pagein, &v->state, &v->page); // 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 // finally build a codec if we have the packet
status = OG(&go, stream_packetout, &v->state, &v->packet); if (OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet) ||
if (status && ((status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet)) || OV(&gv, synthesis_init, &v->decoder, &v->info)) {
(status = OV(&gv, synthesis_init, &v->decoder, &v->info)))) { LOG_ERROR("setup header packet error %d", status);
LOG_ERROR("vorbis setup header packet error %d", status);
// no need to free comment, it's fake // no need to free comment, it's fake
OV(&gv, info_clear, &v->info); OV(&gv, info_clear, &v->info);
status = -1; done = -1;
} else { } else {
OV(&gv, block_init, &v->decoder, &v->block); OV(&gv, block_init, &v->decoder, &v->block);
v->opened = true; v->opened = true;
LOG_INFO("codec up and running (rate: %d, channels:%d)", v->rate, v->channels); LOG_INFO("codec up and running");
status = 1; done = 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");
}
break;
}
default:
break; break;
} }
} }
UNLOCK_S; UNLOCK_S;
return status; return done;
} }
inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) { inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) {
@@ -317,12 +326,12 @@ static decode_state vorbis_decode(void) {
if (v->overflow) { if (v->overflow) {
n = pcm_out(&v->decoder, &pcm); n = pcm_out(&v->decoder, &pcm);
v->overflow = n - min(n, frames); 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); n = OV(&gv, synthesis, &v->block, &v->packet);
if (n == 0) n = OV(&gv, synthesis_blockin, &v->decoder, &v->block); if (n == 0) n = OV(&gv, synthesis_blockin, &v->decoder, &v->block);
if (n == 0) n = pcm_out(&v->decoder, &pcm); if (n == 0) n = pcm_out(&v->decoder, &pcm);
v->overflow = n - min(n, frames); v->overflow = n - min(n, frames);
} else if (!packet && !OG(&go, page_eos, &v->page)) { } else if (!packet) {
UNLOCK_O_direct; UNLOCK_O_direct;
return DECODE_RUNNING; 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->opened = false;
v->status = OGG_SYNC; v->status = OGG_ID_HEADER;
v->overflow = 0; v->overflow = 0;
OG(&go, stream_clear, &v->state); OG(&go, stream_clear, &v->state);