// New version. // Very basic table of contents utility since most of the time it's going to be // built inside a graphical program. #if 1 #include "libmpeg3.h" #include #include #include #include int main(int argc, char *argv[]) { int i, j, l; char *src = 0, *dst = 0; int verbose = 0; if(argc < 3) { fprintf(stderr, "Table of contents generator version %d.%d.%d\n" "Create a table of contents for a DVD or mpeg stream.\n" "Usage: mpeg3toc \n" "\n" "-v Print tracking information\n" "\n" "The path should be absolute unless you plan\n" "to always run your movie editor from the same directory\n" "as the filename. For renderfarms the filesystem prefix\n" "should be / and the movie directory mounted under the same\n" "directory on each node.\n\n" "Example: mpeg3toc -v /cdrom/video_ts/vts_01_0.ifo titanic.toc\n", mpeg3_major(), mpeg3_minor(), mpeg3_release()); exit(1); } for(i = 1; i < argc; i++) { if(!strcmp(argv[i], "-v")) { verbose = 1; } else if(argv[i][0] == '-') { fprintf(stderr, "Unrecognized command %s\n", argv[i]); exit(1); } else if(!src) { src = argv[i]; } else if(!dst) { dst = argv[i]; } else { fprintf(stderr, "Ignoring argument \"%s\"\n", argv[i]); } } if(!src) { fprintf(stderr, "source path not supplied.\n"); exit(1); } if(!dst) { fprintf(stderr, "destination path not supplied.\n"); exit(1); } int64_t total_bytes; mpeg3_t *file = mpeg3_start_toc(src, dst, &total_bytes); if(!file) exit(1); struct timeval new_time; struct timeval prev_time; struct timeval start_time; struct timeval current_time; gettimeofday(&prev_time, 0); gettimeofday(&start_time, 0); while(1) { int64_t bytes_processed = 0; mpeg3_do_toc(file, &bytes_processed); gettimeofday(&new_time, 0); if(verbose && new_time.tv_sec - prev_time.tv_sec > 1) { gettimeofday(¤t_time, 0); int64_t elapsed_seconds = current_time.tv_sec - start_time.tv_sec; int64_t total_seconds = elapsed_seconds * total_bytes / bytes_processed; int64_t eta = total_seconds - elapsed_seconds; fprintf(stderr, "%lld%% ETA: %dm%ds \r", bytes_processed * 100 / total_bytes, eta / 60, eta % 60); fflush(stdout); prev_time = new_time; } if(bytes_processed >= total_bytes) break; } mpeg3_stop_toc(file); gettimeofday(¤t_time, 0); int64_t elapsed = current_time.tv_sec - start_time.tv_sec; if(verbose) { fprintf(stderr, "%dm%ds elapsed \n", elapsed / 60, elapsed % 60); } return 0; } #else #include "libmpeg3.h" #include "mpeg3protos.h" #include #include #include #include #include /* * Generate table of frame and sample offsets for editing. */ #define INIT_VECTORS(data, size, allocation, tracks) \ { \ int k; \ data = calloc(1, sizeof(int64_t*) * tracks); \ size = calloc(1, sizeof(int*) * tracks); \ allocation = calloc(1, sizeof(int*) * tracks); \ \ for(k = 0; k < tracks; k++) \ { \ allocation[k] = 0x100; \ data[k] = calloc(1, sizeof(int64_t) * allocation[k]); \ } \ } #if 1 #define APPEND_VECTOR(data, size, allocation, track, value) \ { \ uint64_t **track_data = &(data)[(track)]; \ int *track_allocation = &(allocation)[(track)]; \ int *track_size = &(size)[(track)]; \ \ if(!(*track_data) || (*track_allocation) <= (*track_size)) \ { \ int64_t *new_data = calloc(1, sizeof(int64_t) * (*track_allocation) * 2); \ \ if((*track_data)) \ { \ memcpy(new_data, (*track_data), sizeof(int64_t) * (*track_allocation)); \ free((*track_data)); \ } \ (*track_allocation) *= 2; \ (*track_data) = new_data; \ } \ \ (*track_data)[(*track_size)++] = (value); \ } #else #define APPEND_VECTOR(data, size, allocation, track, value) \ ; #endif #define DELETE_VECTORS(data, size, allocation, tracks) \ { \ int k; \ for(k = 0; k < tracks; k++) if(data[k]) free(data[k]); \ free(data); \ } #define PUT_INT32(x) \ { \ if(MPEG3_LITTLE_ENDIAN) \ { \ fputc(((unsigned char*)&x)[3], output); \ fputc(((unsigned char*)&x)[2], output); \ fputc(((unsigned char*)&x)[1], output); \ fputc(((unsigned char*)&x)[0], output); \ } \ else \ { \ fputc(((unsigned char*)&x)[0], output); \ fputc(((unsigned char*)&x)[1], output); \ fputc(((unsigned char*)&x)[2], output); \ fputc(((unsigned char*)&x)[3], output); \ } \ } #define PUT_INT64(x) \ { \ if(MPEG3_LITTLE_ENDIAN) \ { \ fputc(((unsigned char*)&x)[7], output); \ fputc(((unsigned char*)&x)[6], output); \ fputc(((unsigned char*)&x)[5], output); \ fputc(((unsigned char*)&x)[4], output); \ fputc(((unsigned char*)&x)[3], output); \ fputc(((unsigned char*)&x)[2], output); \ fputc(((unsigned char*)&x)[1], output); \ fputc(((unsigned char*)&x)[0], output); \ } \ else \ { \ fwrite(&x, 1, 8, output); \ } \ } int main(int argc, char *argv[]) { struct stat st; int i, j, l; char *src = 0, *dst = 0; int astream_override = -1; if(argc < 3) { fprintf(stderr, "Create a table of contents for a DVD or mpeg stream.\n" " Usage: [-a audio streams] mpeg3toc \n" "\n" " -a override the number of audio streams to scan. Must be less than\n" "the total number of audio streams.\n" "\n" " The path should be absolute unless you plan\n" " to always run your movie editor from the same directory\n" " as the filename. For renderfarms the filesystem prefix\n" " should be / and the movie directory mounted under the same\n" " directory on each node.\n" "Example: mpeg3toc /cd2/video_ts/vts_01_0.ifo titanic.toc\n"); exit(1); } for(i = 1; i < argc; i++) { if(!strcmp(argv[i], "-a")) { if(i < argc - 1) { astream_override = atoi(argv[i + 1]); if(astream_override < 0) { fprintf(stderr, "Total audio streams may not be negative\n"); exit(1); } else { fprintf(stderr, "Using first %d audio streams.\n", astream_override); } i++; } else { fprintf(stderr, "-a requires an argument.\n"); exit(1); } } else if(!src) { src = argv[i]; } else if(!dst) { dst = argv[i]; } else { fprintf(stderr, "Ignoring argument \"%s\"\n", argv[i]); } } if(!src) { fprintf(stderr, "source path not supplied.\n"); exit(1); } if(!dst) { fprintf(stderr, "source path not supplied.\n"); exit(1); } stat(src, &st); if(!st.st_size) { fprintf(stderr, "%s is 0 length. Skipping\n", src); } else { int64_t size; int vtracks; int atracks; mpeg3_t *input; uint64_t **frame_offsets; uint64_t **keyframe_numbers; uint64_t **sample_offsets; int *total_frame_offsets; int *total_sample_offsets; int *total_keyframe_numbers; int *frame_offset_allocation; int *sample_offset_allocation; int *keyframe_numbers_allocation; int done = 0; double sample_rate; double frame_rate; FILE *output; int total_samples = 0; int total_frames = 0; int rewind = 1; //printf(__FUNCTION__ " 1\n"); input = mpeg3_open(src); //printf(__FUNCTION__ " 2\n"); vtracks = mpeg3_total_vstreams(input); atracks = mpeg3_total_astreams(input); if(astream_override >= 0) atracks = astream_override; if(atracks) sample_rate = mpeg3_sample_rate(input, 0); if(vtracks) frame_rate = mpeg3_frame_rate(input, 0); //printf(__FUNCTION__ " 3\n"); // Handle titles INIT_VECTORS(frame_offsets, total_frame_offsets, frame_offset_allocation, vtracks); INIT_VECTORS(keyframe_numbers, total_keyframe_numbers, keyframe_numbers_allocation, vtracks); INIT_VECTORS(sample_offsets, total_sample_offsets, sample_offset_allocation, atracks); //printf(__FUNCTION__ " 4\n"); while(!done) { int sample_count = MPEG3_AUDIO_CHUNKSIZE; int frame_count; int have_audio = 0; int have_video = 0; int64_t title_number = 0; // Audio section // Store current position and read sample_count from each atrack for(j = 0; j < atracks; j++) { //printf(__FUNCTION__ " 3 %d\n", total_sample_offsets[j]); if(rewind) { mpeg3_demuxer_t *demuxer = input->atrack[j]->demuxer; mpeg3demux_seek_byte(demuxer, 0); //demuxer->dump = 1; } if(!mpeg3_end_of_audio(input, j)) { // Don't want to maintain separate vectors for offset and title. int64_t position = mpeg3demux_tell_byte(input->atrack[j]->demuxer); int64_t result; if(position < MPEG3_IO_SIZE) position = MPEG3_IO_SIZE; result = position; have_audio = 1; APPEND_VECTOR(sample_offsets, total_sample_offsets, sample_offset_allocation, j, result); //printf(__FUNCTION__ " 6 %d\n", j); // Throw away samples mpeg3_read_audio(input, 0, 0, 0, sample_count, /* Number of samples to decode */ j); printf("\n%lld %lld\n", mpeg3demux_tell_byte(input->atrack[j]->demuxer), mpeg3demux_movie_size(input->atrack[j]->demuxer)); //printf(__FUNCTION__ " 7 %d\n", total_sample_offsets[j]); } if(j == atracks - 1) { total_samples += sample_count; fprintf(stderr, "Audio: title=%lld total_samples=%d ", title_number, total_samples); } } //printf(__FUNCTION__ " 8\n"); if(have_audio) { frame_count = (int)((double)total_samples / sample_rate * frame_rate + 0.5) - total_frames; } else { frame_count = 1; } //printf(__FUNCTION__ " 9 %d\n", vtracks); // Video section for(j = 0; j < vtracks; j++) { mpeg3video_t *video = input->vtrack[j]->video; mpeg3_demuxer_t *demuxer = input->vtrack[j]->demuxer; if(rewind) { mpeg3demux_seek_byte(demuxer, 0); } for(l = 0; l < frame_count; l++) { if(!mpeg3_end_of_video(input, j)) { // Transport streams always return one packet after the start of the frame. int64_t position = mpeg3demux_tell_byte(demuxer) - 2048; int64_t result; uint32_t code = 0; int got_top = 0; int got_bottom = 0; int got_keyframe = 0; int fields = 0; if(position < MPEG3_IO_SIZE) position = MPEG3_IO_SIZE; result = position; have_video = 1; //printf("%llx\n", position); // Store offset of every frame in table APPEND_VECTOR(frame_offsets, total_frame_offsets, frame_offset_allocation, j, result); // Search for next frame start. if(total_frame_offsets[j] == 1) { // Assume first frame is an I-frame and put its number in the keyframe number // table. APPEND_VECTOR(keyframe_numbers, total_keyframe_numbers, keyframe_numbers_allocation, j, 0); // Skip the first frame. mpeg3video_get_header(video, 0); video->current_repeat += 100; } // Get next frame do { mpeg3video_get_header(video, 0); video->current_repeat += 100; if(video->pict_struct == TOP_FIELD) { got_top = 1; } else if(video->pict_struct == BOTTOM_FIELD) { got_bottom = 1; } else if(video->pict_struct == FRAME_PICTURE) { got_top = got_bottom = 1; } fields++; // The way we do it, the I frames have the top field but both the I frame and // subsequent P frame make the keyframe. if(video->pict_type == I_TYPE) got_keyframe = 1; }while(!mpeg3_end_of_video(input, j) && !got_bottom && total_frame_offsets[j] > 1); // Store number of a keyframe in the keyframe number table if(got_keyframe) APPEND_VECTOR(keyframe_numbers, total_keyframe_numbers, keyframe_numbers_allocation, j, total_frame_offsets[j] - 1); if(j == vtracks - 1 && l == frame_count - 1) { total_frames += frame_count; fprintf(stderr, "Video: title=%lld total_frames=%d ", title_number, total_frames); } } } } if(!have_audio && !have_video) done = 1; fprintf(stderr, "\r"); fflush(stderr); /* * if(total_frames > 10000) * { * printf("\n"); * return 0; * } */ rewind = 0; } output = fopen(dst, "w"); // Write file type fputc('T', output); fputc('O', output); fputc('C', output); fputc(' ', output); // Write version fputc(MPEG3_TOC_VERSION, output); if(input->is_program_stream) { fputc(FILE_TYPE_PROGRAM, output); } else if(input->is_transport_stream) { fputc(FILE_TYPE_TRANSPORT, output); } else if(input->is_audio_stream) { fputc(FILE_TYPE_AUDIO, output); } else if(input->is_video_stream) { fputc(FILE_TYPE_VIDEO, output); } // Write stream ID's // Only program and transport streams have these for(i = 0; i < MPEG3_MAX_STREAMS; i++) { if(input->demuxer->astream_table[i]) { fputc(STREAM_AUDIO, output); PUT_INT32(i); PUT_INT32(input->demuxer->astream_table[i]); } if(input->demuxer->vstream_table[i]) { fputc(STREAM_VIDEO, output); PUT_INT32(i); PUT_INT32(input->demuxer->vstream_table[i]); } } // Write titles for(i = 0; i < input->demuxer->total_titles; i++) { mpeg3_title_t *title = input->demuxer->titles[i]; // Path fputc(TITLE_PATH, output); fprintf(output, title->fs->path); fputc(0, output); // Total bytes PUT_INT64(title->total_bytes); // Byte offsets of cells PUT_INT32(input->demuxer->titles[i]->cell_table_size); for(j = 0; j < title->cell_table_size; j++) { mpeg3_cell_t *cell = &title->cell_table[j]; PUT_INT64(cell->title_start); PUT_INT64(cell->title_end); PUT_INT64(cell->program_start); PUT_INT64(cell->program_end); PUT_INT32(cell->program); } } fputc(ATRACK_COUNT, output); PUT_INT32(atracks); fputc(VTRACK_COUNT, output); PUT_INT32(vtracks); // Audio streams for(j = 0; j < atracks; j++) { int channels = mpeg3_audio_channels(input, j); PUT_INT32(channels); PUT_INT32(total_sample_offsets[j]); for(i = 0; i < total_sample_offsets[j]; i++) { PUT_INT64(sample_offsets[j][i]); //printf("Audio: offset=%016llx\n", sample_offsets[j][i]); } } // Video streams for(j = 0; j < vtracks; j++) { PUT_INT32(total_frame_offsets[j]); for(i = 0; i < total_frame_offsets[j]; i++) { PUT_INT64(frame_offsets[j][i]); //printf("Video: offset=%016llx\n", frame_offsets[j][i]); } PUT_INT32(total_keyframe_numbers[j]); for(i = 0; i < total_keyframe_numbers[j]; i++) { PUT_INT64(keyframe_numbers[j][i]); //printf("Video: keyframe=%lld\n", keyframe_numbers[j][i]); } } DELETE_VECTORS(frame_offsets, total_frame_offsets, frame_offset_allocation, vtracks); DELETE_VECTORS(keyframe_numbers, total_keyframe_numbers, keyframe_numbers_allocation, vtracks); DELETE_VECTORS(sample_offsets, total_sample_offsets, sample_offset_allocation, atracks); mpeg3_close(input); fclose(output); } return 0; } #endif