r665: Merged the official release 2.0.
[cinelerra_cv.git] / quicktime / quicktime.c
blobb5bca6c06acd6bc2e10b50a84bc986a6cb950f7b
1 #include "colormodels.h"
2 #include "funcprotos.h"
3 #include "interlacemodes.h"
4 #include "quicktime.h"
5 #include "workarounds.h"
6 #include <sys/stat.h>
8 int quicktime_make_streamable(char *in_path, char *out_path)
10 quicktime_t file, *old_file, new_file;
11 int moov_exists = 0, mdat_exists = 0, result, atoms = 1;
12 int64_t mdat_start, mdat_size;
13 quicktime_atom_t leaf_atom;
14 int64_t moov_length;
16 quicktime_init(&file);
18 /* find the moov atom in the old file */
20 if(!(file.stream = fopen(in_path, "rb")))
22 perror("quicktime_make_streamable");
23 return 1;
26 file.total_length = quicktime_get_file_length(in_path);
28 /* get the locations of moov and mdat atoms */
31 /*printf("%x\n", quicktime_position(&file)); */
32 result = quicktime_atom_read_header(&file, &leaf_atom);
34 if(!result)
36 if(quicktime_atom_is(&leaf_atom, "moov"))
38 moov_exists = atoms;
39 moov_length = leaf_atom.size;
41 else
42 if(quicktime_atom_is(&leaf_atom, "mdat"))
44 mdat_start = quicktime_position(&file) - HEADER_LENGTH;
45 mdat_size = leaf_atom.size;
46 mdat_exists = atoms;
49 quicktime_atom_skip(&file, &leaf_atom);
51 atoms++;
53 }while(!result && quicktime_position(&file) < file.total_length);
55 fclose(file.stream);
57 if(!moov_exists)
59 printf("quicktime_make_streamable: no moov atom\n");
60 return 1;
63 if(!mdat_exists)
65 printf("quicktime_make_streamable: no mdat atom\n");
66 return 1;
69 /* copy the old file to the new file */
70 if(moov_exists && mdat_exists)
72 /* moov wasn't the first atom */
73 if(moov_exists > 1)
75 char *buffer;
76 int64_t buf_size = 1000000;
78 result = 0;
80 /* read the header proper */
81 if(!(old_file = quicktime_open(in_path, 1, 0)))
83 return 1;
86 quicktime_shift_offsets(&(old_file->moov), moov_length);
88 /* open the output file */
89 if(!(new_file.stream = fopen(out_path, "wb")))
91 perror("quicktime_make_streamable");
92 result = 1;
94 else
96 /* set up some flags */
97 new_file.wr = 1;
98 new_file.rd = 0;
99 quicktime_write_moov(&new_file, &(old_file->moov));
100 quicktime_set_position(old_file, mdat_start);
102 if(!(buffer = calloc(1, buf_size)))
104 result = 1;
105 printf("quicktime_make_streamable: out of memory\n");
107 else
109 while(quicktime_position(old_file) < mdat_start + mdat_size && !result)
111 if(quicktime_position(old_file) + buf_size > mdat_start + mdat_size)
112 buf_size = mdat_start + mdat_size - quicktime_position(old_file);
114 if(!quicktime_read_data(old_file, buffer, buf_size)) result = 1;
115 if(!result)
117 if(!quicktime_write_data(&new_file, buffer, buf_size)) result = 1;
120 free(buffer);
122 fclose(new_file.stream);
124 quicktime_close(old_file);
126 else
128 printf("quicktime_make_streamable: header already at 0 offset\n");
129 return 0;
133 return 0;
138 void quicktime_set_copyright(quicktime_t *file, char *string)
140 quicktime_set_udta_string(&(file->moov.udta.copyright), &(file->moov.udta.copyright_len), string);
143 void quicktime_set_name(quicktime_t *file, char *string)
145 quicktime_set_udta_string(&(file->moov.udta.name), &(file->moov.udta.name_len), string);
148 void quicktime_set_info(quicktime_t *file, char *string)
150 quicktime_set_udta_string(&(file->moov.udta.info), &(file->moov.udta.info_len), string);
153 char* quicktime_get_copyright(quicktime_t *file)
155 return file->moov.udta.copyright;
158 char* quicktime_get_name(quicktime_t *file)
160 return file->moov.udta.name;
163 char* quicktime_get_info(quicktime_t *file)
165 return file->moov.udta.info;
169 int quicktime_video_tracks(quicktime_t *file)
171 int i, result = 0;
172 for(i = 0; i < file->moov.total_tracks; i++)
174 if(file->moov.trak[i]->mdia.minf.is_video) result++;
176 return result;
179 int quicktime_audio_tracks(quicktime_t *file)
181 int i, result = 0;
182 quicktime_minf_t *minf;
183 for(i = 0; i < file->moov.total_tracks; i++)
185 minf = &(file->moov.trak[i]->mdia.minf);
186 if(minf->is_audio)
187 result++;
189 return result;
192 int quicktime_set_audio(quicktime_t *file,
193 int channels,
194 long sample_rate,
195 int bits,
196 char *compressor)
198 quicktime_trak_t *trak;
200 /* allocate an arbitrary number of tracks */
201 if(channels)
203 /* Fake the bits parameter for some formats. */
204 if(quicktime_match_32(compressor, QUICKTIME_ULAW) ||
205 quicktime_match_32(compressor, QUICKTIME_IMA4)) bits = 16;
207 file->atracks = (quicktime_audio_map_t*)calloc(1, sizeof(quicktime_audio_map_t));
208 trak = quicktime_add_track(file);
209 quicktime_trak_init_audio(file,
210 trak,
211 channels,
212 sample_rate,
213 bits,
214 compressor);
215 quicktime_init_audio_map(&(file->atracks[0]), trak);
216 file->atracks[file->total_atracks].track = trak;
217 file->atracks[file->total_atracks].channels = channels;
218 file->atracks[file->total_atracks].current_position = 0;
219 file->atracks[file->total_atracks].current_chunk = 1;
220 file->total_atracks++;
222 return 1; /* Return the number of tracks created */
225 int quicktime_set_video(quicktime_t *file,
226 int tracks,
227 int frame_w,
228 int frame_h,
229 double frame_rate,
230 char *compressor)
232 int i;
233 quicktime_trak_t *trak;
235 if(tracks)
237 quicktime_mhvd_init_video(file, &(file->moov.mvhd), frame_rate);
238 file->total_vtracks = tracks;
239 file->vtracks = (quicktime_video_map_t*)calloc(1, sizeof(quicktime_video_map_t) * file->total_vtracks);
240 for(i = 0; i < tracks; i++)
242 trak = quicktime_add_track(file);
243 quicktime_trak_init_video(file, trak, frame_w, frame_h, frame_rate, compressor);
244 quicktime_init_video_map(&(file->vtracks[i]), trak);
248 return 0;
251 void quicktime_set_framerate(quicktime_t *file, double framerate)
253 int i;
254 int new_time_scale, new_sample_duration;
256 if(!file->wr)
258 fprintf(stderr, "quicktime_set_framerate shouldn't be called in read mode.\n");
259 return;
262 new_time_scale = quicktime_get_timescale(framerate);
263 new_sample_duration = (int)((double)new_time_scale / framerate + 0.5);
265 for(i = 0; i < file->total_vtracks; i++)
267 file->vtracks[i].track->mdia.mdhd.time_scale = new_time_scale;
268 file->vtracks[i].track->mdia.minf.stbl.stts.table[0].sample_duration = new_sample_duration;
273 // Used by quicktime_set_video when creating a new file
274 quicktime_trak_t* quicktime_add_track(quicktime_t *file)
276 quicktime_moov_t *moov = &(file->moov);
277 quicktime_trak_t *trak;
278 int i;
280 for(i = moov->total_tracks; i > 0; i--)
281 moov->trak[i] = moov->trak[i - 1];
283 trak =
284 moov->trak[0] =
285 calloc(1, sizeof(quicktime_trak_t));
286 quicktime_trak_init(trak);
287 moov->total_tracks++;
289 for(i = 0; i < moov->total_tracks; i++)
290 moov->trak[i]->tkhd.track_id = i + 1;
291 moov->mvhd.next_track_id++;
292 return trak;
295 /* ============================= Initialization functions */
297 int quicktime_init(quicktime_t *file)
299 bzero(file, sizeof(quicktime_t));
300 quicktime_moov_init(&(file->moov));
301 file->cpus = 1;
302 file->color_model = BC_RGB888;
303 file->current_frame = 0;
304 file->is_odml = 0;
305 return 0;
308 int quicktime_delete(quicktime_t *file)
310 int i;
311 if(file->total_atracks)
313 for(i = 0; i < file->total_atracks; i++)
314 quicktime_delete_audio_map(&(file->atracks[i]));
315 free(file->atracks);
318 if(file->total_vtracks)
320 for(i = 0; i < file->total_vtracks; i++)
321 quicktime_delete_video_map(&(file->vtracks[i]));
322 free(file->vtracks);
325 file->total_atracks = 0;
326 file->total_vtracks = 0;
328 if(file->moov_data)
329 free(file->moov_data);
331 if(file->preload_size)
333 free(file->preload_buffer);
334 file->preload_size = 0;
337 if(file->presave_buffer)
339 free(file->presave_buffer);
342 for(i = 0; i < file->total_riffs; i++)
344 quicktime_delete_riff(file, file->riff[i]);
347 quicktime_moov_delete(&(file->moov));
348 quicktime_mdat_delete(&(file->mdat));
349 return 0;
352 /* =============================== Optimization functions */
354 int quicktime_set_cpus(quicktime_t *file, int cpus)
356 if(cpus > 0) file->cpus = cpus;
357 return 0;
360 void quicktime_set_preload(quicktime_t *file, int64_t preload)
362 file->preload_size = preload;
363 if(file->preload_buffer) free(file->preload_buffer);
364 file->preload_buffer = 0;
365 if(preload)
366 file->preload_buffer = calloc(1, preload);
367 file->preload_start = 0;
368 file->preload_end = 0;
369 file->preload_ptr = 0;
373 int quicktime_get_timescale(double frame_rate)
375 int timescale = 600;
376 /* Encode the 29.97, 23.976, 59.94 framerates */
377 if(frame_rate - (int)frame_rate != 0)
378 timescale = (int)(frame_rate * 1001 + 0.5);
379 else
380 if((600 / frame_rate) - (int)(600 / frame_rate) != 0)
381 timescale = (int)(frame_rate * 100 + 0.5);
382 //printf("quicktime_get_timescale %f %d\n", 600 / frame_rate, (int)(600 / frame_rate));
383 return timescale;
386 int quicktime_seek_end(quicktime_t *file)
388 quicktime_set_position(file, file->mdat.atom.size + file->mdat.atom.start + HEADER_LENGTH * 2);
389 /*printf("quicktime_seek_end %ld\n", file->mdat.atom.size + file->mdat.atom.start); */
390 quicktime_update_positions(file);
391 return 0;
394 int quicktime_seek_start(quicktime_t *file)
396 quicktime_set_position(file, file->mdat.atom.start + HEADER_LENGTH * 2);
397 quicktime_update_positions(file);
398 return 0;
401 long quicktime_audio_length(quicktime_t *file, int track)
403 if(file->total_atracks > 0)
404 return quicktime_track_samples(file, file->atracks[track].track);
406 return 0;
409 long quicktime_video_length(quicktime_t *file, int track)
411 /*printf("quicktime_video_length %d %d\n", quicktime_track_samples(file, file->vtracks[track].track), track); */
412 if(file->total_vtracks > 0)
413 return quicktime_track_samples(file, file->vtracks[track].track);
414 return 0;
417 long quicktime_audio_position(quicktime_t *file, int track)
419 return file->atracks[track].current_position;
422 long quicktime_video_position(quicktime_t *file, int track)
424 return file->vtracks[track].current_position;
427 int quicktime_update_positions(quicktime_t *file)
429 /* Get the sample position from the file offset */
430 /* for routines that change the positions of all tracks, like */
431 /* seek_end and seek_start but not for routines that reposition one track, like */
432 /* set_audio_position. */
434 int64_t mdat_offset = quicktime_position(file) - file->mdat.atom.start;
435 int64_t sample, chunk, chunk_offset;
436 int i;
438 if(file->total_atracks)
440 sample = quicktime_offset_to_sample(file->atracks[0].track, mdat_offset);
441 chunk = quicktime_offset_to_chunk(&chunk_offset, file->atracks[0].track, mdat_offset);
442 for(i = 0; i < file->total_atracks; i++)
444 file->atracks[i].current_position = sample;
445 file->atracks[i].current_chunk = chunk;
449 if(file->total_vtracks)
451 sample = quicktime_offset_to_sample(file->vtracks[0].track, mdat_offset);
452 chunk = quicktime_offset_to_chunk(&chunk_offset, file->vtracks[0].track, mdat_offset);
453 for(i = 0; i < file->total_vtracks; i++)
455 file->vtracks[i].current_position = sample;
456 file->vtracks[i].current_chunk = chunk;
459 return 0;
462 int quicktime_set_audio_position(quicktime_t *file, int64_t sample, int track)
464 int64_t offset, chunk_sample, chunk;
465 quicktime_trak_t *trak;
467 if(track < file->total_atracks)
469 trak = file->atracks[track].track;
470 file->atracks[track].current_position = sample;
471 quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample);
472 file->atracks[track].current_chunk = chunk;
473 offset = quicktime_sample_to_offset(file, trak, sample);
474 quicktime_set_position(file, offset);
476 else
477 fprintf(stderr, "quicktime_set_audio_position: track >= file->total_atracks\n");
479 return 0;
482 int quicktime_set_video_position(quicktime_t *file, int64_t frame, int track)
484 int64_t offset, chunk_sample, chunk;
485 quicktime_trak_t *trak;
486 if(track >= file->total_vtracks)
488 fprintf(stderr,
489 "quicktime_set_video_position: frame=%lld track=%d >= file->total_vtracks %d\n",
490 frame,
491 track,
492 file->total_vtracks);
493 track = file->total_vtracks - 1;
496 if(track < file->total_vtracks && track >= 0)
498 trak = file->vtracks[track].track;
499 file->vtracks[track].current_position = frame;
500 quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, frame);
501 file->vtracks[track].current_chunk = chunk;
502 offset = quicktime_sample_to_offset(file, trak, frame);
503 quicktime_set_position(file, offset);
505 return 0;
508 int quicktime_has_audio(quicktime_t *file)
510 if(quicktime_audio_tracks(file)) return 1;
511 return 0;
514 long quicktime_sample_rate(quicktime_t *file, int track)
516 if(file->total_atracks)
518 quicktime_trak_t *trak = file->atracks[track].track;
519 return trak->mdia.minf.stbl.stsd.table[0].sample_rate;
521 return 0;
524 int quicktime_audio_bits(quicktime_t *file, int track)
526 if(file->total_atracks)
527 return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_size;
529 return 0;
532 char* quicktime_audio_compressor(quicktime_t *file, int track)
534 return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].format;
537 int quicktime_track_channels(quicktime_t *file, int track)
539 if(track < file->total_atracks)
540 return file->atracks[track].channels;
542 return 0;
545 int quicktime_channel_location(quicktime_t *file, int *quicktime_track, int *quicktime_channel, int channel)
547 int current_channel = 0, current_track = 0;
548 *quicktime_channel = 0;
549 *quicktime_track = 0;
550 for(current_channel = 0, current_track = 0; current_track < file->total_atracks; )
552 if(channel >= current_channel)
554 *quicktime_channel = channel - current_channel;
555 *quicktime_track = current_track;
558 current_channel += file->atracks[current_track].channels;
559 current_track++;
561 return 0;
564 int quicktime_has_video(quicktime_t *file)
566 if(quicktime_video_tracks(file)) return 1;
567 return 0;
570 int quicktime_video_width(quicktime_t *file, int track)
572 if(file->total_vtracks)
573 return file->vtracks[track].track->tkhd.track_width;
574 return 0;
577 int quicktime_video_height(quicktime_t *file, int track)
579 if(file->total_vtracks)
580 return file->vtracks[track].track->tkhd.track_height;
581 return 0;
584 int quicktime_video_depth(quicktime_t *file, int track)
586 if(file->total_vtracks)
587 return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].depth;
588 return 0;
591 int quicktime_video_interlacemode(quicktime_t *file, int track)
593 if(file->total_vtracks) {
594 if (file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].fields == 1)
595 return BC_ILACE_MODE_NOTINTERLACED;
596 if (file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].fields == 2)
598 switch (file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].field_dominance)
600 case 0:
601 return BC_ILACE_MODE_UNDETECTED;
602 case 1:
603 return BC_ILACE_MODE_TOP_FIRST;
604 case 6:
605 return BC_ILACE_MODE_BOTTOM_FIRST;
609 return BC_ILACE_MODE_UNDETECTED;
612 void quicktime_set_cmodel(quicktime_t *file, int colormodel)
614 file->color_model = colormodel;
617 void quicktime_set_row_span(quicktime_t *file, int row_span)
619 file->row_span = row_span;
622 void quicktime_set_window(quicktime_t *file,
623 int in_x, /* Location of input frame to take picture */
624 int in_y,
625 int in_w,
626 int in_h,
627 int out_w, /* Dimensions of output frame */
628 int out_h)
630 if(in_x >= 0 && in_y >= 0 && in_w > 0 && in_h > 0 && out_w > 0 && out_h > 0)
632 file->do_scaling = 1;
633 file->in_x = in_x;
634 file->in_y = in_y;
635 file->in_w = in_w;
636 file->in_h = in_h;
637 file->out_w = out_w;
638 file->out_h = out_h;
640 else
642 file->do_scaling = 0;
643 /* quicktime_decode_video now sets the window for every frame based on the */
644 /* track dimensions */
648 void quicktime_set_depth(quicktime_t *file, int depth, int track)
650 int i;
652 for(i = 0; i < file->total_vtracks; i++)
654 file->vtracks[i].track->mdia.minf.stbl.stsd.table[0].depth = depth;
658 double quicktime_frame_rate(quicktime_t *file, int track)
660 if(file->total_vtracks > track)
662 quicktime_trak_t *trak = file->vtracks[track].track;
663 int time_scale = file->vtracks[track].track->mdia.mdhd.time_scale;
664 int sample_duration = quicktime_sample_duration(trak);
665 return (double)time_scale / sample_duration;
666 // return (float)file->vtracks[track].track->mdia.mdhd.time_scale /
667 // file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration;
669 return 0;
672 int quicktime_frame_rate_n(quicktime_t *file, int track)
674 if(file->total_vtracks > track)
675 return file->vtracks[track].track->mdia.mdhd.time_scale;
676 return 0;
679 int quicktime_frame_rate_d(quicktime_t *file, int track)
681 if(file->total_vtracks > track)
682 return file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration;
683 return 0;
686 char* quicktime_video_compressor(quicktime_t *file, int track)
688 return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].format;
691 int quicktime_write_audio(quicktime_t *file,
692 char *audio_buffer,
693 long samples,
694 int track)
696 int result;
697 int64_t bytes;
698 quicktime_atom_t chunk_atom;
699 quicktime_audio_map_t *track_map = &file->atracks[track];
700 quicktime_trak_t *trak = track_map->track;
702 /* write chunk for 1 track */
703 bytes = samples * quicktime_audio_bits(file, track) / 8 * file->atracks[track].channels;
704 quicktime_write_chunk_header(file, trak, &chunk_atom);
705 result = !quicktime_write_data(file, audio_buffer, bytes);
706 quicktime_write_chunk_footer(file,
707 trak,
708 track_map->current_chunk,
709 &chunk_atom,
710 samples);
712 /* file->atracks[track].current_position += samples; */
713 file->atracks[track].current_chunk++;
714 return result;
717 int quicktime_write_frame(quicktime_t *file,
718 unsigned char *video_buffer,
719 int64_t bytes,
720 int track)
722 int64_t offset = quicktime_position(file);
723 int result = 0;
724 quicktime_atom_t chunk_atom;
725 quicktime_video_map_t *vtrack = &file->vtracks[track];
726 quicktime_trak_t *trak = vtrack->track;
728 quicktime_write_chunk_header(file, trak, &chunk_atom);
729 result = !quicktime_write_data(file, video_buffer, bytes);
730 quicktime_write_chunk_footer(file,
731 trak,
732 vtrack->current_chunk,
733 &chunk_atom,
735 file->vtracks[track].current_position++;
736 file->vtracks[track].current_chunk++;
737 return result;
741 long quicktime_read_audio(quicktime_t *file,
742 char *audio_buffer,
743 long samples,
744 int track)
746 int64_t chunk_sample, chunk;
747 int result = 0, track_num;
748 quicktime_trak_t *trak = file->atracks[track].track;
749 int64_t fragment_len, chunk_end;
750 int64_t start_position = file->atracks[track].current_position;
751 int64_t position = file->atracks[track].current_position;
752 int64_t start = position, end = position + samples;
753 int64_t bytes, total_bytes = 0;
754 int64_t buffer_offset;
756 //printf("quicktime_read_audio 1\n");
757 quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position);
758 buffer_offset = 0;
760 while(position < end && !result)
762 quicktime_set_audio_position(file, position, track);
763 fragment_len = quicktime_chunk_samples(trak, chunk);
764 chunk_end = chunk_sample + fragment_len;
765 fragment_len -= position - chunk_sample;
766 if(position + fragment_len > chunk_end) fragment_len = chunk_end - position;
767 if(position + fragment_len > end) fragment_len = end - position;
769 bytes = quicktime_samples_to_bytes(trak, fragment_len);
771 * printf("quicktime_read_audio 2 %llx %llx %d\n",
772 * quicktime_position(file),
773 * quicktime_position(file) + bytes,
774 * samples);
775 * sleep(1);
777 result = !quicktime_read_data(file, &audio_buffer[buffer_offset], bytes);
778 //printf("quicktime_read_audio 4\n");
780 total_bytes += bytes;
781 position += fragment_len;
782 chunk_sample = position;
783 buffer_offset += bytes;
784 chunk++;
786 //printf("quicktime_read_audio 5\n");
788 // Create illusion of track being advanced only by samples
789 file->atracks[track].current_position = start_position + samples;
790 if(result) return 0;
791 return total_bytes;
794 int quicktime_read_chunk(quicktime_t *file, char *output, int track, int64_t chunk, int64_t byte_start, int64_t byte_len)
796 quicktime_set_position(file,
797 quicktime_chunk_to_offset(file, file->atracks[track].track, chunk) +
798 byte_start);
799 if(quicktime_read_data(file, output, byte_len)) return 0;
800 else
801 return 1;
804 long quicktime_frame_size(quicktime_t *file, long frame, int track)
806 long bytes = 0;
807 quicktime_trak_t *trak = file->vtracks[track].track;
809 if(trak->mdia.minf.stbl.stsz.sample_size)
811 bytes = trak->mdia.minf.stbl.stsz.sample_size;
813 else
815 long total_frames = quicktime_track_samples(file, trak);
816 if(frame < 0) frame = 0;
817 else
818 if(frame > total_frames - 1) frame = total_frames - 1;
819 bytes = trak->mdia.minf.stbl.stsz.table[frame].size;
823 return bytes;
827 long quicktime_read_frame(quicktime_t *file, unsigned char *video_buffer, int track)
829 int64_t bytes;
830 int result = 0;
832 quicktime_trak_t *trak = file->vtracks[track].track;
833 bytes = quicktime_frame_size(file, file->vtracks[track].current_position, track);
835 quicktime_set_video_position(file, file->vtracks[track].current_position, track);
836 result = quicktime_read_data(file, video_buffer, bytes);
837 file->vtracks[track].current_position++;
839 if(!result) return 0;
840 return bytes;
843 int64_t quicktime_get_keyframe_before(quicktime_t *file, int64_t frame, int track)
845 quicktime_trak_t *trak = file->vtracks[track].track;
846 quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
847 int i;
853 // Offset 1
854 frame++;
857 for(i = stss->total_entries - 1; i >= 0; i--)
859 if(stss->table[i].sample <= frame) return stss->table[i].sample - 1;
862 return 0;
865 int64_t quicktime_get_keyframe_after(quicktime_t *file, int64_t frame, int track)
867 quicktime_trak_t *trak = file->vtracks[track].track;
868 quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
869 int i;
875 // Offset 1
876 frame++;
879 for(i = 0; i < stss->total_entries; i++)
881 if(stss->table[i].sample >= frame) return stss->table[i].sample - 1;
884 return 0;
887 void quicktime_insert_keyframe(quicktime_t *file, int64_t frame, int track)
889 quicktime_trak_t *trak = file->vtracks[track].track;
890 quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
891 int i;
893 // Set keyframe flag in idx1 table.
894 // Only possible in the first RIFF. After that, there's no keyframe support.
895 if(file->use_avi && file->total_riffs == 1)
896 quicktime_set_idx1_keyframe(file,
897 trak,
898 frame);
900 // Offset 1
901 frame++;
904 // Get the keyframe greater or equal to new frame
905 for(i = 0; i < stss->total_entries; i++)
907 if(stss->table[i].sample >= frame) break;
910 // Expand table
911 if(stss->entries_allocated <= stss->total_entries)
913 stss->entries_allocated *= 2;
914 stss->table = realloc(stss->table, sizeof(quicktime_stss_table_t) * stss->entries_allocated);
917 // Insert before existing frame
918 if(i < stss->total_entries)
920 if(stss->table[i].sample > frame)
922 int j, k;
923 for(j = stss->total_entries, k = stss->total_entries - 1;
924 k >= i;
925 j--, k--)
927 stss->table[j] = stss->table[k];
929 stss->table[i].sample = frame;
932 else
933 // Insert after last frame
934 stss->table[i].sample = frame;
936 stss->total_entries++;
940 int quicktime_has_keyframes(quicktime_t *file, int track)
942 quicktime_trak_t *trak = file->vtracks[track].track;
943 quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
945 return stss->total_entries > 0;
952 int quicktime_read_frame_init(quicktime_t *file, int track)
954 quicktime_trak_t *trak = file->vtracks[track].track;
955 quicktime_set_video_position(file, file->vtracks[track].current_position, track);
956 if(quicktime_ftell(file) != file->file_position)
958 FSEEK(file->stream, file->file_position, SEEK_SET);
959 file->ftell_position = file->file_position;
961 return 0;
964 int quicktime_read_frame_end(quicktime_t *file, int track)
966 file->file_position = quicktime_ftell(file);
967 file->vtracks[track].current_position++;
968 return 0;
971 int quicktime_init_video_map(quicktime_video_map_t *vtrack, quicktime_trak_t *trak)
973 vtrack->track = trak;
974 vtrack->current_position = 0;
975 vtrack->current_chunk = 1;
976 quicktime_init_vcodec(vtrack);
977 return 0;
980 int quicktime_delete_video_map(quicktime_video_map_t *vtrack)
982 int i;
983 quicktime_delete_vcodec(vtrack);
984 return 0;
987 int quicktime_init_audio_map(quicktime_audio_map_t *atrack, quicktime_trak_t *trak)
989 atrack->track = trak;
990 atrack->channels = trak->mdia.minf.stbl.stsd.table[0].channels;
991 atrack->current_position = 0;
992 atrack->current_chunk = 1;
993 quicktime_init_acodec(atrack);
994 return 0;
997 int quicktime_delete_audio_map(quicktime_audio_map_t *atrack)
999 int i;
1000 quicktime_delete_acodec(atrack);
1001 quicktime_clear_vbr(&atrack->vbr);
1002 return 0;
1005 void quicktime_init_maps(quicktime_t *file)
1007 int i, track;
1008 /* get tables for all the different tracks */
1009 file->total_atracks = quicktime_audio_tracks(file);
1010 file->atracks = (quicktime_audio_map_t*)calloc(1, sizeof(quicktime_audio_map_t) * file->total_atracks);
1012 for(i = 0, track = 0; i < file->total_atracks; i++)
1014 while(!file->moov.trak[track]->mdia.minf.is_audio)
1015 track++;
1016 quicktime_init_audio_map(&(file->atracks[i]), file->moov.trak[track]);
1019 file->total_vtracks = quicktime_video_tracks(file);
1020 file->vtracks = (quicktime_video_map_t*)calloc(1, sizeof(quicktime_video_map_t) * file->total_vtracks);
1022 for(track = 0, i = 0; i < file->total_vtracks; i++)
1024 while(!file->moov.trak[track]->mdia.minf.is_video)
1025 track++;
1027 quicktime_init_video_map(&(file->vtracks[i]), file->moov.trak[track]);
1031 int quicktime_read_info(quicktime_t *file)
1033 int result = 0, got_header = 0;
1034 int i, channel, trak_channel, track;
1035 int64_t start_position = quicktime_position(file);
1036 quicktime_atom_t leaf_atom;
1037 quicktime_trak_t *trak;
1038 char avi_avi[4];
1039 int got_avi = 0;
1040 int got_asf = 0;
1042 quicktime_set_position(file, 0LL);
1044 /* Test file format */
1047 file->use_avi = 1;
1048 file->use_asf = 1;
1049 result = quicktime_atom_read_header(file, &leaf_atom);
1051 if(!result && quicktime_atom_is(&leaf_atom, "RIFF"))
1053 quicktime_read_data(file, avi_avi, 4);
1054 if(quicktime_match_32(avi_avi, "AVI "))
1056 got_avi = 1;
1058 else
1060 result = 0;
1061 break;
1064 else
1066 result = 0;
1067 break;
1069 }while(1);
1071 if(!got_avi) file->use_avi = 0;
1072 if(!got_asf) file->use_asf = 0;
1074 quicktime_set_position(file, 0LL);
1076 /* McRoweSoft AVI section */
1077 if(file->use_avi)
1079 //printf("quicktime_read_info 1\n");
1080 /* Import first RIFF */
1083 result = quicktime_atom_read_header(file, &leaf_atom);
1084 if(!result)
1086 if(quicktime_atom_is(&leaf_atom, "RIFF"))
1088 quicktime_read_riff(file, &leaf_atom);
1089 /* Return success */
1090 got_header = 1;
1093 }while(!result &&
1094 !got_header &&
1095 quicktime_position(file) < file->total_length);
1097 //printf("quicktime_read_info 10\n");
1098 /* Construct indexes. */
1099 quicktime_import_avi(file);
1100 //printf("quicktime_read_info 20\n");
1102 /* Quicktime section */
1103 else
1104 if(!file->use_avi)
1108 result = quicktime_atom_read_header(file, &leaf_atom);
1110 if(!result)
1112 if(quicktime_atom_is(&leaf_atom, "mdat"))
1114 quicktime_read_mdat(file, &(file->mdat), &leaf_atom);
1116 else
1117 if(quicktime_atom_is(&leaf_atom, "moov"))
1119 /* Set preload and preload the moov atom here */
1120 int64_t start_position = quicktime_position(file);
1121 long temp_size = leaf_atom.end - start_position;
1122 unsigned char *temp = malloc(temp_size);
1123 quicktime_set_preload(file,
1124 (temp_size < 0x100000) ? 0x100000 : temp_size);
1125 quicktime_read_data(file, temp, temp_size);
1126 quicktime_set_position(file, start_position);
1127 free(temp);
1129 if(quicktime_read_moov(file, &(file->moov), &leaf_atom))
1130 return 1;
1131 got_header = 1;
1133 else
1134 quicktime_atom_skip(file, &leaf_atom);
1136 }while(!result && quicktime_position(file) < file->total_length);
1147 /* go back to the original position */
1148 quicktime_set_position(file, start_position);
1152 /* Initialize track map objects */
1153 if(got_header)
1155 quicktime_init_maps(file);
1158 /* Shut down preload in case of an obsurdly high temp_size */
1159 quicktime_set_preload(file, 0);
1161 //printf("quicktime_read_info 100\n");
1162 return !got_header;
1166 int quicktime_dump(quicktime_t *file)
1168 printf("quicktime_dump\n");
1169 printf("movie data\n");
1170 printf(" size %ld\n", file->mdat.atom.size);
1171 printf(" start %ld\n", file->mdat.atom.start);
1172 quicktime_moov_dump(&(file->moov));
1173 return 0;
1179 // ================================== Entry points =============================
1181 int quicktime_check_sig(char *path)
1183 quicktime_t file;
1184 quicktime_atom_t leaf_atom;
1185 int result = 0, result1 = 0, result2 = 0;
1186 char avi_test[12];
1188 quicktime_init(&file);
1189 result = quicktime_file_open(&file, path, 1, 0);
1191 if(!result)
1193 // Check for Microsoft AVI
1194 quicktime_read_data(&file, avi_test, 12);
1195 quicktime_set_position(&file, 0);
1196 if(quicktime_match_32(avi_test, "RIFF") &&
1197 quicktime_match_32(avi_test + 8, "AVI "))
1199 result2 = 1;
1201 else
1205 result1 = quicktime_atom_read_header(&file, &leaf_atom);
1207 if(!result1)
1209 /* just want the "moov" atom */
1210 if(quicktime_atom_is(&leaf_atom, "moov"))
1212 result2 = 1;
1214 else
1215 quicktime_atom_skip(&file, &leaf_atom);
1217 }while(!result1 && !result2 && quicktime_position(&file) < file.total_length);
1221 //printf(__FUNCTION__ " 2 %d\n", result2);
1222 quicktime_file_close(&file);
1223 quicktime_delete(&file);
1224 return result2;
1227 void quicktime_set_avi(quicktime_t *file, int value)
1229 file->use_avi = value;
1230 quicktime_set_position(file, 0);
1232 // Write RIFF chunk
1233 quicktime_init_riff(file);
1236 int quicktime_is_avi(quicktime_t *file)
1238 return file->use_avi;
1242 void quicktime_set_asf(quicktime_t *file, int value)
1244 file->use_asf = value;
1247 void quicktime_set_frame_start(quicktime_t *file, int64_t value)
1249 file->current_frame = value;
1252 quicktime_t* quicktime_open(char *filename, int rd, int wr)
1254 quicktime_t *new_file = calloc(1, sizeof(quicktime_t));
1255 char flags[10];
1256 int result = 0;
1258 //printf("quicktime_open 1\n");
1259 quicktime_init(new_file);
1260 new_file->wr = wr;
1261 new_file->rd = rd;
1262 new_file->mdat.atom.start = 0;
1264 result = quicktime_file_open(new_file, filename, rd, wr);
1266 if(!result)
1268 if(rd)
1270 if(quicktime_read_info(new_file))
1272 quicktime_close(new_file);
1273 fprintf(stderr, "quicktime_open: error in header\n");
1274 new_file = 0;
1278 /* start the data atom */
1279 /* also don't want to do this if making a streamable file */
1280 if(wr)
1282 quicktime_set_presave(new_file, 1);
1283 quicktime_atom_write_header64(new_file,
1284 &new_file->mdat.atom,
1285 "mdat");
1286 quicktime_set_presave(new_file, 0);
1289 else
1291 //printf("quicktime_open 10\n");
1292 quicktime_close(new_file);
1293 //printf("quicktime_open 100\n");
1294 new_file = 0;
1298 return new_file;
1301 int quicktime_close(quicktime_t *file)
1303 int result = 0;
1304 if(file->wr)
1306 quicktime_codecs_flush(file);
1308 // Reenable buffer for quick header writing.
1309 quicktime_set_presave(file, 1);
1310 if(file->use_avi)
1312 quicktime_atom_t junk_atom;
1313 int i;
1315 // Finalize last header
1316 quicktime_finalize_riff(file, file->riff[file->total_riffs - 1]);
1319 // Finalize the odml header
1320 quicktime_finalize_odml(file, &file->riff[0]->hdrl);
1322 // Finalize super indexes
1323 quicktime_finalize_indx(file);
1325 // Pad ending
1326 quicktime_atom_write_header(file, &junk_atom, "JUNK");
1327 for(i = 0; i < 0x406; i++)
1328 quicktime_write_int32_le(file, 0);
1329 quicktime_atom_write_footer(file, &junk_atom);
1331 else
1333 // Atoms are only written here
1334 if(file->stream)
1336 quicktime_write_moov(file, &(file->moov));
1337 quicktime_atom_write_footer(file, &file->mdat.atom);
1342 quicktime_file_close(file);
1344 quicktime_delete(file);
1345 free(file);
1346 return result;
1349 int quicktime_major()
1351 return QUICKTIME_MAJOR;
1354 int quicktime_minor()
1356 return QUICKTIME_MINOR;
1359 int quicktime_release()
1361 return QUICKTIME_RELEASE;