2 // Very basic table of contents utility since most of the time it's going to be
3 // built inside a graphical program.
18 int main(int argc
, char *argv
[])
21 char *src
= 0, *dst
= 0;
26 fprintf(stderr
, "Table of contents generator version %d.%d.%d\n"
27 "Create a table of contents for a DVD or mpeg stream.\n"
28 "Usage: mpeg3toc <path> <output>\n"
30 "-v Print tracking information\n"
32 "The path should be absolute unless you plan\n"
33 "to always run your movie editor from the same directory\n"
34 "as the filename. For renderfarms the filesystem prefix\n"
35 "should be / and the movie directory mounted under the same\n"
36 "directory on each node.\n\n"
37 "Example: mpeg3toc -v /cdrom/video_ts/vts_01_0.ifo titanic.toc\n",
44 for(i
= 1; i
< argc
; i
++)
46 if(!strcmp(argv
[i
], "-v"))
53 fprintf(stderr
, "Unrecognized command %s\n", argv
[i
]);
68 fprintf(stderr
, "Ignoring argument \"%s\"\n", argv
[i
]);
74 fprintf(stderr
, "source path not supplied.\n");
80 fprintf(stderr
, "destination path not supplied.\n");
87 mpeg3_t
*file
= mpeg3_start_toc(src
, dst
, &total_bytes
);
89 struct timeval new_time
;
90 struct timeval prev_time
;
91 struct timeval start_time
;
92 struct timeval current_time
;
93 gettimeofday(&prev_time
, 0);
94 gettimeofday(&start_time
, 0);
99 int64_t bytes_processed
= 0;
100 mpeg3_do_toc(file
, &bytes_processed
);
102 gettimeofday(&new_time
, 0);
103 if(verbose
&& new_time
.tv_sec
- prev_time
.tv_sec
> 1)
105 gettimeofday(¤t_time
, 0);
106 int64_t elapsed_seconds
= current_time
.tv_sec
- start_time
.tv_sec
;
107 int64_t total_seconds
= elapsed_seconds
* total_bytes
/ bytes_processed
;
108 int64_t eta
= total_seconds
- elapsed_seconds
;
109 fprintf(stderr
, "%lld%% ETA: %dm%ds \r",
110 bytes_processed
* 100 / total_bytes
,
114 prev_time
= new_time
;
117 if(bytes_processed
>= total_bytes
) break;
120 mpeg3_stop_toc(file
);
121 gettimeofday(¤t_time
, 0);
122 int64_t elapsed
= current_time
.tv_sec
- start_time
.tv_sec
;
125 fprintf(stderr
, "%dm%ds elapsed \n",
145 #include "libmpeg3.h"
146 #include "mpeg3protos.h"
152 #include <sys/stat.h>
156 * Generate table of frame and sample offsets for editing.
160 #define INIT_VECTORS(data, size, allocation, tracks) \
163 data = calloc(1, sizeof(int64_t*) * tracks); \
164 size = calloc(1, sizeof(int*) * tracks); \
165 allocation = calloc(1, sizeof(int*) * tracks); \
167 for(k = 0; k < tracks; k++) \
169 allocation[k] = 0x100; \
170 data[k] = calloc(1, sizeof(int64_t) * allocation[k]); \
175 #define APPEND_VECTOR(data, size, allocation, track, value) \
177 uint64_t **track_data = &(data)[(track)]; \
178 int *track_allocation = &(allocation)[(track)]; \
179 int *track_size = &(size)[(track)]; \
181 if(!(*track_data) || (*track_allocation) <= (*track_size)) \
183 int64_t *new_data = calloc(1, sizeof(int64_t) * (*track_allocation) * 2); \
187 memcpy(new_data, (*track_data), sizeof(int64_t) * (*track_allocation)); \
188 free((*track_data)); \
190 (*track_allocation) *= 2; \
191 (*track_data) = new_data; \
194 (*track_data)[(*track_size)++] = (value); \
197 #define APPEND_VECTOR(data, size, allocation, track, value) \
201 #define DELETE_VECTORS(data, size, allocation, tracks) \
204 for(k = 0; k < tracks; k++) if(data[k]) free(data[k]); \
213 #define PUT_INT32(x) \
215 if(MPEG3_LITTLE_ENDIAN) \
217 fputc(((unsigned char*)&x)[3], output); \
218 fputc(((unsigned char*)&x)[2], output); \
219 fputc(((unsigned char*)&x)[1], output); \
220 fputc(((unsigned char*)&x)[0], output); \
224 fputc(((unsigned char*)&x)[0], output); \
225 fputc(((unsigned char*)&x)[1], output); \
226 fputc(((unsigned char*)&x)[2], output); \
227 fputc(((unsigned char*)&x)[3], output); \
234 #define PUT_INT64(x) \
236 if(MPEG3_LITTLE_ENDIAN) \
238 fputc(((unsigned char*)&x)[7], output); \
239 fputc(((unsigned char*)&x)[6], output); \
240 fputc(((unsigned char*)&x)[5], output); \
241 fputc(((unsigned char*)&x)[4], output); \
242 fputc(((unsigned char*)&x)[3], output); \
243 fputc(((unsigned char*)&x)[2], output); \
244 fputc(((unsigned char*)&x)[1], output); \
245 fputc(((unsigned char*)&x)[0], output); \
249 fwrite(&x, 1, 8, output); \
257 int main(int argc
, char *argv
[])
261 char *src
= 0, *dst
= 0;
262 int astream_override
= -1;
266 fprintf(stderr
, "Create a table of contents for a DVD or mpeg stream.\n"
267 " Usage: [-a audio streams] mpeg3toc <path> <output>\n"
269 " -a override the number of audio streams to scan. Must be less than\n"
270 "the total number of audio streams.\n"
272 " The path should be absolute unless you plan\n"
273 " to always run your movie editor from the same directory\n"
274 " as the filename. For renderfarms the filesystem prefix\n"
275 " should be / and the movie directory mounted under the same\n"
276 " directory on each node.\n"
277 "Example: mpeg3toc /cd2/video_ts/vts_01_0.ifo titanic.toc\n");
281 for(i
= 1; i
< argc
; i
++)
283 if(!strcmp(argv
[i
], "-a"))
287 astream_override
= atoi(argv
[i
+ 1]);
288 if(astream_override
< 0)
290 fprintf(stderr
, "Total audio streams may not be negative\n");
296 "Using first %d audio streams.\n",
303 fprintf(stderr
, "-a requires an argument.\n");
319 fprintf(stderr
, "Ignoring argument \"%s\"\n", argv
[i
]);
325 fprintf(stderr
, "source path not supplied.\n");
331 fprintf(stderr
, "source path not supplied.\n");
339 fprintf(stderr
, "%s is 0 length. Skipping\n", src
);
347 uint64_t **frame_offsets
;
348 uint64_t **keyframe_numbers
;
349 uint64_t **sample_offsets
;
350 int *total_frame_offsets
;
351 int *total_sample_offsets
;
352 int *total_keyframe_numbers
;
353 int *frame_offset_allocation
;
354 int *sample_offset_allocation
;
355 int *keyframe_numbers_allocation
;
360 int total_samples
= 0;
361 int total_frames
= 0;
364 //printf(__FUNCTION__ " 1\n");
365 input
= mpeg3_open(src
);
367 //printf(__FUNCTION__ " 2\n");
368 vtracks
= mpeg3_total_vstreams(input
);
369 atracks
= mpeg3_total_astreams(input
);
370 if(astream_override
>= 0) atracks
= astream_override
;
372 if(atracks
) sample_rate
= mpeg3_sample_rate(input
, 0);
373 if(vtracks
) frame_rate
= mpeg3_frame_rate(input
, 0);
375 //printf(__FUNCTION__ " 3\n");
377 INIT_VECTORS(frame_offsets
, total_frame_offsets
, frame_offset_allocation
, vtracks
);
378 INIT_VECTORS(keyframe_numbers
, total_keyframe_numbers
, keyframe_numbers_allocation
, vtracks
);
379 INIT_VECTORS(sample_offsets
, total_sample_offsets
, sample_offset_allocation
, atracks
);
381 //printf(__FUNCTION__ " 4\n");
384 int sample_count
= MPEG3_AUDIO_CHUNKSIZE
;
388 int64_t title_number
= 0;
396 // Store current position and read sample_count from each atrack
397 for(j
= 0; j
< atracks
; j
++)
399 //printf(__FUNCTION__ " 3 %d\n", total_sample_offsets[j]);
402 mpeg3_demuxer_t
*demuxer
= input
->atrack
[j
]->demuxer
;
403 mpeg3demux_seek_byte(demuxer
, 0);
407 if(!mpeg3_end_of_audio(input
, j
))
409 // Don't want to maintain separate vectors for offset and title.
410 int64_t position
= mpeg3demux_tell_byte(input
->atrack
[j
]->demuxer
);
413 if(position
< MPEG3_IO_SIZE
) position
= MPEG3_IO_SIZE
;
417 APPEND_VECTOR(sample_offsets
,
418 total_sample_offsets
,
419 sample_offset_allocation
,
424 //printf(__FUNCTION__ " 6 %d\n", j);
425 // Throw away samples
426 mpeg3_read_audio(input
,
430 sample_count
, /* Number of samples to decode */
433 printf("\n%lld %lld\n",
434 mpeg3demux_tell_byte(input
->atrack
[j
]->demuxer
),
435 mpeg3demux_movie_size(input
->atrack
[j
]->demuxer
));
436 //printf(__FUNCTION__ " 7 %d\n", total_sample_offsets[j]);
441 total_samples
+= sample_count
;
442 fprintf(stderr
, "Audio: title=%lld total_samples=%d ", title_number
, total_samples
);
446 //printf(__FUNCTION__ " 8\n");
450 (int)((double)total_samples
/ sample_rate
* frame_rate
+ 0.5) -
458 //printf(__FUNCTION__ " 9 %d\n", vtracks);
474 for(j
= 0; j
< vtracks
; j
++)
476 mpeg3video_t
*video
= input
->vtrack
[j
]->video
;
477 mpeg3_demuxer_t
*demuxer
= input
->vtrack
[j
]->demuxer
;
480 mpeg3demux_seek_byte(demuxer
, 0);
484 for(l
= 0; l
< frame_count
; l
++)
486 if(!mpeg3_end_of_video(input
, j
))
488 // Transport streams always return one packet after the start of the frame.
489 int64_t position
= mpeg3demux_tell_byte(demuxer
) - 2048;
494 int got_keyframe
= 0;
497 if(position
< MPEG3_IO_SIZE
) position
= MPEG3_IO_SIZE
;
502 //printf("%llx\n", position);
503 // Store offset of every frame in table
504 APPEND_VECTOR(frame_offsets
,
506 frame_offset_allocation
,
512 // Search for next frame start.
513 if(total_frame_offsets
[j
] == 1)
515 // Assume first frame is an I-frame and put its number in the keyframe number
517 APPEND_VECTOR(keyframe_numbers
,
518 total_keyframe_numbers
,
519 keyframe_numbers_allocation
,
525 // Skip the first frame.
526 mpeg3video_get_header(video
, 0);
527 video
->current_repeat
+= 100;
536 mpeg3video_get_header(video
, 0);
537 video
->current_repeat
+= 100;
539 if(video
->pict_struct
== TOP_FIELD
)
544 if(video
->pict_struct
== BOTTOM_FIELD
)
549 if(video
->pict_struct
== FRAME_PICTURE
)
551 got_top
= got_bottom
= 1;
555 // The way we do it, the I frames have the top field but both the I frame and
556 // subsequent P frame make the keyframe.
557 if(video
->pict_type
== I_TYPE
)
559 }while(!mpeg3_end_of_video(input
, j
) &&
561 total_frame_offsets
[j
] > 1);
566 // Store number of a keyframe in the keyframe number table
568 APPEND_VECTOR(keyframe_numbers
,
569 total_keyframe_numbers
,
570 keyframe_numbers_allocation
,
572 total_frame_offsets
[j
] - 1);
574 if(j
== vtracks
- 1 && l
== frame_count
- 1)
576 total_frames
+= frame_count
;
577 fprintf(stderr
, "Video: title=%lld total_frames=%d (%.1f \%)", title_number
, total_frames
,100*((float)position
)/((float) mpeg3demux_movie_size(demuxer
)));
583 if(!have_audio
&& !have_video
) done
= 1;
585 fprintf(stderr
, "\r");
588 * if(total_frames > 10000)
601 output
= fopen(dst
, "w");
612 fputc(MPEG3_TOC_VERSION
, output
);
614 if(input
->is_program_stream
)
616 fputc(FILE_TYPE_PROGRAM
, output
);
619 if(input
->is_transport_stream
)
621 fputc(FILE_TYPE_TRANSPORT
, output
);
624 if(input
->is_audio_stream
)
626 fputc(FILE_TYPE_AUDIO
, output
);
629 if(input
->is_video_stream
)
631 fputc(FILE_TYPE_VIDEO
, output
);
635 // Only program and transport streams have these
636 for(i
= 0; i
< MPEG3_MAX_STREAMS
; i
++)
638 if(input
->demuxer
->astream_table
[i
])
640 fputc(STREAM_AUDIO
, output
);
642 PUT_INT32(input
->demuxer
->astream_table
[i
]);
645 if(input
->demuxer
->vstream_table
[i
])
647 fputc(STREAM_VIDEO
, output
);
649 PUT_INT32(input
->demuxer
->vstream_table
[i
]);
655 for(i
= 0; i
< input
->demuxer
->total_titles
; i
++)
657 mpeg3_title_t
*title
= input
->demuxer
->titles
[i
];
659 fputc(TITLE_PATH
, output
);
660 fprintf(output
, title
->fs
->path
);
663 PUT_INT64(title
->total_bytes
);
664 // Byte offsets of cells
665 PUT_INT32(input
->demuxer
->titles
[i
]->cell_table_size
);
666 for(j
= 0; j
< title
->cell_table_size
; j
++)
668 mpeg3_cell_t
*cell
= &title
->cell_table
[j
];
669 PUT_INT64(cell
->title_start
);
670 PUT_INT64(cell
->title_end
);
671 PUT_INT64(cell
->program_start
);
672 PUT_INT64(cell
->program_end
);
673 PUT_INT32(cell
->program
);
684 fputc(ATRACK_COUNT
, output
);
687 fputc(VTRACK_COUNT
, output
);
691 for(j
= 0; j
< atracks
; j
++)
693 int channels
= mpeg3_audio_channels(input
, j
);
695 PUT_INT32(total_sample_offsets
[j
]);
696 for(i
= 0; i
< total_sample_offsets
[j
]; i
++)
698 PUT_INT64(sample_offsets
[j
][i
]);
699 //printf("Audio: offset=%016llx\n", sample_offsets[j][i]);
704 for(j
= 0; j
< vtracks
; j
++)
706 PUT_INT32(total_frame_offsets
[j
]);
707 for(i
= 0; i
< total_frame_offsets
[j
]; i
++)
709 PUT_INT64(frame_offsets
[j
][i
]);
710 //printf("Video: offset=%016llx\n", frame_offsets[j][i]);
713 PUT_INT32(total_keyframe_numbers
[j
]);
714 for(i
= 0; i
< total_keyframe_numbers
[j
]; i
++)
716 PUT_INT64(keyframe_numbers
[j
][i
]);
717 //printf("Video: keyframe=%lld\n", keyframe_numbers[j][i]);
724 DELETE_VECTORS(frame_offsets
, total_frame_offsets
, frame_offset_allocation
, vtracks
);
725 DELETE_VECTORS(keyframe_numbers
, total_keyframe_numbers
, keyframe_numbers_allocation
, vtracks
);
726 DELETE_VECTORS(sample_offsets
, total_sample_offsets
, sample_offset_allocation
, atracks
);