split processWebStream/processWebFile
This commit is contained in:
@@ -5,8 +5,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)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -333,7 +333,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;
|
||||||
@@ -2548,7 +2548,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2603,12 +2604,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.
|
||||||
@@ -2625,8 +2634,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
|
||||||
@@ -3113,9 +3122,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, (uint32_t)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;
|
||||||
@@ -3201,15 +3291,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){
|
||||||
@@ -3237,7 +3327,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;
|
||||||
@@ -3266,7 +3356,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);
|
||||||
}
|
}
|
||||||
@@ -3436,7 +3526,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;
|
||||||
@@ -3558,7 +3649,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);
|
||||||
@@ -3792,7 +3884,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:")) {
|
||||||
@@ -3940,20 +4032,19 @@ 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='';"
|
||||||
@@ -3968,6 +4059,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 @@
|
|||||||
* 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)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -239,10 +239,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();
|
||||||
@@ -542,7 +543,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;
|
||||||
|
|||||||
Reference in New Issue
Block a user