1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 //		QUIKWRIT.C		QuickTime file writing
20 //		Rex E. Bradford
21 
22 /*
23  * $Source: r:/prj/lib/src/afile/RCS/quikwrit.c $
24  * $Revision: 1.6 $
25  * $Author: rex $
26  * $Date: 1994/10/18 16:01:53 $
27  * $Log: quikwrit.c $
28  * Revision 1.6  1994/10/18  16:01:53  rex
29  * Got 16-bit depth working
30  *
31  * Revision 1.5  1994/10/04  20:42:01  rex
32  * Fixed bugs related to variable-timed frames, put in basic support
33  * for 16-bit depth frames, but doesn't work because rgb bitorder unknown
34  *
35  * Revision 1.4  1994/10/03  19:43:38  rex
36  * Modified to respect varying frame rate
37  *
38  * Revision 1.3  1994/10/03  18:05:20  rex
39  * Modified QuikAddVideoSample() to handle bitmaps where w != row
40  *
41  * Revision 1.2  1994/09/30  17:00:18  rex
42  * Made it actually work
43  *
44  * Revision 1.1  1994/09/29  10:36:52  rex
45  * Initial revision
46  *
47  *
48  */
49 
50 #include <ctype.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 
55 #include "lg.h"
56 #include "quiktime.h"
57 
58 //	Handy macros
59 
60 #define FIX8(v) ((v) >> 8)
61 #define DEFAULT_MACTIME 2855653576
62 #define DEFAULT_TIMESCALE 600
63 #define FRAME_ALLOC_INCR 128
64 #define MDHD_TIMESCALE 30
65 
66 //	Internal prototypes
67 
68 void QuikStartSubChunk(QTM *pqtm, FILE *fp, QT_Ctype ctype, void *data, long len);
69 void QuikEndSubChunk(QTM *pqtm, FILE *fp);
70 void QuikWriteMVHD(QTM *pqtm, FILE *fp, ulong timeTot);
71 void QuikWriteVideoTrack(QTM *pqtm, FILE *fp, ulong timeTot);
72 void WriteChunkWithString(FILE *fp, QT_Ctype ctype, void *data, long len, char *str);
73 ulong QuikComputeTotalTime(QTM *pqtm);
74 long QuikSampleLenMsec(fix t, fix tlast);
75 fix QuikSampleLenMsecBackToFix(long msec);
76 
77 //	-------------------------------------------------------------
78 //		MOVIE WRITING
79 //	-------------------------------------------------------------
80 //
81 //	QuikCreateMovie() creates a quiktime movie ready for writing.
82 
QuikCreateMovie(QTM * pqtm,FILE * fp)83 void QuikCreateMovie(QTM *pqtm, FILE *fp) {
84   memset(pqtm, 0, sizeof(QTM));
85 
86   QuikStartSubChunk(pqtm, fp, QT_MDAT, NULL, 0);
87 }
88 /*
89 //	-------------------------------------------------------------
90 //
91 //	QuikSetPal() sets the video track's palette
92 
93 void QuikSetPal(QTM *pqtm, uchar *pal)
94 {
95         if (pqtm->pVideoTrack == NULL)
96                 pqtm->pVideoTrack = &pqtm->track[pqtm->numTracks++];
97 
98         pqtm->pVideoTrack->palette = Malloc(768);
99         memcpy(pqtm->pVideoTrack->palette, pal, 768);
100 }
101 */
102 //	-------------------------------------------------------------
103 //
104 //	QuikAddVideoSample() adds a video sample
105 
QuikAddVideoSample(QTM * pqtm,FILE * fp,grs_bitmap * pbm,fix time)106 void QuikAddVideoSample(QTM *pqtm, FILE *fp, grs_bitmap *pbm, fix time) {
107   MovieTrack *ptrack;
108   uchar *p;
109   uchar *ppal;
110   uchar *pbuff;
111   uchar *pd;
112   uchar *prgb;
113   int x, y;
114   ushort color16;
115 
116   //	Can't handle anything but 8-bit raw
117 
118   if (pbm->type != BMT_FLAT8) {
119     printf("QuikAddVideoSample: only uncompressed bitmaps allowed\n");
120     return;
121   }
122 
123   //	Write out frame bits in 8-bit of 16-bit mode
124 
125   if (pqtm->depth16) {
126     if ((pqtm->pVideoTrack == NULL) || (pqtm->pVideoTrack->palette == NULL)) {
127       printf("QuikAddVideoSample: need pal to convert to 16-bits!\n");
128       return;
129     }
130     ppal = pqtm->pVideoTrack->palette;
131     pbuff = (uchar *)malloc(pbm->w * sizeof(ushort));
132     for (y = 0, p = pbm->bits; y < pbm->h; y++, p += (pbm->row - pbm->w)) {
133       for (x = 0, pd = pbuff; x < pbm->w; x++) {
134         prgb = &ppal[((ushort)*p++) * 3];
135         color16 = (ushort)(*prgb++ >> 3) << 10;
136         color16 |= (ushort)(*prgb++ >> 3) << 5;
137         color16 |= (ushort)(*prgb++ >> 3);
138         *pd++ = color16 >> 8;
139         *pd++ = color16 & 0xFF;
140       }
141       fwrite(pbuff, pbm->w * sizeof(ushort), 1, fp);
142     }
143     free(pbuff);
144   } else {
145     if (pbm->w == pbm->row)
146       fwrite(pbm->bits, (long)pbm->w * (long)pbm->h, 1, fp);
147     else {
148       for (y = 0, p = pbm->bits; y < pbm->h; y++, p += pbm->row)
149         fwrite(p, pbm->w, 1, fp);
150     }
151   }
152 
153   //	If video track not assigned, assign it
154 
155   if (pqtm->pVideoTrack == NULL)
156     pqtm->pVideoTrack = &pqtm->track[pqtm->numTracks++];
157 
158   ptrack = pqtm->pVideoTrack;
159 
160   //	If video track not set up, set it up
161 
162   if (pqtm->compTypeQT == 0) {
163     pqtm->compTypeQT = MAKE4('r', 'a', 'w', ' ');
164     pqtm->frameWidth = pbm->w;
165     pqtm->frameHeight = pbm->h;
166 
167     ptrack->qt_tkhd.flags[2] = 0x0F;
168     ptrack->qt_tkhd.createTime = DEFAULT_MACTIME;
169     ptrack->qt_tkhd.modTime = DEFAULT_MACTIME;
170     ptrack->qt_tkhd.trackId = (ptrack - &pqtm->track[0]) + 1;
171     ptrack->qt_tkhd.matrix[0] = FIX_UNIT;
172     ptrack->qt_tkhd.matrix[4] = FIX_UNIT;
173     ptrack->qt_tkhd.matrix[8] = fix_make(16384, 0);
174     ptrack->qt_tkhd.trackWidth = fix_make(pbm->w, 0);
175     ptrack->qt_tkhd.trackHeight = fix_make(pbm->h, 0);
176 
177     ptrack->qt_mdhd.createTime = DEFAULT_MACTIME;
178     ptrack->qt_mdhd.modTime = DEFAULT_MACTIME;
179 
180     ptrack->qt_stsd.base.numEntries = 1;
181     if (pqtm->depth16)
182       ptrack->qt_stsd.idesc.descSize = 86;
183     else
184       ptrack->qt_stsd.idesc.descSize = 2142;
185     ptrack->qt_stsd.idesc.dataFormat = MAKE4('r', 'a', 'w', ' ');
186     ptrack->qt_stsd.idesc.dataRefIndex = 1;
187     ptrack->qt_stsd.idesc.version = 1;
188     ptrack->qt_stsd.idesc.revLevel = 1;
189     ptrack->qt_stsd.idesc.vendor = MAKE4('a', 'p', 'p', 'l');
190     ptrack->qt_stsd.idesc.temporalQuality = 0;
191     ptrack->qt_stsd.idesc.spatialQuality = fix_make(0, 65536 / 50);
192     ptrack->qt_stsd.idesc.width = pbm->w;
193     ptrack->qt_stsd.idesc.height = pbm->h;
194     ptrack->qt_stsd.idesc.hRes = fix_make(72, 0);
195     ptrack->qt_stsd.idesc.vRes = fix_make(72, 0);
196     ptrack->qt_stsd.idesc.frameCount = 1;
197     strcpy((char *)ptrack->qt_stsd.idesc.name, (char *)"None");
198     if (pqtm->depth16) {
199       ptrack->qt_stsd.idesc.depth = 16;
200       ptrack->qt_stsd.idesc.clutId = -1;
201     } else {
202       ptrack->qt_stsd.idesc.depth = 8;
203       if (ptrack->palette)
204         ptrack->qt_stsd.idesc.clutId = 0;
205       else
206         ptrack->qt_stsd.idesc.clutId = 8;
207     }
208 
209     ptrack->numSamplesAlloced = FRAME_ALLOC_INCR;
210     ptrack->pSampleTime = (fix *)malloc(ptrack->numSamplesAlloced * sizeof(fix));
211   }
212 
213   //	Grow sample if need to
214 
215   ptrack->pSampleTime[ptrack->numSamps++] = time;
216   if (ptrack->numSamps >= ptrack->numSamplesAlloced) {
217     ptrack->numSamplesAlloced += FRAME_ALLOC_INCR;
218     ptrack->pSampleTime = (fix *)realloc(ptrack->pSampleTime, ptrack->numSamplesAlloced * sizeof(fix));
219   }
220 }
221 
222 //	-------------------------------------------------------------
223 //
224 //	QuikWriteMovieAndClose() writes out movie and closes file
225 
QuikWriteMovieAndClose(QTM * pqtm,FILE * fp)226 void QuikWriteMovieAndClose(QTM *pqtm, FILE *fp) {
227   ulong timeTot;
228 
229   //	Close 'mdat' chunk
230 
231   QuikEndSubChunk(pqtm, fp);
232 
233   //	Write 'moov' chunk, which has everything else
234 
235   QuikStartSubChunk(pqtm, fp, QT_MOOV, NULL, 0);
236   timeTot = QuikComputeTotalTime(pqtm);
237   QuikWriteMVHD(pqtm, fp, timeTot);
238   QuikWriteVideoTrack(pqtm, fp, timeTot);
239 
240   //	Unpop stacked chunk lengths
241 
242   while (pqtm->indexOffsetStack > 0)
243     QuikEndSubChunk(pqtm, fp);
244 
245   //	Close file and clean up
246 
247   fclose(fp);
248   QuikFreeMovie(pqtm);
249   memset(pqtm, 0, sizeof(QTM));
250 }
251 
252 //	-------------------------------------------------------------
253 //		INTERNAL ROUTINES
254 //	-------------------------------------------------------------
255 //
256 //	QuikStartSubChunk() starts a new subchunk.
257 
QuikStartSubChunk(QTM * pqtm,FILE * fp,QT_Ctype ctype,void * data,long len)258 void QuikStartSubChunk(QTM *pqtm, FILE *fp, QT_Ctype ctype, void *data, long len) {
259   if (pqtm->indexOffsetStack >= QTM_MAX_CHUNK_NESTING) {
260     printf("QuikStartSubChunk: nesting too deep\n");
261     return;
262   }
263 
264   pqtm->offsetStack[pqtm->indexOffsetStack++] = ftell(fp);
265 
266   QuikWriteChunk(fp, ctype, data, len);
267 }
268 
269 //	-------------------------------------------------------------
270 //
271 //	QuikEndSubChunk() ends a subchunk.
272 
QuikEndSubChunk(QTM * pqtm,FILE * fp)273 void QuikEndSubChunk(QTM *pqtm, FILE *fp) {
274   long currPos, length;
275 
276   if (pqtm->indexOffsetStack == 0) {
277     printf("QuikEndSubChunk: chunk nesting underflow\n");
278     return;
279   }
280 
281   pqtm->indexOffsetStack--;
282 
283   currPos = ftell(fp);
284   fseek(fp, pqtm->offsetStack[pqtm->indexOffsetStack], SEEK_SET);
285   length = currPos - ftell(fp);
286   QuikWriteChunkLength(fp, length);
287   fseek(fp, currPos, SEEK_SET);
288 }
289 
290 //	-------------------------------------------------------------
291 //
292 //	QuikWriteMVHD() writes MVHD chunk
293 
QuikWriteMVHD(QTM * pqtm,FILE * fp,ulong timeTot)294 void QuikWriteMVHD(QTM *pqtm, FILE *fp, ulong timeTot) {
295   pqtm->qt_mvhd.createTime = DEFAULT_MACTIME;
296   pqtm->qt_mvhd.modTime = DEFAULT_MACTIME;
297   pqtm->qt_mvhd.timeScale = DEFAULT_TIMESCALE;
298   if (pqtm->pVideoTrack)
299     pqtm->qt_mvhd.duration = timeTot;
300   pqtm->qt_mvhd.preferredRate = FIX_UNIT;
301   pqtm->qt_mvhd.preferredVol = FIX8(FIX_UNIT);
302   pqtm->qt_mvhd.matrix[0] = FIX_UNIT;
303   pqtm->qt_mvhd.matrix[4] = FIX_UNIT;
304   pqtm->qt_mvhd.matrix[8] = fix_make(16384, 0);
305   pqtm->qt_mvhd.nextTrackId = 1 + (pqtm->pVideoTrack != NULL) + (pqtm->pAudioTrack != NULL);
306 
307   QuikWriteChunk(fp, QT_MVHD, &pqtm->qt_mvhd, sizeof(pqtm->qt_mvhd));
308 }
309 
310 //	-------------------------------------------------------------
311 //
312 //	QuikWriteVideoTrack() writes video track out.
313 
QuikWriteVideoTrack(QTM * pqtm,FILE * fp,ulong timeTot)314 void QuikWriteVideoTrack(QTM *pqtm, FILE *fp, ulong timeTot) {
315   MovieTrack *ptrack;
316   uchar *pStsd;
317   int i, ilast;
318   ushort *ppw;
319   uchar *ppr;
320   ushort r, g, b;
321   long len;
322   ulong offset;
323   long tlen, tlenLast;
324   fix tlast;
325   long sampSize;
326   QTS_ELST elst;
327   QTS_HDLR hdlr;
328   QTS_VMHD vmhd;
329   uchar dref[20];
330 
331   //	If no video track, don't write it obviously
332 
333   ptrack = pqtm->pVideoTrack;
334   if (ptrack == NULL)
335     return;
336 
337   //	Write TRAK chunk hdr and TKHD chunk
338 
339   QuikStartSubChunk(pqtm, fp, QT_TRAK, NULL, 0);
340   ptrack->qt_tkhd.duration = timeTot;
341   QuikWriteChunk(fp, QT_TKHD, &ptrack->qt_tkhd, sizeof(QTS_TKHD));
342 
343   //	Write EDTS chunk hdr and ELST chunk
344 
345   QuikStartSubChunk(pqtm, fp, QT_EDTS, NULL, 0);
346   memset(&elst, 0, sizeof(elst));
347   elst.numEntries = 1;
348   elst.editList[0].trackDuration = timeTot;
349   elst.editList[0].mediaRate = FIX_UNIT;
350   QuikStartSubChunk(pqtm, fp, QT_ELST, &elst, sizeof(elst));
351   QuikEndSubChunk(pqtm, fp);
352   QuikEndSubChunk(pqtm, fp);
353 
354   //	Write MDIA chunk hdr and MDHD chunk
355 
356   QuikStartSubChunk(pqtm, fp, QT_MDIA, NULL, 0);
357   ptrack->qt_mdhd.timeScale = MDHD_TIMESCALE;
358   ptrack->qt_mdhd.duration = (timeTot * MDHD_TIMESCALE) / DEFAULT_TIMESCALE;
359   QuikWriteChunk(fp, QT_MDHD, &ptrack->qt_mdhd, sizeof(QTS_MDHD));
360 
361   //	Write HDLR chunk
362 
363   memset(&hdlr, 0, sizeof(hdlr));
364   hdlr.compType = MAKE4('m', 'h', 'l', 'r');
365   hdlr.compSubtype = MAKE4('v', 'i', 'd', 'e');
366   hdlr.compManufacturer = MAKE4('a', 'p', 'p', 'l');
367   hdlr.compFlags = 0x40000000;
368   if (pqtm->depth16)
369     hdlr.compFlagsMask = 0x20072;
370   else
371     hdlr.compFlagsMask = 0x10011;
372   WriteChunkWithString(fp, QT_HDLR, &hdlr, sizeof(hdlr), "Apple Video Media Handler");
373 
374   //	Write MINF chunk hdr and VMHD chunk
375 
376   QuikStartSubChunk(pqtm, fp, QT_MINF, NULL, 0);
377   memset(&vmhd, 0, sizeof(vmhd));
378   vmhd.flags[2] = 1;
379   vmhd.graphicsMode = 0x40;
380   vmhd.opColor[0] = 0x8000;
381   vmhd.opColor[1] = 0x8000;
382   vmhd.opColor[2] = 0x8000;
383   QuikWriteChunk(fp, QT_VMHD, &vmhd, sizeof(vmhd));
384 
385   //	Write HDLR chunk
386 
387   memset(&hdlr, 0, sizeof(hdlr));
388   hdlr.compType = MAKE4('d', 'h', 'l', 'r');
389   hdlr.compSubtype = MAKE4('a', 'l', 'i', 's');
390   hdlr.compManufacturer = MAKE4('a', 'p', 'p', 'l');
391   if (pqtm->depth16) {
392     hdlr.compFlags = 0xC0000000;
393     hdlr.compFlagsMask = 0x1005D;
394   } else {
395     hdlr.compFlags = 0x40000000;
396     hdlr.compFlagsMask = 0x10016;
397   }
398   WriteChunkWithString(fp, QT_HDLR, &hdlr, sizeof(hdlr), "Apple Alias Data Handler");
399 
400   //	Write DINF chunk hdr and DREF chunk
401 
402   QuikStartSubChunk(pqtm, fp, QT_DINF, NULL, 0);
403   memset(dref, 0, sizeof(dref));
404   dref[7] = 1;
405   dref[11] = 0x0C;
406   dref[12] = 'a';
407   dref[13] = 'l';
408   dref[14] = 'i';
409   dref[15] = 's';
410   dref[19] = 1;
411   QuikWriteChunk(fp, QT_DREF, dref, sizeof(dref));
412   QuikEndSubChunk(pqtm, fp);
413 
414   //	Write STBL chunk hdr and STSD chunk
415 
416   QuikStartSubChunk(pqtm, fp, QT_STBL, NULL, 0);
417   pStsd = (uchar *)malloc(sizeof(ptrack->qt_stsd) + 0x0800 + 8);
418   memcpy(pStsd, &ptrack->qt_stsd, sizeof(ptrack->qt_stsd));
419   if (ptrack->palette && !pqtm->depth16) {
420     ppw = (ushort *)(pStsd + sizeof(QTS_STSD_Base) + sizeof(QT_ImageDesc) + 8);
421     *(ppw - 1) = 0xFF00;
422     ppr = ptrack->palette;
423     for (i = 0; i < 256; i++) {
424       *ppw++ = i << 8;
425       r = *ppr++;
426       *ppw++ = (r << 8) | r;
427       g = *ppr++;
428       *ppw++ = (g << 8) | g;
429       b = *ppr++;
430       *ppw++ = (b << 8) | b;
431     }
432   }
433   QuikWriteChunk(fp, QT_STSD, pStsd, sizeof(ptrack->qt_stsd) + 0x0800 + 8);
434   free(pStsd);
435 
436   //	Write STTS chunk
437 
438   ptrack->qt_stts = (QTS_STTS *)malloc(sizeof(QTS_STTS) + (sizeof(QT_Time2Samp) * ptrack->numSamps));
439   tlenLast = 0;
440   tlast = 0;
441   ilast = 1;
442   for (i = 1; i < ptrack->numSamps; i++) {
443     tlen = QuikSampleLenMsec(ptrack->pSampleTime[i], tlast);
444     if (tlen != tlenLast) {
445       if (i != 1) {
446         ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].count = i - ilast;
447         ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].duration = tlenLast;
448         ptrack->qt_stts->numEntries++;
449         ilast = i;
450       }
451       tlenLast = tlen;
452     }
453     tlast += QuikSampleLenMsecBackToFix(tlen);
454   }
455   ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].count = (i - ilast) + 1;
456   ptrack->qt_stts->time2samp[ptrack->qt_stts->numEntries].duration = tlen;
457   ptrack->qt_stts->numEntries++;
458   QuikWriteChunk(fp, QT_STTS, ptrack->qt_stts,
459                  sizeof(QTS_STTS) + (sizeof(QT_Time2Samp) * (ptrack->qt_stts->numEntries - 1)));
460 
461   //	Write STSC chunk
462 
463   len = sizeof(QTS_STSC);
464   ptrack->qt_stsc = (QTS_STSC *)malloc(len);
465   ptrack->qt_stsc->numEntries = 1;
466   ptrack->qt_stsc->samp2chunk[0].firstChunk = 1;
467   ptrack->qt_stsc->samp2chunk[0].sampsPerChunk = 1;
468   ptrack->qt_stsc->samp2chunk[0].sampDescId = 1;
469   QuikWriteChunk(fp, QT_STSC, ptrack->qt_stsc, len);
470 
471   //	Write STSZ chunk
472 
473   len = sizeof(QTS_STSZ) - sizeof(ulong); // don't include sampSizeTab[]
474   ptrack->qt_stsz = (QTS_STSZ *)malloc(len);
475   sampSize = (long)pqtm->frameWidth * (long)pqtm->frameHeight;
476   if (pqtm->depth16)
477     sampSize *= 2;
478   ptrack->qt_stsz->sampSize = sampSize;
479   ptrack->qt_stsz->numEntries = ptrack->numSamps;
480   QuikWriteChunk(fp, QT_STSZ, ptrack->qt_stsz, len);
481 
482   //	Write STCO chunk
483 
484   len = sizeof(QTS_STCO) + ((ptrack->numSamps - 1) * sizeof(ulong));
485   ptrack->qt_stco = (QTS_STCO *)malloc(len);
486   ptrack->qt_stco->numEntries = ptrack->numSamps;
487   offset = sizeof(QT_ChunkHdr);
488   for (i = 0; i < ptrack->numSamps; i++) {
489     ptrack->qt_stco->offset[i] = offset;
490     offset += sampSize;
491   }
492   QuikWriteChunk(fp, QT_STCO, ptrack->qt_stco, len);
493 }
494 
495 //	-------------------------------------------------------------
496 //
497 //	SetPascalString() puts C string into PStr field.
498 
SetPascalString(PStr * pstr,char * str)499 static void SetPascalString(PStr *pstr, char *str) {
500   pstr->len = strlen(str);
501   memcpy(pstr->str, str, pstr->len);
502 }
503 
504 //	-------------------------------------------------------------
505 //
506 //	WriteChunkWithString() writes a var-length data chunk with trailing PStr.
507 
WriteChunkWithString(FILE * fp,QT_Ctype ctype,void * data,long len,char * str)508 static void WriteChunkWithString(FILE *fp, QT_Ctype ctype, void *data, long len, char *str) {
509   char buff[128];
510 
511   memcpy(buff, data, len - sizeof(PStr));
512   SetPascalString((PStr *)(buff + len - sizeof(PStr)), str);
513   QuikWriteChunk(fp, ctype, buff, (len - sizeof(PStr)) + strlen(str) + 1);
514 }
515 
516 //	---------------------------------------------------------------
517 //
518 //	QuikComputeTotalTime() computes total time of movie.
519 
QuikComputeTotalTime(QTM * pqtm)520 ulong QuikComputeTotalTime(QTM *pqtm) {
521   MovieTrack *ptrack;
522   fix fixtime;
523 
524   ptrack = pqtm->pVideoTrack;
525   if ((ptrack == NULL) || (ptrack->pSampleTime == NULL))
526     return (0);
527 
528   fixtime = ptrack->pSampleTime[ptrack->numSamps - 1] +
529             (ptrack->pSampleTime[ptrack->numSamps - 1] - ptrack->pSampleTime[ptrack->numSamps - 2]);
530   return (fix_mul(fixtime, DEFAULT_TIMESCALE));
531 }
532 
533 //	---------------------------------------------------------------
534 //
535 //	QuikSampleLenMsec() returns sample length in msecs.
536 
QuikSampleLenMsec(fix t,fix tlast)537 long QuikSampleLenMsec(fix t, fix tlast) {
538   fix fixtime;
539   long msec;
540 
541   fixtime = t - tlast;
542 #if MDHD_TIMESCALE == 1000
543   msec = ((fixtime * 125) + 0x1000) >> 13; // overflows at about 8 minutes
544 #elif MDHD_TIMESCALE == 100
545   msec = ((fixtime * 25) + 0x2000) >> 14;
546 #elif MDHD_TIMESCALE == 30
547   msec = ((fixtime * 15) + 0x4000) >> 15;
548 #else
549   msec = 0;
550   Warning(("QuikSampleLenMsec: invalid MDHD_TIMESCALE\n"));
551 #endif
552   return (msec);
553 }
554 
555 //	--------------------------------------------------------------
556 //
557 //	QuikSampleLenMsecBackToFix() converts msec to fix
558 
QuikSampleLenMsecBackToFix(long msec)559 fix QuikSampleLenMsecBackToFix(long msec) {
560   fix tfix;
561 
562 #if MDHD_TIMESCALE == 1000
563   tfix = (msec << 13) / 125;
564 #elif MDHD_TIMESCALE == 100
565   tfix = (msec << 14) / 25;
566 #elif MDHD_TIMESCALE == 30
567   tfix = (msec << 15) / 15;
568 #else
569   tfix = 0;
570   Warning(("QuikSampleLenMsecBackToFix: invalid MDHD_TIMESCALE\n"));
571 #endif
572   return (tfix);
573 }
574