Skip to content

Commit 32385d0

Browse files
authored
m4a subtype 'mp42' enabled and better read metadata
If there is metadata behind the audio block and it is a local file, it will now be read new function 'seek_m4a_ilst()' // item list atom, contains the metadata
1 parent 30bee2c commit 32385d0

File tree

2 files changed

+144
-29
lines changed

2 files changed

+144
-29
lines changed

src/Audio.cpp

Lines changed: 141 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Created on: Oct 26.2018
55
*
6-
* Version 2.0.8a
6+
* Version 2.0.8b
77
* Updated on: Jan 10.2023
88
* Author: Wolle (schreibfaul1)
99
*
@@ -338,6 +338,7 @@ void Audio::setDefaults() {
338338
m_f_m3u8data = false; // set again in processM3U8entries() if necessary
339339
m_f_continue = false;
340340
m_f_ts = false;
341+
m_f_m4aID3dataAreRead = false;
341342

342343
m_streamType = ST_NONE;
343344
m_codec = CODEC_NONE;
@@ -1823,9 +1824,10 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) {
18231824
}
18241825
int m4a = specialIndexOf(data, "M4A ", 20);
18251826
int isom = specialIndexOf(data, "isom", 20);
1827+
int mp42 = specialIndexOf(data, "mp42", 20);
18261828

