/** Testuje wczytywanie drzewa. @param state Środowisko testowe. */ static void trie_load_test(void** state) { Trie *trie = NULL; IO *io = io_new(stdin, stdout, stderr); push_word_to_io_mock(L""); trie = trie_load(io); assert_non_null(trie); trie_done(trie); // Poprawny zapis push_word_to_io_mock(L"ciupagą*^^^^^^^\n"); trie = trie_load(io); pop_remaining_chars(io); assert_true(trie_has_word(trie, L"ciupagą")); assert_false(trie_has_word(trie, L"ciupaga")); assert_false(trie_has_word(trie, L"ciupag")); assert_false(trie_has_word(trie, L"ciupagąą")); trie_done(trie); // Próba dojścia wyżej niż korzeń push_word_to_io_mock(L"a*^^\n"); trie = trie_load(io); pop_remaining_chars(io); assert_null(trie); // Znaki spoza alfabetu push_word_to_io_mock(L"&*^\n"); trie = trie_load(io); pop_remaining_chars(); assert_null(trie); io_done(io); }
struct hfp_gw *hfp_gw_new(int fd) { struct hfp_gw *hfp; if (fd < 0) return NULL; hfp = new0(struct hfp_gw, 1); if (!hfp) return NULL; hfp->fd = fd; hfp->close_on_unref = false; hfp->read_buf = ringbuf_new(4096); if (!hfp->read_buf) { free(hfp); return NULL; } hfp->write_buf = ringbuf_new(4096); if (!hfp->write_buf) { ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->io = io_new(fd); if (!hfp->io) { ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->cmd_handlers = queue_new(); if (!hfp->cmd_handlers) { io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } if (!io_set_read_handler(hfp->io, can_read_data, hfp, read_watch_destroy)) { queue_destroy(hfp->cmd_handlers, destroy_cmd_handler); io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->writer_active = false; hfp->result_pending = false; return hfp_gw_ref(hfp); }
static struct bt_hci *create_hci(int fd) { struct bt_hci *hci; if (fd < 0) return NULL; hci = new0(struct bt_hci, 1); if (!hci) return NULL; hci->io = io_new(fd); if (!hci->io) { free(hci); return NULL; } hci->is_stream = true; hci->writer_active = false; hci->num_cmds = 1; hci->next_cmd_id = 1; hci->next_evt_id = 1; hci->cmd_queue = queue_new(); if (!hci->cmd_queue) { io_destroy(hci->io); free(hci); return NULL; } hci->rsp_queue = queue_new(); if (!hci->rsp_queue) { queue_destroy(hci->cmd_queue, NULL); io_destroy(hci->io); free(hci); return NULL; } hci->evt_list = queue_new(); if (!hci->evt_list) { queue_destroy(hci->rsp_queue, NULL); queue_destroy(hci->cmd_queue, NULL); io_destroy(hci->io); free(hci); return NULL; } if (!io_set_read_handler(hci->io, io_read_callback, hci, NULL)) { queue_destroy(hci->evt_list, NULL); queue_destroy(hci->rsp_queue, NULL); queue_destroy(hci->cmd_queue, NULL); io_destroy(hci->io); free(hci); return NULL; } return bt_hci_ref(hci); }
struct bt_att *bt_att_new(int fd) { struct bt_att *att; if (fd < 0) return NULL; att = new0(struct bt_att, 1); if (!att) return NULL; att->fd = fd; att->mtu = ATT_DEFAULT_LE_MTU; att->buf = malloc(att->mtu); if (!att->buf) goto fail; att->io = io_new(fd); if (!att->io) goto fail; att->req_queue = queue_new(); if (!att->req_queue) goto fail; att->ind_queue = queue_new(); if (!att->ind_queue) goto fail; att->write_queue = queue_new(); if (!att->write_queue) goto fail; att->notify_list = queue_new(); if (!att->notify_list) goto fail; if (!io_set_read_handler(att->io, can_read_data, att, NULL)) goto fail; return bt_att_ref(att); fail: queue_destroy(att->req_queue, NULL); queue_destroy(att->ind_queue, NULL); queue_destroy(att->write_queue, NULL); io_destroy(att->io); free(att->buf); free(att); return NULL; }
/* ------------------------------------------------------------------------ * * Open a file. * * ------------------------------------------------------------------------ */ int io_open(const char *path, int mode, ...) { int fd; int flags = 0; int ret; struct stat st; va_list args; #ifdef O_BINARY flags |= O_BINARY; #endif switch(mode & IO_OPEN_RDWR) { case IO_OPEN_READ: flags |= O_RDONLY; break; case IO_OPEN_WRITE: flags |= O_WRONLY; break; case IO_OPEN_RDWR: flags |= O_RDWR; break; } if(mode & IO_OPEN_CREATE) flags |= O_CREAT; if(mode & IO_OPEN_APPEND) flags |= O_APPEND; if(mode & IO_OPEN_TRUNCATE) flags |= O_TRUNC; if(!(flags & O_CREAT) && (stat(path, &st) == -1)) return -1; va_start(args, mode); fd = syscall_open(path, flags, va_arg(args, long)); va_end(args); if(fd == -1) return -1; if(fd >= IO_MAX_FDS) { syscall_close(fd); return -1; } io_list[fd].stat = st; ret = io_new(fd, FD_FILE); if(ret >= 0) { io_note(fd, "%s", path); } return ret; }
static int do_client_connect(int sock) { struct sockaddr_un saremote; socklen_t addrlen; int s; addrlen = sizeof(saremote); s = accept(sock, (struct sockaddr *)&saremote, &addrlen); if (s == -1) { log_err("Couldn't accept new connection: %s.", strerror(errno)); return -1; } io_new(s); return 0; }
struct bt_uhid *bt_uhid_new(int fd) { struct bt_uhid *uhid; uhid = new0(struct bt_uhid, 1); uhid->io = io_new(fd); if (!uhid->io) goto failed; uhid->notify_list = queue_new(); if (!io_set_read_handler(uhid->io, uhid_read_handler, uhid, NULL)) goto failed; return bt_uhid_ref(uhid); failed: uhid_free(uhid); return NULL; }
void sploit_init(void) { log(sploit_log, L_startup, "%s v%s sploit", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_RELEASE); log_init(STDOUT_FILENO, LOG_ALL, L_status); io_init_except(STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO); sploit_stdin = io_new(STDIN_FILENO, FD_PIPE); mem_init(); timer_init(); connect_init(); queue_init(); dlink_init(); str_init(); net_init(); ssl_init(); sploit_log = log_source_register("sploit"); }
/** Testuje zapisywanie drzewa. @param state Środowisko testowe. */ static void trie_save_test(void** state) { Trie *trie = trie_new(); FILE *stream; wchar_t *buf = NULL; size_t len; stream = open_wmemstream(&buf, &len); if (stream == NULL) { fprintf(stderr, "Failed to open memory stream\n"); exit(EXIT_FAILURE); } IO *io = io_new(stdin, stream, stderr); assert_true(trie_save(trie, io) == 0); fflush(stream); assert_true(wcscmp(L"\n", buf) == 0); fseek(stream, 0, SEEK_SET); trie_insert_word(trie, L"ciupaga"); assert_true(trie_save(trie, io) == 0); fflush(stream); assert_true(wcscmp(L"ciupaga*^^^^^^^\n", buf) == 0); fseek(stream, 0, SEEK_SET); trie_delete_word(trie, L"ciupaga"); assert_true(trie_save(trie, io) == 0); fflush(stream); assert_true(wcscmp(L"\n", buf) == 0); fclose(stream); io_done(io); # undef free free(buf); # define free(ptr) _test_free(ptr, __FILE__, __LINE__) trie_done(trie); }
int err_setlogfile(char *file) { char *mode; int result=TRUE; /* if(strcmp(file,err_filename) == 0) return TRUE; */ if(err_file) { io_close(err_file); } else { err_file = io_new(); if(!err_file) { err_logdest &= ~LOGDEST_LOGFILE; if(!err_syslog_open) os_opensyslog(); os_syslog(1,"Error initializing logfile"); } } mode = "a"; if(err_truncate) mode = "w"; strncpy(err_filename,file,sizeof(err_filename)-1); if(!io_open(err_file,"file://%U?mode=%s&ascii=1",err_filename,mode)) { fprintf(stderr,"Error opening logfile: %s",io_errstr(err_file)); err_logdest &= ~LOGDEST_LOGFILE; if(!err_syslog_open) os_opensyslog(); os_syslog(1,"Error opening logfile"); result=FALSE; } return result; }
/* -------------------------------------------------------------------------- * * Program entry. * * -------------------------------------------------------------------------- */ int main(int argc, char *argv[]) { int i, j; int recvfd = 0, sendfd = 1; net_addr_t localhost = net_addr_loopback; /* get control connection */ if(argc >= 2) recvfd = str_tol(argv[1], NULL, 10); if(argc >= 3) sendfd = str_tol(argv[2], NULL, 10); io_init_except(recvfd, sendfd, -1); if(recvfd == sendfd) { /* socketpair() */ io_new(recvfd, FD_SOCKET); } else { /* 2x pipe() */ io_new(recvfd, FD_PIPE); io_new(sendfd, FD_PIPE); } /* initialize library */ servauth_init(); /* Make sure we are running under hybrid.. */ /* if(str_ncmp(argv[0], "-sauth", 6)) sauth_usage();*/ /* clear argv */ for(i = 1; i < argc; i++) for(j = 0; argv[i][j]; j++) argv[i][j] = '\0'; /* set file descriptor to nonblocking mode */ /* io_nonblocking(recvfd); io_nonblocking(sendfd);*/ /* initialize dns client */ dns_init(); dns_updatedb(); /* initialize caches */ cache_auth_new(&servauth_authcache, CACHE_AUTH_SIZE); cache_dns_new(&servauth_dnscache, CACHE_DNS_SIZE); cache_proxy_new(&servauth_proxycache, CACHE_PROXY_SIZE); cache_dns_put(&servauth_dnscache, CACHE_DNS_REVERSE, localhost, "localhost", (uint64_t)-1LL); /* initialize control connection */ control_init(&servauth_control, recvfd, sendfd); /* clear query structs */ memset(servauth_queries, 0, sizeof(servauth_queries)); /* enter io loop */ servauth_loop(); /* NOTREACHED */ return(0); }
struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; if (fd < 0) return NULL; att = new0(struct bt_att, 1); if (!att) return NULL; att->fd = fd; att->mtu = BT_ATT_DEFAULT_LE_MTU; att->buf = malloc(att->mtu); if (!att->buf) goto fail; att->io = io_new(fd); if (!att->io) goto fail; /* crypto is optional, if not available leave it NULL */ if (!ext_signed) att->crypto = bt_crypto_new(); att->req_queue = queue_new(); if (!att->req_queue) goto fail; att->ind_queue = queue_new(); if (!att->ind_queue) goto fail; att->write_queue = queue_new(); if (!att->write_queue) goto fail; att->notify_list = queue_new(); if (!att->notify_list) goto fail; att->disconn_list = queue_new(); if (!att->disconn_list) goto fail; if (!io_set_read_handler(att->io, can_read_data, att, NULL)) goto fail; if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL)) goto fail; att->io_on_l2cap = is_io_l2cap_based(att->fd); if (!att->io_on_l2cap) att->io_sec_level = BT_SECURITY_LOW; return bt_att_ref(att); fail: bt_att_free(att); return NULL; }
struct mgmt *mgmt_new(int fd) { struct mgmt *mgmt; if (fd < 0) return NULL; mgmt = new0(struct mgmt, 1); if (!mgmt) return NULL; mgmt->fd = fd; mgmt->close_on_unref = false; mgmt->len = 512; mgmt->buf = malloc(mgmt->len); if (!mgmt->buf) { free(mgmt); return NULL; } mgmt->io = io_new(fd); if (!mgmt->io) { free(mgmt->buf); free(mgmt); return NULL; } mgmt->request_queue = queue_new(); if (!mgmt->request_queue) { io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } mgmt->reply_queue = queue_new(); if (!mgmt->reply_queue) { queue_destroy(mgmt->request_queue, NULL); io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } mgmt->pending_list = queue_new(); if (!mgmt->pending_list) { queue_destroy(mgmt->reply_queue, NULL); queue_destroy(mgmt->request_queue, NULL); io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } mgmt->notify_list = queue_new(); if (!mgmt->notify_list) { queue_destroy(mgmt->pending_list, NULL); queue_destroy(mgmt->reply_queue, NULL); queue_destroy(mgmt->request_queue, NULL); io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, read_watch_destroy)) { queue_destroy(mgmt->notify_list, NULL); queue_destroy(mgmt->pending_list, NULL); queue_destroy(mgmt->reply_queue, NULL); queue_destroy(mgmt->request_queue, NULL); io_destroy(mgmt->io); free(mgmt->buf); free(mgmt); return NULL; } mgmt->writer_active = false; return mgmt_ref(mgmt); }
/** * Get info from a "url" file -- a media stream file. * This should really get more metainfo, but I'll leave that * to later. * * @param filename .url file to process * @param pmp3 MP3FILE structure that must be filled * @returns TRUE if file should be added to db, FALSE otherwise */ int scan_get_urlinfo(char *filename, MP3FILE *pmp3) { IOHANDLE hfile; char *head, *tail; char linebuffer[256]; uint32_t len; DPRINTF(E_DBG,L_SCAN,"Getting URL file info\n"); if(!(hfile = io_new())) { DPRINTF(E_LOG,L_SCAN,"Can't create file handle\n"); return FALSE; } if(!io_open(hfile,"file://%U?ascii=1",filename)) { DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading: %s\n",filename, io_errstr(hfile)); io_dispose(hfile); return FALSE; } io_buffer(hfile); len = sizeof(linebuffer); if(!io_readline(hfile,(unsigned char *)linebuffer,&len)) { DPRINTF(E_WARN,L_SCAN,"Error reading from file %s: %s",filename,io_errstr(hfile)); io_close(hfile); io_dispose(hfile); return FALSE; } while((linebuffer[strlen(linebuffer)-1] == '\n') || (linebuffer[strlen(linebuffer)-1] == '\r')) { linebuffer[strlen(linebuffer)-1] = '\0'; } head=linebuffer; tail=strchr(head,','); if(!tail) { DPRINTF(E_LOG,L_SCAN,"Badly formatted .url file - must be bitrate,descr,url\n"); io_close(hfile); io_dispose(hfile); return FALSE; } pmp3->bitrate=atoi(head); head=++tail; tail=strchr(head,','); if(!tail) { DPRINTF(E_LOG,L_SCAN,"Badly formatted .url file - must be bitrate,descr,url\n"); io_close(hfile); io_dispose(hfile); return FALSE; } *tail++='\0'; pmp3->title=strdup(head); pmp3->url=strdup(tail); io_close(hfile); io_dispose(hfile); DPRINTF(E_DBG,L_SCAN," Title: %s\n",pmp3->title); DPRINTF(E_DBG,L_SCAN," Bitrate: %d\n",pmp3->bitrate); DPRINTF(E_DBG,L_SCAN," URL: %s\n",pmp3->url); return TRUE; }
struct hfp_hf *hfp_hf_new(int fd) { struct hfp_hf *hfp; if (fd < 0) return NULL; hfp = new0(struct hfp_hf, 1); if (!hfp) return NULL; hfp->fd = fd; hfp->close_on_unref = false; hfp->read_buf = ringbuf_new(4096); if (!hfp->read_buf) { free(hfp); return NULL; } hfp->write_buf = ringbuf_new(4096); if (!hfp->write_buf) { ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->io = io_new(fd); if (!hfp->io) { ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->event_handlers = queue_new(); if (!hfp->event_handlers) { io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } hfp->cmd_queue = queue_new(); if (!hfp->cmd_queue) { io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); queue_destroy(hfp->event_handlers, NULL); free(hfp); return NULL; } hfp->writer_active = false; if (!io_set_read_handler(hfp->io, hf_can_read_data, hfp, read_watch_destroy)) { queue_destroy(hfp->event_handlers, destroy_event_handler); io_destroy(hfp->io); ringbuf_free(hfp->write_buf); ringbuf_free(hfp->read_buf); free(hfp); return NULL; } return hfp_hf_ref(hfp); }
/** * main aac scanning routing. * * @param filename file to scan * @param pmp3 pointer to the MP3FILE to fill with data * @returns FALSE if file should not be added to database, TRUE otherwise */ int scan_get_aacinfo(char *filename, MP3FILE *pmp3) { IOHANDLE hfile; uint64_t atom_offset, pos; uint32_t bytes_read; unsigned int atom_length; long current_offset=0; uint32_t current_size; char current_atom[4]; char *current_data; unsigned short us_data; int genre; int len; uint32_t sample_size; uint32_t samples; uint32_t bit_rate; int ms; unsigned char buffer[2]; uint32_t time = 0; hfile = io_new(); if(!hfile) DPRINTF(E_FATAL,L_SCAN,"Malloc error in scan_get_aacinfo\n"); if(!io_open(hfile,"file://%U",filename)) { DPRINTF(E_INF,L_SCAN,"Cannot open file %s for reading: %s\n",filename, io_errstr(hfile)); io_dispose(hfile); return FALSE; } atom_offset=scan_aac_drilltoatom(hfile, "moov:udta:meta:ilst", &atom_length); if(atom_offset != -1) { /* found the tag section - need to walk through now */ while(current_offset < (uint64_t)atom_length) { bytes_read = sizeof(uint32_t); if(!io_read(hfile,(unsigned char *)¤t_size,&bytes_read) || !bytes_read) { DPRINTF(E_LOG,L_SCAN,"Error reading mp4 atoms: %s\n",io_errstr(hfile)); io_dispose(hfile); return FALSE; } current_size=ntohl(current_size); DPRINTF(E_SPAM,L_SCAN,"Current size: %d\n",current_size); if(current_size <= 7) { /* something not right */ DPRINTF(E_LOG,L_SCAN,"mp4 atom too small. Bad aac tags?\n"); io_dispose(hfile); return FALSE; } bytes_read = 4; if(!io_read(hfile,(unsigned char *)current_atom,&bytes_read) || !bytes_read) { DPRINTF(E_LOG,L_SCAN,"Error reading mp4 atoms: %s\n",io_errstr(hfile)); io_dispose(hfile); return FALSE; } DPRINTF(E_SPAM,L_SCAN,"Current Atom: %c%c%c%c\n", current_atom[0],current_atom[1],current_atom[2], current_atom[3]); if(current_size > 4096) { /* Does this break anything? */ /* too big! cover art, maybe? */ io_setpos(hfile,current_size - 8, SEEK_CUR); DPRINTF(E_SPAM,L_SCAN,"Atom too big... skipping\n"); } else { len=current_size-7; /* for ill-formed too-short tags */ if(len < 22) { len=22; } current_data=(char*)malloc(len); /* extra byte */ memset(current_data,0x00,len); bytes_read = current_size - 8; if(!io_read(hfile,(unsigned char *)current_data,&bytes_read) || (!bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading mp4 data: %s\n",io_errstr(hfile)); free(current_data); io_dispose(hfile); return FALSE; } if(!memcmp(current_atom,"\xA9" "nam",4)) { /* Song name */ pmp3->title=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"aART",4)) { pmp3->album_artist=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "ART",4)) { pmp3->artist=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "alb",4)) { pmp3->album=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "cmt",4)) { pmp3->comment=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "wrt",4)) { pmp3->composer=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "grp",4)) { pmp3->grouping=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"\xA9" "gen",4)) { /* can this be a winamp genre??? */ pmp3->genre=strdup((char*)¤t_data[16]); } else if(!memcmp(current_atom,"tmpo",4)) { us_data=*((unsigned short *)¤t_data[16]); us_data=ntohs(us_data); pmp3->bpm=us_data; } else if(!memcmp(current_atom,"trkn",4)) { us_data=*((unsigned short *)¤t_data[18]); us_data=ntohs(us_data); pmp3->track=us_data; us_data=*((unsigned short *)¤t_data[20]); us_data=ntohs(us_data); pmp3->total_tracks=us_data; } else if(!memcmp(current_atom,"disk",4)) { us_data=*((unsigned short *)¤t_data[18]); us_data=ntohs(us_data); pmp3->disc=us_data; us_data=*((unsigned short *)¤t_data[20]); us_data=ntohs(us_data); pmp3->total_discs=us_data; } else if(!memcmp(current_atom,"\xA9" "day",4)) { pmp3->year=atoi((char*)¤t_data[16]); } else if(!memcmp(current_atom,"gnre",4)) { genre=(int)(*((char*)¤t_data[17])); genre--; if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) genre=WINAMP_GENRE_UNKNOWN; pmp3->genre=strdup(scan_winamp_genre[genre]); } else if (!memcmp(current_atom, "cpil", 4)) { pmp3->compilation = current_data[16]; } free(current_data); } current_offset+=current_size; } } /* got the tag info, now let's get bitrate, etc */ atom_offset = scan_aac_drilltoatom(hfile, "moov:mvhd", &atom_length); if(atom_offset != -1) { io_setpos(hfile,4,SEEK_CUR); /* FIXME: error handling */ bytes_read = sizeof(uint32_t); if(!io_read(hfile,(unsigned char *)&time, &bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading time from moov:mvhd: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } time = ntohl(time); pmp3->time_added = (int)scan_aac_mac_to_unix_time(time); bytes_read = sizeof(uint32_t); if(!io_read(hfile,(unsigned char *)&time, &bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading time from moov:mvhd: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } time = ntohl(time); pmp3->time_modified = (int)scan_aac_mac_to_unix_time(time); bytes_read = sizeof(uint32_t); if(!io_read(hfile,(unsigned char *)&sample_size,&bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading sample_size from moov:mvhd: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } bytes_read = sizeof(uint32_t); if(!io_read(hfile,(unsigned char*)&samples, &bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading samples from moov:mvhd: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } sample_size=ntohl(sample_size); samples=ntohl(samples); /* avoid overflowing on large sample_sizes (90000) */ ms=1000; while((ms > 9) && (!(sample_size % 10))) { sample_size /= 10; ms /= 10; } /* DWB: use ms time instead of sec */ pmp3->song_length=(uint32_t)((samples * ms) / sample_size); DPRINTF(E_DBG,L_SCAN,"Song length: %d seconds\n", pmp3->song_length / 1000); } pmp3->bitrate = 0; /* see if it is aac or alac */ atom_offset = scan_aac_drilltoatom(hfile, "moov:trak:mdia:minf:stbl:stsd:alac", &atom_length); if(atom_offset != -1) { /* should we still pull samplerate, etc from the this atom? */ if(pmp3->codectype) { free(pmp3->codectype); } pmp3->codectype=strdup("alac"); } /* Get the sample rate from the 'mp4a' atom (timescale). This is also found in the 'mdhd' atom which is a bit closer but we need to navigate to the 'mp4a' atom anyways to get to the 'esds' atom. */ atom_offset=scan_aac_drilltoatom(hfile, "moov:trak:mdia:minf:stbl:stsd:mp4a", &atom_length); if(atom_offset == -1) { atom_offset=scan_aac_drilltoatom(hfile, "moov:trak:mdia:minf:stbl:stsd:drms", &atom_length); } if (atom_offset != -1) { io_setpos(hfile, atom_offset + 32, SEEK_SET); /* Timescale here seems to be 2 bytes here (the 2 bytes before it are * "reserved") though the timescale in the 'mdhd' atom is 4. Not sure * how this is dealt with when sample rate goes higher than 64K. */ bytes_read = 2; if(!io_read(hfile, (unsigned char *)buffer, &bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading timescale from drms atom: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } pmp3->samplerate = (buffer[0] << 8) | (buffer[1]); /* Seek to end of atom. */ io_setpos(hfile, 2, SEEK_CUR); /* Get the bit rate from the 'esds' atom. We are already positioned in the parent atom so just scan ahead. */ io_getpos(hfile,&pos); atom_offset = scan_aac_findatom(hfile, atom_length-(pos-atom_offset), "esds", &atom_length); if (atom_offset != -1) { /* Roku Soundbridge seems to believe anything above 320K is * an ALAC encoded m4a. We'll lie on their behalf. */ io_setpos(hfile, atom_offset + 22, SEEK_CUR); bytes_read = sizeof(unsigned int); if(!io_read(hfile, (unsigned char *)&bit_rate, &bytes_read)) { DPRINTF(E_LOG,L_SCAN,"Error reading bitrate from esds: %s\n", io_errstr(hfile)); io_dispose(hfile); return FALSE; } pmp3->bitrate = ntohl(bit_rate) / 1000; DPRINTF(E_DBG,L_SCAN,"esds bitrate: %d\n",pmp3->bitrate); if(pmp3->bitrate > 320) { pmp3->bitrate = 320; } } else { DPRINTF(E_DBG,L_SCAN, "Couldn't find 'esds' atom for bit rate.\n"); } } else { DPRINTF(E_DBG,L_SCAN, "Couldn't find 'mp4a' atom for sample rate.\n"); } /* Fallback if we can't find the info in the atoms. */ if (pmp3->bitrate == 0) { /* calculate bitrate from song length... Kinda cheesy */ DPRINTF(E_DBG,L_SCAN, "Guesstimating bit rate.\n"); atom_offset=scan_aac_drilltoatom(hfile,"mdat",&atom_length); if ((atom_offset != -1) && (pmp3->song_length >= 1000)) { pmp3->bitrate = atom_length / ((pmp3->song_length / 1000) * 128); } } io_close(hfile); io_dispose(hfile); return TRUE; /* we'll return as much as we got. */ }