#include "system/app_core.h" #include "system/includes.h" #include "app_config.h" #include "music/music_id3.h" #include "system/fs/fs.h" #if ((TCFG_DEC_ID3_V1_ENABLE) || (TCFG_DEC_ID3_V2_ENABLE)) #define LOG_TAG_CONST APP_MUSIC #define LOG_TAG "[APP_MUSIC_ID3]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" #define ID3_ANALYSIS_EN 1 // ID3解析 struct id3_v1_hdl { char header[3]; // TAG char title[30]; // 标题 char artist[30]; // 作者 char album[30]; // 专辑 char year[4]; // 出品年代 char comment[30]; // 备注 char genre; // 类型 }; struct id3_v2_hdl { char header[3]; // ID3 char ver; // 版本号 char revision; // 副版本号 char flag; // 标志 u8 size[4]; // 标签大小,不包括标签头的10个字节 }; struct id3_v2_frame { char frameID[4]; // 帧标识 u8 size[4]; // 帧大小,不包括帧头10个字节,不小于1 char flags[2]; // 标志 }; #define ID3_SIZEOF_ALIN(var,al) ((((var)+(al)-1)/(al))*(al)) #define ID3_V1_HEADER_SIZE (128) // sizeof(struct id3_v1_hdl) #define ID3_V2_HEADER_SIZE (10) // sizeof(struct id3_v2_hdl) #define ID3_V2_HEADER_SIZE_MAX (1024 * 2) #define ID3_V2_FRAME_SIZE (10) // sizeof(struct id3_v2_frame) static u8 id3_v1_match(u8 *buf) { if (buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G') { return true; } else { return false; } } static u8 id3_v2_match(u8 *buf) { //put_buf(buf, 16); if ((buf[0] == 'I') && (buf[1] == 'D') && (buf[2] == '3') && (buf[3] != 0xff) && (buf[4] != 0xff) && ((buf[6] & 0x80) == 0) && ((buf[7] & 0x80) == 0) && ((buf[8] & 0x80) == 0) && ((buf[9] & 0x80) == 0)) { return true; } else { return false; } } static u32 id3v2_get_tag_len(u8 *buf) { u32 len = (((u32)buf[6] & 0x7f) << 21) + (((u32)buf[7] & 0x7f) << 14) + (((u32)buf[8] & 0x7f) << 7) + (buf[9] & 0x7f) + ID3_V2_HEADER_SIZE; if (buf[5] & 0x10) { len += ID3_V2_HEADER_SIZE; } log_info("----id3v2_len%d---\n", len); return len; } #if ID3_ANALYSIS_EN static void id3_v1_analysis(MP3_ID3_OBJ *obj) { struct id3_v1_hdl *hdl = (struct id3_v1_hdl *)obj->id3_buf; /* log_info("header:%s \n", hdl->header); */ log_info("title:%s \n", hdl->title); log_info("artist:%s \n", hdl->artist); log_info("album:%s \n", hdl->album); log_info("year:%s \n", hdl->year); log_info("comment:%s \n", hdl->comment); log_info("genre:%d \n", hdl->genre); } static const u8 ID3_V2_FRAME_ID[][4] = { "TIT2", // 标题 "TPE1", // 作者 "TALB", // 专辑 "TYER", // 年代 "TCON", // 类型 "COMM", // 备注 }; static void id3_v2_analysis(void *file, void *buf) { #define FRAME_DAT_MAX 512 u8 *frame_dat = malloc(FRAME_DAT_MAX); u8 frame_hdl[ID3_V2_FRAME_SIZE]; u32 frame_len; struct id3_v2_hdl *hdl = (struct id3_v2_hdl *)buf; /* log_info("header:%s \n", hdl->header); */ if (!frame_dat) { return ; } u32 addr = ID3_V2_HEADER_SIZE; u32 addr_end = id3v2_get_tag_len(buf) + ID3_V2_HEADER_SIZE; log_info("frame total len:0x%x ", addr_end); while (addr < addr_end) { fseek(file, addr, SEEK_SET); fread(file, frame_hdl, ID3_V2_FRAME_SIZE); int idx; struct id3_v2_frame *frame = (struct id3_v2_frame *)frame_hdl; frame_len = (((u32)frame->size[0]) << 24) + (((u32)frame->size[1]) << 16) + (((u32)frame->size[2]) << 8) + (u32)frame->size[3]; log_info_hexdump(frame, ID3_V2_FRAME_SIZE); log_info("frame ID:%s ", frame->frameID); log_info("frame len:0x%x ", frame_len); log_info("addr :0x%x ", addr); if (!frame_len) { break; } if (frame_len <= FRAME_DAT_MAX) { memset(frame_dat, 0, FRAME_DAT_MAX); fread(file, frame_dat, frame_len); } for (idx = 0; idx < sizeof(ID3_V2_FRAME_ID) / 4; idx++) { if (!memcmp(frame->frameID, ID3_V2_FRAME_ID[idx], 4)) { log_info("find frameID \n"); break; } } if (frame_len <= FRAME_DAT_MAX) { log_info("frame dat:%s, \n", frame_dat); log_info_hexdump(frame_dat, frame_len); } addr += ID3_V2_FRAME_SIZE + frame_len; } free(frame_dat); } #endif void id3_obj_post(MP3_ID3_OBJ **obj) { if (obj == NULL || (*obj) == NULL) { return ; } free(*obj); *obj = NULL; } MP3_ID3_OBJ *id3_v1_obj_get(void *file) { if (file == NULL) { return NULL; } MP3_ID3_OBJ *obj = NULL; u8 *need_buf = NULL; u32 need_buf_size; u32 cur_fptr = fpos(file); u32 file_len = flen(file); need_buf_size = ID3_SIZEOF_ALIN(sizeof(MP3_ID3_OBJ), 4) + ID3_SIZEOF_ALIN(ID3_V1_HEADER_SIZE, 4); need_buf = (u8 *)zalloc(need_buf_size); if (need_buf == NULL) { log_error("malloc err !!\n"); return NULL; } obj = (MP3_ID3_OBJ *)need_buf; obj->id3_buf = (u8 *)(need_buf + ID3_SIZEOF_ALIN(sizeof(MP3_ID3_OBJ), 4)); obj->id3_len = ID3_V1_HEADER_SIZE; fseek(file, (file_len - ID3_V1_HEADER_SIZE), SEEK_SET); fread(file, obj->id3_buf, ID3_V1_HEADER_SIZE); if (id3_v1_match(obj->id3_buf) == true) { log_info("find id3 v1 !!\n"); put_buf(obj->id3_buf, obj->id3_len); } else { goto __id3_v1_err; } #if ID3_ANALYSIS_EN id3_v1_analysis(obj); #else fseek(file, cur_fptr, SEEK_SET); return obj; #endif __id3_v1_err: fseek(file, cur_fptr, SEEK_SET); if (need_buf) { free(need_buf); } return NULL; } MP3_ID3_OBJ *id3_v2_obj_get(void *file) { if (file == NULL) { return NULL; } MP3_ID3_OBJ *obj = NULL; u8 *need_buf = NULL; u32 need_buf_size; u8 tmp_buf[ID3_V2_HEADER_SIZE]; u32 cur_fptr = fpos(file); fseek(file, 0, SEEK_SET); fread(file, tmp_buf, ID3_V2_HEADER_SIZE); if (id3_v2_match(tmp_buf) == true) { log_info("find id3 v2 !!\n"); #if ID3_ANALYSIS_EN id3_v2_analysis(file, (MP3_ID3_OBJ *)tmp_buf); #else u32 tag_len = id3v2_get_tag_len(tmp_buf); if (tag_len > (ID3_V2_HEADER_SIZE_MAX)) { log_error("id3 v2 info is too big!!\n", tag_len); goto __id3_v2_err; } need_buf_size = ID3_SIZEOF_ALIN(sizeof(MP3_ID3_OBJ), 4) + ID3_SIZEOF_ALIN(tag_len, 4); need_buf = (u8 *)zalloc(need_buf_size); if (need_buf == NULL) { log_error("no space for id3 v2 %d\n", tag_len); goto __id3_v2_err; } obj = (MP3_ID3_OBJ *)need_buf; obj->id3_buf = (u8 *)(need_buf + ID3_SIZEOF_ALIN(sizeof(MP3_ID3_OBJ), 4)); obj->id3_len = tag_len; fseek(file, 0, SEEK_SET); fread(file, obj->id3_buf, obj->id3_len); log_info("id3_v2 size = %d\n", obj->id3_len); put_buf(obj->id3_buf, obj->id3_len); #endif } else { goto __id3_v2_err; } fseek(file, cur_fptr, SEEK_SET); return obj; __id3_v2_err: fseek(file, cur_fptr, SEEK_SET); if (need_buf) { free(need_buf); } return NULL; } #endif