1827-
if((m4a !=8) && (isom != 8)){
1828-
log_e("subtype 'MA4 ' or 'isom' expected, but found '%s '", (data + 8));
1829+
if((m4a !=8) && (isom != 8) && (mp42 != 8)){
1830+
log_e("subtype 'MA4 ', 'isom' or 'mp42' expected, but found '%s '", (data + 8));
18291831
stopSong();
18301832
return -1;
18311833
}
@@ -1975,30 +1977,34 @@ int Audio::read_M4A_Header(uint8_t *data, size_t len) {
19751977
const char info[12][6] = { "nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0",
19761978
"tmpo\0", "trkn\0","day\0", "cpil\0", "aART\0", "gen\0"};
19771979
int offset;
1978-
for(int i=0; i < 12; i++){
1979-
offset = specialIndexOf(data, info[i], len, true); // seek info[] with '\0'
1980-
if(offset>0) {
1981-
offset += 19; if(*(data + offset) == 0) offset ++;
1982-
char value[256];
1983-
size_t tmp = strlen((const char*)data + offset);
1984-
if(tmp > 254) tmp = 254;
1985-
memcpy(value, (data + offset), tmp);
1986-
value[tmp] = 0;
1987-
m_chbuf[0] = 0;
1988-
if(i == 0) sprintf(m_chbuf, "Title: %s", value);
1989-
if(i == 1) sprintf(m_chbuf, "Artist: %s", value);
1990-
if(i == 2) sprintf(m_chbuf, "Album: %s", value);
1991-
if(i == 3) sprintf(m_chbuf, "Encoder: %s", value);
1992-
if(i == 4) sprintf(m_chbuf, "Comment: %s", value);
1993-
if(i == 5) sprintf(m_chbuf, "Composer: %s", value);
1994-
if(i == 6) sprintf(m_chbuf, "BPM: %s", value);
1995-
if(i == 7) sprintf(m_chbuf, "Track Number: %s", value);
1996-
if(i == 8) sprintf(m_chbuf, "Year: %s", value);
1997-
if(i == 9) sprintf(m_chbuf, "Compile: %s", value);
1998-
if(i == 10) sprintf(m_chbuf, "Album Artist: %s", value);
1999-
if(i == 11) sprintf(m_chbuf, "Types of: %s", value);
2000-
if(m_chbuf[0] != 0) {
2001-
if(audio_id3data) audio_id3data(m_chbuf);
1980+
// If it's a local file, the metadata has already been read, even if it comes after the audio block.
1981+
// In the event that they are in front of the audio block in a web stream, read them now
1982+
if(!m_f_m4aID3dataAreRead){
1983+
for(int i=0; i < 12; i++){
1984+
offset = specialIndexOf(data, info[i], len, true); // seek info[] with '\0'
1985+
if(offset>0) {
1986+
offset += 19; if(*(data + offset) == 0) offset ++;
1987+
char value[256];
1988+
size_t tmp = strlen((const char*)data + offset);
1989+
if(tmp > 254) tmp = 254;
1990+
memcpy(value, (data + offset), tmp);
1991+
value[tmp] = 0;
1992+
m_chbuf[0] = 0;
1993+
if(i == 0) sprintf(m_chbuf, "Title: %s", value);
1994+
if(i == 1) sprintf(m_chbuf, "Artist: %s", value);
1995+
if(i == 2) sprintf(m_chbuf, "Album: %s", value);
1996+
if(i == 3) sprintf(m_chbuf, "Encoder: %s", value);
1997+
if(i == 4) sprintf(m_chbuf, "Comment: %s", value);
1998+
if(i == 5) sprintf(m_chbuf, "Composer: %s", value);
1999+
if(i == 6) sprintf(m_chbuf, "BPM: %s", value);
2000+
if(i == 7) sprintf(m_chbuf, "Track Number: %s", value);
2001+
if(i == 8) sprintf(m_chbuf, "Year: %s", value);
2002+
if(i == 9) sprintf(m_chbuf, "Compile: %s", value);
2003+
if(i == 10) sprintf(m_chbuf, "Album Artist: %s", value);
2004+
if(i == 11) sprintf(m_chbuf, "Types of: %s", value);
2005+
if(m_chbuf[0] != 0) {
2006+
if(audio_id3data) audio_id3data(m_chbuf);
2007+
}
20022008
}
20032009
}
20042010
}
@@ -2681,6 +2687,7 @@ void Audio::processLocalFile() {
26812687
f_fileDataComplete = false;
26822688
byteCounter = 0;
26832689
if(m_codec == CODEC_M4A) seek_m4a_stsz(); // determine the pos of atom stsz
2690+
if(m_codec == CODEC_M4A) seek_m4a_ilst(); // looking for metadata
26842691
return;
26852692
}
26862693

@@ -4901,6 +4908,111 @@ boolean Audio::streamDetection(uint32_t bytesAvail){
49014908
return false;
49024909
}
49034910
//----------------------------------------------------------------------------------------------------------------------
4911+
void Audio::seek_m4a_ilst(){
4912+
// ilist - item list atom, contains the metadata
4913+
4914+
/* atom hierarchy (example)_________________________________________________________________________________________
4915+
4916+
ftyp -> moov -> udta -> meta -> ilst -> data
4917+
4918+
__________________________________________________________________________________________________________________*/
4919+
4920+
struct m4a_Atom{
4921+
int pos;
4922+
int size;
4923+
char name[5];
4924+
} atom, at, tmp;
4925+
4926+
// c99 has no inner functions, lambdas are only allowed from c11, please don't use ancient compiler
4927+
auto atomItems = [&](uint32_t startPos){ // lambda, inner function
4928+
char tmp[5];
4929+
audiofile.seek(startPos);
4930+
audiofile.readBytes(tmp, 4);
4931+
atom.size = bigEndian((uint8_t*)tmp, 4);
4932+
if(!atom.size) atom.size = 4; // has no data, length is 0
4933+
audiofile.readBytes(atom.name, 4);
4934+
atom.name[4] = '\0';
4935+
atom.pos = startPos;
4936+
return atom;
4937+
};
4938+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4939+
4940+
boolean found = false;
4941+
uint32_t seekpos = 0;
4942+
uint32_t filesize = getFileSize();
4943+
char name[6][5] = {"moov", "udta", "meta", "ilst"};
4944+
const char info[12][6] = { "nam\0", "ART\0", "alb\0", "too\0", "cmt\0", "wrt\0",
4945+
"tmpo\0", "trkn\0","day\0", "cpil\0", "aART\0", "gen\0"};
4946+
4947+
if(!audiofile) return; // guard
4948+
4949+
at.pos = 0;
4950+
at.size = filesize;
4951+
seekpos = 0;
4952+
4953+
for(int i = 0; i < 4; i++){
4954+
found = false;
4955+
while(seekpos < at.pos + at.size){
4956+
tmp = atomItems(seekpos);
4957+
seekpos += tmp.size;
4958+
if(strcmp(tmp.name, name[i]) == 0) {memcpy((void*)&at, (void*)&tmp, sizeof(tmp)); found = true;}
4959+
log_i("name %s pos %d, size %d", tmp.name, tmp.pos, tmp.size);
4960+
}
4961+
if(!found){
4962+
log_w("m4a atom ilst not found");
4963+
audiofile.seek(0);
4964+
return;
4965+
}
4966+
seekpos = at.pos + 8; // 4 bytes size + 4 bytes name
4967+
}
4968+
4969+
int len = tmp.size - 8;
4970+
if(len >1024) len = 1024;
4971+
log_i("found at pos %i, len %i", seekpos, len);
4972+
4973+
uint8_t* data = (uint8_t*)malloc(len);
4974+
if(!data){
4975+
log_e("out od memory");
4976+
audiofile.seek(0);
4977+
return;
4978+
}
4979+
audiofile.seek(seekpos);
4980+
audiofile.read(data, len);
4981+
4982+
int offset;
4983+
for(int i=0; i < 12; i++){
4984+
offset = specialIndexOf(data, info[i], len, true); // seek info[] with '\0'
4985+
if(offset>0) {
4986+
offset += 19; if(*(data + offset) == 0) offset ++;
4987+
char value[256];
4988+
size_t tmp = strlen((const char*)data + offset);
4989+
if(tmp > 254) tmp = 254;
4990+
memcpy(value, (data + offset), tmp);
4991+
value[tmp] = 0;
4992+
m_chbuf[0] = 0;
4993+
if(i == 0) sprintf(m_chbuf, "Title: %s", value);
4994+
if(i == 1) sprintf(m_chbuf, "Artist: %s", value);
4995+
if(i == 2) sprintf(m_chbuf, "Album: %s", value);
4996+
if(i == 3) sprintf(m_chbuf, "Encoder: %s", value);
4997+
if(i == 4) sprintf(m_chbuf, "Comment: %s", value);
4998+
if(i == 5) sprintf(m_chbuf, "Composer: %s", value);
4999+
if(i == 6) sprintf(m_chbuf, "BPM: %s", value);
5000+
if(i == 7) sprintf(m_chbuf, "Track Number: %s", value);
5001+
if(i == 8) sprintf(m_chbuf, "Year: %s", value);
5002+
if(i == 9) sprintf(m_chbuf, "Compile: %s", value);
5003+
if(i == 10) sprintf(m_chbuf, "Album Artist: %s", value);
5004+
if(i == 11) sprintf(m_chbuf, "Types of: %s", value);
5005+
if(m_chbuf[0] != 0) {
5006+
if(audio_id3data) audio_id3data(m_chbuf);
5007+
}
5008+
}
5009+
}
5010+
m_f_m4aID3dataAreRead = true;
5011+
if(data) free(data);
5012+
audiofile.seek(0);
5013+
return;
5014+
}
5015+
//----------------------------------------------------------------------------------------------------------------------
49045016
void Audio::seek_m4a_stsz(){
49055017
// stsz says what size each sample is in bytes. This is important for the decoder to be able to start at a chunk,
49065018
// and then go through each sample by its size. The stsz atom can be behind the audio block. Therefore, searching
@@ -4910,7 +5022,7 @@ void Audio::seek_m4a_stsz(){
49105022
49115023
ftyp -> moov -> trak -> tkhd
49125024
free udta mdia -> mdhd
4913-
mdat udta hdlr
5025+
mdat hdlr
49145026
mvhd minf -> smhd
49155027
dinf
49165028
stbl -> stsd
@@ -4932,6 +5044,7 @@ void Audio::seek_m4a_stsz(){
49325044
audiofile.seek(startPos);
49335045
audiofile.readBytes(tmp, 4);
49345046
atom.size = bigEndian((uint8_t*)tmp, 4);
5047+
if(!atom.size) atom.size = 4; // has no data, length is 0
49355048
audiofile.readBytes(atom.name, 4);
49365049
atom.name[4] = '\0';
49375050
atom.pos = startPos;

src/Audio.h

Lines changed: 3 additions & 1 deletion
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: Jan 09,2023
5+
* Updated on: Jan 10,2023
66
* Author: Wolle (schreibfaul1)
77
*/
88

@@ -274,6 +274,7 @@ class Audio : private AudioBuffer{
274274
bool readID3V1Tag();
275275
boolean streamDetection(uint32_t bytesAvail);
276276
void seek_m4a_stsz();
277+
void seek_m4a_ilst();
277278
uint32_t m4a_correctResumeFilePos(uint32_t resumeFilePos);
278279
uint32_t flac_correctResumeFilePos(uint32_t resumeFilePos);
279280
uint32_t mp3_correctResumeFilePos(uint32_t resumeFilePos);
@@ -547,6 +548,7 @@ class Audio : private AudioBuffer{
547548
bool m_f_Log = false; // set in platformio.ini -DAUDIO_LOG and -DCORE_DEBUG_LEVEL=3 or 4
548549
bool m_f_continue = false; // next m3u8 chunk is available
549550
bool m_f_ts = true; // transport stream
551+
bool m_f_m4aID3dataAreRead = false; // has the m4a-ID3data already been read?
550552
uint8_t m_f_channelEnabled = 3; // internal DAC, both channels
551553
uint32_t m_audioFileDuration = 0;
552554
float m_audioCurrentTime = 0;

0 commit comments

Comments
 (0)