static void demux_mp4_parse_mp4a(struct demux *d) { unsigned long atom_size; unsigned long size; /* Get size */ atom_size = ATOM_LEN(d->buffer); stream_read(d->stream, 28); /* Get track properties */ d->mp4a_channel_count = ATOM_READ16(&d->buffer[16]); d->mp4a_sample_size = ATOM_READ16(&d->buffer[18]); d->mp4a_samplerate = ATOM_READ16(&d->buffer[24]); /* Set track found flag */ d->track_found = 1; /* Skip reserved bytes */ atom_size -= 36; /* Get size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Parse "esds" atom */ if(ATOM_CHECK(d->buffer, "esds") == 0) { demux_mp4_parse_esds(d); atom_size -= size; } /* Go to next atom */ stream_seek(d->stream, atom_size, SEEK_CUR); }
static void demux_mp4_parse_trkn(struct demux *d) { unsigned long size; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); size -= 8; /* Check sub-atom */ if(ATOM_CHECK(d->buffer, "data") == 0 && ATOM_LEN(d->buffer) == 24) { /* Skip version and flags */ stream_read(d->stream, 10); size -= 20; /* Read track */ stream_read(d->stream, 2); d->track = ATOM_READ16(d->buffer); /* Read total tack */ stream_read(d->stream, 2); d->total_track = ATOM_READ16(d->buffer); } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
int lexigraphic_compare(atom_t x, atom_t y) { int xl = ATOM_LEN(x); int yl = ATOM_LEN(y); return memcmp(ATOM_PTR(x),ATOM_PTR(y),xl < yl ? xl : yl) || xl - yl; }
static void demux_mp4_parse_udta(struct demux *d) { unsigned long atom_size; unsigned long count = 8; unsigned long size; /* Get size of current atom */ atom_size = ATOM_LEN(d->buffer); /* Get all children atoms */ while(count < atom_size) { /* Size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Process sub-atom */ if(ATOM_CHECK(d->buffer, "meta") == 0) { /* Parse "meta" atom */ demux_mp4_parse_meta(d); } else { /* Ignore other sub-atoms */ stream_seek(d->stream, size, SEEK_CUR); } count += size; } /* Finish atom reading */ stream_seek(d->stream, atom_size-count, SEEK_CUR); }
int stringtable_get(atom_t cl, char buf[MAX_ENTRY_SIZE]) { assert(ATOM_VALID(cl)); memcpy(buf,ATOM_PTR(cl),ATOM_LEN(cl)); return ATOM_LEN(cl); }
void dump_table(void) { for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) { atom_t a = htable[i].atom; if(ATOM_VALID(a)) { printf("%p %u: ",ATOM_PTR(a),ATOM_LEN(a)); fwrite(ATOM_PTR(a),1,ATOM_LEN(a),stdout); fwrite("\n",1,1,stdout); } } }
void dump_to_file(void) { FILE *file = fopen("atom.dump","w"); for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) { atom_t a = htable[i].atom; if(ATOM_VALID(a)) { fprintf(file,"%u:",ATOM_LEN(a)); print_quoted(file, ATOM_PTR(a),ATOM_LEN(a)); fwrite("\n",1,1,file); } } }
static void demux_mp4_parse_txt(struct demux *d, char **str) { unsigned long pos = 0; unsigned long count; unsigned long size; unsigned long len; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); size -= 8; /* Check sub-atom */ if(ATOM_CHECK(d->buffer, "data") == 0) { /* Get string length */ len = ATOM_LEN(d->buffer) - 16; /* Skip version and flags */ stream_read(d->stream, 8); size -= 16; /* Free previous string */ if(*str != NULL) free(*str); /* Allocate new string */ *str = calloc(1, len + 1); if(*str != NULL) { /* Copy string */ while(len > 0) { count = d->buffer_size; if(count > len) count = len; count = stream_read(d->stream, count); memcpy(*str + pos, d->buffer, count); pos += count; len -= count; size -= count; } } } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_mdhd(struct demux *d) { unsigned long version; unsigned long size; /* Get size */ size = ATOM_LEN(d->buffer); /* Get Version */ stream_read(d->stream, 4); version = ATOM_READ32(d->buffer); /* Go to first entry */ size -= 12; if(version == 1) { stream_read(d->stream, 28); d->mdhd_time_scale = ATOM_READ32(&d->buffer[16]); d->mdhd_duration = ATOM_READ64(&d->buffer[20]); } else { stream_read(d->stream, 16); d->mdhd_time_scale = ATOM_READ32(&d->buffer[8]); d->mdhd_duration = ATOM_READ32(&d->buffer[12]); } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static atom_t add_string(unsigned char *cs, int len) { pthread_mutex_lock(&mutex_string); //printf("add_string(%c,%c,%i)\n",cs[0],cs[1],len); assert(len >= 0); assert(len < MAX_ENTRY_SIZE); assert(next_free_offset < CHUNK_SIZE); if(next_free_offset + 1 > CHUNK_SIZE - MAX_ENTRY_SIZE) { dieif(current_chunk >= NUM_CHUNKS - 1, "No more chunks"); current_chunk++; assert(!stringtable_chunks[current_chunk]); stringtable_chunks[current_chunk] = malloc(CHUNK_SIZE); dieif(!stringtable_chunks[current_chunk], "error alocating memory"); next_free_offset = 0; } memcpy(stringtable_chunks[current_chunk] + next_free_offset, cs, len); atom_t r = MAKE_ATOM(current_chunk, next_free_offset, len); assert(CHUNK_INDEX(r) == current_chunk); assert(CHUNK_OFFSET(r) == next_free_offset); assert(ATOM_PTR(r) == stringtable_chunks[current_chunk] + next_free_offset); assert(ATOM_LEN(r) == len); next_free_offset += len; assert(next_free_offset < CHUNK_SIZE); assert(current_chunk < NUM_CHUNKS); pthread_mutex_unlock(&mutex_string); return r; }
int stringtable_find(atom_t cl, unsigned char **res) { assert(ATOM_VALID(cl)); *res = ATOM_PTR(cl); return ATOM_LEN(cl); }
static void hash_insert(struct hentry x) { assert(ATOM_VALID(x.atom)); // fprintf(stderr,"hash_insert(%x,%p:%i,%x,%x,[%x,%x]", x.atom, ATOM_PTR(x.atom), ATOM_LEN(x.atom), x.hashes[0], x.hashes[1],HASH_INDEX(0,x.hashes[0]),HASH_INDEX(1,x.hashes[1])); assert(!atom_exists(x.atom)); assert(!item_exists(ATOM_PTR(x.atom),ATOM_LEN(x.atom))); atom_t start = x.atom; for(int loop = 0; loop < DEPTH_LIMIT;loop++) { for(int i = 0; i < CUCKOO_HASHES; i++) { // int e = HASH_INDEX(i,FHASH(x,i)); for(int j = 0; j < CUCKOO_BUCKETS; j++) { //#struct hentry *b = &(htable[(e + j) & HASHJMASK ]); //struct hentry *b = &htable[HASH_BUCKET(e,j)]; struct hentry *b = &htable[HASH_INDEX(i,FHASH(x,i) + j)]; if(!ATOM_VALID(b->atom)) { *b = x; // fprintf(stderr,")\n"); return; } struct hentry tb = x; x = *b; *b = tb; } // struct hentry *b = &(htable[e]); } if(x.atom == start) { break; } } grow_table(); // fprintf(stderr,"R"); return hash_insert(x); }
static void demux_mp4_parse_ilst(struct demux *d) { unsigned long atom_size; unsigned long count = 8; unsigned long size; /* Get size of current atom */ atom_size = ATOM_LEN(d->buffer); /* Get all children atoms */ while(count < atom_size) { /* Size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Process sub-atom */ if(ATOM_CHECK(d->buffer, "\251alb") == 0) demux_mp4_parse_txt(d, &d->album); else if(ATOM_CHECK(d->buffer, "\251ART") == 0) demux_mp4_parse_txt(d, &d->artist); else if(ATOM_CHECK(d->buffer, "\251cmt") == 0) demux_mp4_parse_txt(d, &d->comment); else if(ATOM_CHECK(d->buffer, "\251day") == 0) demux_mp4_parse_txt(d, &d->year); else if(ATOM_CHECK(d->buffer, "\251nam") == 0) demux_mp4_parse_txt(d, &d->title); else if(ATOM_CHECK(d->buffer, "\251gen") == 0) demux_mp4_parse_txt(d, &d->genre); else if(ATOM_CHECK(d->buffer, "trkn") == 0) demux_mp4_parse_trkn(d); else if(ATOM_CHECK(d->buffer, "gnre") == 0) demux_mp4_parse_gnre(d); else if(ATOM_CHECK(d->buffer, "covr") == 0) demux_mp4_parse_covr(d); else { /* Ignore other sub-atoms */ stream_seek(d->stream, size, SEEK_CUR); } count += size; } /* Finish atom reading */ stream_seek(d->stream, atom_size-count, SEEK_CUR); }
static int demux_mp4_parse_stsd(struct demux *d) { unsigned long atom_size; unsigned long size; int32_t count; int32_t i; int is_mp4a = 0; /* Get size */ atom_size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); /* Count entries */ count = ATOM_READ32(&d->buffer[4]); /* Go to first entry */ atom_size -= 16; /* Parse each entries */ for(i = 0; i < count; i++) { /* Get size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Process sub-atom */ if(ATOM_CHECK(d->buffer, "mp4a") == 0) { /* Parse "mp4a" atom */ demux_mp4_parse_mp4a(d); is_mp4a = 1; } else { stream_seek(d->stream, size, SEEK_CUR); } atom_size -= size; } /* Go to next atom */ stream_seek(d->stream, atom_size, SEEK_CUR); return is_mp4a; }
atom_t stringtable_lookup(unsigned char *cs, int len) { // static FILE *file = NULL; // if(!file) // file = fopen("atom.lookup","w"); // fprintf(file,"stringtable_lookup("); // print_quoted(file,cs,len); // fprintf(file,")\n"); pthread_mutex_lock(&mutex_hash); assert(len >= 0); assert(len < MAX_ENTRY_SIZE); hash_t h[CUCKOO_HASHES]; for(uint32_t i = 0; i < CUCKOO_HASHES; i++) { h[i] = hashlittle(cs,len,i); //int e = HASH_INDEX(i,h[i]); for(int j = 0; j < CUCKOO_BUCKETS; j++) { //struct hentry *b = &htable[(e + i) & HASHJMASK ]; //struct hentry *b = &htable[HASH_BUCKET(e,j)]; struct hentry *b = &htable[HASH_INDEX(i,h[i] + j)]; #if KEEP_HASH if (ATOM_VALID(b->atom) && h[i] == b->hashes[i] && len == ATOM_LEN(b->atom) && !memcmp(ATOM_PTR(b->atom),cs,len)) { pthread_mutex_unlock(&mutex_hash); return b->atom; } #else if (ATOM_VALID(b->atom) && len == ATOM_LEN(b->atom) && !memcmp(ATOM_PTR(b->atom),cs,len)) { pthread_mutex_unlock(&mutex_hash); return b->atom; } #endif } } atom_t na = add_string(cs,len); struct hentry hb; hb.atom = na; #if KEEP_HASH memcpy(hb.hashes,h,sizeof hb.hashes); #endif hash_insert(hb); pthread_mutex_unlock(&mutex_hash); return na; }
static bool item_exists(unsigned char *cs, int len) { for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) { atom_t a = htable[i].atom; if(ATOM_VALID(a)) { if(len == ATOM_LEN(a) && !memcmp(ATOM_PTR(a),cs,len)) return true; } } return false; }
static void demux_mp4_parse_gnre(struct demux *d) { unsigned long size; uint16_t genre; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); size -= 8; /* Check sub-atom */ if(ATOM_CHECK(d->buffer, "data") == 0 && ATOM_LEN(d->buffer) == 18) { /* Skip version and flags */ stream_read(d->stream, 8); size -= 16; /* Read genre index */ stream_read(d->stream, 2); genre = ATOM_READ16(d->buffer); /* Check genre */ if(genre > 0 || genre <= ID3v1_genres_count) { /* Free previous genre */ if(d->genre != NULL) free(d->genre); /* Copy genre */ d->genre = strdup(ID3v1_genres[genre-1]); } } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_stsc(struct demux *d) { unsigned long size; unsigned int i, j, count; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); /* Get chunk count */ d->stsc_entry_count = ATOM_READ32(&d->buffer[4]); /* Go to first entry */ size -= 16; /* Create chunk table */ d->stsc_first_chunk = malloc(d->stsc_entry_count * sizeof(int32_t)); d->stsc_samples_per_chunk = malloc(d->stsc_entry_count * sizeof(int32_t)); d->stsc_sample_desc_index = malloc(d->stsc_entry_count * sizeof(int32_t)); /* Fill table */ for(i = 0; i < d->stsc_entry_count; i += count) { count = d->buffer_size / 12; if(count > d->stsc_entry_count - i) count = d->stsc_entry_count - i; stream_read(d->stream, count*12); for(j = 0; j < count; j++) { d->stsc_first_chunk[i+j] = ATOM_READ32( &d->buffer[j*12]); d->stsc_samples_per_chunk[i+j] = ATOM_READ32( &d->buffer[4+(j*12)]); d->stsc_sample_desc_index[i+j] = ATOM_READ32( &d->buffer[8+(j*12)]); } } size -= 12 * d->stsc_entry_count; /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_stts(struct demux *d) { unsigned long size; unsigned int i, j, count; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); /* Get count */ d->stts_entry_count = ATOM_READ32(&d->buffer[4]); /* Go to first entry */ size -= 16; /* Create stts table */ d->stts_sample_count = malloc(d->stts_entry_count * sizeof(int32_t)); d->stts_sample_delta = malloc(d->stts_entry_count * sizeof(int32_t)); d->num_samples = 0; /* Fill table */ for(i = 0; i < d->stts_entry_count; i += count) { count = d->buffer_size / 8; if(count > d->stts_entry_count - i) count = d->stts_entry_count - i; stream_read(d->stream, count*8); for(j = 0; j < count; j++) { d->stts_sample_count[i+j] = ATOM_READ32( &d->buffer[j*8]); d->stts_sample_delta[i+j] = ATOM_READ32( &d->buffer[4+(j*8)]); d->num_samples += d->stts_sample_count[i+j]; } } size -= 8 * d->stts_entry_count; /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_stsz(struct demux *d) { unsigned long size; unsigned int i, j, count; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 12); /* Get sample size and sample count */ d->stsz_sample_size = ATOM_READ32(&d->buffer[4]); d->stsz_sample_count = ATOM_READ32(&d->buffer[8]); /* Go to first entry */ size -= 20; /* Create sample size table */ if(d->stsz_sample_size == 0) { d->stsz_table = malloc(d->stsz_sample_count * sizeof(int32_t)); /* Fill table */ for(i = 0; i < d->stsz_sample_count; i += count) { count = d->buffer_size / 4; if(count > d->stsz_sample_count - i) count = d->stsz_sample_count - i; stream_read(d->stream, count*4); for(j = 0; j < count; j++) { d->stsz_table[i+j] = ATOM_READ32( &d->buffer[j*4]); } } size -= 4 * d->stsz_sample_count; } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_stco(struct demux *d) { unsigned long size; unsigned int i, j, count; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); /* Get chunk offset count */ d->stco_entry_count = ATOM_READ32(&d->buffer[4]); /* Go to first entry */ size -= 16; /* Create chunk offset table */ d->stco_chunk_offset = malloc(d->stco_entry_count * sizeof(int32_t)); /* Fill table */ for(i = 0; i < d->stco_entry_count; i += count) { count = d->buffer_size / 4; if(count > d->stco_entry_count - i) count = d->stco_entry_count - i; stream_read(d->stream, count*4); for(j = 0; j < count; j++) { d->stco_chunk_offset[i+j] = ATOM_READ32( &d->buffer[j*4]); } } size -= 4 * d->stco_entry_count; /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
int demux_mp4_open(struct demux **demux, struct stream_handle *stream, unsigned long *samplerate, unsigned char *channels) { struct demux *d; const unsigned char *buffer; unsigned long mdat_pos = 0; unsigned long moov_pos = 0; unsigned long count = 0; unsigned long size; if(stream == NULL) return -1; /* Get stream buffer */ buffer = stream_get_buffer(stream); /* Read 8 first bytes for first atom header */ if(stream_read(stream, 8) != 8) return -1; /* Check "ftyp" atom */ if(ATOM_CHECK(buffer, "ftyp") != 0) return -1; size = ATOM_LEN(buffer); count = size; /* Allocate demux data structure */ *demux = malloc(sizeof(struct demux)); if(*demux == NULL) return -1; d = *demux; /* Init demux structure */ memset(d, 0, sizeof(struct demux)); d->stream = stream; d->buffer = buffer; d->buffer_size = stream_get_buffer_size(stream); d->size = stream_get_size(stream); /* Seek to next atom and get next atom header */ stream_seek(d->stream, size, SEEK_CUR); /* Read all atom until "mdat" */ while(count < d->size) { /* Get size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Process sub-atom */ if(ATOM_CHECK(d->buffer, "moov") == 0) { /* Process "moov" */ demux_mp4_parse_moov(d); moov_pos = count; } else { if(ATOM_CHECK(d->buffer, "mdat") == 0) { mdat_pos = count; if(moov_pos > 0) break; } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); } /* Update read bytes count */ count += size; } /* Check if a valid mp4 file and have found a mp4a track */ if(mdat_pos == 0 || d->track_found == 0) return -1; /* Go to first frame */ if(d->stsz_sample_size != 0) d->cur_sample_size = d->stsz_sample_size; else d->cur_sample_size = d->stsz_table[0]; d->cur_sample = 0; d->cur_chunk_sample = 0; d->cur_chunk_idx = 0; d->cur_chunk = 0; d->cur_offset = d->stco_chunk_offset[0]; /* Fill format */ d->format.samplerate = d->mp4a_samplerate; d->format.channels = d->mp4a_channel_count; d->format.bitrate = d->esds_avg_bitrate / 1000; d->format.title = d->title; d->format.artist = d->artist; d->format.album = d->album; d->format.comment = d->comment; d->format.genre = d->genre; d->format.track = d->track; d->format.total_track = d->total_track; if(d->year != NULL) d->format.year = strtol(d->year, NULL, 10); d->format.picture.data = d->pic; d->format.picture.mime = d->pic_mime; d->format.picture.size = d->pic_len; /* Calculate stream duration */ if(d->mdhd_time_scale != 0) d->format.length = d->mdhd_duration / d->mdhd_time_scale; /* Update samplerate and channels */ *samplerate = d->mp4a_samplerate; *channels = d->mp4a_channel_count; return 0; }
static int demux_mp4_parse_esds(struct demux *d) { unsigned long atom_size; unsigned char tag; /* Get size */ atom_size = ATOM_LEN(d->buffer); /* Skip version and flags */ stream_seek(d->stream, 12, SEEK_CUR); atom_size -= 12; /* Check ES_DescrTag */ stream_read(d->stream, 1); tag = d->buffer[0]; atom_size--; if(tag == 0x03) { /* Read length */ if(demux_mp4_read_len(d, &atom_size) < 20) goto end; /* Skip 3 bytes */ stream_read(d->stream, 3); atom_size -= 3; } else { /* Skip 2 bytes */ stream_read(d->stream, 2); atom_size -= 2; } /* Check DecoderConfigDescrTab */ stream_read(d->stream, 1); atom_size--; if(d->buffer[0] != 0x04) goto end; /* Read length */ if(demux_mp4_read_len(d, &atom_size) < 13) goto end; /* Get esds properties */ stream_read(d->stream, 14); atom_size -= 14; d->esds_audio_type = d->buffer[0]; d->esds_max_bitrate = ATOM_READ32(&d->buffer[5]); d->esds_avg_bitrate = ATOM_READ32(&d->buffer[9]); /* Check DecSpecificInfoTag */ if(d->buffer[13] != 0x05) goto end; /* Read length */ d->esds_size = demux_mp4_read_len(d, &atom_size); /* Copy decoder config */ if(d->esds_buffer) free(d->esds_buffer); d->esds_buffer = malloc(d->esds_size); if(d->esds_buffer != NULL) { /* Read coder config */ stream_read(d->stream, d->esds_size); memcpy(d->esds_buffer, d->buffer, d->esds_size); } else { d->esds_size = 0; } end: /* Go to next atom */ stream_seek(d->stream, atom_size, SEEK_CUR); return 0; }
static void demux_mp4_parse_covr(struct demux *d) { unsigned long pos = 0; unsigned long count; unsigned long size; unsigned long len; uint32_t flags; /* Get size */ size = ATOM_LEN(d->buffer); stream_read(d->stream, 8); size -= 8; /* Check sub-atom */ if(ATOM_CHECK(d->buffer, "data") == 0) { /* Get length */ len = ATOM_LEN(d->buffer) - 16; /* Get flags for type */ stream_read(d->stream, 8); flags = ATOM_READ32(d->buffer); size -= 8; /* Free previous buffer */ if(d->pic != NULL) free(d->pic); if(d->pic_mime != NULL) free(d->pic_mime); d->pic_mime = NULL; d->pic_len = 0; /* Allocate new buffer */ d->pic = malloc(len); if(d->pic != NULL) { /* Copy length */ d->pic_len = len; /* Copy data */ while(len > 0) { count = d->buffer_size; if(count > len) count = len; count = stream_read(d->stream, count); memcpy(d->pic + pos, d->buffer, count); pos += count; len -= count; size -= count; } /* Generate mime */ if(flags == 13) d->pic_mime = strdup("image/jpeg"); else if(flags == 14) d->pic_mime = strdup("image/png"); } } /* Go to next atom */ stream_seek(d->stream, size, SEEK_CUR); }
static void demux_mp4_parse_track(struct demux *d) { unsigned long atom_size; unsigned long count = 8; unsigned long size; int is_mp4a = 0; /* Get size of current atom */ atom_size = ATOM_LEN(d->buffer); /* Get all children atoms */ while(count < atom_size) { /* Size of sub-atom */ stream_read(d->stream, 8); size = ATOM_LEN(d->buffer); /* Process sub-atom */ if(ATOM_CHECK(d->buffer, "mdia") == 0 || ATOM_CHECK(d->buffer, "minf") == 0 || ATOM_CHECK(d->buffer, "stbl") == 0) { /* Parse sub-atom: get mdia -> minf -> stbl */ demux_mp4_parse_track(d); } else if(ATOM_CHECK(d->buffer, "mdhd") == 0) { /* Parse "mdhd" atom */ demux_mp4_parse_mdhd(d); } else if(ATOM_CHECK(d->buffer, "stsd") == 0) { /* Parse "stsd" atom */ is_mp4a = demux_mp4_parse_stsd(d); } else if(ATOM_CHECK(d->buffer, "stts") == 0 && is_mp4a) { /* Parse "stts" atom */ demux_mp4_parse_stts(d); } else if(ATOM_CHECK(d->buffer, "stsc") == 0 && is_mp4a) { /* Parse "stsc" atom */ demux_mp4_parse_stsc(d); } else if(ATOM_CHECK(d->buffer, "stsz") == 0 && is_mp4a) { /* Parse "stsz" atom */ demux_mp4_parse_stsz(d); } else if(ATOM_CHECK(d->buffer, "stco") == 0 && is_mp4a) { /* Parse "stco" atom */ demux_mp4_parse_stco(d); } else { /* Ignore other sub-atoms */ stream_seek(d->stream, size, SEEK_CUR); } count += size; } /* Finish atom reading */ stream_seek(d->stream, atom_size-count, SEEK_CUR); }