#include "libmpeg3.h" #include "mpeg3protos.h" #include #include #define MAX(a, b) ((a) > (b) ? (a) : (b)) mpeg3_t* mpeg3_new(char *path) { int i; mpeg3_t *file = (mpeg3_t*)calloc(1, sizeof(mpeg3_t)); file->cpus = 1; file->fs = mpeg3_new_fs(path); file->have_mmx = mpeg3_mmx_test(); file->demuxer = mpeg3_new_demuxer(file, 0, 0, -1); return file; } int mpeg3_delete(mpeg3_t *file) { int i; for(i = 0; i < file->total_vstreams; i++) mpeg3_delete_vtrack(file, file->vtrack[i]); for(i = 0; i < file->total_astreams; i++) mpeg3_delete_atrack(file, file->atrack[i]); mpeg3_delete_fs(file->fs); mpeg3_delete_demuxer(file->demuxer); free(file); } int mpeg3_check_sig(char *path) { mpeg3_fs_t *fs; unsigned int bits; char *ext; int result = 0; fs = mpeg3_new_fs(path); if(mpeg3io_open_file(fs)) { /* File not found */ return mpeg3_delete_fs(fs); } bits = mpeg3io_read_int32(fs); /* Test header */ if(bits == MPEG3_TOC_PREFIX || bits == MPEG3_TOC_PREFIXLOWER) { result = 1; } else if((((bits >> 24) & 0xff) == MPEG3_SYNC_BYTE) || (bits == MPEG3_PACK_START_CODE) || ((bits & 0xfff00000) == 0xfff00000) || (bits == MPEG3_SEQUENCE_START_CODE) || (bits == MPEG3_PICTURE_START_CODE) || (((bits & 0xffff0000) >> 16) == MPEG3_AC3_START_CODE) || ((bits >> 8) == MPEG3_ID3_PREFIX) || (bits == MPEG3_RIFF_CODE)) { result = 1; ext = strrchr(path, '.'); if(ext) { /* Test file extension. */ if(strncasecmp(ext, ".mp2", 4) && // strncasecmp(ext, ".mp3", 4) && strncasecmp(ext, ".m1v", 4) && strncasecmp(ext, ".m2v", 4) && strncasecmp(ext, ".m2s", 4) && strncasecmp(ext, ".mpg", 4) && strncasecmp(ext, ".vob", 4) && strncasecmp(ext, ".mpeg", 4) && strncasecmp(ext, ".ac3", 4)) result = 0; } } mpeg3io_close_file(fs); mpeg3_delete_fs(fs); return result; } mpeg3_t* mpeg3_open_copy(char *path, mpeg3_t *old_file) { mpeg3_t *file = 0; unsigned int bits; int i, done; /* Initialize the file structure */ file = mpeg3_new(path); /* Need to perform authentication before reading a single byte. */ if(mpeg3io_open_file(file->fs)) { mpeg3_delete(file); return 0; } /* =============================== Create the title objects ========================= */ bits = mpeg3io_read_int32(file->fs); if(bits == MPEG3_TOC_PREFIX || bits == MPEG3_TOC_PREFIXLOWER) /* TOCV */ { /* Table of contents for another file */ if(mpeg3_read_toc(file)) { mpeg3_delete(file); return 0; } mpeg3io_close_file(file->fs); } else if(((bits >> 24) & 0xff) == MPEG3_SYNC_BYTE) { /* Transport stream */ file->packet_size = MPEG3_TS_PACKET_SIZE; file->is_transport_stream = 1; } else if(bits == MPEG3_PACK_START_CODE) { /* Program stream */ file->packet_size = MPEG3_DVD_PACKET_SIZE; file->is_program_stream = 1; } else if((bits & 0xfff00000) == 0xfff00000 || ((bits >> 8) == MPEG3_ID3_PREFIX) || (bits == MPEG3_RIFF_CODE)) { /* MPEG Audio only */ file->packet_size = MPEG3_DVD_PACKET_SIZE; file->has_audio = 1; file->is_audio_stream = 1; } else if(bits == MPEG3_SEQUENCE_START_CODE || bits == MPEG3_PICTURE_START_CODE) { /* Video only */ file->packet_size = MPEG3_DVD_PACKET_SIZE; file->is_video_stream = 1; } else if(((bits & 0xffff0000) >> 16) == MPEG3_AC3_START_CODE) { /* AC3 Audio only */ file->packet_size = MPEG3_DVD_PACKET_SIZE; file->has_audio = 1; file->is_audio_stream = 1; } else { /* file->packet_size = MPEG3_DVD_PACKET_SIZE; */ /* file->is_audio_stream = 1; */ mpeg3_delete(file); fprintf(stderr, "mpeg3_open: not an MPEG 2 stream\n"); return 0; } /* Create title */ /* Copy timecodes from an old demuxer */ if(old_file && mpeg3_get_demuxer(old_file)) { mpeg3demux_copy_titles(file->demuxer, mpeg3_get_demuxer(old_file)); } else /* Start from scratch */ if(!file->demuxer->total_titles) { mpeg3demux_create_title(file->demuxer, 0, 0); } /* =============================== Get title information ========================= */ if(file->is_transport_stream || file->is_program_stream) { /* Create video tracks */ /* Video must be created before audio because audio uses the video timecode */ /* to get its length. */ for(i = 0; i < MPEG3_MAX_STREAMS; i++) { if(file->demuxer->vstream_table[i]) { file->vtrack[file->total_vstreams] = mpeg3_new_vtrack(file, i, file->demuxer); if(file->vtrack[file->total_vstreams]) file->total_vstreams++; } } /* Create audio tracks */ for(i = 0; i < MPEG3_MAX_STREAMS; i++) { if(file->demuxer->astream_table[i]) { file->atrack[file->total_astreams] = mpeg3_new_atrack(file, i, file->demuxer->astream_table[i], file->demuxer); if(file->atrack[file->total_astreams]) file->total_astreams++; } } } else if(file->is_video_stream) { /* Create video tracks */ file->vtrack[0] = mpeg3_new_vtrack(file, -1, file->demuxer); if(file->vtrack[0]) file->total_vstreams++; } else if(file->is_audio_stream) { /* Create audio tracks */ file->atrack[0] = mpeg3_new_atrack(file, -1, AUDIO_UNKNOWN, file->demuxer); if(file->atrack[0]) file->total_astreams++; } if(file->total_vstreams) file->has_video = 1; if(file->total_astreams) file->has_audio = 1; mpeg3io_close_file(file->fs); return file; } mpeg3_t* mpeg3_open(char *path) { return mpeg3_open_copy(path, 0); } int mpeg3_close(mpeg3_t *file) { /* File is closed in the same procedure it is opened in. */ mpeg3_delete(file); return 0; } int mpeg3_set_cpus(mpeg3_t *file, int cpus) { int i; file->cpus = cpus; for(i = 0; i < file->total_vstreams; i++) mpeg3video_set_cpus(file->vtrack[i]->video, cpus); return 0; } int mpeg3_set_mmx(mpeg3_t *file, int use_mmx) { int i; file->have_mmx = use_mmx; for(i = 0; i < file->total_vstreams; i++) mpeg3video_set_mmx(file->vtrack[i]->video, use_mmx); return 0; } int mpeg3_generate_toc(FILE *output, char *path, int timecode_search, int print_streams) { mpeg3_t *file = mpeg3_open(path); mpeg3_demuxer_t *demuxer; int i; if(file) { fprintf(output, "TOCVERSION 2\n" "PATH: %s\n", path); demuxer = mpeg3_new_demuxer(file, 0, 0, -1); mpeg3demux_create_title(demuxer, timecode_search, output); /* Just print the first title's streams */ if(print_streams) mpeg3demux_print_streams(demuxer, output); fprintf(output, "SIZE: %ld\n", demuxer->titles[demuxer->current_title]->total_bytes); fprintf(output, "PACKETSIZE: %ld\n", demuxer->packet_size); mpeg3demux_print_timecodes(demuxer->titles[demuxer->current_title], output); mpeg3_delete_demuxer(demuxer); mpeg3_close(file); return 0; } return 1; } int mpeg3_read_toc(mpeg3_t *file) { char string[MPEG3_STRLEN]; int number1; /* Test version number */ file->is_program_stream = 1; mpeg3io_seek(file->fs, 0); fscanf(file->fs->fd, "%s %d", string, &number1); if(number1 > 2 || number1 < 2) return 1; /* Read titles */ mpeg3demux_read_titles(file->demuxer); return 0; } int mpeg3_has_audio(mpeg3_t *file) { return file->has_audio; } int mpeg3_total_astreams(mpeg3_t *file) { return file->total_astreams; } int mpeg3_audio_channels(mpeg3_t *file, int stream) { if(file->has_audio) return file->atrack[stream]->channels; return -1; } int mpeg3_sample_rate(mpeg3_t *file, int stream) { if(file->has_audio) return file->atrack[stream]->sample_rate; return -1; } long mpeg3_get_sample(mpeg3_t *file, int stream) { if(file->has_audio) return file->atrack[stream]->current_position; return -1; } int mpeg3_set_sample(mpeg3_t *file, long sample, int stream) { if(file->has_audio) { file->atrack[stream]->current_position = sample; mpeg3audio_seek_sample(file->atrack[stream]->audio, sample); return 0; } return -1; } long mpeg3_audio_samples(mpeg3_t *file, int stream) { if(file->has_audio) return file->atrack[stream]->total_samples; return -1; } int mpeg3_has_video(mpeg3_t *file) { return file->has_video; } int mpeg3_total_vstreams(mpeg3_t *file) { return file->total_vstreams; } int mpeg3_video_width(mpeg3_t *file, int stream) { if(file->has_video) return file->vtrack[stream]->width; return -1; } int mpeg3_video_height(mpeg3_t *file, int stream) { if(file->has_video) return file->vtrack[stream]->height; return -1; } float mpeg3_frame_rate(mpeg3_t *file, int stream) { if(file->has_video) return file->vtrack[stream]->frame_rate; return -1; } long mpeg3_video_frames(mpeg3_t *file, int stream) { if(file->has_video) return file->vtrack[stream]->total_frames; return -1; } long mpeg3_get_frame(mpeg3_t *file, int stream) { if(file->has_video) return file->vtrack[stream]->current_position; return -1; } int mpeg3_set_frame(mpeg3_t *file, long frame, int stream) { if(file->has_video) { file->vtrack[stream]->current_position = frame; mpeg3video_seek_frame(file->vtrack[stream]->video, frame); return 0; } return -1; } int mpeg3_seek_percentage(mpeg3_t *file, double percentage) { int i; for(i = 0; i < file->total_astreams; i++) { mpeg3audio_seek_percentage(file->atrack[i]->audio, percentage); } for(i = 0; i < file->total_vstreams; i++) { mpeg3video_seek_percentage(file->vtrack[i]->video, percentage); } return 0; } int mpeg3_previous_frame(mpeg3_t *file, int stream) { file->last_type_read = 2; file->last_stream_read = stream; if(file->has_video) return mpeg3video_previous_frame(file->vtrack[stream]->video); } double mpeg3_tell_percentage(mpeg3_t *file) { double percent = 0; if(file->last_type_read == 1) { percent = mpeg3demux_tell_percentage(file->atrack[file->last_stream_read]->demuxer); } if(file->last_type_read == 2) { percent = mpeg3demux_tell_percentage(file->vtrack[file->last_stream_read]->demuxer); } return percent; } double mpeg3_get_time(mpeg3_t *file) { double atime = 0, vtime = 0; if(file->is_transport_stream || file->is_program_stream) { /* Timecode only available in transport stream */ if(file->last_type_read == 1) { atime = mpeg3demux_get_time(file->atrack[file->last_stream_read]->demuxer); } else if(file->last_type_read == 2) { vtime = mpeg3demux_get_time(file->vtrack[file->last_stream_read]->demuxer); } } else { /* Use percentage and total time */ if(file->has_audio) { atime = mpeg3demux_tell_percentage(file->atrack[0]->demuxer) * mpeg3_audio_samples(file, 0) / mpeg3_sample_rate(file, 0); } if(file->has_video) { vtime = mpeg3demux_tell_percentage(file->vtrack[0]->demuxer) * mpeg3_video_frames(file, 0) / mpeg3_frame_rate(file, 0); } } return MAX(atime, vtime); } int mpeg3_end_of_audio(mpeg3_t *file, int stream) { int result = 0; result = mpeg3demux_eof(file->atrack[stream]->demuxer); return result; } int mpeg3_end_of_video(mpeg3_t *file, int stream) { int result = 0; result = mpeg3demux_eof(file->vtrack[stream]->demuxer); return result; } int mpeg3_read_frame(mpeg3_t *file, unsigned char **output_rows, int in_x, int in_y, int in_w, int in_h, int out_w, int out_h, int color_model, int stream) { int result = -1; if(file->has_video) { result = mpeg3video_read_frame(file->vtrack[stream]->video, file->vtrack[stream]->current_position, output_rows, in_x, in_y, in_w, in_h, out_w, out_h, color_model); file->last_type_read = 2; file->last_stream_read = stream; file->vtrack[stream]->current_position++; } return result; } int mpeg3_drop_frames(mpeg3_t *file, long frames, int stream) { int result = -1; if(file->has_video) { result = mpeg3video_drop_frames(file->vtrack[stream]->video, frames); if(frames > 0) file->vtrack[stream]->current_position += frames; file->last_type_read = 2; file->last_stream_read = stream; } return result; } int mpeg3_read_yuvframe(mpeg3_t *file, char *y_output, char *u_output, char *v_output, int in_x, int in_y, int in_w, int in_h, int stream) { int result = -1; //printf("mpeg3_read_yuvframe 1 %d %d\n", mpeg3demux_tell(file->vtrack[stream]->demuxer), mpeg3demuxer_total_bytes(file->vtrack[stream]->demuxer)); if(file->has_video) { result = mpeg3video_read_yuvframe(file->vtrack[stream]->video, file->vtrack[stream]->current_position, y_output, u_output, v_output, in_x, in_y, in_w, in_h); file->last_type_read = 2; file->last_stream_read = stream; file->vtrack[stream]->current_position++; } //printf("mpeg3_read_yuvframe 2 %d %d\n", mpeg3demux_tell(file->vtrack[stream]->demuxer), mpeg3demuxer_total_bytes(file->vtrack[stream]->demuxer)); return result; } int mpeg3_read_audio(mpeg3_t *file, mpeg3_real_t *output_f, short *output_i, int sampleSpacing, int channel, long samples, int stream) { int result = -1; //printf("mpeg3_read_audio 1 %d %d\n", mpeg3demux_tell(file->atrack[stream]->demuxer), mpeg3demuxer_total_bytes(file->atrack[stream]->demuxer)); if(file->has_audio) { result = mpeg3audio_decode_audio(file->atrack[stream]->audio, output_f, output_i, sampleSpacing, channel, file->atrack[stream]->current_position, samples); file->last_type_read = 1; file->last_stream_read = stream; file->atrack[stream]->current_position += samples; } //printf("mpeg3_read_audio 2 %d %d\n", mpeg3demux_tell(file->atrack[stream]->demuxer), mpeg3demuxer_total_bytes(file->atrack[stream]->demuxer)); return result; } int mpeg3_reread_audio(mpeg3_t *file, mpeg3_real_t *output_f, short *output_i, int sampleSpacing, int channel, long samples, int stream) { if(file->has_audio) { mpeg3_set_sample(file, file->atrack[stream]->current_position - samples, stream); file->last_type_read = 1; file->last_stream_read = stream; return mpeg3_read_audio(file, output_f, output_i, sampleSpacing, channel, samples, stream); } return -1; } int mpeg3_read_audio_chunk(mpeg3_t *file, unsigned char *output, long *size, long max_size, int stream) { int result = 0; if(file->has_audio) { result = mpeg3audio_read_raw(file->atrack[stream]->audio, output, size, max_size); file->last_type_read = 1; file->last_stream_read = stream; } return result; } int mpeg3_read_video_chunk(mpeg3_t *file, unsigned char *output, long *size, long max_size, int stream) { int result = 0; if(file->has_video) { result = mpeg3video_read_raw(file->vtrack[stream]->video, output, size, max_size); file->last_type_read = 2; file->last_stream_read = stream; } return result; }