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+ // ----------------------------------------------------------------------------------------------------------------------
49045016void 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;
0 commit comments