ref: c0ae72652fc9619e8b1e8f365ab977614179779a
dir: /common/mp4ff/mp4ff.c/
#include "mp4ff.h" #if 0 int mp4ff_make_streamable(mp4_callback_t *in_path, mp4_callback_t *out_path) { mp4ff_t file, *old_file, new_file; int moov_exists = 0, mdat_exists = 0, result, atoms = 1; long mdat_start, mdat_size; mp4ff_atom_t leaf_atom; long moov_length; mp4ff_init(&file); /* find the moov atom in the old file */ if(!(file.stream = FOPEN(in_path, _T("rb")))) { //perror("mp4ff_make_streamable"); return 1; } file.total_length = file.stream->get_length(); /* get the locations of moov and mdat atoms */ do { /*printf("%x\n", mp4ff_position(&file)); */ result = mp4ff_atom_read_header(&file, &leaf_atom); if(!result) { if(mp4ff_atom_is(&leaf_atom, "moov")) { moov_exists = atoms; moov_length = leaf_atom.size; } else if(mp4ff_atom_is(&leaf_atom, "mdat")) { mdat_start = mp4ff_position(&file) - HEADER_LENGTH; mdat_size = leaf_atom.size; mdat_exists = atoms; } mp4ff_atom_skip(&file, &leaf_atom); atoms++; } }while(!result && mp4ff_position(&file) < file.total_length); if(!moov_exists) { printf("mp4ff_make_streamable: no moov atom\n"); return 1; } if(!mdat_exists) { printf("mp4ff_make_streamable: no mdat atom\n"); return 1; } /* copy the old file to the new file */ if(moov_exists && mdat_exists) { /* moov wasn't the first atom */ if(moov_exists > 1) { char *buffer; long buf_size = 1000000; result = 0; /* read the header proper */ if(!(old_file = mp4ff_open(in_path, 1, 0, 0))) { return 1; } mp4ff_shift_offsets(&(old_file->moov), moov_length); /* open the output file */ if(!(new_file.stream = FOPEN(out_path, _T("wb")))) { //perror("mp4ff_make_streamable"); result = 1; } else { /* set up some flags */ new_file.wr = 1; new_file.rd = 0; mp4ff_write_moov(&new_file, &(old_file->moov)); mp4ff_set_position(old_file, mdat_start); if(!(buffer = calloc(1, buf_size))) { result = 1; printf("mp4ff_make_streamable: out of memory\n"); } else { while(mp4ff_position(old_file) < mdat_start + mdat_size && !result) { if(mp4ff_position(old_file) + buf_size > mdat_start + mdat_size) buf_size = mdat_start + mdat_size - mp4ff_position(old_file); if(!mp4ff_read_data(old_file, buffer, buf_size)) result = 1; if(!result) { if(!mp4ff_write_data(&new_file, buffer, buf_size)) result = 1; } } free(buffer); } fclose(new_file.stream); } mp4ff_close(old_file); } else { printf("mp4ff_make_streamable: header already at 0 offset\n"); return 0; } } return 0; } #endif int mp4ff_set_time_scale(mp4ff_t *file, int time_scale) { file->moov.mvhd.time_scale = time_scale; } int mp4ff_set_copyright(mp4ff_t *file, char *string) { mp4ff_set_udta_string(&(file->moov.udta.copyright), &(file->moov.udta.copyright_len), string); } int mp4ff_set_name(mp4ff_t *file, char *string) { mp4ff_set_udta_string(&(file->moov.udta.name), &(file->moov.udta.name_len), string); } int mp4ff_set_info(mp4ff_t *file, char *string) { mp4ff_set_udta_string(&(file->moov.udta.info), &(file->moov.udta.info_len), string); } int mp4ff_get_time_scale(mp4ff_t *file) { return file->moov.mvhd.time_scale; } char* mp4ff_get_copyright(mp4ff_t *file) { return file->moov.udta.copyright; } char* mp4ff_get_name(mp4ff_t *file) { return file->moov.udta.name; } char* mp4ff_get_info(mp4ff_t *file) { return file->moov.udta.info; } int mp4ff_get_iod_audio_profile_level(mp4ff_t *file) { return file->moov.iods.audioProfileId; } int mp4ff_set_iod_audio_profile_level(mp4ff_t *file, int id) { mp4ff_iods_set_audio_profile(&file->moov.iods, id); } int mp4ff_get_iod_video_profile_level(mp4ff_t *file) { return file->moov.iods.videoProfileId; } int mp4ff_set_iod_video_profile_level(mp4ff_t *file, int id) { mp4ff_iods_set_video_profile(&file->moov.iods, id); } int mp4ff_video_tracks(mp4ff_t *file) { int i, result = 0; for(i = 0; i < file->moov.total_tracks; i++) { if(file->moov.trak[i]->mdia.minf.is_video) result++; } return result; } int mp4ff_audio_tracks(mp4ff_t *file) { int i, result = 0; mp4ff_minf_t *minf; for(i = 0; i < file->moov.total_tracks; i++) { minf = &(file->moov.trak[i]->mdia.minf); if(minf->is_audio) result++; } return result; } int mp4ff_set_audio(mp4ff_t *file, int channels, long sample_rate, int bits, int sample_size, int time_scale, int sample_duration, char *compressor) { int i, j; mp4ff_trak_t *trak; /* delete any existing tracks */ for(i = 0; i < file->total_atracks; i++) { mp4ff_delete_audio_map(&(file->atracks[i])); mp4ff_delete_trak(&(file->moov), file->atracks[i].track); } free(file->atracks); file->atracks = NULL; file->total_atracks = 0; if(channels) { #if 0 /* Fake the bits parameter for some formats. */ if(mp4ff_match_32(compressor, mp4ff_ULAW) || mp4ff_match_32(compressor, mp4ff_IMA4)) bits = 16; #endif file->atracks = (mp4ff_audio_map_t*) calloc(1, sizeof(mp4ff_audio_map_t)); trak = mp4ff_add_track(&(file->moov)); mp4ff_trak_init_audio(file, trak, channels, sample_rate, bits, sample_size, time_scale, sample_duration, compressor); mp4ff_init_audio_map(&(file->atracks[0]), trak); file->atracks[file->total_atracks].track = trak; file->atracks[file->total_atracks].channels = channels; file->atracks[file->total_atracks].current_position = 0; file->atracks[file->total_atracks].current_chunk = 1; file->total_atracks++; } return 1; /* Return the number of tracks created */ } int mp4ff_set_video(mp4ff_t *file, int tracks, int frame_w, int frame_h, float frame_rate, int time_scale, char *compressor) { int i, j; mp4ff_trak_t *trak; /* delete any existing tracks */ for(i = 0; i < file->total_vtracks; i++) { mp4ff_delete_video_map(&(file->vtracks[i])); mp4ff_delete_trak(&(file->moov), file->vtracks[i].track); } free(file->vtracks); file->vtracks = NULL; file->total_vtracks = 0; if (tracks > 0) { file->total_vtracks = tracks; file->vtracks = (mp4ff_video_map_t*)calloc(1, sizeof(mp4ff_video_map_t) * file->total_vtracks); for(i = 0; i < tracks; i++) { trak = mp4ff_add_track(&(file->moov)); mp4ff_trak_init_video(file, trak, frame_w, frame_h, frame_rate, time_scale, compressor); mp4ff_init_video_map(&(file->vtracks[i]), trak); } } return 0; } int mp4ff_set_framerate(mp4ff_t *file, float framerate) { int i; int new_time_scale, new_sample_duration; new_time_scale = mp4ff_get_timescale(framerate); new_sample_duration = (int)((float)new_time_scale / framerate + 0.5); for(i = 0; i < file->total_vtracks; i++) { file->vtracks[i].track->mdia.mdhd.time_scale = new_time_scale; file->vtracks[i].track->mdia.minf.stbl.stts.table[0].sample_duration = new_sample_duration; } } mp4ff_trak_t* mp4ff_add_track(mp4ff_moov_t *moov) { mp4ff_trak_t *trak; trak = moov->trak[moov->total_tracks] = calloc(1, sizeof(mp4ff_trak_t)); mp4ff_trak_init(trak); trak->tkhd.track_id = moov->mvhd.next_track_id; moov->mvhd.next_track_id++; moov->total_tracks++; return trak; } /* ============================= Initialization functions */ int mp4ff_init(mp4ff_t *file) { memset(file, 0, sizeof(mp4ff_t)); mp4ff_mdat_init(&(file->mdat)); mp4ff_moov_init(&(file->moov)); return 0; } int mp4ff_delete(mp4ff_t *file) { int i; if(file->total_atracks) { for(i = 0; i < file->total_atracks; i++) mp4ff_delete_audio_map(&(file->atracks[i])); free(file->atracks); } if(file->total_vtracks) { for(i = 0; i < file->total_vtracks; i++) mp4ff_delete_video_map(&(file->vtracks[i])); free(file->vtracks); } file->total_atracks = 0; file->total_vtracks = 0; mp4ff_moov_delete(&(file->moov)); mp4ff_mdat_delete(&(file->mdat)); return 0; } /* =============================== Optimization functions */ int mp4ff_get_timescale(float frame_rate) { int timescale = 600; /* Encode the 29.97, 23.976, 59.94 framerates as per DV freaks */ if(frame_rate - (int)frame_rate != 0) timescale = (int)(frame_rate * 1001 + 0.5); else if((600 / frame_rate) - (int)(600 / frame_rate) != 0) timescale = (int)(frame_rate * 100 + 0.5); return timescale; } int mp4ff_seek_end(mp4ff_t *file) { mp4ff_set_position(file, file->mdat.size + file->mdat.start); /*printf("mp4ff_seek_end %ld\n", file->mdat.size + file->mdat.start); */ mp4ff_update_positions(file); return 0; } int mp4ff_seek_start(mp4ff_t *file) { mp4ff_set_position(file, file->mdat.start + HEADER_LENGTH); mp4ff_update_positions(file); return 0; } long mp4ff_audio_length(mp4ff_t *file, int track) { if(file->total_atracks > 0) return mp4ff_track_samples(file, file->atracks[track].track); return 0; } long mp4ff_video_length(mp4ff_t *file, int track) { /*printf("mp4ff_video_length %d %d\n", mp4ff_track_samples(file, file->vtracks[track].track), track); */ if(file->total_vtracks > 0) return mp4ff_track_samples(file, file->vtracks[track].track); return 0; } long mp4ff_audio_position(mp4ff_t *file, int track) { return file->atracks[track].current_position; } long mp4ff_video_position(mp4ff_t *file, int track) { return file->vtracks[track].current_position; } int mp4ff_update_positions(mp4ff_t *file) { /* Used for routines that change the positions of all tracks, like */ /* seek_end and seek_start but not for routines that reposition one track, like */ /* set_audio_position. */ long mdat_offset = mp4ff_position(file) - file->mdat.start; long sample, chunk, chunk_offset; int i; if(file->total_atracks) { sample = mp4ff_offset_to_sample(file->atracks[0].track, mdat_offset); chunk = mp4ff_offset_to_chunk(&chunk_offset, file->atracks[0].track, mdat_offset); for(i = 0; i < file->total_atracks; i++) { file->atracks[i].current_position = sample; file->atracks[i].current_chunk = chunk; } } if(file->total_vtracks) { sample = mp4ff_offset_to_sample(file->vtracks[0].track, mdat_offset); chunk = mp4ff_offset_to_chunk(&chunk_offset, file->vtracks[0].track, mdat_offset); for(i = 0; i < file->total_vtracks; i++) { file->vtracks[i].current_position = sample; file->vtracks[i].current_chunk = chunk; } } return 0; } int mp4ff_set_audio_position(mp4ff_t *file, long sample, int track) { long offset, chunk_sample, chunk; mp4ff_trak_t *trak; if(file->total_atracks) { trak = file->atracks[track].track; file->atracks[track].current_position = sample; mp4ff_chunk_of_sample(&chunk_sample, &chunk, trak, sample); file->atracks[track].current_chunk = chunk; offset = mp4ff_sample_to_offset(trak, sample); mp4ff_set_position(file, offset); /*mp4ff_update_positions(file); */ } return 0; } int mp4ff_set_video_position(mp4ff_t *file, long frame, int track) { long offset, chunk_sample, chunk; mp4ff_trak_t *trak; if(file->total_vtracks) { trak = file->vtracks[track].track; file->vtracks[track].current_position = frame; mp4ff_chunk_of_sample(&chunk_sample, &chunk, trak, frame); file->vtracks[track].current_chunk = chunk; offset = mp4ff_sample_to_offset(trak, frame); mp4ff_set_position(file, offset); /*mp4ff_update_positions(file); */ } return 0; } int mp4ff_has_audio(mp4ff_t *file) { if(mp4ff_audio_tracks(file)) return 1; return 0; } long mp4ff_audio_sample_rate(mp4ff_t *file, int track) { if(file->total_atracks) return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_rate; return 0; } int mp4ff_audio_bits(mp4ff_t *file, int track) { if(file->total_atracks) return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_size; return 0; } int mp4ff_audio_time_scale(mp4ff_t *file, int track) { if(file->total_atracks) { return file->atracks[track].track->mdia.mdhd.time_scale; } return 0; } int mp4ff_audio_sample_duration(mp4ff_t *file, int track) { if(file->total_atracks) { return file->atracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration; } return 0; } char* mp4ff_audio_compressor(mp4ff_t *file, int track) { if (file->atracks[track].track == NULL) return (NULL); return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].format; } int mp4ff_track_channels(mp4ff_t *file, int track) { if(track < file->total_atracks) return file->atracks[track].channels; return 0; } int mp4ff_channel_location(mp4ff_t *file, int *mp4ff_track, int *mp4ff_channel, int channel) { int current_channel = 0, current_track = 0; *mp4ff_channel = 0; *mp4ff_track = 0; for(current_channel = 0, current_track = 0; current_track < file->total_atracks; ) { if(channel >= current_channel) { *mp4ff_channel = channel - current_channel; *mp4ff_track = current_track; } current_channel += file->atracks[current_track].channels; current_track++; } return 0; } int mp4ff_has_video(mp4ff_t *file) { if(mp4ff_video_tracks(file)) return 1; return 0; } int mp4ff_video_width(mp4ff_t *file, int track) { if(file->total_vtracks) { int width = file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].width; if (width) { return width; } return file->vtracks[track].track->tkhd.track_width; } return 0; } int mp4ff_video_height(mp4ff_t *file, int track) { if(file->total_vtracks) { int height = file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].height; if (height) { return height; } return file->vtracks[track].track->tkhd.track_height; } return 0; } int mp4ff_video_depth(mp4ff_t *file, int track) { if(file->total_vtracks) return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].depth; return 0; } int mp4ff_set_depth(mp4ff_t *file, int depth, int track) { int i; for(i = 0; i < file->total_vtracks; i++) { file->vtracks[i].track->mdia.minf.stbl.stsd.table[0].depth = depth; } } float mp4ff_video_frame_rate(mp4ff_t *file, int track) { float ret = 0; int num = 0; if(file->total_vtracks) { ret = file->vtracks[track].track->mdia.mdhd.time_scale; if (file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration == 0) num = 1; ret /= file->vtracks[track].track->mdia.minf.stbl.stts.table[num].sample_duration; } return ret; } char* mp4ff_video_compressor(mp4ff_t *file, int track) { if (file->vtracks[track].track == NULL) return (NULL); return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].format; } int mp4ff_video_time_scale(mp4ff_t *file, int track) { if(file->total_vtracks) { return file->vtracks[track].track->mdia.mdhd.time_scale; } return 0; } int mp4ff_video_frame_time(mp4ff_t *file, int track, long frame, long *start, int *duration) { mp4ff_stts_t *stts; int i; long f; if (file->total_vtracks == 0) { return 0; } stts = &(file->vtracks[track].track->mdia.minf.stbl.stts); if (frame < file->last_frame) { /* we need to reset our cached values */ file->last_frame = 0; file->last_start = 0; file->last_stts_index = 0; } i = file->last_stts_index; f = file->last_frame; *start = file->last_start; while (i < stts->total_entries) { if (f + stts->table[i].sample_count <= frame) { *start += stts->table[i].sample_duration * stts->table[i].sample_count; f += stts->table[i].sample_count; i++; } else { /* cache the results for future use */ file->last_stts_index = i; file->last_frame = f; file->last_start = *start; *start += stts->table[i].sample_duration * (frame - f); *duration = stts->table[i].sample_duration; return 1; } } /* error */ return 0; } int mp4ff_get_mp4_video_decoder_config(mp4ff_t *file, int track, unsigned char** ppBuf, int* pBufSize) { mp4ff_esds_t* esds; if (!file->total_vtracks) { return 0; } esds = &file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].esds; return mp4ff_esds_get_decoder_config(esds, ppBuf, pBufSize); } int mp4ff_set_mp4_video_decoder_config(mp4ff_t *file, int track, unsigned char* pBuf, int bufSize) { mp4ff_esds_t* esds; if (!file->total_vtracks) { return 0; } esds = &file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].esds; return mp4ff_esds_set_decoder_config(esds, pBuf, bufSize); } int mp4ff_get_mp4_audio_decoder_config(mp4ff_t *file, int track, unsigned char** ppBuf, int* pBufSize) { mp4ff_esds_t* esds; if (!file->total_atracks) { return 0; } esds = &file->atracks[track].track->mdia.minf.stbl.stsd.table[0].esds; return mp4ff_esds_get_decoder_config(esds, ppBuf, pBufSize); } int mp4ff_set_mp4_audio_decoder_config(mp4ff_t *file, int track, unsigned char* pBuf, int bufSize) { mp4ff_esds_t* esds; if (!file->total_atracks) { return 0; } esds = &file->atracks[track].track->mdia.minf.stbl.stsd.table[0].esds; return mp4ff_esds_set_decoder_config(esds, pBuf, bufSize); } long mp4ff_samples_to_bytes(mp4ff_trak_t *track, long samples) { return samples * track->mdia.minf.stbl.stsd.table[0].channels * track->mdia.minf.stbl.stsd.table[0].sample_size / 8; } int mp4ff_write_audio(mp4ff_t *file, char *audio_buffer, long samples, int track) { long offset; int result; long bytes; /* Defeat 32 bit file size limit. */ if(mp4ff_test_position(file)) return 1; /* write chunk for 1 track */ bytes = samples * mp4ff_audio_bits(file, track) / 8 * file->atracks[track].channels; offset = mp4ff_position(file); result = mp4ff_write_data(file, audio_buffer, bytes); if(result) result = 0; else result = 1; /* defeat fwrite's return */ mp4ff_update_tables(file, file->atracks[track].track, offset, file->atracks[track].current_chunk, file->atracks[track].current_position, samples, 0, 0, 0, 0); file->atracks[track].current_position += samples; file->atracks[track].current_chunk++; return result; } int mp4ff_write_audio_frame(mp4ff_t *file, unsigned char *audio_buffer, long bytes, int track) { long offset = mp4ff_position(file); int result = 0; /* Defeat 32 bit file size limit. */ if(mp4ff_test_position(file)) return 1; result = mp4ff_write_data(file, audio_buffer, bytes); if(result) result = 0; else result = 1; mp4ff_update_tables(file, file->atracks[track].track, offset, file->atracks[track].current_chunk, file->atracks[track].current_position, 1, bytes, 0, 0, 0); file->atracks[track].current_position += 1; file->atracks[track].current_chunk++; return result; } int mp4ff_write_video_frame(mp4ff_t *file, unsigned char *video_buffer, long bytes, int track, unsigned char isKeyFrame, long duration, long renderingOffset) { long offset = mp4ff_position(file); int result = 0; /* Defeat 32 bit file size limit. */ if(mp4ff_test_position(file)) return 1; result = mp4ff_write_data(file, video_buffer, bytes); if(result) result = 0; else result = 1; mp4ff_update_tables(file, file->vtracks[track].track, offset, file->vtracks[track].current_chunk, file->vtracks[track].current_position, 1, bytes, duration, isKeyFrame, renderingOffset); file->vtracks[track].current_position += 1; file->vtracks[track].current_chunk++; return result; } mp4_callback_t* mp4ff_get_fd(mp4ff_t *file) { if (file->stream->get_position() != file->file_position) file->stream->seek(file->file_position); return file->stream; } int mp4ff_write_frame_init(mp4ff_t *file, int track) { if(file->stream->get_position() != file->file_position) file->stream->seek(file->file_position); file->offset = mp4ff_position(file); return 0; } int mp4ff_write_frame_end(mp4ff_t *file, int track) { long bytes; file->file_position = file->stream->get_position(); bytes = mp4ff_position(file) - file->offset; mp4ff_update_tables(file, file->vtracks[track].track, file->offset, file->vtracks[track].current_chunk, file->vtracks[track].current_position, 1, bytes, 0, 0, 0); file->vtracks[track].current_position += 1; file->vtracks[track].current_chunk++; return 0; } int mp4ff_write_audio_init(mp4ff_t *file, int track) { return mp4ff_write_frame_init(file, track); } int mp4ff_write_audio_end(mp4ff_t *file, int track, long samples) { long bytes; file->file_position = file->stream->get_position(); bytes = mp4ff_position(file) - file->offset; mp4ff_update_tables(file, file->atracks[track].track, file->offset, file->atracks[track].current_chunk, file->atracks[track].current_position, samples, bytes, 0, 0, 0); file->atracks[track].current_position += samples; file->atracks[track].current_chunk++; return 0; } long mp4ff_read_audio(mp4ff_t *file, char *audio_buffer, long samples, int track) { long chunk_sample, chunk; int result = 1, track_num; mp4ff_trak_t *trak = file->atracks[track].track; long fragment_len, chunk_end; long position = file->atracks[track].current_position; long start = position, end = position + samples; long bytes, total_bytes = 0; long buffer_offset; mp4ff_chunk_of_sample(&chunk_sample, &chunk, trak, position); buffer_offset = 0; while(position < end && result) { mp4ff_set_audio_position(file, position, track); fragment_len = mp4ff_chunk_samples(trak, chunk); chunk_end = chunk_sample + fragment_len; fragment_len -= position - chunk_sample; if(position + fragment_len > chunk_end) fragment_len = chunk_end - position; if(position + fragment_len > end) fragment_len = end - position; bytes = mp4ff_samples_to_bytes(trak, fragment_len); result = mp4ff_read_data(file, &audio_buffer[buffer_offset], bytes); total_bytes += bytes; position += fragment_len; chunk_sample = position; buffer_offset += bytes; chunk++; } file->atracks[track].current_position = position; if(!result) return 0; return total_bytes; } int mp4ff_read_chunk(mp4ff_t *file, char *output, int track, long chunk, long byte_start, long byte_len) { mp4ff_set_position(file, mp4ff_chunk_to_offset(file->atracks[track].track, chunk) + byte_start); if(mp4ff_read_data(file, output, byte_len)) return 0; else return 1; } long mp4ff_frame_size(mp4ff_t *file, long frame, int track) { int bytes; mp4ff_trak_t *trak = file->vtracks[track].track; if(trak->mdia.minf.stbl.stsz.sample_size) { bytes = trak->mdia.minf.stbl.stsz.sample_size; } else { bytes = trak->mdia.minf.stbl.stsz.table[frame].size; } return bytes; } long mp4ff_get_sample_duration(mp4ff_t *file, long frame, int track) { int i, ci = 0, co = 0; mp4ff_trak_t *trak = file->atracks[track].track; for (i = 0; i < trak->mdia.minf.stbl.stts.total_entries; i++) { int j; for (j = 0; j < trak->mdia.minf.stbl.stts.table[i].sample_count; j++) { if (co == frame) return trak->mdia.minf.stbl.stts.table[ci].sample_duration; co++; } ci++; } } long mp4ff_audio_frame_size(mp4ff_t *file, long frame, int track) { int bytes; mp4ff_trak_t *trak = file->atracks[track].track; if(trak->mdia.minf.stbl.stsz.sample_size) { bytes = trak->mdia.minf.stbl.stsz.sample_size; } else { bytes = trak->mdia.minf.stbl.stsz.table[frame].size; } return bytes; } long mp4ff_read_audio_frame(mp4ff_t *file, unsigned char *audio_buffer, int maxBytes, int track) { long bytes; int result = 0; mp4ff_trak_t *trak = file->atracks[track].track; bytes = mp4ff_audio_frame_size(file, file->atracks[track].current_position, track); if (bytes > maxBytes) { return -bytes; } mp4ff_set_audio_position(file, file->atracks[track].current_position, track); result = mp4ff_read_data(file, audio_buffer, bytes); file->atracks[track].current_position++; if (!result) return 0; return bytes; } long mp4ff_read_frame(mp4ff_t *file, unsigned char *video_buffer, int track) { long bytes; int result = 0; mp4ff_trak_t *trak = file->vtracks[track].track; bytes = mp4ff_frame_size(file, file->vtracks[track].current_position, track); if(!file->vtracks[track].frames_cached) { mp4ff_set_video_position(file, file->vtracks[track].current_position, track); result = mp4ff_read_data(file, video_buffer, bytes); } else { int i; unsigned char *cached_frame; if(file->vtracks[track].current_position >= file->vtracks[track].frames_cached) result = 1; if(!result) { cached_frame = file->vtracks[track].frame_cache[file->vtracks[track].current_position]; for(i = 0; i < bytes; i++) video_buffer[i] = cached_frame[i]; } } file->vtracks[track].current_position++; if(!result) return 0; return bytes; } int mp4ff_read_frame_init(mp4ff_t *file, int track) { mp4ff_trak_t *trak = file->vtracks[track].track; mp4ff_set_video_position(file, file->vtracks[track].current_position, track); if(file->stream->get_position() != file->file_position) file->stream->seek(file->file_position); return 0; } int mp4ff_read_frame_end(mp4ff_t *file, int track) { file->file_position = file->stream->get_position(); file->vtracks[track].current_position++; return 0; } int mp4ff_init_video_map(mp4ff_video_map_t *vtrack, mp4ff_trak_t *trak) { vtrack->track = trak; vtrack->current_position = 0; vtrack->current_chunk = 1; vtrack->frame_cache = 0; vtrack->frames_cached = 0; return 0; } int mp4ff_delete_video_map(mp4ff_video_map_t *vtrack) { int i; if(vtrack->frames_cached) { for(i = 0; i < vtrack->frames_cached; i++) { free(vtrack->frame_cache[i]); } free(vtrack->frame_cache); vtrack->frames_cached = 0; } return 0; } int mp4ff_init_audio_map(mp4ff_audio_map_t *atrack, mp4ff_trak_t *trak) { atrack->track = trak; atrack->channels = trak->mdia.minf.stbl.stsd.table[0].channels; atrack->current_position = 0; atrack->current_chunk = 1; return 0; } int mp4ff_delete_audio_map(mp4ff_audio_map_t *atrack) { int i; return 0; } int mp4ff_read_info(mp4ff_t *file) { int result = 0, found_moov = 0; int i, j, k, m, channel, trak_channel, track; long start_position = mp4ff_position(file); mp4ff_atom_t leaf_atom; mp4ff_trak_t *trak; mp4ff_set_position(file, 0); do { result = mp4ff_atom_read_header(file, &leaf_atom); if(!result) { if(mp4ff_atom_is(&leaf_atom, "mdat")) { mp4ff_read_mdat(file, &(file->mdat), &leaf_atom); } else if(mp4ff_atom_is(&leaf_atom, "moov")) { mp4ff_read_moov(file, &(file->moov), &leaf_atom); found_moov = 1; } else { mp4ff_atom_skip(file, &leaf_atom); } } }while(!result && mp4ff_position(file) < file->total_length); /* go back to the original position */ mp4ff_set_position(file, start_position); if(found_moov) { /* get tables for all the different tracks */ file->total_atracks = mp4ff_audio_tracks(file); file->atracks = (mp4ff_audio_map_t*)calloc(1, sizeof(mp4ff_audio_map_t) * file->total_atracks); for(i = 0, track = 0; i < file->total_atracks; i++) { while(!file->moov.trak[track]->mdia.minf.is_audio) track++; mp4ff_init_audio_map(&(file->atracks[i]), file->moov.trak[track]); } file->total_vtracks = mp4ff_video_tracks(file); file->vtracks = (mp4ff_video_map_t*)calloc(1, sizeof(mp4ff_video_map_t) * file->total_vtracks); for(track = 0, i = 0; i < file->total_vtracks; i++) { while(!file->moov.trak[track]->mdia.minf.is_video) track++; mp4ff_init_video_map(&(file->vtracks[i]), file->moov.trak[track]); } } if(found_moov) return 0; else return 1; } int mp4ff_dump(mp4ff_t *file) { printf("mp4ff_dump\n"); printf("movie data\n"); printf(" size %ld\n", file->mdat.size); printf(" start %ld\n", file->mdat.start); mp4ff_moov_dump(&(file->moov)); return 0; } /* ================================== Entry points ========================== */ int mp4ff_check_sig(mp4_callback_t *path) { mp4ff_t file; mp4ff_atom_t leaf_atom; int result1 = 0, result2 = 0; mp4ff_init(&file); file.stream = path; file.total_length = file.stream->get_length(); do { result1 = mp4ff_atom_read_header(&file, &leaf_atom); if(!result1) { /* just want the "moov" atom */ if(mp4ff_atom_is(&leaf_atom, "moov")) { result2 = 1; } else mp4ff_atom_skip(&file, &leaf_atom); } }while(!result1 && !result2 && mp4ff_position(&file) < file.total_length); mp4ff_delete(&file); return result2; } mp4ff_t* mp4ff_open(mp4_callback_t *callbacks, int rd, int wr, int append) { mp4ff_t *new_file = malloc(sizeof(mp4ff_t)); int exists = 0; mp4ff_init(new_file); new_file->stream = callbacks; new_file->wr = wr; new_file->rd = rd; new_file->mdat.start = 0; if(rd /*&& exists*/) { new_file->total_length = new_file->stream->get_length(); if(mp4ff_read_info(new_file)) { mp4ff_close(new_file); new_file = 0; } } if(wr) { if(!exists || !append) { /* start the data atom */ mp4ff_write_int32(new_file, 0); mp4ff_write_char32(new_file, "mdat"); } else { mp4ff_set_position(new_file, new_file->mdat.start + new_file->mdat.size); new_file->stream->seek(new_file->mdat.start + new_file->mdat.size); } } return new_file; } int mp4ff_write(mp4ff_t *file) { int result = -1; if(!file->wr) { return result; } mp4ff_write_mdat(file, &(file->mdat)); mp4ff_write_moov(file, &(file->moov)); return result; } int mp4ff_destroy(mp4ff_t *file) { mp4ff_delete(file); free(file); return 0; } int mp4ff_close(mp4ff_t *file) { int result = 0; if(file->wr) { mp4ff_write_mdat(file, &(file->mdat)); mp4ff_write_moov(file, &(file->moov)); } mp4ff_delete(file); free(file); return result; }