Unverified Commit b2b53126 authored by Wolle's avatar Wolle Committed by GitHub

split processWebStream/processWebFile

processWebstream is completely new and faster, better InBuff filling
processWebFile is still the old routine
parent b3c87efe
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
* *
* Created on: Oct 26.2018 * Created on: Oct 26.2018
* *
* Version 2.0.5g * Version 2.0.5h
* Updated on: Aug 12.2022 * Updated on: Aug 17.2022
* Author: Wolle (schreibfaul1) * Author: Wolle (schreibfaul1)
* *
*/ */
...@@ -316,7 +316,7 @@ void Audio::setDefaults() { ...@@ -316,7 +316,7 @@ void Audio::setDefaults() {
m_f_firstmetabyte = false; m_f_firstmetabyte = false;
m_f_playing = false; m_f_playing = false;
m_f_ssl = false; m_f_ssl = false;
m_f_swm = true; // Assume no metaint (stream without metadata) m_f_metadata = false;
m_f_tts = false; m_f_tts = false;
m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile m_f_firstCall = true; // InitSequence for processWebstream and processLokalFile
m_f_running = false; m_f_running = false;
...@@ -2389,7 +2389,8 @@ void Audio::loop() { ...@@ -2389,7 +2389,8 @@ void Audio::loop() {
if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX()); if(m_playlistFormat == FORMAT_ASX) connecttohost(parsePlaylist_ASX());
break; break;
case AUDIO_DATA: case AUDIO_DATA:
processWebStream(); if(m_streamType == ST_WEBSTREAM) processWebStream();
if(m_streamType == ST_WEBFILE) processWebFile();
break; break;
} }
} }
...@@ -2444,12 +2445,20 @@ void Audio::loop() { ...@@ -2444,12 +2445,20 @@ void Audio::loop() {
} }
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
size_t Audio::chunkedDataTransfer(){ size_t Audio::chunkedDataTransfer(uint8_t* bytes){
size_t chunksize = 0; size_t chunksize = 0;
int b = 0; int b = 0;
uint32_t ctime = millis();
uint32_t timeout = 2000; // ms
while(true){ while(true){
if(ctime + timeout < millis()) {
log_e("timeout");
stopSong();
return 0;
}
b = _client->read(); b = _client->read();
if(b < 0) break; *bytes++;
if(b < 0) continue; // -1 no data available
if(b == '\n') break; if(b == '\n') break;
if(b < '0') continue; if(b < '0') continue;
// We have received a hexadecimal character. Decode it and add to the result. // We have received a hexadecimal character. Decode it and add to the result.
...@@ -2466,8 +2475,8 @@ bool Audio::readPlayListData() { ...@@ -2466,8 +2475,8 @@ bool Audio::readPlayListData() {
if(getDatamode() != AUDIO_PLAYLISTINIT) return false; if(getDatamode() != AUDIO_PLAYLISTINIT) return false;
if(_client->available() == 0) return false; if(_client->available() == 0) return false;
uint32_t chunksize = 0; uint32_t chunksize = 0; uint8_t readedBytes = 0;
if(m_f_chunked) chunksize = chunkedDataTransfer(); if(m_f_chunked) chunksize = chunkedDataTransfer(&readedBytes);
// reads the content of the playlist and stores it in the vector m_contentlength // reads the content of the playlist and stores it in the vector m_contentlength
// m_contentlength is a table of pointers to the lines // m_contentlength is a table of pointers to the lines
...@@ -2956,9 +2965,90 @@ void Audio::processLocalFile() { ...@@ -2956,9 +2965,90 @@ void Audio::processLocalFile() {
if(afn) {free(afn); afn = NULL;} if(afn) {free(afn); afn = NULL;}
} }
} }
//--------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
void Audio::processWebStream() { void Audio::processWebStream() {
const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger
static bool f_tmr_1s;
static bool f_stream; // first audio data received
static uint8_t cnt_slow;
static uint32_t chunkSize; // chunkcount read from stream
static uint32_t tmr_1s; // timer 1 sec
static uint32_t loopCnt; // count loops if clientbuffer is empty
// first call, set some values to default - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(m_f_firstCall) { // runs only ont time per connection, prepare for start
m_f_firstCall = false;
f_stream = false;
cnt_slow = 0;
chunkSize = 0;
loopCnt = 0;
tmr_1s = millis();
m_metacount = m_metaint;
readMetadata(0, true); // reset all static vars
}
if(getDatamode() != AUDIO_DATA) return; // guard
uint32_t availableBytes = _client->available(); // available from stream
// chunked data tramsfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(m_f_chunked){
uint8_t readedBytes = 0;
if(!chunkSize) chunkSize = chunkedDataTransfer(&readedBytes);
availableBytes = min(availableBytes, chunkSize);
}
// we have metadata - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(m_f_metadata){
if(availableBytes) if(m_metacount == 0) {chunkSize -= readMetadata(availableBytes); return;}
availableBytes = min(availableBytes, m_metacount);
}
// timer, triggers every second - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if((tmr_1s + 1000) < millis()) {
f_tmr_1s = true; // flag will be set every second for one loop only
tmr_1s = millis();
}
// if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(InBuff.bufferFilled() > maxFrameSize) {f_tmr_1s = false; cnt_slow = 0; loopCnt = 0;}
if(f_tmr_1s){
cnt_slow ++;
if(cnt_slow > 25){cnt_slow = 0; AUDIO_INFO("slow stream, dropouts are possible");}
}
// if the buffer can't filled for several seconds try a new connection - - - - - - - - - - - - - - - - - - - - - - -
if(f_stream && !availableBytes){
loopCnt++;
if(loopCnt > 200000) { // wait several seconds
AUDIO_INFO("Stream lost -> try new connection");
connecttohost(m_lastHost);
return;
}
}
// buffer fill routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(availableBytes) {
availableBytes = min(availableBytes, InBuff.writeSpace());
int16_t bytesAddedToBuffer = _client->read(InBuff.getWritePtr(), availableBytes);
if(bytesAddedToBuffer > 0) {
if(m_f_metadata) m_metacount -= bytesAddedToBuffer;
if(m_f_chunked) chunkSize -= bytesAddedToBuffer;
InBuff.bytesWritten(bytesAddedToBuffer);
}
if(InBuff.bufferFilled() > maxFrameSize && !f_stream) { // waiting for buffer filled
f_stream = true; // ready to play the audio data
AUDIO_INFO("stream ready");
}
if(!f_stream) return;
}
// play audio data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(f_stream){
static uint8_t cnt = 0;
cnt++;
if(cnt == 3){playAudioData(); cnt = 0;}
}
}
//---------------------------------------------------------------------------------------------------------------------
void Audio::processWebFile() {
const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger const uint16_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger
uint32_t availableBytes; // available bytes in stream uint32_t availableBytes; // available bytes in stream
static bool f_tmr_1s; static bool f_tmr_1s;
...@@ -3044,15 +3134,15 @@ void Audio::processWebStream() { ...@@ -3044,15 +3134,15 @@ void Audio::processWebStream() {
} }
// if we have metadata: get them - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // if we have metadata: get them - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(!m_metacount && !m_f_swm){ // if(!m_metacount && !m_f_swm){
int bytes = 0; // int bytes = 0;
int res = 0; // int res = 0;
if(m_f_chunked) bytes = min(m_chunkcount, availableBytes); // if(m_f_chunked) bytes = min(m_chunkcount, availableBytes);
else bytes = availableBytes; // else bytes = availableBytes;
res = readMetadata(bytes); // res = readMetadata(bytes);
if(m_f_chunked) m_chunkcount -= res; // if(m_f_chunked) m_chunkcount -= res;
if(!m_metacount) return; // if(!m_metacount) return;
} // }
// if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // if the buffer is often almost empty issue a warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(InBuff.bufferFilled() < maxFrameSize && f_stream && !f_webFileDataComplete){ if(InBuff.bufferFilled() < maxFrameSize && f_stream && !f_webFileDataComplete){
...@@ -3080,7 +3170,7 @@ void Audio::processWebStream() { ...@@ -3080,7 +3170,7 @@ void Audio::processWebStream() {
// buffer fill routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // buffer fill routine - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(true) { // statement has no effect if(true) { // statement has no effect
uint32_t bytesCanBeWritten = InBuff.writeSpace(); uint32_t bytesCanBeWritten = InBuff.writeSpace();
if(!m_f_swm) bytesCanBeWritten = min(m_metacount, bytesCanBeWritten); // if(!m_f_swm) bytesCanBeWritten = min(m_metacount, bytesCanBeWritten);
if(m_f_chunked) bytesCanBeWritten = min(m_chunkcount, bytesCanBeWritten); if(m_f_chunked) bytesCanBeWritten = min(m_chunkcount, bytesCanBeWritten);
int16_t bytesAddedToBuffer = 0; int16_t bytesAddedToBuffer = 0;
...@@ -3109,7 +3199,7 @@ void Audio::processWebStream() { ...@@ -3109,7 +3199,7 @@ void Audio::processWebStream() {
if(bytesAddedToBuffer > 0) { if(bytesAddedToBuffer > 0) {
if(m_streamType == ST_WEBFILE) byteCounter += bytesAddedToBuffer; // Pull request #42 if(m_streamType == ST_WEBFILE) byteCounter += bytesAddedToBuffer; // Pull request #42
if(!m_f_swm) m_metacount -= bytesAddedToBuffer; // if(!m_f_swm) m_metacount -= bytesAddedToBuffer;
if(m_f_chunked) m_chunkcount -= bytesAddedToBuffer; if(m_f_chunked) m_chunkcount -= bytesAddedToBuffer;
InBuff.bytesWritten(bytesAddedToBuffer); InBuff.bytesWritten(bytesAddedToBuffer);
} }
...@@ -3279,7 +3369,8 @@ void Audio::processWebStreamTS() { ...@@ -3279,7 +3369,8 @@ void Audio::processWebStreamTS() {
availableBytes = _client->available(); availableBytes = _client->available();
if(availableBytes){ if(availableBytes){
if(m_f_chunked) chunkSize = chunkedDataTransfer(); uint8_t readedBytes = 0;
if(m_f_chunked) chunkSize = chunkedDataTransfer(&readedBytes);
int res = _client->read(ts_packet + ts_packetPtr, ts_packetsize - ts_packetPtr); int res = _client->read(ts_packet + ts_packetPtr, ts_packetsize - ts_packetPtr);
if(res > 0){ if(res > 0){
ts_packetPtr += res; ts_packetPtr += res;
...@@ -3401,7 +3492,8 @@ void Audio::processWebStreamHLS() { ...@@ -3401,7 +3492,8 @@ void Audio::processWebStreamHLS() {
availableBytes = _client->available(); availableBytes = _client->available();
if(availableBytes){ if(availableBytes){
if(m_f_chunked) chunkSize = chunkedDataTransfer(); uint8_t readedBytes = 0;
if(m_f_chunked) chunkSize = chunkedDataTransfer(&readedBytes);
size_t bytesWasWritten = 0; size_t bytesWasWritten = 0;
if(InBuff.writeSpace() >= availableBytes){ if(InBuff.writeSpace() >= availableBytes){
bytesWasWritten = _client->read(InBuff.getWritePtr(), availableBytes); bytesWasWritten = _client->read(InBuff.getWritePtr(), availableBytes);
...@@ -3623,7 +3715,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque ...@@ -3623,7 +3715,7 @@ bool Audio::parseHttpResponseHeader() { // this is the response to a GET / reque
const char* c_metaint = (rhl + 12); const char* c_metaint = (rhl + 12);
int32_t i_metaint = atoi(c_metaint); int32_t i_metaint = atoi(c_metaint);
m_metaint = i_metaint; m_metaint = i_metaint;
if(m_metaint) m_f_swm = false ; // Multimediastream if(m_metaint) m_f_metadata = true; // Multimediastream
} }
else if(startsWith(rhl, "icy-name:")) { else if(startsWith(rhl, "icy-name:")) {
...@@ -3768,20 +3860,18 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { ...@@ -3768,20 +3860,18 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) {
metalen = b * 16 ; // New count for metadata including length byte metalen = b * 16 ; // New count for metadata including length byte
if(metalen > 512){ if(metalen > 512){
AUDIO_INFO("Metadata block to long! Skipping all Metadata from now on."); AUDIO_INFO("Metadata block to long! Skipping all Metadata from now on.");
m_f_swm = true; // expect stream without metadata m_f_metadata = false; // expect stream without metadata
return 1;
} }
pos_ml = 0; chbuf[pos_ml] = 0; // Prepare for new line pos_ml = 0; chbuf[pos_ml] = 0; // Prepare for new line
res = 1; res = 1;
} }
if(!metalen) {m_metacount = m_metaint; return res;} if(!metalen) {m_metacount = m_metaint; return res;} // metalen is 0
uint16_t a = _client->readBytes(&chbuf[pos_ml], min((uint16_t)(metalen - pos_ml), (uint16_t)(maxBytes -1))); uint16_t a = _client->readBytes(&chbuf[pos_ml], min((uint16_t)(metalen - pos_ml), (uint16_t)(maxBytes -1)));
res += a; res += a;
pos_ml += a; pos_ml += a;
if(pos_ml == metalen) { if(pos_ml == metalen) {
metalen = 0;
chbuf[pos_ml] = '\0'; chbuf[pos_ml] = '\0';
m_metacount = m_metaint;
if(strlen(chbuf)) { // Any info present? if(strlen(chbuf)) { // Any info present?
// metaline contains artist and song name. For example: // metaline contains artist and song name. For example:
// "StreamTitle='Don McLean - American Pie';StreamUrl='';" // "StreamTitle='Don McLean - American Pie';StreamUrl='';"
...@@ -3796,6 +3886,8 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) { ...@@ -3796,6 +3886,8 @@ uint16_t Audio::readMetadata(uint16_t maxBytes, bool first) {
} }
showstreamtitle(chbuf); // Show artist and title if present in metadata showstreamtitle(chbuf); // Show artist and title if present in metadata
} }
m_metacount = m_metaint;
metalen = 0;
pos_ml = 0; pos_ml = 0;
} }
return res; return res;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Audio.h * Audio.h
* *
* Created on: Oct 28,2018 * Created on: Oct 28,2018
* Updated on: Aug 12,2022 * Updated on: Aug 17,2022
* Author: Wolle (schreibfaul1) * Author: Wolle (schreibfaul1)
*/ */
...@@ -218,10 +218,11 @@ private: ...@@ -218,10 +218,11 @@ private:
bool httpPrint(const char* host); bool httpPrint(const char* host);
void processLocalFile(); void processLocalFile();
void processWebStream(); void processWebStream();
void processWebFile();
void processWebStreamTS(); void processWebStreamTS();
void processWebStreamHLS(); void processWebStreamHLS();
void playAudioData(); void playAudioData();
size_t chunkedDataTransfer(); size_t chunkedDataTransfer(uint8_t* bytes);
bool readPlayListData(); bool readPlayListData();
const char* parsePlaylist_M3U(); const char* parsePlaylist_M3U();
const char* parsePlaylist_PLS(); const char* parsePlaylist_PLS();
...@@ -509,7 +510,7 @@ private: ...@@ -509,7 +510,7 @@ private:
uint32_t m_PlayingStartTime = 0; // Stores the milliseconds after the start of the audio uint32_t m_PlayingStartTime = 0; // Stores the milliseconds after the start of the audio
uint32_t m_resumeFilePos = 0; // the return value from stopSong() can be entered here uint32_t m_resumeFilePos = 0; // the return value from stopSong() can be entered here
uint16_t m_m3u8_targetDuration = 10; // uint16_t m_m3u8_targetDuration = 10; //
bool m_f_swm = true; // Stream without metadata bool m_f_metadata = false; // assume stream without metadata
bool m_f_unsync = false; // set within ID3 tag but not used bool m_f_unsync = false; // set within ID3 tag but not used
bool m_f_exthdr = false; // ID3 extended header bool m_f_exthdr = false; // ID3 extended header
bool m_f_ssl = false; bool m_f_ssl = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment