Skip to content

Commit ed150bd

Browse files
authored
Jump correction for m4a files
1 parent 974fa46 commit ed150bd

File tree

2 files changed

+128
-37
lines changed

2 files changed

+128
-37
lines changed

src/Audio.cpp

Lines changed: 120 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
*
44
* Created on: Oct 26.2018
55
*
6-
* Version 2.0.6s
7-
* Updated on: Nov 21.2022
6+
* Version 2.0.7
7+
* Updated on: Nov 22.2022
88
* Author: Wolle (schreibfaul1)
99
*
1010
*/
@@ -2853,23 +2853,18 @@ void Audio::processLocalFile() {
28532853
const uint32_t maxFrameSize = InBuff.getMaxBlockSize(); // every mp3/aac frame is not bigger
28542854
static bool f_stream;
28552855
static bool f_fileDataComplete;
2856-
static uint32_t pos_stsz;
28572856
static uint32_t byteCounter; // count received data
2858-
static uint32_t stszEntries = 0;
28592857
uint32_t availableBytes = 0;
28602858

28612859
if(m_f_firstCall) { // runs only one time per connection, prepare for start
28622860
m_f_firstCall = false;
28632861
f_stream = false;
28642862
f_fileDataComplete = false;
28652863
byteCounter = 0;
2866-
pos_stsz = 0;
2867-
if(m_codec == CODEC_M4A) pos_stsz = seek_m4a_stsz(&stszEntries); // returns the pos of atom stsz
28682864
return;
28692865
}
2870-
(void) pos_stsz;
28712866

2872-
availableBytes = 16 * 1024;
2867+
availableBytes = 16 * 1024; // set some large value
28732868

28742869
availableBytes = min(availableBytes, InBuff.writeSpace());
28752870
availableBytes = min(availableBytes, audiofile.size() - byteCounter);
@@ -2893,24 +2888,40 @@ void Audio::processLocalFile() {
28932888
}
28942889
return;
28952890
}
2896-
# ifndef CONFIG_IDF_TARGET_ESP32S
2897-
if((InBuff.freeSpace() > maxFrameSize) && (m_file_size - byteCounter) > maxFrameSize) return;
2898-
# endif
28992891
else{
2892+
if((InBuff.freeSpace() > maxFrameSize) && (m_file_size - byteCounter) > maxFrameSize){
2893+
// fill the buffer before playing
2894+
return;
2895+
}
2896+
29002897
f_stream = true;
29012898
AUDIO_INFO("stream ready");
29022899
if(m_f_Log) log_i("m_audioDataStart %d", m_audioDataStart);
2903-
if(m_resumeFilePos){
2904-
if(m_resumeFilePos < m_audioDataStart) m_resumeFilePos = m_audioDataStart;
2905-
if(m_avr_bitrate) m_audioCurrentTime = ((m_resumeFilePos - m_audioDataStart) / m_avr_bitrate) * 8;
2906-
audiofile.seek(m_resumeFilePos);
2907-
InBuff.resetBuffer();
2908-
byteCounter = m_resumeFilePos;
2909-
if(m_f_Log) log_i("m_resumeFilePos %i", m_resumeFilePos);
2910-
}
29112900
}
29122901
}
29132902

