1 #include "colormodels.h"
2 #include "funcprotos.h"
3 #include "interlacemodes.h"
7 #include "workarounds.h"
9 int quicktime_make_streamable(char *in_path
, char *out_path
)
11 quicktime_t file
, *old_file
, new_file
;
12 int moov_exists
= 0, mdat_exists
= 0, result
, atoms
= 1;
13 int64_t mdat_start
, mdat_size
;
14 quicktime_atom_t leaf_atom
;
17 quicktime_init(&file
);
19 /* find the moov atom in the old file */
21 if(!(file
.stream
= fopen(in_path
, "rb")))
23 perror("quicktime_make_streamable");
27 file
.total_length
= quicktime_get_file_length(in_path
);
29 /* get the locations of moov and mdat atoms */
32 /*printf("%x\n", quicktime_position(&file)); */
33 result
= quicktime_atom_read_header(&file
, &leaf_atom
);
37 if(quicktime_atom_is(&leaf_atom
, "moov"))
40 moov_length
= leaf_atom
.size
;
43 if(quicktime_atom_is(&leaf_atom
, "mdat"))
45 mdat_start
= quicktime_position(&file
) - HEADER_LENGTH
;
46 mdat_size
= leaf_atom
.size
;
50 quicktime_atom_skip(&file
, &leaf_atom
);
54 }while(!result
&& quicktime_position(&file
) < file
.total_length
);
60 printf("quicktime_make_streamable: no moov atom\n");
66 printf("quicktime_make_streamable: no mdat atom\n");
70 /* copy the old file to the new file */
71 if(moov_exists
&& mdat_exists
)
73 /* moov wasn't the first atom */
77 int64_t buf_size
= 1000000;
81 /* read the header proper */
82 if(!(old_file
= quicktime_open(in_path
, 1, 0)))
87 quicktime_shift_offsets(&(old_file
->moov
), moov_length
);
89 /* open the output file */
90 if(!(new_file
.stream
= fopen(out_path
, "wb")))
92 perror("quicktime_make_streamable");
97 /* set up some flags */
100 quicktime_write_moov(&new_file
, &(old_file
->moov
));
101 quicktime_set_position(old_file
, mdat_start
);
103 if(!(buffer
= calloc(1, buf_size
)))
106 printf("quicktime_make_streamable: out of memory\n");
110 while(quicktime_position(old_file
) < mdat_start
+ mdat_size
&& !result
)
112 if(quicktime_position(old_file
) + buf_size
> mdat_start
+ mdat_size
)
113 buf_size
= mdat_start
+ mdat_size
- quicktime_position(old_file
);
115 if(!quicktime_read_data(old_file
, buffer
, buf_size
)) result
= 1;
118 if(!quicktime_write_data(&new_file
, buffer
, buf_size
)) result
= 1;
123 fclose(new_file
.stream
);
125 quicktime_close(old_file
);
129 printf("quicktime_make_streamable: header already at 0 offset\n");
139 void quicktime_set_copyright(quicktime_t
*file
, char *string
)
141 quicktime_set_udta_string(&(file
->moov
.udta
.copyright
), &(file
->moov
.udta
.copyright_len
), string
);
144 void quicktime_set_name(quicktime_t
*file
, char *string
)
146 quicktime_set_udta_string(&(file
->moov
.udta
.name
), &(file
->moov
.udta
.name_len
), string
);
149 void quicktime_set_info(quicktime_t
*file
, char *string
)
151 quicktime_set_udta_string(&(file
->moov
.udta
.info
), &(file
->moov
.udta
.info_len
), string
);
154 char* quicktime_get_copyright(quicktime_t
*file
)
156 return file
->moov
.udta
.copyright
;
159 char* quicktime_get_name(quicktime_t
*file
)
161 return file
->moov
.udta
.name
;
164 char* quicktime_get_info(quicktime_t
*file
)
166 return file
->moov
.udta
.info
;
170 int quicktime_video_tracks(quicktime_t
*file
)
173 for(i
= 0; i
< file
->moov
.total_tracks
; i
++)
175 if(file
->moov
.trak
[i
]->mdia
.minf
.is_video
) result
++;
180 int quicktime_audio_tracks(quicktime_t
*file
)
183 quicktime_minf_t
*minf
;
184 for(i
= 0; i
< file
->moov
.total_tracks
; i
++)
186 minf
= &(file
->moov
.trak
[i
]->mdia
.minf
);
193 int quicktime_set_audio(quicktime_t
*file
,
199 quicktime_trak_t
*trak
;
201 /* allocate an arbitrary number of tracks */
204 /* Fake the bits parameter for some formats. */
205 if(quicktime_match_32(compressor
, QUICKTIME_ULAW
) ||
206 quicktime_match_32(compressor
, QUICKTIME_IMA4
)) bits
= 16;
208 file
->atracks
= (quicktime_audio_map_t
*)calloc(1, sizeof(quicktime_audio_map_t
));
209 trak
= quicktime_add_track(file
);
210 quicktime_trak_init_audio(file
,
216 quicktime_init_audio_map(&(file
->atracks
[0]), trak
);
217 file
->atracks
[file
->total_atracks
].track
= trak
;
218 file
->atracks
[file
->total_atracks
].channels
= channels
;
219 file
->atracks
[file
->total_atracks
].current_position
= 0;
220 file
->atracks
[file
->total_atracks
].current_chunk
= 1;
221 file
->total_atracks
++;
223 return 1; /* Return the number of tracks created */
226 int quicktime_set_video(quicktime_t
*file
,
234 quicktime_trak_t
*trak
;
238 quicktime_mhvd_init_video(file
, &(file
->moov
.mvhd
), frame_rate
);
239 file
->total_vtracks
= tracks
;
240 file
->vtracks
= (quicktime_video_map_t
*)calloc(1, sizeof(quicktime_video_map_t
) * file
->total_vtracks
);
241 for(i
= 0; i
< tracks
; i
++)
243 trak
= quicktime_add_track(file
);
244 quicktime_trak_init_video(file
, trak
, frame_w
, frame_h
, frame_rate
, compressor
);
245 quicktime_init_video_map(&(file
->vtracks
[i
]), trak
);
252 void quicktime_set_framerate(quicktime_t
*file
, double framerate
)
255 int new_time_scale
, new_sample_duration
;
259 fprintf(stderr
, "quicktime_set_framerate shouldn't be called in read mode.\n");
263 new_time_scale
= quicktime_get_timescale(framerate
);
264 new_sample_duration
= (int)((double)new_time_scale
/ framerate
+ 0.5);
266 for(i
= 0; i
< file
->total_vtracks
; i
++)
268 file
->vtracks
[i
].track
->mdia
.mdhd
.time_scale
= new_time_scale
;
269 file
->vtracks
[i
].track
->mdia
.minf
.stbl
.stts
.table
[0].sample_duration
= new_sample_duration
;
274 // Used by quicktime_set_video when creating a new file
275 quicktime_trak_t
* quicktime_add_track(quicktime_t
*file
)
277 quicktime_moov_t
*moov
= &(file
->moov
);
278 quicktime_trak_t
*trak
;
281 for(i
= moov
->total_tracks
; i
> 0; i
--)
282 moov
->trak
[i
] = moov
->trak
[i
- 1];
286 calloc(1, sizeof(quicktime_trak_t
));
287 quicktime_trak_init(trak
);
288 moov
->total_tracks
++;
290 for(i
= 0; i
< moov
->total_tracks
; i
++)
291 moov
->trak
[i
]->tkhd
.track_id
= i
+ 1;
292 moov
->mvhd
.next_track_id
++;
296 /* ============================= Initialization functions */
298 int quicktime_init(quicktime_t
*file
)
300 bzero(file
, sizeof(quicktime_t
));
301 quicktime_moov_init(&(file
->moov
));
303 file
->color_model
= BC_RGB888
;
304 file
->current_frame
= 0;
309 int quicktime_delete(quicktime_t
*file
)
312 if(file
->total_atracks
)
314 for(i
= 0; i
< file
->total_atracks
; i
++)
315 quicktime_delete_audio_map(&(file
->atracks
[i
]));
319 if(file
->total_vtracks
)
321 for(i
= 0; i
< file
->total_vtracks
; i
++)
322 quicktime_delete_video_map(&(file
->vtracks
[i
]));
326 file
->total_atracks
= 0;
327 file
->total_vtracks
= 0;
330 free(file
->moov_data
);
332 if(file
->preload_size
)
334 free(file
->preload_buffer
);
335 file
->preload_size
= 0;
338 if(file
->presave_buffer
)
340 free(file
->presave_buffer
);
343 for(i
= 0; i
< file
->total_riffs
; i
++)
345 quicktime_delete_riff(file
, file
->riff
[i
]);
348 quicktime_moov_delete(&(file
->moov
));
349 quicktime_mdat_delete(&(file
->mdat
));
353 /* =============================== Optimization functions */
355 int quicktime_set_cpus(quicktime_t
*file
, int cpus
)
357 if(cpus
> 0) file
->cpus
= cpus
;
361 void quicktime_set_preload(quicktime_t
*file
, int64_t preload
)
363 file
->preload_size
= preload
;
364 if(file
->preload_buffer
) free(file
->preload_buffer
);
365 file
->preload_buffer
= 0;
367 file
->preload_buffer
= calloc(1, preload
);
368 file
->preload_start
= 0;
369 file
->preload_end
= 0;
370 file
->preload_ptr
= 0;
374 int quicktime_get_timescale(double frame_rate
)
377 /* Encode the 29.97, 23.976, 59.94 framerates */
378 if(frame_rate
- (int)frame_rate
!= 0)
379 timescale
= (int)(frame_rate
* 1001 + 0.5);
381 if((600 / frame_rate
) - (int)(600 / frame_rate
) != 0)
382 timescale
= (int)(frame_rate
* 100 + 0.5);
383 //printf("quicktime_get_timescale %f %d\n", 600 / frame_rate, (int)(600 / frame_rate));
387 int quicktime_seek_end(quicktime_t
*file
)
389 quicktime_set_position(file
, file
->mdat
.atom
.size
+ file
->mdat
.atom
.start
+ HEADER_LENGTH
* 2);
390 /*printf("quicktime_seek_end %ld\n", file->mdat.atom.size + file->mdat.atom.start); */
391 quicktime_update_positions(file
);
395 int quicktime_seek_start(quicktime_t
*file
)
397 quicktime_set_position(file
, file
->mdat
.atom
.start
+ HEADER_LENGTH
* 2);
398 quicktime_update_positions(file
);
402 long quicktime_audio_length(quicktime_t
*file
, int track
)
404 if(file
->total_atracks
> 0)
405 return quicktime_track_samples(file
, file
->atracks
[track
].track
);
410 long quicktime_video_length(quicktime_t
*file
, int track
)
412 /*printf("quicktime_video_length %d %d\n", quicktime_track_samples(file, file->vtracks[track].track), track); */
413 if(file
->total_vtracks
> 0)
414 return quicktime_track_samples(file
, file
->vtracks
[track
].track
);
418 long quicktime_audio_position(quicktime_t
*file
, int track
)
420 return file
->atracks
[track
].current_position
;
423 long quicktime_video_position(quicktime_t
*file
, int track
)
425 return file
->vtracks
[track
].current_position
;
428 int quicktime_update_positions(quicktime_t
*file
)
430 /* Get the sample position from the file offset */
431 /* for routines that change the positions of all tracks, like */
432 /* seek_end and seek_start but not for routines that reposition one track, like */
433 /* set_audio_position. */
435 int64_t mdat_offset
= quicktime_position(file
) - file
->mdat
.atom
.start
;
436 int64_t sample
, chunk
, chunk_offset
;
439 if(file
->total_atracks
)
441 sample
= quicktime_offset_to_sample(file
->atracks
[0].track
, mdat_offset
);
442 chunk
= quicktime_offset_to_chunk(&chunk_offset
, file
->atracks
[0].track
, mdat_offset
);
443 for(i
= 0; i
< file
->total_atracks
; i
++)
445 file
->atracks
[i
].current_position
= sample
;
446 file
->atracks
[i
].current_chunk
= chunk
;
450 if(file
->total_vtracks
)
452 sample
= quicktime_offset_to_sample(file
->vtracks
[0].track
, mdat_offset
);
453 chunk
= quicktime_offset_to_chunk(&chunk_offset
, file
->vtracks
[0].track
, mdat_offset
);
454 for(i
= 0; i
< file
->total_vtracks
; i
++)
456 file
->vtracks
[i
].current_position
= sample
;
457 file
->vtracks
[i
].current_chunk
= chunk
;
463 int quicktime_set_audio_position(quicktime_t
*file
, int64_t sample
, int track
)
465 int64_t offset
, chunk_sample
, chunk
;
466 quicktime_trak_t
*trak
;
468 if(track
< file
->total_atracks
)
470 trak
= file
->atracks
[track
].track
;
471 file
->atracks
[track
].current_position
= sample
;
472 quicktime_chunk_of_sample(&chunk_sample
, &chunk
, trak
, sample
);
473 file
->atracks
[track
].current_chunk
= chunk
;
474 offset
= quicktime_sample_to_offset(file
, trak
, sample
);
475 quicktime_set_position(file
, offset
);
478 fprintf(stderr
, "quicktime_set_audio_position: track >= file->total_atracks\n");
483 int quicktime_set_video_position(quicktime_t
*file
, int64_t frame
, int track
)
485 int64_t offset
, chunk_sample
, chunk
;
486 quicktime_trak_t
*trak
;
487 if(track
>= file
->total_vtracks
)
490 "quicktime_set_video_position: frame=%lld track=%d >= file->total_vtracks %d\n",
493 file
->total_vtracks
);
494 track
= file
->total_vtracks
- 1;
497 if(track
< file
->total_vtracks
&& track
>= 0)
499 trak
= file
->vtracks
[track
].track
;
500 file
->vtracks
[track
].current_position
= frame
;
501 quicktime_chunk_of_sample(&chunk_sample
, &chunk
, trak
, frame
);
502 file
->vtracks
[track
].current_chunk
= chunk
;
503 offset
= quicktime_sample_to_offset(file
, trak
, frame
);
504 quicktime_set_position(file
, offset
);
509 int quicktime_has_audio(quicktime_t
*file
)
511 if(quicktime_audio_tracks(file
)) return 1;
515 long quicktime_sample_rate(quicktime_t
*file
, int track
)
517 if(file
->total_atracks
)
519 quicktime_trak_t
*trak
= file
->atracks
[track
].track
;
520 return trak
->mdia
.minf
.stbl
.stsd
.table
[0].sample_rate
;
525 int quicktime_audio_bits(quicktime_t
*file
, int track
)
527 if(file
->total_atracks
)
528 return file
->atracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].sample_size
;
533 char* quicktime_audio_compressor(quicktime_t
*file
, int track
)
535 return file
->atracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].format
;
538 int quicktime_track_channels(quicktime_t
*file
, int track
)
540 if(track
< file
->total_atracks
)
541 return file
->atracks
[track
].channels
;
546 /* Input is absolute channel number in stream, output is quicktime_track in which the channel resides and quicktime_channel relative to the track */
548 int quicktime_channel_location(quicktime_t
*file
, int *quicktime_track
, int *quicktime_channel
, int channel
)
550 int current_channel
= 0, current_track
= 0;
551 *quicktime_channel
= 0;
552 *quicktime_track
= 0;
553 for(current_channel
= 0, current_track
= 0; current_track
< file
->total_atracks
; )
555 if(channel
>= current_channel
)
557 *quicktime_channel
= channel
- current_channel
;
558 *quicktime_track
= current_track
;
561 current_channel
+= file
->atracks
[current_track
].channels
;
567 int quicktime_has_video(quicktime_t
*file
)
569 if(quicktime_video_tracks(file
)) return 1;
573 int quicktime_video_width(quicktime_t
*file
, int track
)
575 if(file
->total_vtracks
)
576 return file
->vtracks
[track
].track
->tkhd
.track_width
;
580 int quicktime_video_height(quicktime_t
*file
, int track
)
582 if(file
->total_vtracks
)
583 return file
->vtracks
[track
].track
->tkhd
.track_height
;
587 int quicktime_video_depth(quicktime_t
*file
, int track
)
589 if(file
->total_vtracks
)
590 return file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].depth
;
594 int quicktime_video_interlacemode(quicktime_t
*file
, int track
)
596 if(file
->total_vtracks
) {
597 if (file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].fields
== 1)
598 return BC_ILACE_MODE_NOTINTERLACED
;
599 if (file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].fields
== 2)
601 switch (file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].field_dominance
)
604 return BC_ILACE_MODE_UNDETECTED
;
606 return BC_ILACE_MODE_TOP_FIRST
;
608 return BC_ILACE_MODE_BOTTOM_FIRST
;
612 return BC_ILACE_MODE_UNDETECTED
;
615 void quicktime_set_cmodel(quicktime_t
*file
, int colormodel
)
617 file
->color_model
= colormodel
;
620 void quicktime_set_row_span(quicktime_t
*file
, int row_span
)
622 file
->row_span
= row_span
;
625 void quicktime_set_window(quicktime_t
*file
,
626 int in_x
, /* Location of input frame to take picture */
630 int out_w
, /* Dimensions of output frame */
633 if(in_x
>= 0 && in_y
>= 0 && in_w
> 0 && in_h
> 0 && out_w
> 0 && out_h
> 0)
635 file
->do_scaling
= 1;
645 file
->do_scaling
= 0;
646 /* quicktime_decode_video now sets the window for every frame based on the */
647 /* track dimensions */
651 void quicktime_set_depth(quicktime_t
*file
, int depth
, int track
)
655 for(i
= 0; i
< file
->total_vtracks
; i
++)
657 file
->vtracks
[i
].track
->mdia
.minf
.stbl
.stsd
.table
[0].depth
= depth
;
661 double quicktime_frame_rate(quicktime_t
*file
, int track
)
663 if(file
->total_vtracks
> track
)
665 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
666 int time_scale
= file
->vtracks
[track
].track
->mdia
.mdhd
.time_scale
;
667 int sample_duration
= quicktime_sample_duration(trak
);
668 return (double)time_scale
/ sample_duration
;
669 // return (float)file->vtracks[track].track->mdia.mdhd.time_scale /
670 // file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration;
675 int quicktime_frame_rate_n(quicktime_t
*file
, int track
)
677 if(file
->total_vtracks
> track
)
678 return file
->vtracks
[track
].track
->mdia
.mdhd
.time_scale
;
682 int quicktime_frame_rate_d(quicktime_t
*file
, int track
)
684 if(file
->total_vtracks
> track
)
685 return file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stts
.table
[0].sample_duration
;
689 char* quicktime_video_compressor(quicktime_t
*file
, int track
)
691 return file
->vtracks
[track
].track
->mdia
.minf
.stbl
.stsd
.table
[0].format
;
694 int quicktime_write_audio(quicktime_t
*file
,
701 quicktime_atom_t chunk_atom
;
702 quicktime_audio_map_t
*track_map
= &file
->atracks
[track
];
703 quicktime_trak_t
*trak
= track_map
->track
;
705 /* write chunk for 1 track */
706 bytes
= samples
* quicktime_audio_bits(file
, track
) / 8 * file
->atracks
[track
].channels
;
707 quicktime_write_chunk_header(file
, trak
, &chunk_atom
);
708 result
= !quicktime_write_data(file
, audio_buffer
, bytes
);
709 quicktime_write_chunk_footer(file
,
711 track_map
->current_chunk
,
715 /* file->atracks[track].current_position += samples; */
716 file
->atracks
[track
].current_chunk
++;
720 int quicktime_write_frame(quicktime_t
*file
,
721 unsigned char *video_buffer
,
725 int64_t offset
= quicktime_position(file
);
727 quicktime_atom_t chunk_atom
;
728 quicktime_video_map_t
*vtrack
= &file
->vtracks
[track
];
729 quicktime_trak_t
*trak
= vtrack
->track
;
731 quicktime_write_chunk_header(file
, trak
, &chunk_atom
);
732 result
= !quicktime_write_data(file
, video_buffer
, bytes
);
733 quicktime_write_chunk_footer(file
,
735 vtrack
->current_chunk
,
738 file
->vtracks
[track
].current_position
++;
739 file
->vtracks
[track
].current_chunk
++;
744 long quicktime_read_audio(quicktime_t
*file
,
749 int64_t chunk_sample
, chunk
;
750 int result
= 0, track_num
;
751 quicktime_trak_t
*trak
= file
->atracks
[track
].track
;
752 int64_t fragment_len
, chunk_end
;
753 int64_t start_position
= file
->atracks
[track
].current_position
;
754 int64_t position
= file
->atracks
[track
].current_position
;
755 int64_t start
= position
, end
= position
+ samples
;
756 int64_t bytes
, total_bytes
= 0;
757 int64_t buffer_offset
;
759 //printf("quicktime_read_audio 1\n");
760 quicktime_chunk_of_sample(&chunk_sample
, &chunk
, trak
, position
);
763 while(position
< end
&& !result
)
765 quicktime_set_audio_position(file
, position
, track
);
766 fragment_len
= quicktime_chunk_samples(trak
, chunk
);
767 chunk_end
= chunk_sample
+ fragment_len
;
768 fragment_len
-= position
- chunk_sample
;
769 if(position
+ fragment_len
> chunk_end
) fragment_len
= chunk_end
- position
;
770 if(position
+ fragment_len
> end
) fragment_len
= end
- position
;
772 bytes
= quicktime_samples_to_bytes(trak
, fragment_len
);
774 * printf("quicktime_read_audio 2 %llx %llx %d\n",
775 * quicktime_position(file),
776 * quicktime_position(file) + bytes,
780 result
= !quicktime_read_data(file
, &audio_buffer
[buffer_offset
], bytes
);
781 //printf("quicktime_read_audio 4\n");
783 total_bytes
+= bytes
;
784 position
+= fragment_len
;
785 chunk_sample
= position
;
786 buffer_offset
+= bytes
;
789 //printf("quicktime_read_audio 5\n");
791 // Create illusion of track being advanced only by samples
792 file
->atracks
[track
].current_position
= start_position
+ samples
;
797 int quicktime_read_chunk(quicktime_t
*file
, char *output
, int track
, int64_t chunk
, int64_t byte_start
, int64_t byte_len
)
799 quicktime_set_position(file
,
800 quicktime_chunk_to_offset(file
, file
->atracks
[track
].track
, chunk
) +
802 if(quicktime_read_data(file
, output
, byte_len
)) return 0;
807 long quicktime_frame_size(quicktime_t
*file
, long frame
, int track
)
810 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
812 if(trak
->mdia
.minf
.stbl
.stsz
.sample_size
)
814 bytes
= trak
->mdia
.minf
.stbl
.stsz
.sample_size
;
818 long total_frames
= quicktime_track_samples(file
, trak
);
819 if(frame
< 0) frame
= 0;
821 if(frame
> total_frames
- 1) frame
= total_frames
- 1;
822 bytes
= trak
->mdia
.minf
.stbl
.stsz
.table
[frame
].size
;
830 long quicktime_read_frame(quicktime_t
*file
, unsigned char *video_buffer
, int track
)
835 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
836 bytes
= quicktime_frame_size(file
, file
->vtracks
[track
].current_position
, track
);
838 quicktime_set_video_position(file
, file
->vtracks
[track
].current_position
, track
);
839 result
= quicktime_read_data(file
, video_buffer
, bytes
);
840 file
->vtracks
[track
].current_position
++;
842 if(!result
) return 0;
846 int64_t quicktime_get_keyframe_before(quicktime_t
*file
, int64_t frame
, int track
)
848 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
849 quicktime_stss_t
*stss
= &trak
->mdia
.minf
.stbl
.stss
;
860 for(i
= stss
->total_entries
- 1; i
>= 0; i
--)
862 if(stss
->table
[i
].sample
<= frame
) return stss
->table
[i
].sample
- 1;
868 int64_t quicktime_get_keyframe_after(quicktime_t
*file
, int64_t frame
, int track
)
870 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
871 quicktime_stss_t
*stss
= &trak
->mdia
.minf
.stbl
.stss
;
882 for(i
= 0; i
< stss
->total_entries
; i
++)
884 if(stss
->table
[i
].sample
>= frame
) return stss
->table
[i
].sample
- 1;
890 void quicktime_insert_keyframe(quicktime_t
*file
, int64_t frame
, int track
)
892 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
893 quicktime_stss_t
*stss
= &trak
->mdia
.minf
.stbl
.stss
;
896 // Set keyframe flag in idx1 table.
897 // Only possible in the first RIFF. After that, there's no keyframe support.
898 if(file
->use_avi
&& file
->total_riffs
== 1)
899 quicktime_set_idx1_keyframe(file
,
907 // Get the keyframe greater or equal to new frame
908 for(i
= 0; i
< stss
->total_entries
; i
++)
910 if(stss
->table
[i
].sample
>= frame
) break;
914 if(stss
->entries_allocated
<= stss
->total_entries
)
916 stss
->entries_allocated
*= 2;
917 stss
->table
= realloc(stss
->table
, sizeof(quicktime_stss_table_t
) * stss
->entries_allocated
);
920 // Insert before existing frame
921 if(i
< stss
->total_entries
)
923 if(stss
->table
[i
].sample
> frame
)
926 for(j
= stss
->total_entries
, k
= stss
->total_entries
- 1;
930 stss
->table
[j
] = stss
->table
[k
];
932 stss
->table
[i
].sample
= frame
;
936 // Insert after last frame
937 stss
->table
[i
].sample
= frame
;
939 stss
->total_entries
++;
943 int quicktime_has_keyframes(quicktime_t
*file
, int track
)
945 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
946 quicktime_stss_t
*stss
= &trak
->mdia
.minf
.stbl
.stss
;
948 return stss
->total_entries
> 0;
955 int quicktime_read_frame_init(quicktime_t
*file
, int track
)
957 quicktime_trak_t
*trak
= file
->vtracks
[track
].track
;
958 quicktime_set_video_position(file
, file
->vtracks
[track
].current_position
, track
);
959 if(quicktime_ftell(file
) != file
->file_position
)
961 FSEEK(file
->stream
, file
->file_position
, SEEK_SET
);
962 file
->ftell_position
= file
->file_position
;
967 int quicktime_read_frame_end(quicktime_t
*file
, int track
)
969 file
->file_position
= quicktime_ftell(file
);
970 file
->vtracks
[track
].current_position
++;
974 int quicktime_init_video_map(quicktime_video_map_t
*vtrack
, quicktime_trak_t
*trak
)
976 vtrack
->track
= trak
;
977 vtrack
->current_position
= 0;
978 vtrack
->current_chunk
= 1;
979 quicktime_init_vcodec(vtrack
);
980 vtrack
->frame_cache
= quicktime_new_cache();
984 int quicktime_delete_video_map(quicktime_video_map_t
*vtrack
)
987 quicktime_delete_vcodec(vtrack
);
988 if(vtrack
->frame_cache
) quicktime_delete_cache(vtrack
->frame_cache
);
989 vtrack
->frame_cache
= 0;
993 int64_t quicktime_memory_usage(quicktime_t
*file
)
997 //printf("quicktime_memory_usage %d\n", file->total_vtracks);
998 for(i
= 0; i
< file
->total_vtracks
; i
++)
1000 result
+= quicktime_cache_usage(file
->vtracks
[i
].frame_cache
);
1006 int quicktime_init_audio_map(quicktime_audio_map_t
*atrack
, quicktime_trak_t
*trak
)
1008 atrack
->track
= trak
;
1009 atrack
->channels
= trak
->mdia
.minf
.stbl
.stsd
.table
[0].channels
;
1010 atrack
->current_position
= 0;
1011 atrack
->current_chunk
= 1;
1012 quicktime_init_acodec(atrack
);
1016 int quicktime_delete_audio_map(quicktime_audio_map_t
*atrack
)
1019 quicktime_delete_acodec(atrack
);
1020 quicktime_clear_vbr(&atrack
->vbr
);
1024 void quicktime_init_maps(quicktime_t
*file
)
1027 /* get tables for all the different tracks */
1028 file
->total_atracks
= quicktime_audio_tracks(file
);
1029 file
->atracks
= (quicktime_audio_map_t
*)calloc(1, sizeof(quicktime_audio_map_t
) * file
->total_atracks
);
1031 for(i
= 0, track
= 0; i
< file
->total_atracks
; i
++)
1033 while(!file
->moov
.trak
[track
]->mdia
.minf
.is_audio
)
1035 quicktime_init_audio_map(&(file
->atracks
[i
]), file
->moov
.trak
[track
]);
1038 file
->total_vtracks
= quicktime_video_tracks(file
);
1039 file
->vtracks
= (quicktime_video_map_t
*)calloc(1, sizeof(quicktime_video_map_t
) * file
->total_vtracks
);
1041 for(track
= 0, i
= 0; i
< file
->total_vtracks
; i
++)
1043 while(!file
->moov
.trak
[track
]->mdia
.minf
.is_video
)
1046 quicktime_init_video_map(&(file
->vtracks
[i
]), file
->moov
.trak
[track
]);
1050 int quicktime_read_info(quicktime_t
*file
)
1052 int result
= 0, got_header
= 0;
1053 int i
, channel
, trak_channel
, track
;
1054 int64_t start_position
= quicktime_position(file
);
1055 quicktime_atom_t leaf_atom
;
1056 quicktime_trak_t
*trak
;
1061 quicktime_set_position(file
, 0LL);
1063 /* Test file format */
1068 result
= quicktime_atom_read_header(file
, &leaf_atom
);
1070 if(!result
&& quicktime_atom_is(&leaf_atom
, "RIFF"))
1072 quicktime_read_data(file
, avi_avi
, 4);
1073 if(quicktime_match_32(avi_avi
, "AVI "))
1090 if(!got_avi
) file
->use_avi
= 0;
1091 if(!got_asf
) file
->use_asf
= 0;
1093 quicktime_set_position(file
, 0LL);
1095 /* McRoweSoft AVI section */
1098 //printf("quicktime_read_info 1\n");
1099 /* Import first RIFF */
1102 result
= quicktime_atom_read_header(file
, &leaf_atom
);
1105 if(quicktime_atom_is(&leaf_atom
, "RIFF"))
1107 quicktime_read_riff(file
, &leaf_atom
);
1108 /* Return success */
1114 quicktime_position(file
) < file
->total_length
);
1116 //printf("quicktime_read_info 10\n");
1117 /* Construct indexes. */
1118 quicktime_import_avi(file
);
1119 //printf("quicktime_read_info 20\n");
1121 /* Quicktime section */
1127 result
= quicktime_atom_read_header(file
, &leaf_atom
);
1131 if(quicktime_atom_is(&leaf_atom
, "mdat"))
1133 quicktime_read_mdat(file
, &(file
->mdat
), &leaf_atom
);
1136 if(quicktime_atom_is(&leaf_atom
, "moov"))
1138 /* Set preload and preload the moov atom here */
1139 int64_t start_position
= quicktime_position(file
);
1140 long temp_size
= leaf_atom
.end
- start_position
;
1141 unsigned char *temp
= malloc(temp_size
);
1142 quicktime_set_preload(file
,
1143 (temp_size
< 0x100000) ? 0x100000 : temp_size
);
1144 quicktime_read_data(file
, temp
, temp_size
);
1145 quicktime_set_position(file
, start_position
);
1148 if(quicktime_read_moov(file
, &(file
->moov
), &leaf_atom
))
1153 quicktime_atom_skip(file
, &leaf_atom
);
1155 }while(!result
&& quicktime_position(file
) < file
->total_length
);
1166 /* go back to the original position */
1167 quicktime_set_position(file
, start_position
);
1171 /* Initialize track map objects */
1174 quicktime_init_maps(file
);
1177 /* Shut down preload in case of an obsurdly high temp_size */
1178 quicktime_set_preload(file
, 0);
1180 //printf("quicktime_read_info 100\n");
1185 int quicktime_dump(quicktime_t
*file
)
1187 printf("quicktime_dump\n");
1188 printf("movie data\n");
1189 printf(" size %ld\n", file
->mdat
.atom
.size
);
1190 printf(" start %ld\n", file
->mdat
.atom
.start
);
1191 quicktime_moov_dump(&(file
->moov
));
1199 int quicktime_check_sig(char *path
)
1202 quicktime_atom_t leaf_atom
;
1203 int result
= 0, result1
= 0, result2
= 0;
1206 quicktime_init(&file
);
1207 result
= quicktime_file_open(&file
, path
, 1, 0);
1211 // Check for Microsoft AVI
1212 quicktime_read_data(&file
, avi_test
, 12);
1213 quicktime_set_position(&file
, 0);
1214 if(quicktime_match_32(avi_test
, "RIFF") &&
1215 quicktime_match_32(avi_test
+ 8, "AVI "))
1223 result1
= quicktime_atom_read_header(&file
, &leaf_atom
);
1227 /* just want the "moov" atom */
1228 if(quicktime_atom_is(&leaf_atom
, "moov"))
1233 quicktime_atom_skip(&file
, &leaf_atom
);
1235 }while(!result1
&& !result2
&& quicktime_position(&file
) < file
.total_length
);
1239 //printf(__FUNCTION__ " 2 %d\n", result2);
1240 quicktime_file_close(&file
);
1241 quicktime_delete(&file
);
1245 void quicktime_set_avi(quicktime_t
*file
, int value
)
1247 file
->use_avi
= value
;
1248 quicktime_set_position(file
, 0);
1251 quicktime_init_riff(file
);
1254 int quicktime_is_avi(quicktime_t
*file
)
1256 return file
->use_avi
;
1260 void quicktime_set_asf(quicktime_t
*file
, int value
)
1262 file
->use_asf
= value
;
1265 void quicktime_set_frame_start(quicktime_t
*file
, int64_t value
)
1267 file
->current_frame
= value
;
1270 quicktime_t
* quicktime_open(char *filename
, int rd
, int wr
)
1272 quicktime_t
*new_file
= calloc(1, sizeof(quicktime_t
));
1276 //printf("quicktime_open 1\n");
1277 quicktime_init(new_file
);
1280 new_file
->mdat
.atom
.start
= 0;
1282 result
= quicktime_file_open(new_file
, filename
, rd
, wr
);
1288 if(quicktime_read_info(new_file
))
1290 quicktime_close(new_file
);
1291 fprintf(stderr
, "quicktime_open: error in header\n");
1296 /* start the data atom */
1297 /* also don't want to do this if making a streamable file */
1300 quicktime_set_presave(new_file
, 1);
1301 quicktime_atom_write_header64(new_file
,
1302 &new_file
->mdat
.atom
,
1304 quicktime_set_presave(new_file
, 0);
1309 //printf("quicktime_open 10\n");
1310 quicktime_close(new_file
);
1311 //printf("quicktime_open 100\n");
1319 int quicktime_close(quicktime_t
*file
)
1324 quicktime_codecs_flush(file
);
1326 // Reenable buffer for quick header writing.
1327 quicktime_set_presave(file
, 1);
1330 quicktime_atom_t junk_atom
;
1333 // Finalize last header
1334 quicktime_finalize_riff(file
, file
->riff
[file
->total_riffs
- 1]);
1336 int64_t position
= quicktime_position(file
);
1338 // Finalize the odml header
1339 quicktime_finalize_odml(file
, &file
->riff
[0]->hdrl
);
1341 // Finalize super indexes
1342 quicktime_finalize_indx(file
);
1345 quicktime_set_position(file
, position
);
1346 quicktime_atom_write_header(file
, &junk_atom
, "JUNK");
1347 for(i
= 0; i
< 0x406; i
++)
1348 quicktime_write_int32_le(file
, 0);
1349 quicktime_atom_write_footer(file
, &junk_atom
);
1353 // Atoms are only written here
1356 quicktime_write_moov(file
, &(file
->moov
));
1357 quicktime_atom_write_footer(file
, &file
->mdat
.atom
);
1362 quicktime_file_close(file
);
1364 quicktime_delete(file
);
1369 int quicktime_major()
1371 return QUICKTIME_MAJOR
;
1374 int quicktime_minor()
1376 return QUICKTIME_MINOR
;
1379 int quicktime_release()
1381 return QUICKTIME_RELEASE
;