allow ffmpeg video to resample curr_pos, add bluray format
[goodguy/history.git] / cinelerra-5.0 / quicktime / quicktime.c
1 #include "colormodels.h"
2 #include "funcprotos.h"
3 #include "qtasf_codes.h"
4 #include "quicktime.h"
5 #include <string.h>
6 #include <sys/stat.h>
7 #include "workarounds.h"
8
9 int quicktime_make_streamable(char *in_path, char *out_path)
10 {
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;
15         int64_t moov_start, moov_end;
16         int ftyp_exists = 0;
17         int ftyp_size = 0;
18         unsigned char *ftyp_data = 0;
19
20         quicktime_init(&file);
21
22 /* find the moov atom in the old file */
23
24         if(!(file.stream = fopen(in_path, "rb")))
25         {
26                 perror("quicktime_make_streamable");
27                 return 1;
28         }
29
30         file.total_length = quicktime_get_file_length(in_path);
31
32 /* get the locations of moov and mdat atoms */
33         do
34         {
35                 result = quicktime_atom_read_header(&file, &leaf_atom);
36 //printf("0x%llx %s\n", quicktime_position(&file), leaf_atom.type);
37
38                 if(!result)
39                 {
40                         if(quicktime_atom_is(&leaf_atom, "ftyp"))
41                         {
42                                 ftyp_exists = 1;
43                                 ftyp_data = calloc(1, leaf_atom.size);
44                                 ftyp_size = leaf_atom.size;
45                                 quicktime_set_position(&file,
46                                         quicktime_position(&file) - HEADER_LENGTH);
47                                 quicktime_read_data(&file, (char*)ftyp_data, ftyp_size);
48                         }
49                         else
50                         if(quicktime_atom_is(&leaf_atom, "moov"))
51                         {
52                                 moov_exists = atoms;
53                         }
54                         else
55                         if(quicktime_atom_is(&leaf_atom, "mdat"))
56                         {
57                                 mdat_start = quicktime_position(&file) - HEADER_LENGTH;
58                                 mdat_size = leaf_atom.size;
59                                 mdat_exists = atoms;
60                         }
61
62                         quicktime_atom_skip(&file, &leaf_atom);
63
64                         atoms++;
65                 }
66         }while(!result && quicktime_position(&file) < file.total_length);
67
68         fclose(file.stream);
69
70         if(!moov_exists)
71         {
72                 printf("quicktime_make_streamable: no moov atom\n");
73                 if(ftyp_data) free(ftyp_data);
74                 return 1;
75         }
76
77         if(!mdat_exists)
78         {
79                 printf("quicktime_make_streamable: no mdat atom\n");
80                 if(ftyp_data) free(ftyp_data);
81                 return 1;
82         }
83
84 /* copy the old file to the new file */
85         if(moov_exists && mdat_exists)
86         {
87 /* moov wasn't the first atom */
88                 if(moov_exists > 1)
89                 {
90                         char *buffer;
91                         int64_t buf_size = 1000000;
92
93                         result = 0;
94
95 /* read the header proper */
96                         if(!(old_file = quicktime_open(in_path, 1, 0)))
97                         {
98                                 if(ftyp_data) free(ftyp_data);
99                                 return 1;
100                         }
101
102
103 /* open the output file */
104                         if(!(new_file.stream = fopen(out_path, "wb")))
105                         {
106                                 perror("quicktime_make_streamable");
107                                 result =  1;
108                         }
109                         else
110                         {
111 /* set up some flags */
112                                 new_file.wr = 1;
113                                 new_file.rd = 0;
114
115 /* Write ftyp header */
116                                 if(ftyp_exists)
117                                 {
118                                         quicktime_write_data(&new_file, (char*)ftyp_data, ftyp_size);
119                                 }
120
121
122 /* Write moov once to get final size with our substituted headers */
123                                 moov_start = quicktime_position(&new_file);
124                                 quicktime_write_moov(&new_file, &(old_file->moov), 0);
125                                 moov_end = quicktime_position(&new_file);
126
127 printf("make_streamable 0x%llx 0x%llx\n", (long long)moov_end - moov_start, (long long)mdat_start);
128                                 quicktime_shift_offsets(&(old_file->moov),
129                                         moov_end - moov_start - mdat_start + ftyp_size);
130
131 /* Write again with shifted offsets */
132                                 quicktime_set_position(&new_file, moov_start);
133                                 quicktime_write_moov(&new_file, &(old_file->moov), 0);
134                                 quicktime_set_position(old_file, mdat_start);
135
136                                 if(!(buffer = calloc(1, buf_size)))
137                                 {
138                                         result = 1;
139                                         printf("quicktime_make_streamable: out of memory\n");
140                                 }
141                                 else
142                                 {
143                                         while(quicktime_position(old_file) < mdat_start + mdat_size && !result)
144                                         {
145                                                 if(quicktime_position(old_file) + buf_size > mdat_start + mdat_size)
146                                                         buf_size = mdat_start + mdat_size - quicktime_position(old_file);
147
148                                                 if(!quicktime_read_data(old_file, buffer, buf_size)) result = 1;
149                                                 if(!result)
150                                                 {
151                                                         if(!quicktime_write_data(&new_file, buffer, buf_size)) result = 1;
152                                                 }
153                                         }
154                                         free(buffer);
155                                 }
156                                 fclose(new_file.stream);
157                         }
158                         quicktime_close(old_file);
159                 }
160                 else
161                 {
162                         printf("quicktime_make_streamable: header already at 0 offset\n");
163                         if(ftyp_data) free(ftyp_data);
164                         return 0;
165                 }
166         }
167
168         if(ftyp_data) free(ftyp_data);
169         return 0;
170 }
171
172
173
174 void quicktime_set_copyright(quicktime_t *file, const char *string)
175 {
176         quicktime_set_udta_string(&(file->moov.udta.copyright),
177                 &(file->moov.udta.copyright_len), (char*)string);
178 }
179
180 void quicktime_set_name(quicktime_t *file, const char *string)
181 {
182         quicktime_set_udta_string(&(file->moov.udta.name),
183                 &(file->moov.udta.name_len), (char*)string);
184 }
185
186 void quicktime_set_info(quicktime_t *file, const char *string)
187 {
188         quicktime_set_udta_string(&(file->moov.udta.info),
189                 &(file->moov.udta.info_len), (char*)string);
190 }
191
192 char* quicktime_get_copyright(quicktime_t *file)
193 {
194         return file->moov.udta.copyright;
195 }
196
197 char* quicktime_get_name(quicktime_t *file)
198 {
199         return file->moov.udta.name;
200 }
201
202 char* quicktime_get_info(quicktime_t *file)
203 {
204         return file->moov.udta.info;
205 }
206
207
208 int quicktime_video_tracks(quicktime_t *file)
209 {
210         int i, result = 0;
211         for(i = 0; i < file->moov.total_tracks; i++)
212         {
213                 if(file->moov.trak[i]->mdia.minf.is_video) result++;
214         }
215         return result;
216 }
217
218 int quicktime_audio_tracks(quicktime_t *file)
219 {
220         int i, result = 0;
221         quicktime_minf_t *minf;
222         for(i = 0; i < file->moov.total_tracks; i++)
223         {
224                 minf = &(file->moov.trak[i]->mdia.minf);
225                 if(minf->is_audio)
226                         result++;
227         }
228         return result;
229 }
230
231 int quicktime_set_audio(quicktime_t *file,
232                                                 int channels,
233                                                 long sample_rate,
234                                                 int bits,
235                                                 char *compressor)
236 {
237         quicktime_trak_t *trak;
238
239 /* allocate an arbitrary number of tracks */
240         if(channels)
241         {
242 /* Fake the bits parameter for some formats. */
243                 if(quicktime_match_32(compressor, QUICKTIME_ULAW)) bits = 8;
244                 else if(quicktime_match_32(compressor, QUICKTIME_IMA4)) bits = 16;
245
246                 file->atracks = (quicktime_audio_map_t*)calloc(1, sizeof(quicktime_audio_map_t));
247                 trak = quicktime_add_track(file);
248                 quicktime_trak_init_audio(file,
249                         trak,
250                         channels,
251                         sample_rate,
252                         bits,
253                         compressor);
254                 quicktime_init_audio_map(&(file->atracks[0]), trak);
255                 file->atracks[file->total_atracks].track = trak;
256                 file->atracks[file->total_atracks].channels = channels;
257                 file->atracks[file->total_atracks].current_position = 0;
258                 file->atracks[file->total_atracks].current_chunk = 1;
259                 file->total_atracks++;
260         }
261         return 1;   /* Return the number of tracks created */
262 }
263
264 int quicktime_set_video(quicktime_t *file,
265                                                 int tracks,
266                                                 int frame_w,
267                                                 int frame_h,
268                                                 double frame_rate,
269                                                 char *compressor)
270 {
271         int i;
272         quicktime_trak_t *trak;
273
274         if(tracks)
275         {
276                 quicktime_mhvd_init_video(file, &(file->moov.mvhd), frame_rate);
277                 file->total_vtracks = tracks;
278                 file->vtracks = (quicktime_video_map_t*)calloc(1, sizeof(quicktime_video_map_t) * file->total_vtracks);
279                 for(i = 0; i < tracks; i++)
280                 {
281                         trak = quicktime_add_track(file);
282                         quicktime_trak_init_video(file, trak, frame_w, frame_h, frame_rate, compressor);
283                         quicktime_init_video_map(&(file->vtracks[i]), trak);
284                 }
285         }
286
287         quicktime_set_cache_max(file, file->cache_size);
288
289         return 0;
290 }
291
292 void quicktime_set_framerate(quicktime_t *file, double framerate)
293 {
294         int i;
295         int new_time_scale, new_sample_duration;
296
297         if(!file->wr)
298         {
299                 fprintf(stderr, "quicktime_set_framerate shouldn't be called in read mode.\n");
300                 return;
301         }
302
303         new_time_scale = quicktime_get_timescale(framerate);
304         new_sample_duration = (int)((double)new_time_scale / framerate + 0.5);
305
306         for(i = 0; i < file->total_vtracks; i++)
307         {
308                 file->vtracks[i].track->mdia.mdhd.time_scale = new_time_scale;
309                 file->vtracks[i].track->mdia.minf.stbl.stts.table[0].sample_duration = new_sample_duration;
310         }
311 }
312
313
314 // Used by quicktime_set_video when creating a new file
315 quicktime_trak_t* quicktime_add_track(quicktime_t *file)
316 {
317         quicktime_moov_t *moov = &(file->moov);
318         quicktime_trak_t *trak;
319         int i;
320
321         for(i = moov->total_tracks; i > 0; i--)
322                 moov->trak[i] = moov->trak[i - 1];
323
324         trak =
325                 moov->trak[0] =
326                 calloc(1, sizeof(quicktime_trak_t));
327         quicktime_trak_init(trak);
328         moov->total_tracks++;
329
330         for(i = 0; i < moov->total_tracks; i++)
331                 moov->trak[i]->tkhd.track_id = i + 1;
332         moov->mvhd.next_track_id++;
333         return trak;
334 }
335
336 /* ============================= Initialization functions */
337
338 int quicktime_init(quicktime_t *file)
339 {
340         bzero(file, sizeof(quicktime_t));
341         quicktime_moov_init(&(file->moov));
342         file->cpus = 1;
343         file->color_model = BC_RGB888;
344         file->current_frame = 0;
345         file->is_odml = 0;
346         return 0;
347 }
348
349 int quicktime_delete(quicktime_t *file)
350 {
351         int i;
352         if(file->total_atracks)
353         {
354                 for(i = 0; i < file->total_atracks; i++)
355                         quicktime_delete_audio_map(&(file->atracks[i]));
356                 free(file->atracks);
357         }
358
359         if(file->total_vtracks)
360         {
361                 for(i = 0; i < file->total_vtracks; i++)
362                         quicktime_delete_video_map(&(file->vtracks[i]));
363                 free(file->vtracks);
364         }
365
366         file->total_atracks = 0;
367         file->total_vtracks = 0;
368
369         if(file->moov_data)
370                 free(file->moov_data);
371
372         if(file->preload_size)
373         {
374                 free(file->preload_buffer);
375                 file->preload_size = 0;
376         }
377
378         if(file->presave_buffer)
379         {
380                 free(file->presave_buffer);
381         }
382
383         for(i = 0; i < file->total_riffs; i++)
384         {
385                 quicktime_delete_riff(file, file->riff[i]);
386         }
387
388         quicktime_moov_delete(&(file->moov));
389         quicktime_mdat_delete(&(file->mdat));
390         quicktime_delete_asf(file->asf);
391         return 0;
392 }
393
394 /* =============================== Optimization functions */
395
396 int quicktime_set_cpus(quicktime_t *file, int cpus)
397 {
398         if(cpus > 0) file->cpus = cpus;
399         return 0;
400 }
401
402 void quicktime_set_preload(quicktime_t *file, int64_t preload)
403 {
404         file->preload_size = preload;
405         if(file->preload_buffer) free(file->preload_buffer);
406         file->preload_buffer = 0;
407         if(preload)
408                 file->preload_buffer = calloc(1, preload);
409         file->preload_start = 0;
410         file->preload_end = 0;
411         file->preload_ptr = 0;
412 }
413
414
415 int quicktime_get_timescale(double frame_rate)
416 {
417         int timescale = 600;
418 /* Encode the 29.97, 23.976, 59.94 framerates */
419         if(frame_rate - (int)frame_rate != 0)
420                 timescale = (int)(frame_rate * 1001 + 0.5);
421         else
422         if((600 / frame_rate) - (int)(600 / frame_rate) != 0)
423                         timescale = (int)(frame_rate * 100 + 0.5);
424 //printf("quicktime_get_timescale %f %d\n", 600 / frame_rate, (int)(600 / frame_rate));
425         return timescale;
426 }
427
428 int quicktime_seek_end(quicktime_t *file)
429 {
430         quicktime_set_position(file, file->mdat.atom.size + file->mdat.atom.start + HEADER_LENGTH * 2);
431 /*printf("quicktime_seek_end %ld\n", file->mdat.atom.size + file->mdat.atom.start); */
432         quicktime_update_positions(file);
433         return 0;
434 }
435
436 int quicktime_seek_start(quicktime_t *file)
437 {
438         quicktime_set_position(file, file->mdat.atom.start + HEADER_LENGTH * 2);
439         quicktime_update_positions(file);
440         return 0;
441 }
442
443 long quicktime_audio_length(quicktime_t *file, int track)
444 {
445         if(file->total_atracks > 0)
446                 return quicktime_track_samples(file, file->atracks[track].track);
447
448         return 0;
449 }
450
451 long quicktime_video_length(quicktime_t *file, int track)
452 {
453 /*printf("quicktime_video_length %d %d\n", quicktime_track_samples(file, file->vtracks[track].track), track); */
454         if(file->total_vtracks > 0)
455                 return quicktime_track_samples(file, file->vtracks[track].track);
456         return 0;
457 }
458
459 long quicktime_audio_position(quicktime_t *file, int track)
460 {
461         return file->atracks[track].current_position;
462 }
463
464 long quicktime_video_position(quicktime_t *file, int track)
465 {
466         return file->vtracks[track].current_position;
467 }
468
469 int quicktime_update_positions(quicktime_t *file)
470 {
471 /* Get the sample position from the file offset */
472 /* for routines that change the positions of all tracks, like */
473 /* seek_end and seek_start but not for routines that reposition one track, like */
474 /* set_audio_position. */
475
476         int64_t mdat_offset = quicktime_position(file) - file->mdat.atom.start;
477         int64_t sample, chunk, chunk_offset;
478         int i;
479
480         if(file->total_atracks)
481         {
482                 sample = quicktime_offset_to_sample(file->atracks[0].track, mdat_offset);
483                 chunk = quicktime_offset_to_chunk(&chunk_offset, file->atracks[0].track, mdat_offset);
484                 for(i = 0; i < file->total_atracks; i++)
485                 {
486                         file->atracks[i].current_position = sample;
487                         file->atracks[i].current_chunk = chunk;
488                 }
489         }
490
491         if(file->total_vtracks)
492         {
493                 sample = quicktime_offset_to_sample(file->vtracks[0].track, mdat_offset);
494                 chunk = quicktime_offset_to_chunk(&chunk_offset, file->vtracks[0].track, mdat_offset);
495                 for(i = 0; i < file->total_vtracks; i++)
496                 {
497                         file->vtracks[i].current_position = sample;
498                         file->vtracks[i].current_chunk = chunk;
499                 }
500         }
501         return 0;
502 }
503
504 int quicktime_set_audio_position(quicktime_t *file, int64_t sample, int track)
505 {
506         int64_t offset, chunk_sample, chunk;
507         quicktime_trak_t *trak;
508
509         if(track < file->total_atracks)
510         {
511                 trak = file->atracks[track].track;
512                 file->atracks[track].current_position = sample;
513                 quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, sample);
514                 file->atracks[track].current_chunk = chunk;
515                 offset = quicktime_sample_to_offset(file, trak, sample);
516                 quicktime_set_position(file, offset);
517         }
518         else
519                 fprintf(stderr, "quicktime_set_audio_position: track >= file->total_atracks\n");
520
521         return 0;
522 }
523
524 int quicktime_set_video_position(quicktime_t *file, int64_t frame, int track)
525 {
526         int64_t offset, chunk_sample, chunk;
527         quicktime_trak_t *trak;
528         if(track >= file->total_vtracks)
529         {
530                 fprintf(stderr,
531                         "quicktime_set_video_position: frame=%lld track=%d >= file->total_vtracks %d\n",
532                         (long long)frame,
533                         track,
534                         file->total_vtracks);
535                 track = file->total_vtracks - 1;
536         }
537
538         if(track < file->total_vtracks && track >= 0)
539         {
540                 trak = file->vtracks[track].track;
541                 file->vtracks[track].current_position = frame;
542                 quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, frame);
543                 file->vtracks[track].current_chunk = chunk;
544                 offset = quicktime_sample_to_offset(file, trak, frame);
545                 quicktime_set_position(file, offset);
546         }
547         return 0;
548 }
549
550 int quicktime_has_audio(quicktime_t *file)
551 {
552         if(quicktime_audio_tracks(file)) return 1;
553         return 0;
554 }
555
556 long quicktime_sample_rate(quicktime_t *file, int track)
557 {
558         if(file->total_atracks)
559         {
560                 quicktime_trak_t *trak = file->atracks[track].track;
561                 return trak->mdia.minf.stbl.stsd.table[0].sample_rate;
562         }
563         return 0;
564 }
565
566 int quicktime_audio_bits(quicktime_t *file, int track)
567 {
568         if(file->total_atracks)
569                 return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].sample_size;
570
571         return 0;
572 }
573
574 char* quicktime_audio_compressor(quicktime_t *file, int track)
575 {
576         return file->atracks[track].track->mdia.minf.stbl.stsd.table[0].format;
577 }
578
579 int quicktime_track_channels(quicktime_t *file, int track)
580 {
581         if(track < file->total_atracks)
582                 return file->atracks[track].channels;
583
584         return 0;
585 }
586
587 /* Input is absolute channel number in stream, output is quicktime_track in which the channel resides and quicktime_channel relative to the track */
588
589 int quicktime_channel_location(quicktime_t *file, int *quicktime_track, int *quicktime_channel, int channel)
590 {
591         int current_channel = 0, current_track = 0;
592         *quicktime_channel = 0;
593         *quicktime_track = 0;
594         for(current_channel = 0, current_track = 0; current_track < file->total_atracks; )
595         {
596                 if(channel >= current_channel)
597                 {
598                         *quicktime_channel = channel - current_channel;
599                         *quicktime_track = current_track;
600                 }
601
602                 current_channel += file->atracks[current_track].channels;
603                 current_track++;
604         }
605         return 0;
606 }
607
608 int quicktime_has_video(quicktime_t *file)
609 {
610         if(quicktime_video_tracks(file)) return 1;
611         return 0;
612 }
613
614 int quicktime_video_width(quicktime_t *file, int track)
615 {
616         if(file->total_vtracks)
617                 return file->vtracks[track].track->tkhd.track_width;
618         return 0;
619 }
620
621 int quicktime_video_height(quicktime_t *file, int track)
622 {
623         if(file->total_vtracks)
624                 return file->vtracks[track].track->tkhd.track_height;
625         return 0;
626 }
627
628 int quicktime_video_depth(quicktime_t *file, int track)
629 {
630         if(file->total_vtracks)
631                 return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].depth;
632         return 0;
633 }
634
635 void quicktime_set_cmodel(quicktime_t *file, int colormodel)
636 {
637         file->color_model = colormodel;
638 }
639
640 void quicktime_set_row_span(quicktime_t *file, int row_span)
641 {
642         file->row_span = row_span;
643 }
644
645 void quicktime_set_window(quicktime_t *file,
646         int in_x,                    /* Location of input frame to take picture */
647         int in_y,
648         int in_w,
649         int in_h,
650         int out_w,                   /* Dimensions of output frame */
651         int out_h)
652 {
653         if(in_x >= 0 && in_y >= 0 && in_w > 0 && in_h > 0 && out_w > 0 && out_h > 0)
654         {
655                 file->do_scaling = 1;
656                 file->in_x = in_x;
657                 file->in_y = in_y;
658                 file->in_w = in_w;
659                 file->in_h = in_h;
660                 file->out_w = out_w;
661                 file->out_h = out_h;
662         }
663         else
664         {
665                 file->do_scaling = 0;
666 /* quicktime_decode_video now sets the window for every frame based on the */
667 /* track dimensions */
668         }
669 }
670
671 void quicktime_set_depth(quicktime_t *file, int depth, int track)
672 {
673         int i;
674
675         for(i = 0; i < file->total_vtracks; i++)
676         {
677                 file->vtracks[i].track->mdia.minf.stbl.stsd.table[0].depth = depth;
678         }
679 }
680
681 double quicktime_frame_rate(quicktime_t *file, int track)
682 {
683         if(file->total_vtracks > track)
684         {
685                 quicktime_trak_t *trak = file->vtracks[track].track;
686                 int time_scale = file->vtracks[track].track->mdia.mdhd.time_scale;
687                 int sample_duration = quicktime_sample_duration(trak);
688                 return (double)time_scale / sample_duration;
689 //              return (float)file->vtracks[track].track->mdia.mdhd.time_scale /
690 //                      file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration;
691         }
692         return 0;
693 }
694
695 int quicktime_frame_rate_n(quicktime_t *file, int track)
696 {
697         if(file->total_vtracks > track)
698                 return file->vtracks[track].track->mdia.mdhd.time_scale;
699         return 0;
700 }
701
702 int quicktime_frame_rate_d(quicktime_t *file, int track)
703 {
704         if(file->total_vtracks > track)
705                 return file->vtracks[track].track->mdia.minf.stbl.stts.table[0].sample_duration;
706         return 0;
707 }
708
709 char* quicktime_video_compressor(quicktime_t *file, int track)
710 {
711         return file->vtracks[track].track->mdia.minf.stbl.stsd.table[0].format;
712 }
713
714 int quicktime_write_audio(quicktime_t *file,
715         char *audio_buffer,
716         long samples,
717         int track)
718 {
719         int result;
720         int64_t bytes;
721         quicktime_atom_t chunk_atom;
722         quicktime_audio_map_t *track_map = &file->atracks[track];
723         quicktime_trak_t *trak = track_map->track;
724
725 /* write chunk for 1 track */
726         bytes = samples * quicktime_audio_bits(file, track) / 8 * file->atracks[track].channels;
727         quicktime_write_chunk_header(file, trak, &chunk_atom);
728         result = !quicktime_write_data(file, audio_buffer, bytes);
729         quicktime_write_chunk_footer(file,
730                                         trak,
731                                         track_map->current_chunk,
732                                         &chunk_atom,
733                                         samples);
734
735 /*      file->atracks[track].current_position += samples; */
736         file->atracks[track].current_chunk++;
737         return result;
738 }
739
740 int quicktime_write_frame(quicktime_t *file,
741         unsigned char *video_buffer,
742         int64_t bytes,
743         int track)
744 {
745 //      int64_t offset = quicktime_position(file);
746         int result = 0;
747         quicktime_atom_t chunk_atom;
748         quicktime_video_map_t *vtrack = &file->vtracks[track];
749         quicktime_trak_t *trak = vtrack->track;
750
751         if(!bytes) return 0;
752
753         quicktime_write_chunk_header(file, trak, &chunk_atom);
754         result = !quicktime_write_data(file, (char*)video_buffer, bytes);
755         quicktime_write_chunk_footer(file,
756                                         trak,
757                                         vtrack->current_chunk,
758                                         &chunk_atom,
759                                         1);
760         file->vtracks[track].current_position++;
761         file->vtracks[track].current_chunk++;
762         return result;
763 }
764
765
766 long quicktime_read_audio(quicktime_t *file,
767         char *audio_buffer, long samples, int track)
768 {
769         quicktime_audio_map_t *track_map = &file->atracks[track];
770         quicktime_trak_t *trak = track_map->track;
771         int64_t position, chunk, chunk_sample;
772         long ret, bytes, chunk_offset, chunk_len;
773         int result = 0;
774
775         position = file->atracks[track].current_position;
776         quicktime_chunk_of_sample(&chunk_sample, &chunk, trak, position);
777         ret = 0;
778
779         while( samples > 0 ) {
780                 int64_t offset = quicktime_sample_to_offset(file, trak, position);
781                 quicktime_set_position(file, offset);
782                 chunk_offset = position - chunk_sample;
783                 chunk_len = quicktime_chunk_samples(trak, chunk) - chunk_offset;
784                 if( chunk_len > samples ) chunk_len = samples;
785                 else ++chunk;
786                 chunk_sample = (position += chunk_len);
787                 bytes = quicktime_samples_to_bytes(trak, chunk_len);
788                 result = !quicktime_read_data(file, &audio_buffer[ret], bytes);
789                 if( result ) break;
790                 ret += bytes;
791                 samples -= chunk_len;
792         }
793 //printf("quicktime_read_audio 5\n");
794
795         track_map->current_position = position;
796         track_map->current_chunk = chunk;
797         return !result ? ret : 0;
798 }
799
800 int quicktime_read_chunk(quicktime_t *file, char *output, int track, int64_t chunk, int64_t byte_start, int64_t byte_len)
801 {
802         quicktime_set_position(file,
803                 quicktime_chunk_to_offset(file, file->atracks[track].track, chunk) +
804                 byte_start);
805         if(quicktime_read_data(file, output, byte_len)) return 0;
806         else
807         return 1;
808 }
809
810 long quicktime_frame_size(quicktime_t *file, long frame, int track)
811 {
812         long bytes = 0;
813         quicktime_trak_t *trak = file->vtracks[track].track;
814
815         if(trak->mdia.minf.stbl.stsz.sample_size)
816         {
817                 bytes = trak->mdia.minf.stbl.stsz.sample_size;
818         }
819         else
820         {
821                 long total_frames = quicktime_track_samples(file, trak);
822                 if(frame < 0) frame = 0;
823                 else
824                 if(frame > total_frames - 1) frame = total_frames - 1;
825                 bytes = trak->mdia.minf.stbl.stsz.table[frame].size;
826         }
827
828
829         return bytes;
830 }
831
832
833 long quicktime_read_frame(quicktime_t *file, unsigned char *video_buffer, int track)
834 {
835         int result = 0;
836         int64_t bytes = quicktime_frame_size(file, file->vtracks[track].current_position, track);
837         quicktime_set_video_position(file, file->vtracks[track].current_position, track);
838
839 /*
840  * printf("quicktime_read_frame 0x%llx %lld\n",
841  * quicktime_ftell(file),
842  * bytes);
843  */
844         result = quicktime_read_data(file, (char*)video_buffer, bytes);
845         file->vtracks[track].current_position++;
846
847         if(!result) return 0;
848         return bytes;
849 }
850
851 int64_t quicktime_get_keyframe_before(quicktime_t *file, int64_t frame, int track)
852 {
853         quicktime_trak_t *trak = file->vtracks[track].track;
854         quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
855         int i;
856
857
858
859
860
861 // Offset 1
862         frame++;
863
864
865         for(i = stss->total_entries - 1; i >= 0; i--)
866         {
867                 if(stss->table[i].sample <= frame) return stss->table[i].sample - 1;
868         }
869
870         return 0;
871 }
872
873 int64_t quicktime_get_keyframe_after(quicktime_t *file, int64_t frame, int track)
874 {
875         quicktime_trak_t *trak = file->vtracks[track].track;
876         quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
877         int i;
878
879
880
881
882
883 // Offset 1
884         frame++;
885
886
887         for(i = 0; i < stss->total_entries; i++)
888         {
889                 if(stss->table[i].sample >= frame) return stss->table[i].sample - 1;
890         }
891
892         return 0;
893 }
894
895 void quicktime_insert_keyframe(quicktime_t *file, int64_t frame, int track)
896 {
897         quicktime_trak_t *trak = file->vtracks[track].track;
898         quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
899         int i;
900
901 // Set keyframe flag in idx1 table.
902 // Only possible in the first RIFF.  After that, there's no keyframe support.
903         if(file->use_avi && file->total_riffs == 1)
904                 quicktime_set_idx1_keyframe(file,
905                         trak,
906                         frame);
907
908 // Offset 1
909         frame++;
910
911
912 // Get the keyframe greater or equal to new frame
913         for(i = 0; i < stss->total_entries; i++)
914         {
915                 if(stss->table[i].sample >= frame) break;
916         }
917
918 // Expand table
919         if(stss->entries_allocated <= stss->total_entries)
920         {
921                 stss->entries_allocated *= 2;
922                 stss->table = realloc(stss->table, sizeof(quicktime_stss_table_t) * stss->entries_allocated);
923         }
924
925 // Insert before existing frame
926         if(i < stss->total_entries)
927         {
928                 if(stss->table[i].sample > frame)
929                 {
930                         int j, k;
931                         for(j = stss->total_entries, k = stss->total_entries - 1;
932                                 k >= i;
933                                 j--, k--)
934                         {
935                                 stss->table[j] = stss->table[k];
936                         }
937                         stss->table[i].sample = frame;
938                 }
939         }
940         else
941 // Insert after last frame
942                 stss->table[i].sample = frame;
943
944         stss->total_entries++;
945 }
946
947
948 int quicktime_has_keyframes(quicktime_t *file, int track)
949 {
950         quicktime_trak_t *trak = file->vtracks[track].track;
951         quicktime_stss_t *stss = &trak->mdia.minf.stbl.stss;
952
953         return stss->total_entries > 0;
954 }
955
956
957
958
959
960 int quicktime_read_frame_init(quicktime_t *file, int track)
961 {
962         quicktime_set_video_position(file, file->vtracks[track].current_position, track);
963         if(quicktime_ftell(file) != file->file_position)
964         {
965                 FSEEK(file->stream, file->file_position, SEEK_SET);
966                 file->ftell_position = file->file_position;
967         }
968         return 0;
969 }
970
971 int quicktime_read_frame_end(quicktime_t *file, int track)
972 {
973         file->file_position = quicktime_ftell(file);
974         file->vtracks[track].current_position++;
975         return 0;
976 }
977
978 int quicktime_init_video_map(quicktime_video_map_t *vtrack, quicktime_trak_t *trak)
979 {
980         vtrack->track = trak;
981         vtrack->current_position = 0;
982         vtrack->current_chunk = 1;
983         quicktime_init_vcodec(vtrack);
984         vtrack->frame_cache = quicktime_new_cache();
985         return 0;
986 }
987
988 int quicktime_delete_video_map(quicktime_video_map_t *vtrack)
989 {
990         quicktime_delete_vcodec(vtrack);
991         if(vtrack->frame_cache) quicktime_delete_cache(vtrack->frame_cache);
992         vtrack->frame_cache = 0;
993         return 0;
994 }
995
996 int64_t quicktime_memory_usage(quicktime_t *file)
997 {
998         int i;
999         int64_t result = 0;
1000 //printf("quicktime_memory_usage %d\n", file->total_vtracks);
1001         for(i = 0; i < file->total_vtracks; i++)
1002         {
1003                 result += quicktime_cache_usage(file->vtracks[i].frame_cache);
1004         }
1005         return result;
1006 }
1007
1008 void quicktime_set_cache_max(quicktime_t *file, int bytes)
1009 {
1010         int i;
1011         file->cache_size = bytes;
1012
1013
1014 //printf("quicktime_set_cache_max %d %d %d\n", __LINE__, bytes, file->total_vtracks);
1015         for(i = 0; i < file->total_vtracks; i++)
1016         {
1017                 quicktime_cache_max(file->vtracks[i].frame_cache, bytes);
1018         }
1019 }
1020
1021
1022
1023
1024
1025 int quicktime_init_audio_map(quicktime_audio_map_t *atrack, quicktime_trak_t *trak)
1026 {
1027         atrack->track = trak;
1028         atrack->channels = trak->mdia.minf.stbl.stsd.table[0].channels;
1029         atrack->current_position = 0;
1030         atrack->current_chunk = 1;
1031         quicktime_init_acodec(atrack);
1032         return 0;
1033 }
1034
1035 int quicktime_delete_audio_map(quicktime_audio_map_t *atrack)
1036 {
1037         quicktime_delete_acodec(atrack);
1038         quicktime_clear_vbr(&atrack->vbr);
1039         return 0;
1040 }
1041
1042 void quicktime_init_maps(quicktime_t *file)
1043 {
1044         int i, track;
1045 /* get tables for all the different tracks */
1046         file->total_atracks = quicktime_audio_tracks(file);
1047         file->atracks = (quicktime_audio_map_t*)calloc(1, sizeof(quicktime_audio_map_t) * file->total_atracks);
1048
1049         for(i = 0, track = 0; i < file->total_atracks; i++)
1050         {
1051                 while(!file->moov.trak[track]->mdia.minf.is_audio)
1052                         track++;
1053                 quicktime_init_audio_map(&(file->atracks[i]), file->moov.trak[track]);
1054         }
1055
1056         file->total_vtracks = quicktime_video_tracks(file);
1057         file->vtracks = (quicktime_video_map_t*)calloc(1, sizeof(quicktime_video_map_t) * file->total_vtracks);
1058
1059         for(track = 0, i = 0; i < file->total_vtracks; i++)
1060         {
1061                 while(!file->moov.trak[track]->mdia.minf.is_video)
1062                         track++;
1063
1064                 quicktime_init_video_map(&(file->vtracks[i]), file->moov.trak[track]);
1065         }
1066
1067         quicktime_set_cache_max(file, file->cache_size);
1068 }
1069
1070 int quicktime_read_info(quicktime_t *file)
1071 {
1072         int result = 0, got_header = 0;
1073         int64_t start_position = quicktime_position(file);
1074         quicktime_atom_t leaf_atom;
1075         quicktime_guid_t guid;
1076         char avi_avi[4];
1077         int got_avi = 0;
1078         int got_asf = 0;
1079         file->use_avi = 0;
1080         file->use_asf = 0;
1081
1082         quicktime_set_position(file, 0LL);
1083
1084 /* Test for ASF */
1085         quicktime_read_guid(file, &guid);
1086         quicktime_set_position(file, 0LL);
1087         if(!memcmp(&guid, &asf_header, sizeof(guid)))
1088         {
1089                 printf("quicktime_read_info: Got ASF\n");
1090                 got_asf = 1;
1091                 got_header = 1;
1092         }
1093
1094 /* Test file format */
1095         if(!got_asf)
1096         {
1097                 quicktime_set_position(file, 0LL);
1098                 do
1099                 {
1100                         file->use_avi = 1;
1101                         result = quicktime_atom_read_header(file, &leaf_atom);
1102
1103                         if(!result && quicktime_atom_is(&leaf_atom, "RIFF"))
1104                         {
1105                                 quicktime_read_data(file, avi_avi, 4);
1106                                 if(quicktime_match_32(avi_avi, "AVI "))
1107                                 {
1108                                         got_avi = 1;
1109                                 }
1110                                 else
1111                                 {
1112                                         file->use_avi = 0;
1113                                         result = 0;
1114                                         break;
1115                                 }
1116                         }
1117                         else
1118                         {
1119                                 file->use_avi = 0;
1120                                 result = 0;
1121                                 break;
1122                         }
1123                 }while(1);
1124         }
1125
1126         if(got_avi) file->use_avi = 1;
1127         else
1128         if(got_asf) file->use_asf = 1;
1129
1130         quicktime_set_position(file, 0LL);
1131
1132
1133 /* McRoweSoft AVI section */
1134         if(file->use_avi)
1135         {
1136 /* Import first RIFF */
1137                 do
1138                 {
1139                         result = quicktime_atom_read_header(file, &leaf_atom);
1140                         if(!result)
1141                         {
1142                                 if(quicktime_atom_is(&leaf_atom, "RIFF"))
1143                                 {
1144                                         quicktime_read_riff(file, &leaf_atom);
1145 /* Return success */
1146                                         got_header = 1;
1147                                 }
1148                         }
1149                 }while(!result &&
1150                         !got_header &&
1151                         quicktime_position(file) < file->total_length);
1152
1153 /* Construct indexes. */
1154                 quicktime_import_avi(file);
1155         }
1156 /* Quicktime section */
1157         else
1158         if(file->use_asf)
1159         {
1160                 result = quicktime_read_asf(file);
1161                 if(result) got_header = 0;
1162                 else
1163                 quicktime_dump_asf(file->asf);
1164         }
1165         else
1166         if(!file->use_avi)
1167         {
1168                 do
1169                 {
1170                         result = quicktime_atom_read_header(file, &leaf_atom);
1171
1172                         if(!result)
1173                         {
1174                                 if(quicktime_atom_is(&leaf_atom, "mdat"))
1175                                 {
1176                                         quicktime_read_mdat(file, &(file->mdat), &leaf_atom);
1177                                 }
1178                                 else
1179                                 if(quicktime_atom_is(&leaf_atom, "moov"))
1180                                 {
1181 /* Set preload and preload the moov atom here */
1182                                         int64_t start_position = quicktime_position(file);
1183                                         long temp_size = leaf_atom.end - start_position;
1184                                         char *temp = malloc(temp_size);
1185                                         quicktime_set_preload(file,
1186                                                 (temp_size < 0x100000) ? 0x100000 : temp_size);
1187                                         quicktime_read_data(file, temp, temp_size);
1188                                         quicktime_set_position(file, start_position);
1189                                         free(temp);
1190
1191                                         if(quicktime_read_moov(file, &(file->moov), &leaf_atom))
1192                                                 return 1;
1193                                         got_header = 1;
1194                                 }
1195                                 else
1196                                         quicktime_atom_skip(file, &leaf_atom);
1197                         }
1198                 }while(!result && quicktime_position(file) < file->total_length);
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 /* go back to the original position */
1210                 quicktime_set_position(file, start_position);
1211
1212         }
1213
1214 /* Initialize track map objects */
1215         if(got_header)
1216         {
1217                 quicktime_init_maps(file);
1218         }
1219
1220 /* Shut down preload in case of an obsurdly high temp_size */
1221         quicktime_set_preload(file, 0);
1222
1223         return !got_header;
1224 }
1225
1226
1227 int quicktime_dump(quicktime_t *file)
1228 {
1229         printf("quicktime_dump\n");
1230         printf("movie data\n");
1231         printf(" size %ld\n", file->mdat.atom.size);
1232         printf(" start %ld\n", file->mdat.atom.start);
1233         quicktime_moov_dump(&(file->moov));
1234         return 0;
1235 }
1236
1237
1238
1239
1240
1241 int quicktime_check_sig(char *path)
1242 {
1243         quicktime_t file;
1244         quicktime_atom_t leaf_atom;
1245         int result = 0, result1 = 0, result2 = 0;
1246         char avi_test[12];
1247
1248         quicktime_init(&file);
1249         result = quicktime_file_open(&file, path, 1, 0);
1250
1251         if(!result)
1252         {
1253 // Check for Microsoft AVI
1254                 quicktime_read_data(&file, avi_test, 12);
1255                 quicktime_set_position(&file, 0);
1256                 if(quicktime_match_32(avi_test, "RIFF") &&
1257                         quicktime_match_32(avi_test + 8, "AVI "))
1258                 {
1259                         result2 = 1;
1260                 }
1261
1262
1263
1264 /*
1265  *              if(!result2)
1266  * // Check for Microsoft ASF
1267  *              {
1268  *                      quicktime_guid_t guid;
1269  *                      quicktime_read_guid(&file, &guid);
1270  *                      quicktime_set_position(&file, 0);
1271  *                      if(!memcmp(&guid, &asf_header, sizeof(guid)))
1272  *                      {
1273  *                              printf("quicktime_check_sig: Got ASF\n");
1274  *                              result2 = 1;
1275  *                      }
1276  *              }
1277  */
1278
1279                 if(!result2)
1280                 {
1281                         do
1282                         {
1283                                 result1 = quicktime_atom_read_header(&file, &leaf_atom);
1284
1285                                 if(!result1)
1286                                 {
1287 /* just want the "moov" atom */
1288                                         if(quicktime_atom_is(&leaf_atom, "moov"))
1289                                         {
1290                                                 result2 = 1;
1291                                         }
1292                                         else
1293                                                 quicktime_atom_skip(&file, &leaf_atom);
1294                                 }
1295                         }while(!result1 && !result2 && quicktime_position(&file) < file.total_length);
1296                 }
1297         }
1298
1299 //printf(__FUNCTION__ " 2 %d\n", result2);
1300         quicktime_file_close(&file);
1301         quicktime_delete(&file);
1302         return result2;
1303 }
1304
1305 void quicktime_set_avi(quicktime_t *file, int value)
1306 {
1307         file->use_avi = value;
1308         quicktime_set_position(file, 0);
1309
1310 // Write RIFF chunk
1311         quicktime_init_riff(file);
1312 }
1313
1314 int quicktime_is_avi(quicktime_t *file)
1315 {
1316         return file->use_avi;
1317 }
1318
1319
1320 void quicktime_set_asf(quicktime_t *file, int value)
1321 {
1322         file->use_asf = value;
1323 }
1324
1325 void quicktime_set_frame_start(quicktime_t *file, int64_t value)
1326 {
1327         file->current_frame = value;
1328 }
1329
1330 quicktime_t* quicktime_open(char *filename, int rd, int wr)
1331 {
1332         quicktime_t *new_file = calloc(1, sizeof(quicktime_t));
1333         int result = 0;
1334
1335 //printf("quicktime_open 1\n");
1336         quicktime_init(new_file);
1337         new_file->wr = wr;
1338         new_file->rd = rd;
1339         new_file->mdat.atom.start = 0;
1340
1341         result = quicktime_file_open(new_file, filename, rd, wr);
1342
1343         if(!result)
1344         {
1345                 if(rd)
1346                 {
1347                         if(quicktime_read_info(new_file))
1348                         {
1349                                 quicktime_close(new_file);
1350                                 fprintf(stderr, "quicktime_open: error in header\n");
1351                                 new_file = 0;
1352                         }
1353                 }
1354
1355 /* start the data atom */
1356 /* also don't want to do this if making a streamable file */
1357                 if(wr)
1358                 {
1359                         quicktime_set_presave(new_file, 1);
1360
1361
1362
1363
1364 // android requires the ftyp header
1365                         const unsigned char ftyp_data[] =
1366                         {
1367                                 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70,
1368                                 0x6d, 0x70, 0x34, 0x32, 0x00, 0x00, 0x00, 0x01,
1369                                 0x6d, 0x70, 0x34, 0x32, 0x61, 0x76, 0x63, 0x31
1370                         };
1371                         quicktime_write_data(new_file, (char*)ftyp_data, sizeof(ftyp_data));
1372
1373                         quicktime_atom_write_header64(new_file,
1374                                 &new_file->mdat.atom,
1375                                 "mdat");
1376                         quicktime_set_presave(new_file, 0);
1377                 }
1378         }
1379         else
1380         {
1381 //printf("quicktime_open 10\n");
1382                 quicktime_close(new_file);
1383 //printf("quicktime_open 100\n");
1384                 new_file = 0;
1385         }
1386
1387
1388         return new_file;
1389 }
1390
1391 int quicktime_close(quicktime_t *file)
1392 {
1393         int result = 0;
1394         if(file->wr)
1395         {
1396                 quicktime_codecs_flush(file);
1397
1398 // Reenable buffer for quick header writing.
1399                 quicktime_set_presave(file, 1);
1400                 if(file->use_avi)
1401                 {
1402                         quicktime_atom_t junk_atom;
1403                         int i;
1404
1405 // Finalize last header
1406                         quicktime_finalize_riff(file, file->riff[file->total_riffs - 1]);
1407
1408                         int64_t position = quicktime_position(file);
1409
1410 // Finalize the odml header
1411                         quicktime_finalize_odml(file, &file->riff[0]->hdrl);
1412
1413 // Finalize super indexes
1414                         quicktime_finalize_indx(file);
1415
1416 // Pad ending
1417                         quicktime_set_position(file, position);
1418                         quicktime_atom_write_header(file, &junk_atom, "JUNK");
1419                         for(i = 0; i < 0x406; i++)
1420                                 quicktime_write_int32_le(file, 0);
1421                         quicktime_atom_write_footer(file, &junk_atom);
1422                 }
1423                 else
1424                 {
1425 // Atoms are only written here
1426                         if(file->stream)
1427                         {
1428                                 quicktime_write_moov(file, &(file->moov), 1);
1429                                 quicktime_atom_write_footer(file, &file->mdat.atom);
1430                         }
1431                 }
1432         }
1433
1434         quicktime_file_close(file);
1435
1436         quicktime_delete(file);
1437         free(file);
1438         return result;
1439 }
1440
1441 int quicktime_major()
1442 {
1443         return QUICKTIME_MAJOR;
1444 }
1445
1446 int quicktime_minor()
1447 {
1448         return QUICKTIME_MINOR;
1449 }
1450
1451 int quicktime_release()
1452 {
1453         return QUICKTIME_RELEASE;
1454 }
1455