2903+
if(m_resumeFilePos){
2904+
if(m_resumeFilePos < m_audioDataStart) m_resumeFilePos = m_audioDataStart;
2905+
if(m_resumeFilePos > m_file_size) m_resumeFilePos = m_file_size;
2906+
if(m_codec == CODEC_M4A) m_resumeFilePos = m4a_correctResumeFilePos(m_resumeFilePos);
2907+
if(m_codec == CODEC_WAV) {while((m_resumeFilePos % 4) != 0) m_resumeFilePos++;} // must be divisible by four
2908+
if(m_codec == CODEC_FLAC) {m_resumeFilePos = flac_correctResumeFilePos(m_resumeFilePos); FLACDecoderReset();}
2909+
if(m_codec == CODEC_MP3) {m_resumeFilePos = mp3_correctResumeFilePos(m_resumeFilePos);}
2910+
if(m_avr_bitrate) m_audioCurrentTime = ((m_resumeFilePos - m_audioDataStart) / m_avr_bitrate) * 8;
2911+
audiofile.seek(m_resumeFilePos);
2912+
InBuff.resetBuffer();
2913+
byteCounter = m_resumeFilePos;
2914+
2915+
if(m_f_Log){
2916+
log_i("m_resumeFilePos %d", m_resumeFilePos);
2917+
log_i("m_audioDataStart %d", m_audioDataStart);
2918+
log_i("m_audioCurrentTime %f", (double)m_audioCurrentTime);
2919+
log_i("m_file_size %d", m_file_size);
2920+
}
2921+
m_resumeFilePos = 0;
2922+
f_stream = false;
2923+
}
2924+
29142925
// end of file reached? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
29152926
if(f_fileDataComplete && InBuff.bufferFilled() < InBuff.getMaxBlockSize()){
29162927
if(InBuff.bufferFilled()){
@@ -4299,16 +4310,10 @@ bool Audio::setTimeOffset(int sec){
42994310
//---------------------------------------------------------------------------------------------------------------------
43004311
bool Audio::setFilePos(uint32_t pos) {
43014312
if(!audiofile) return false;
4302-
// if(!m_avr_bitrate) return false;
4303-
if(m_codec == CODEC_M4A) return false;
4304-
m_f_playing = false;
4305-
if(m_codec == CODEC_MP3) MP3Decoder_ClearBuffer();
4306-
if(m_codec == CODEC_WAV) {while((pos % 4) != 0) pos++;} // must be divisible by four
4307-
if(m_codec == CODEC_FLAC) FLACDecoderReset();
4308-
InBuff.resetBuffer();
43094313
if(pos < m_audioDataStart) pos = m_audioDataStart; // issue #96
4310-
if(m_avr_bitrate) m_audioCurrentTime = ((pos-m_audioDataStart) / m_avr_bitrate) * 8; // #96
4311-
return audiofile.seek(pos);
4314+
if(pos > m_file_size) pos = m_file_size;
4315+
m_resumeFilePos = pos;
4316+
return true;
43124317
}
43134318
//---------------------------------------------------------------------------------------------------------------------
43144319
bool Audio::audioFileSeek(const float speed) {
@@ -5117,7 +5122,7 @@ void Audio::lostStreamDetection(uint32_t bytesAvail){
51175122
else loopCnt = 0;
51185123
}
51195124
//----------------------------------------------------------------------------------------------------------------------
5120-
uint32_t Audio::seek_m4a_stsz(uint32_t* numEntries){
5125+
void Audio::seek_m4a_stsz(){
51215126
// stsz says what size each sample is in bytes. This is important for the decoder to be able to start at a chunk,
51225127
// and then go through each sample by its size. The stsz atom can be behind the audio block. Therefore, searching
51235128
// for the stsz atom is only applicable to local files.
@@ -5159,8 +5164,9 @@ uint32_t Audio::seek_m4a_stsz(uint32_t* numEntries){
51595164
uint32_t seekpos = 0;
51605165
uint32_t filesize = getFileSize();
51615166
char name[6][5] = {"moov", "trak", "mdia", "minf", "stbl", "stsz"};
5167+
char noe[4];
51625168

5163-
if(!audiofile) return 0; // guard
5169+
if(!audiofile) return; // guard
51645170

51655171
at.pos = 0;
51665172
at.size = filesize;
@@ -5178,12 +5184,91 @@ uint32_t Audio::seek_m4a_stsz(uint32_t* numEntries){
51785184
seekpos = at.pos + 8; // 4 bytes size + 4 bytes name
51795185
}
51805186

5187+
seekpos += 8; // 1 byte version + 3 bytes flags + 4 bytes sample size
5188+
audiofile.seek(seekpos);
5189+
audiofile.readBytes(noe, 4); //number of entries
5190+
m_stsz_numEntries = bigEndian((uint8_t*)noe, 4);
5191+
if(m_f_Log) log_i("number of entries in stsz: %d", m_stsz_numEntries);
5192+
m_stsz_position = seekpos + 4;
51815193
audiofile.seek(0);
5182-
*numEntries = at.size / 4;
5183-
return at.pos;
5194+
return;
51845195

51855196
noSuccess:
5186-
log_e("error, returns 0");
5197+
m_stsz_numEntries= 0;
5198+
m_stsz_position = 0;
5199+
log_e("m4a atom stsz not found");
51875200
audiofile.seek(0);
5188-
return 0;
5189-
}
5201+
return;
5202+
}
5203+
//----------------------------------------------------------------------------------------------------------------------
5204+
uint32_t Audio::m4a_correctResumeFilePos(uint32_t resumeFilePos){
5205+
// In order to jump within an m4a file, the exact beginning of an aac block must be found. Since m4a cannot be
5206+
// streamed, i.e. there is no syncword, an imprecise jump can lead to a crash.
5207+
5208+
if(!m_stsz_position) return m_audioDataStart; // guard
5209+
5210+
typedef union{
5211+
uint8_t u8[4];
5212+
uint32_t u32;
5213+
} tu;
5214+
tu uu;
5215+
5216+
uint32_t i = 0, pos = m_audioDataStart;
5217+
audiofile.seek(m_stsz_position);
5218+
5219+
while(i < m_stsz_numEntries){
5220+
i++;
5221+
uu.u8[3] = audiofile.read();
5222+
uu.u8[2] = audiofile.read();
5223+
uu.u8[1] = audiofile.read();
5224+
uu.u8[0] = audiofile.read();
5225+
pos += uu.u32;
5226+
if(pos >= resumeFilePos) break;
5227+
}
5228+
return pos;
5229+
}
5230+
//----------------------------------------------------------------------------------------------------------------------
5231+
uint32_t Audio::flac_correctResumeFilePos(uint32_t resumeFilePos){
5232+
// The starting point is the next FLAC syncword
5233+
uint8_t p1, p2;
5234+
boolean found = false;
5235+
uint32_t pos = resumeFilePos;
5236+
audiofile.seek(pos);
5237+
5238+
p1 = audiofile.read();
5239+
p2 = audiofile.read();
5240+
pos+=2;
5241+
while(!found || pos == m_file_size){
5242+
if(p1 == 0xFF && p2 == 0xF8){found = true; log_i("found"); break;}
5243+
p1 = p2;
5244+
p2 = audiofile.read();
5245+
pos++;
5246+
}
5247+
5248+
if(found) return (pos - 2);
5249+
return m_audioDataStart;
5250+
}
5251+
//----------------------------------------------------------------------------------------------------------------------
5252+
uint32_t Audio::mp3_correctResumeFilePos(uint32_t resumeFilePos){
5253+
// The starting point is the next MP3 syncword
5254+
uint8_t p1, p2;
5255+
boolean found = false;
5256+
uint32_t pos = resumeFilePos;
5257+
audiofile.seek(pos);
5258+
5259+
p1 = audiofile.read();
5260+
p2 = audiofile.read();
5261+
pos+=2;
5262+
while(!found || pos == m_file_size){
5263+
if(p1 == 0xFF && (p2 & 0xF0) == 0xF0){found = true; break;}
5264+
p1 = p2;
5265+
p2 = audiofile.read();
5266+
pos++;
5267+
}
5268+
5269+
if(found) return (pos - 2);
5270+
return m_audioDataStart;
5271+
}
5272+
//----------------------------------------------------------------------------------------------------------------------
5273+
5274+

src/Audio.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Audio.h
33
*
44
* Created on: Oct 28,2018
5-
* Updated on: Nov 21,2022
5+
* Updated on: Nov 22,2022
66
* Author: Wolle (schreibfaul1)
77
*/
88

@@ -275,7 +275,11 @@ class Audio : private AudioBuffer{
275275
bool readID3V1Tag();
276276
void slowStreamDetection(uint32_t inBuffFilled, uint32_t maxFrameSize);
277277
void lostStreamDetection(uint32_t bytesAvail);
278-
uint32_t seek_m4a_stsz(uint32_t* numEntries);
278+
void seek_m4a_stsz();
279+
uint32_t m4a_correctResumeFilePos(uint32_t resumeFilePos);
280+
uint32_t flac_correctResumeFilePos(uint32_t resumeFilePos);
281+
uint32_t mp3_correctResumeFilePos(uint32_t resumeFilePos);
282+
279283

280284
//++++ implement several function with respect to the index of string ++++
281285
void trim(char *s) {
@@ -523,6 +527,8 @@ class Audio : private AudioBuffer{
523527
uint32_t m_PlayingStartTime = 0; // Stores the milliseconds after the start of the audio
524528
uint32_t m_resumeFilePos = 0; // the return value from stopSong() can be entered here
525529
uint16_t m_m3u8_targetDuration = 10; //
530+
uint32_t m_stsz_numEntries = 0; // num of entries inside stsz atom (uint32_t)
531+
uint32_t m_stsz_position = 0; // pos of stsz atom within file
526532
bool m_f_metadata = false; // assume stream without metadata
527533
bool m_f_unsync = false; // set within ID3 tag but not used
528534
bool m_f_exthdr = false; // ID3 extended header

0 commit comments

Comments
 (0)