1 //
2 //  AVI.c
3 //  CoolCV
4 //
5 //  Created by Oscar Toledo on 02/11/15.
6 //  Copyright (c) 2015 Oscar Toledo. All rights reserved.
7 //
8 //  With portions of my private video converter to AVI
9 //
10 //  Modified by J. Zbiciak for jzIntv:
11 //   -- Variable audio rates
12 //   -- 8bpp palette-based encoding mode (was 32bpp)
13 //   -- Border color and border region
14 //   -- Selectable compression
15 //   -- Slight code and data restructuring
16 
17 //  ZMBV specs from http://wiki.multimedia.cx/?title=DosBox_Capture_Codec
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include "config.h"
23 #include "plat/plat_lib.h"
24 #include "zlib/zlib.h"
25 #include "gfx/palette.h"
26 #include "avi/avi.h"
27 
28 #define APP_TITLE       "jzIntv"
29 
30 #define AVI_AUDIO_BUF   (65536)
31 #define AVI_VIDEO_BUF_BYTES     (1 << 20)   /* 1MB should be waaay plenty   */
32 #define AVI_VIDEO_BUF_FRAMES    (4096)      /* waaaaay plenty               */
33 
34 #define INPUT_DIM_X     (320)
35 #define INPUT_DIM_Y     (192)
36 
37 #define AVI_BORD_X_SZ   (16)
38 #define AVI_BORD_Y_SZ   (16)
39 #define AVI_DIM_X       (INPUT_DIM_X + 2*AVI_BORD_X_SZ)
40 #define AVI_DIM_Y       (INPUT_DIM_Y + 2*AVI_BORD_Y_SZ)
41 #define AVI_BPP         (8)
42 #define AVI_FRAME_PIX   (AVI_DIM_X * AVI_DIM_Y)
43 #define AVI_FRAME_BYTES (AVI_DIM_X * AVI_DIM_Y * AVI_BPP / 8)
44 
45 #define AVI_VIDEO_ENCODE_BYTES (AVI_FRAME_BYTES + 4096)
46 #define AVI_VIDEO_WRAP_THRESH (AVI_VIDEO_BUF_BYTES - AVI_VIDEO_ENCODE_BYTES)
47 
48 #define BLOCK_WIDTH     (16)
49 #define BLOCK_HEIGHT    (16)
50 
51 #define NUM_X_BLOCK     (AVI_DIM_X / BLOCK_WIDTH)
52 #define NUM_Y_BLOCK     (AVI_DIM_Y / BLOCK_HEIGHT)
53 
54 #if AVI_BPP != 8
55 #error  "Code needs more work to support BPP != 8"
56 #endif
57 
58 #if AVI_DIM_X % BLOCK_WIDTH || AVI_DIM_Y % BLOCK_HEIGHT
59 #error  "Image dimensions must be a multiple of block size"
60 #endif
61 
62 #define STRUCT_ALIGN (64)
63 
64 static double avi_time_scale         = 1.0;
65 static double audio_time_scale       = 1.0;
66 static double audio_time_scale_ratio = 1.0;
67 
68 
69 /* Locations to be patched */
70 typedef struct avi_container_t
71 {
72     long    pos[6];
73     long    base_movie_size;
74 } avi_container_t;
75 
76 /* RIFF state */
77 typedef struct avi_riff_t
78 {
79     FILE   *file;
80     int    *index;
81     int    *base_index;
82     int     index_size;
83     int     nesting_level;
84     long    nesting[512];
85 } avi_riff_t;
86 
87 /* Audio buffering */
88 typedef struct avi_audio_t
89 {
90     uint16_t    buffer[AVI_AUDIO_BUF];
91     uint16_t   *buffer_read;
92     uint16_t   *buffer_write;
93     int         buffer_total_read;
94     int         buffer_total_write;
95     double      time_remainder;
96 } avi_audio_t ALIGN(STRUCT_ALIGN);
97 
98 typedef struct
99 {
100     uint8_t f[AVI_DIM_Y][AVI_DIM_X];
101 } avi_frame_t ALIGN(STRUCT_ALIGN);
102 
103 /* Video buffering */
104 typedef struct avi_video_t
105 {
106     avi_frame_t frame[2];
107     uint8_t     toggle;
108     uint8_t     palette[256][3];
109     uint8_t     encode_buf[AVI_VIDEO_BUF_BYTES];
110     uint32_t    encode_buf_wr, encode_buf_rd;
111     uint32_t    encode_len[AVI_VIDEO_BUF_FRAMES];
112     uint32_t    encode_len_wr, encode_len_rd;
113     uint32_t    advance_frames_remaining;
114     double      time_remainder;
115 } avi_video_t ALIGN(STRUCT_ALIGN);
116 
117 typedef struct avi_pvt_t
118 {
119     avi_container_t container;  /* Locations to be patched */
120     avi_info_t      info;       /* Information and statistics */
121     avi_riff_t      riff;       /* RIFF state */
122     avi_audio_t     audio;      /* Audio buffering */
123     avi_video_t     video;      /* Video buffering */
124     z_stream        zstream;    /* ZLib state */
125 } avi_pvt_t ALIGN(STRUCT_ALIGN);
126 
127 
128 /*
129  ** Write a 32 bits code
130  */
riff_write_code(avi_riff_t * const riff,char * code)131 LOCAL INLINE void riff_write_code(avi_riff_t *const riff, char *code)
132 {
133     fwrite(code, 1, 4, riff->file);
134 }
135 
136 /*
137  ** Write a 16 bits integer
138  */
riff_write_int_16(avi_riff_t * const riff,int value)139 LOCAL INLINE void riff_write_int_16(avi_riff_t *const riff, int value)
140 {
141     char buf[2];
142 
143     buf[0] = value;
144     buf[1] = value >> 8;
145     fwrite(buf, 1, 2, riff->file);
146 }
147 
148 /*
149  ** Write a 32 bits integer
150  */
riff_write_int_32(avi_riff_t * const riff,int value)151 LOCAL INLINE void riff_write_int_32(avi_riff_t *const riff, int value)
152 {
153     char buf[4];
154 
155     buf[0] = value;
156     buf[1] = value >> 8;
157     buf[2] = value >> 16;
158     buf[3] = value >> 24;
159     fwrite(buf, 1, 4, riff->file);
160 }
161 
162 #if 0
163 /*
164  ** Writes a text string
165  */
166 LOCAL INLINE void riff_write_string(avi_riff_t *const riff, char *text)
167 {
168     fwrite(text, 1, strlen(text) + 1, riff->file);
169 }
170 #endif
171 
172 /*
173  ** Starts a RIFF element
174  */
riff_start_element(avi_riff_t * const riff,char * element)175 LOCAL INLINE void riff_start_element(avi_riff_t *const riff, char *element)
176 {
177     riff_write_code(riff, element);
178     riff_write_int_32(riff, 0);
179     riff->nesting[riff->nesting_level++] = ftell(riff->file);
180 }
181 
182 /*
183  ** Closes a RIFF element
184  */
riff_close_element(avi_riff_t * const riff)185 LOCAL INLINE int riff_close_element(avi_riff_t *const riff)
186 {
187     long c;
188     long a;
189 
190     c = riff->nesting[--riff->nesting_level];
191     a = ftell(riff->file);
192     if (a & 1)
193         fwrite("\0", 1, 1, riff->file);
194     fseek(riff->file, c - 4, SEEK_SET);
195     riff_write_int_32(riff, (int) (a - c));
196     if (a & 1)
197         a++;
198     fseek(riff->file, a, SEEK_SET);
199     return (int) (a - c);
200 }
201 
202 /*
203  ** Starts a RIFF structure
204  */
riff_start_structure(avi_riff_t * const riff,char * list,char * title)205 LOCAL INLINE void riff_start_structure(avi_riff_t *const riff,
206                                        char *list, char *title)
207 {
208     riff_start_element(riff, list);
209     riff_write_code(riff, title);
210 }
211 
212 /*
213  ** Terminate a RIFF structure
214  */
riff_close_structure(avi_riff_t * const riff)215 LOCAL INLINE int riff_close_structure(avi_riff_t *const riff)
216 {
217     return riff_close_element(riff);
218 }
219 
220 // Check in following order:
221 //  -- Zero offset
222 //  -- 1 STIC tile in four cardinal directions (N/S/E/W)
223 //  -- Clockwise spiral from center
224 // Vectors constrained to even pixels due to pixel-doubled nature of Inty.
225 static const int movement[][2] =
226 {
227     {   0,   0 }, { -16,   0 }, {  16,   0 }, {   0,  16 }, {   0, -16 },
228     {   2,   0 }, {   2,   2 }, {   0,   2 }, {  -2,   2 }, {  -2,   0 },
229     {  -2,  -2 }, {   0,  -2 }, {   2,  -2 }, {   4,  -2 }, {   4,   0 },
230     {   4,   2 }, {   4,   4 }, {   2,   4 }, {   0,   4 }, {  -2,   4 },
231     {  -4,   4 }, {  -4,   2 }, {  -4,   0 }, {  -4,  -2 }, {  -4,  -4 },
232     {  -2,  -4 }, {   0,  -4 }, {   2,  -4 }, {   4,  -4 }, {   6,  -4 },
233     {   6,  -2 }, {   6,   0 }, {   6,   2 }, {   6,   4 }, {   6,   6 },
234     {   4,   6 }, {   2,   6 }, {   0,   6 }, {  -2,   6 }, {  -4,   6 },
235     {  -6,   6 }, {  -6,   4 }, {  -6,   2 }, {  -6,   0 }, {  -6,  -2 },
236     {  -6,  -4 }, {  -6,  -6 }, {  -4,  -6 }, {  -2,  -6 }, {   0,  -6 },
237     {   2,  -6 }, {   4,  -6 }, {   6,  -6 }, {   8,  -6 }, {   8,  -4 },
238     {   8,  -2 }, {   8,   0 }, {   8,   2 }, {   8,   4 }, {   8,   6 },
239     {   8,   8 }, {   6,   8 }, {   4,   8 }, {   2,   8 }, {   0,   8 },
240     {  -2,   8 }, {  -4,   8 }, {  -6,   8 }, {  -8,   8 }, {  -8,   6 },
241     {  -8,   4 }, {  -8,   2 }, {  -8,   0 }, {  -8,  -2 }, {  -8,  -4 },
242     {  -8,  -6 }, {  -8,  -8 }, {  -6,  -8 }, {  -4,  -8 }, {  -2,  -8 },
243     {   0,  -8 }, {   2,  -8 }, {   4,  -8 }, {   6,  -8 }, {   8,  -8 },
244 };
245 
avi_zalloc(void * opaque,uInt items,uInt size)246 LOCAL void *avi_zalloc( void *opaque, uInt items, uInt size)
247 {
248     UNUSED(opaque);
249     return (void *)malloc(items * size);
250 }
251 
avi_zfree(void * opaque,void * to_free)252 LOCAL void avi_zfree( void *opaque, void *to_free )
253 {
254     UNUSED(opaque);
255     free(to_free);
256 }
257 
258 #define NUM_MOVEMENT ((int)(sizeof(movement)/sizeof(movement[0])))
259 
avi_pvt_align(void * pvt_alloc)260 LOCAL INLINE avi_pvt_t *avi_pvt_align(void *pvt_alloc)
261 {
262     intptr_t pvt_i = (intptr_t)pvt_alloc;
263     pvt_i += (STRUCT_ALIGN - pvt_i) & (STRUCT_ALIGN - 1);
264     return (avi_pvt_t *)pvt_i;
265 }
266 
267 /*
268  ** Start video recording
269  */
avi_start_video(avi_writer_t * const avi,FILE * const avi_file,const int fps,const int audio_rate,const int compress,const uint64_t start_time)270 int avi_start_video
271 (
272     avi_writer_t *const avi,
273     FILE         *const avi_file,
274     const int           fps,
275     const int           audio_rate,
276     const int           compress,
277     const uint64_t      start_time
278 )
279 {
280     // For now, don't try to handle silent AVIs.
281     if (!audio_rate)
282         return 0;
283 
284     int stereo = 0;
285     int bits = 16;
286     double real_fps = fps == 60 ? 59.92274 : 50.08012; // STIC_FRAMCLKS/MHz
287 
288     if (!avi->pvt_alloc)
289     {
290         /* Allocate a fresh avi_pvt_t for all of our stuff */
291         avi->pvt_alloc = (void*)calloc(sizeof(avi_pvt_t) + STRUCT_ALIGN, 1);
292         if (!avi->pvt_alloc)
293             return -1;
294 
295         avi->pvt = avi_pvt_align(avi->pvt_alloc);
296     } else
297     {
298         memset((void*)avi->pvt, 0, sizeof(avi_pvt_t));
299     }
300 
301     avi_pvt_t       *const pvt       = avi->pvt;
302     avi_container_t *const container = &(pvt->container);
303     avi_info_t      *const info      = &(pvt->info);
304     avi_riff_t      *const riff      = &(pvt->riff);
305     avi_audio_t     *const audio     = &(pvt->audio);
306     avi_video_t     *const video     = &(pvt->video);
307     z_stream        *const zstream   = &(pvt->zstream);
308 
309     info->start_time                = start_time;
310     info->active                    = 1;
311     info->frames_per_sec            = real_fps * 1000000;
312     info->usec_per_frame            = 1000000 / real_fps;
313     info->audio_rate                = audio_rate;
314     info->key_frames                = 32;   // Must be power-of-2
315     info->advance_audio_frames      = 0;
316     info->total_frames              = 0;
317     info->max_size_frame            = 0;
318     info->max_size_audio            = 0;
319     info->total_audio               = 0;
320     info->compress                  = compress;
321 
322     audio->buffer_read              = audio->buffer;
323     audio->buffer_write             = audio->buffer;
324     audio->buffer_total_read        = 0;
325     audio->buffer_total_write       = 0;
326     audio->time_remainder           = 0;
327 
328     video->toggle                   = 0;
329     video->encode_buf_rd            = 0;
330     video->encode_buf_wr            = 0;
331     video->encode_len_rd            = 0;
332     video->encode_len_wr            = 0;
333     video->advance_frames_remaining = info->advance_audio_frames;
334     video->time_remainder           = 0;
335 
336     riff->file                      = avi_file;
337     riff->nesting_level             = 0;
338     riff->index_size                = 1048576;
339     riff->index                     = CALLOC(int, riff->index_size * 4);
340     riff->base_index                = riff->index;
341     if (!riff->index)
342         return -1;
343 
344     riff_start_structure(riff, "RIFF", "AVI ");
345     riff_start_structure(riff, "LIST", "hdrl");
346     riff_start_element(riff, "avih");
347     riff_write_int_32(riff, info->usec_per_frame);  /* Microsecs per frame */
348     riff_write_int_32(riff, 300000);            /* Maximum bytes per second */
349     riff_write_int_32(riff, 0);                 /* Reserved */
350     riff_write_int_32(riff, 0x0110);            /* Flags */
351     /* bit 4 = Includes idx1 */
352     /* bit 5 = Use idx1 for everything */
353     /* bit 8 = Video and audio intermixed */
354     /* bit 16 = Optimized for realtime capture */
355     /* bit 17 = Copyrighted data */
356     container->pos[4] = ftell(riff->file);
357     riff_write_int_32(riff, 0);                 /* Total frames */
358     riff_write_int_32(riff, info->advance_audio_frames);
359                                                 /* Sound frames in advance */
360     riff_write_int_32(riff, 2);                 /* Total streams */
361     container->pos[0] = ftell(riff->file);
362     riff_write_int_32(riff, 0);                 /* Suggested buffer size */
363     riff_write_int_32(riff, AVI_DIM_X);         /* X size */
364     riff_write_int_32(riff, AVI_DIM_Y);         /* Y size */
365     riff_write_int_32(riff, 0);                 /* Scale */
366     riff_write_int_32(riff, 0);                 /* Speed */
367     riff_write_int_32(riff, 0);                 /* Start */
368     riff_write_int_32(riff, 0);                 /* Length */
369     riff_close_element(riff);
370     /* Video stream */
371     riff_start_structure(riff,"LIST","strl");   /* Stream Leader */
372     riff_start_element(riff, "strh");           /* Stream Header */
373     riff_write_code(riff, "vids");              /* Video */
374     riff_write_code(riff, "ZMBV");              /* Codec */
375     riff_write_int_32(riff, 0);                 /* Flags */
376     riff_write_int_32(riff, 0);                 /* Reserved */
377     riff_write_int_32(riff, 0);                 /* Frames in advance */
378     riff_write_int_32(riff, 1000000);           /* Scale */
379     riff_write_int_32(riff, info->frames_per_sec); /* Speed */
380     riff_write_int_32(riff, 0);                 /* Start of clip */
381     container->pos[5] = ftell(riff->file);
382     riff_write_int_32(riff, 0);                 /* Length of clip in frames */
383     container->pos[1] = ftell(riff->file);
384     riff_write_int_32(riff, 0);                 /* Suggested buffer site */
385     riff_write_int_32(riff, 8000);              /* Quality (by 100) */
386     riff_write_int_32(riff, 0);                 /* Sample size */
387     riff_write_int_32(riff, 0);                 /* Reserved */
388     riff_write_int_32(riff, 0);                 /* Reserved */
389     riff_close_element(riff);
390     riff_start_element(riff, "strf");           /* Stream Format (idem. BMP) */
391     riff_write_int_32(riff, 40);                /* Header size (incl itself) */
392     riff_write_int_32(riff, AVI_DIM_X);         /* X size */
393     riff_write_int_32(riff, AVI_DIM_Y);         /* Y size */
394     riff_write_int_16(riff, 1);                 /* Number of planes */
395     riff_write_int_16(riff, AVI_BPP);            /* Bits per pixel */
396     riff_write_code(riff, "ZMBV");              /* Codec */
397     riff_write_int_32(riff, AVI_FRAME_BYTES);   /* Image size (in bytes) */
398     riff_write_int_32(riff, 0);                 /* X pixels per meter */
399     riff_write_int_32(riff, 0);                 /* Y pixels per meter */
400     riff_write_int_32(riff, 0);                 /* Used colors */
401     riff_write_int_32(riff, 0);                 /* Important colors */
402     riff_close_element(riff);
403     riff_close_structure(riff);
404     /* Audio stream */
405     riff_start_structure(riff, "LIST", "strl"); /* Stream Leader */
406     riff_start_element(riff, "strh");           /* Stream Header */
407     riff_write_code(riff, "auds");              /* Audio */
408     riff_write_code(riff, "\0\0\0\0");          /* Codec (no compression) */
409     riff_write_int_32(riff, 0);                 /* Flags */
410     riff_write_int_32(riff, 0);                 /* Reserved */
411     riff_write_int_32(riff, info->advance_audio_frames);
412                                                 /* Frames in advance */
413     riff_write_int_32(riff, 1);                 /* Scale */
414     riff_write_int_32(riff, audio_rate);        /* Audio sample rate */
415     riff_write_int_32(riff, 0);                 /* Start of clip */
416     container->pos[2] = ftell(riff->file);
417     riff_write_int_32(riff, 0);                 /* Length of clip */
418     container->pos[3] = ftell(riff->file);
419     riff_write_int_32(riff, 0);                 /* Suggested buffer size */
420     riff_write_int_32(riff, 8000);              /* Quality (by 100) */
421     riff_write_int_32(riff, (stereo + 1) * (bits / 8)); /* Sample size */
422     riff_write_int_32(riff, 0);                 /* Reserved */
423     riff_write_int_32(riff, 0);                 /* Reserved */
424     riff_close_element(riff);
425     riff_start_element(riff, "strf");           /* Stream Format (idem. WAV) */
426     riff_write_int_16(riff, 1);                 /* Format = PCM */
427     riff_write_int_16(riff, stereo + 1);        /* Number of channels */
428     riff_write_int_32(riff, audio_rate);        /* Audio sample rate */
429     riff_write_int_32(riff,
430         audio_rate * (stereo + 1) * (bits / 8)); /* Frequency x Chans x Bytes */
431     riff_write_int_16(riff, (stereo + 1) * (bits / 8)); /* Bytes per sample */
432     riff_write_int_16(riff, bits);            /* Bits per individual sample */
433     riff_close_element(riff);
434     riff_close_structure(riff);
435 
436     riff_start_structure(riff, "LIST", "info");
437 
438     riff_start_element(riff, "ISFT");
439     fwrite(APP_TITLE, 1, sizeof(APP_TITLE) - 1, riff->file);
440     riff_close_element(riff);
441 
442     riff_close_structure(riff);
443 
444     riff_close_structure(riff);
445     container->base_movie_size = ftell(riff->file) + 8;
446     riff_start_structure(riff, "LIST", "movi");
447 
448     if (info->compress)
449     {
450         int ret;
451 
452         // These are needed because jzIntv's embedded zlib is Z_SOLO
453         zstream->zalloc = avi_zalloc;
454         zstream->zfree  = avi_zfree;
455         ret = deflateInit(zstream, 6);
456         if (ret != Z_OK) {
457             jzp_printf("deflateInit returned %d; disabling compression\n", ret);
458             info->compress = 0;
459         }
460     }
461     return 0;
462 }
463 
464 // Avoid cumulative rounding error...
465 //
466 // For now, just be lazy and use multiplies and divides.  This *can* be
467 // done completely with adds/subs and 32-bit arithmetic using the same
468 // error-bounding approach Bresenham line drawing uses.  I'm just too lazy
469 // at the moment.
next_audio_frame_size(const avi_info_t * info)470 LOCAL int next_audio_frame_size(const avi_info_t *info)
471 {
472     const uint64_t curr = (int64_t)info->total_frames * info->usec_per_frame;
473     const uint64_t next = curr + info->usec_per_frame;
474     const uint32_t curr_samples = (curr * info->audio_rate) / 1000000;
475     const uint32_t next_samples = (next * info->audio_rate) / 1000000;
476 
477     return next_samples - curr_samples;
478 }
479 
480 /*
481  ** Grow AVI index
482  */
grow_base_riff(avi_riff_t * const riff)483 LOCAL int grow_base_riff(avi_riff_t *const riff)
484 {
485     int *new_pointer;
486 
487     if (riff->base_index == riff->index + riff->index_size * 4) {
488         new_pointer = (int *)realloc((void *)riff->index,
489                                      riff->index_size * 2 * 4 * sizeof(int));
490         if (new_pointer == NULL)
491             return 0;
492         riff->base_index = (riff->base_index - riff->index) + new_pointer;
493         riff->index = new_pointer;
494         riff->index_size *= 2;
495     }
496     return 1;
497 }
498 
499 LOCAL void avi_end_video_internal
500 (
501     const avi_writer_t *const avi,
502     const int leave_active
503 );
504 
505 /*
506  ** Write out the interleaved A/V stream.
507  */
avi_write_interleaved_stream(const avi_writer_t * const avi,const int flush)508 LOCAL int avi_write_interleaved_stream
509 (
510     const avi_writer_t *const avi,
511     const int                 flush
512 )
513 {
514     avi_pvt_t       *const pvt       = avi->pvt;
515     avi_info_t      *const info      = &(pvt->info);
516     avi_riff_t      *const riff      = &(pvt->riff);
517     avi_audio_t     *const audio     = &(pvt->audio);
518     avi_video_t     *const video     = &(pvt->video);
519     avi_container_t *const container = &(pvt->container);
520 
521     if (!riff->file)
522         return 0;
523 
524     long v0;
525     long v1;
526     long v2;
527     const int audio_frame_size = next_audio_frame_size(info);
528     const int audio_avail = audio->buffer_total_write -
529                             audio->buffer_total_read;
530     const int video_avail = video->encode_len_wr - video->encode_len_rd +
531                             video->advance_frames_remaining;
532     uint32_t encoded_video_size = 0;
533     int key_frame = 0;
534 
535     if (!video_avail || audio_avail < audio_frame_size)
536         return 0;
537 
538     v0 = ftell(riff->file);
539     riff_start_structure(riff, "LIST", "rec ");
540     v1 = ftell(riff->file);
541     if (!video->advance_frames_remaining)
542     {
543         riff_start_element(riff, "00dc");
544         uint8_t *const encoded_video = video->encode_buf + video->encode_buf_rd;
545         encoded_video_size =
546             video->encode_len[video->encode_len_rd++ % AVI_VIDEO_BUF_FRAMES];
547 
548         key_frame = (*encoded_video == 0x01);
549 
550         fwrite(encoded_video, 1, encoded_video_size, riff->file);
551         riff_close_element(riff);
552         if (info->max_size_frame < encoded_video_size)
553             info->max_size_frame = encoded_video_size;
554 
555         video->encode_buf_rd += encoded_video_size;
556         if (video->encode_buf_rd >= AVI_VIDEO_WRAP_THRESH)
557             video->encode_buf_rd = 0;
558     }
559     v2 = ftell(riff->file);
560 
561     const uint32_t encoded_audio_size = audio_frame_size * sizeof(int16_t);
562     if (video->advance_frames_remaining || !flush)
563     {
564         riff_start_element(riff, "01wb");
565         uint32_t audio_remain = audio_frame_size;
566         if (audio->buffer_read + audio_remain > audio->buffer + AVI_AUDIO_BUF)
567         {
568             const uint32_t c = (audio->buffer + AVI_AUDIO_BUF) -
569                                 audio->buffer_read;
570             if (c)
571                 fwrite(audio->buffer_read, 1, c * sizeof(int16_t), riff->file);
572             audio->buffer_read = audio->buffer;
573             audio->buffer_total_read += c;
574             audio_remain -= c;
575         }
576         fwrite(audio->buffer_read, sizeof(int16_t), audio_remain, riff->file);
577         audio->buffer_read += audio_remain;
578         audio->buffer_total_read += audio_remain;
579         riff_close_element(riff);
580         if (info->max_size_audio < encoded_audio_size)
581             info->max_size_audio = encoded_audio_size;
582         info->total_audio += audio_frame_size;
583     }
584     const uint32_t container_size = riff_close_structure(riff);
585 
586     if (grow_base_riff(riff)) {
587         *riff->base_index++ = 0x20636572;
588         *riff->base_index++ = 0x00000001;
589         *riff->base_index++ = (int) (v0 - container->base_movie_size);
590         *riff->base_index++ = (int) container_size;
591     }
592     if (!video->advance_frames_remaining) {
593         if (grow_base_riff(riff)) {
594             *riff->base_index++ = 0x63643030;
595             *riff->base_index++ = (key_frame ? 0x10 : 0x00) | (0x02);
596             *riff->base_index++ = (int) (v1 - container->base_movie_size);
597             *riff->base_index++ = (int) encoded_video_size;
598         }
599     }
600     if (info->total_frames < info->advance_audio_frames || !flush) {
601         if (grow_base_riff(riff)) {
602             *riff->base_index++ = 0x62773130;
603             *riff->base_index++ = 0;
604             *riff->base_index++ = (int) (v2 - container->base_movie_size);
605             *riff->base_index++ = (int) encoded_audio_size;
606         }
607     }
608     info->total_frames++;
609     if (video->advance_frames_remaining)
610         video->advance_frames_remaining--;
611 
612     /* Safeguard, stop AVI file before reaching 2 GB */
613     long size_thresh = (ftell(riff->file)
614                      + audio_frame_size * info->advance_audio_frames
615                      + (riff->base_index - riff->index) * 4);
616     if (size_thresh > 0x7fff0000l)
617     {
618         jzp_printf("Closing AVI near to 2GB\n");
619         avi_end_video_internal(avi,1);
620         return 0;
621     }
622     return 1;
623 }
624 
625 
626 /*
627  ** Record a frame of audio
628  */
avi_record_audio_internal(const avi_writer_t * const avi,const int16_t * const audio_data,const int num_samples)629 static void avi_record_audio_internal
630 (
631     const avi_writer_t *const avi,
632     const int16_t      *const audio_data,
633     const int                 num_samples
634 )
635 {
636     // Push out as much interleaved audio/video as we can
637     while (avi_write_interleaved_stream(avi, 0))
638         ;
639 
640     avi_audio_t *const audio = &(avi->pvt->audio);
641     size_t wrap = 0;
642 
643     if (audio->buffer_write + num_samples > audio->buffer + AVI_AUDIO_BUF) {
644         wrap = (audio->buffer + AVI_AUDIO_BUF) - audio->buffer_write;
645         if (wrap)
646             memcpy(audio->buffer_write, audio_data, wrap * sizeof(int16_t));
647         audio->buffer_write = audio->buffer;
648     }
649 
650     memcpy(audio->buffer_write, audio_data + wrap,
651            (num_samples - wrap) * sizeof(int16_t));
652 
653     audio->buffer_write += num_samples - wrap;
654     audio->buffer_total_write += num_samples;
655 }
656 
657 // Note: Audio comes in pre-time-scaled according to whatever the --ratecontrol
658 // setting is.  The audio_time_scale_ratio is the ratio between avi_time_scale
659 // and the ratecontrol setting.
avi_record_audio(const avi_writer_t * const avi,const int16_t * const audio_data,const int num_samples,const int silent)660 void avi_record_audio
661 (
662     const avi_writer_t *const avi,
663     const int16_t      *const audio_data,
664     const int                 num_samples,
665     const int                 silent
666 )
667 {
668     if (!avi->pvt || !avi->pvt->riff.file)
669         return;
670 
671     if (num_samples >= AVI_AUDIO_BUF)
672         return;
673 
674     if (audio_time_scale_ratio == 1.0)
675     {
676         avi_record_audio_internal(avi, audio_data, num_samples);
677         return;
678     }
679 
680     avi_audio_t *audio = &(avi->pvt->audio);
681 
682     // This could behave oddly if num_samples differs wildly call-to-call.
683     // Ordinarily, though, it won't, because the snd subsystem mixes in
684     // audio_buf sized quanta.
685     audio->time_remainder -= num_samples;
686 
687 #if 1
688     // If we're scaling to faster than real-time, allow non-silent segments
689     // to borrow into silent segments by a few extra buffers.
690     if (audio_time_scale_ratio > 1.0 && silent &&
691         audio->time_remainder >= -5.0 * num_samples)
692         return;
693 #endif
694 
695     while (audio->time_remainder < 0.0)
696     {
697         audio->time_remainder += audio_time_scale_ratio * num_samples;
698         avi_record_audio_internal(avi, audio_data, num_samples);
699         if (audio_time_scale_ratio > 1.0)
700             break;
701     }
702 }
703 
704 
705 // Compute block difference, decimated by 4x in each direction.
block_diff_decim4(const avi_frame_t * const RESTRICT prev,const avi_frame_t * const RESTRICT curr,const int px,const int py,const int cx,const int cy)706 LOCAL INLINE int block_diff_decim4(const avi_frame_t *const RESTRICT prev,
707                                    const avi_frame_t *const RESTRICT curr,
708                                    const int px, const int py,
709                                    const int cx, const int cy)
710 {
711     int x, y, dif = 0;
712 
713     assert(px >= 0);    assert(px <= AVI_DIM_X - BLOCK_WIDTH);
714     assert(py >= 0);    assert(py <= AVI_DIM_Y - BLOCK_HEIGHT);
715     assert(cx >= 0);    assert(cx <= AVI_DIM_X - BLOCK_WIDTH);
716     assert(cy >= 0);    assert(cy <= AVI_DIM_Y - BLOCK_HEIGHT);
717 
718     for (y = 0; y < BLOCK_HEIGHT; y += 4)
719         for (x = 0; x < BLOCK_WIDTH; x += 4)
720             dif += prev->f[py + y][px + x] != curr->f[cy + y][cx + x];
721 
722     return dif << 4;
723 }
724 
725 // Compute block difference across all pixels.
block_diff(const avi_frame_t * const RESTRICT prev,const avi_frame_t * const RESTRICT curr,const int px,const int py,const int cx,const int cy)726 LOCAL INLINE int block_diff(const avi_frame_t *const RESTRICT prev,
727                             const avi_frame_t *const RESTRICT curr,
728                             const int px, const int py,
729                             const int cx, const int cy)
730 {
731     int x, y, dif = 0;
732 
733     assert(px >= 0);    assert(px <= AVI_DIM_X - BLOCK_WIDTH);
734     assert(py >= 0);    assert(py <= AVI_DIM_Y - BLOCK_HEIGHT);
735     assert(cx >= 0);    assert(cx <= AVI_DIM_X - BLOCK_WIDTH);
736     assert(cy >= 0);    assert(cy <= AVI_DIM_Y - BLOCK_HEIGHT);
737 
738     for (y = 0; y < BLOCK_HEIGHT; y++)
739         for (x = 0; x < BLOCK_WIDTH; x++)
740             dif += prev->f[py + y][px + x] != curr->f[cy + y][cx + x];
741 
742     return dif;
743 }
744 
745 // Search for the best match for the current block in the previous image.
motion_search(const avi_frame_t * const RESTRICT prev,const avi_frame_t * const RESTRICT curr,const int x,const int y,int * const RESTRICT move_x,int * const RESTRICT move_y)746 LOCAL INLINE int motion_search(const avi_frame_t *const RESTRICT prev,
747                                const avi_frame_t *const RESTRICT curr,
748                                const int x, const int y,
749                                int *const RESTRICT move_x,
750                                int *const RESTRICT move_y)
751 {
752     int best_dif = INT_MAX;
753     int best_explore = 0;
754     int explore, ex, ey, dif;
755 
756     for (explore = 0; explore < NUM_MOVEMENT; explore++)
757     {
758         ex = x + movement[explore][0];
759         ey = y + movement[explore][1];
760 
761         /* First see if we're out of bounds */
762         if (ex < 0 || ex > (AVI_DIM_X - BLOCK_WIDTH )) continue;
763         if (ey < 0 || ey > (AVI_DIM_Y - BLOCK_HEIGHT)) continue;
764 
765         /* Next see if this is even worth it. */
766         dif = block_diff_decim4(prev, curr, ex, ey, x, y);
767 
768         /* If it's a possible improvement, do a finer-grain search */
769         if (dif < 64)
770             dif = block_diff(prev, curr, ex, ey, x, y);
771 
772         if (dif < best_dif)
773         {
774             best_dif = dif;
775             best_explore = explore;
776 
777             /* If it's a small enough difference, stop now. */
778             if (dif < 4)
779                 break;
780         }
781     }
782 
783     *move_x = movement[best_explore][0];
784     *move_y = movement[best_explore][1];
785     return best_dif;
786 }
787 
send_block_delta(const avi_frame_t * const RESTRICT prev,const avi_frame_t * const RESTRICT curr,const int px,const int py,const int cx,const int cy,uint8_t * const RESTRICT out)788 LOCAL INLINE uint8_t *send_block_delta(const avi_frame_t *const RESTRICT prev,
789                                        const avi_frame_t *const RESTRICT curr,
790                                        const int px, const int py,
791                                        const int cx, const int cy,
792                                        uint8_t *const RESTRICT out)
793 {
794     int x, y, cnt = 0;
795 
796     for (y = 0; y < BLOCK_HEIGHT; y++)
797         for (x = 0; x < BLOCK_WIDTH; x++)
798             out[cnt++] = prev->f[py + y][px + x] ^ curr->f[cy + y][cx + x];
799 
800     return out + cnt;
801 }
802 
send_frame(const uint8_t * const palette,void * const data,const int data_size,uint8_t * const output,const int output_avail,const int compress,z_stream * const zstream)803 LOCAL INLINE uint8_t *send_frame
804 (
805     const uint8_t *const palette,
806     void *const data, const int data_size,
807     uint8_t *const output, const int output_avail,
808     const int compress, z_stream *const zstream
809 )
810 {
811     assert(compress || output != data || !palette);
812 
813     if (!compress)
814     {
815         int offset = 0;
816         if (palette)
817         {
818             memcpy((void *)output, palette, 256 * 3);
819             offset = 256 * 3;
820         }
821         if ((void *)output != data)
822             memcpy((void *)(output + offset), data, data_size);
823         return output + offset + data_size;
824     }
825 
826     if (palette)    // XXX: palette != NULL means intraframe
827         deflateReset(zstream);
828     zstream->total_in = 0;
829     zstream->total_out = 0;
830     zstream->next_out = output;
831     zstream->avail_out = output_avail;
832 
833     if (palette)
834     {
835         zstream->next_in = palette;
836         zstream->avail_in = 256 * 3;
837         deflate(zstream, Z_SYNC_FLUSH);
838         assert(zstream->avail_in == 0);
839         assert(zstream->avail_out != 0);
840     }
841 
842     zstream->next_in = (const z_Bytef*)data;
843     zstream->avail_in = data_size;
844     deflate(zstream, Z_SYNC_FLUSH);
845     assert(zstream->avail_in == 0);
846     assert(zstream->avail_out != 0);
847     return output + zstream->total_out;
848 }
849 
850 /*
851  ** Set the palette for 8bpp mode.
852  ** Note, code doesn't support dynamic palettes.  Call once when opening video.
853  */
avi_set_palette(const avi_writer_t * const avi,const palette_t * const palette,const int length,const int offset)854 void avi_set_palette
855 (
856     const avi_writer_t *const avi,
857     const palette_t    *const palette,
858     const int           length,
859     const int           offset
860 )
861 {
862     if (avi->pvt)
863     {
864         memcpy((void*)&avi->pvt->video.palette[offset],
865                (const void *)&palette->color[0][0], length * 3);
866     }
867 }
868 
869 // Only encode video frame into video frame circular buffer
avi_record_video_internal(const avi_writer_t * const avi,const uint8_t * const image,const uint8_t border)870 LOCAL void avi_record_video_internal
871 (
872     const avi_writer_t *const avi,
873     const uint8_t      *const image,
874     const uint8_t             border
875 )
876 {
877     avi_pvt_t       *const pvt       = avi->pvt;
878     avi_info_t      *const info      = &(pvt->info);
879     avi_video_t     *const video     = &(pvt->video);
880     z_stream        *const zstream   = &(pvt->zstream);
881 
882     unsigned char *base_output;
883     unsigned char *output;
884     unsigned char *motion_vecs;
885     unsigned char *block_deltas;
886     static unsigned char buffer_codec[AVI_FRAME_BYTES + 4096];
887     int x, y;
888     int block;
889     const int rel_frame = info->total_frames - info->advance_audio_frames;
890     const int key_frame = (rel_frame & (info->key_frames - 1)) == 0;
891 
892     avi_frame_t       *const RESTRICT curr = &(video->frame[ video->toggle]);
893     const avi_frame_t *const RESTRICT prev = &(video->frame[!video->toggle]);
894 
895     if (rel_frame < 0)
896         return;
897 
898     // Push out as much interleaved audio/video as we can
899     while (avi_write_interleaved_stream(avi, 0))
900         ;
901 
902     // Enough room for a full encoded frame?
903     if (video->encode_buf_wr < AVI_VIDEO_WRAP_THRESH)
904     {
905         output = base_output = video->encode_buf + video->encode_buf_wr;
906     } else
907     {
908         video->encode_buf_wr = 0;
909         output = base_output = video->encode_buf;
910     }
911 
912     // Did we overrun circular buffer?
913     if (video->encode_buf_wr < video->encode_buf_rd &&
914         video->encode_buf_wr + AVI_VIDEO_ENCODE_BYTES > video->encode_buf_rd)
915     {
916         jzp_printf("Video overrun! Closing AVI.");
917         avi_end_video_internal(avi,1);
918     }
919 
920     video->toggle = !video->toggle;
921 
922 #if AVI_BORD_X_SZ > 0 || AVI_BORD_Y_SZ > 0
923     if (border != curr->f[0][0])
924         memset((void*)&(curr->f), border, sizeof(avi_frame_t));
925 #endif
926 
927     // NOTE: This pixel-doubles horizontally for the Intellivision.
928     // If we want to be more efficient, we would teach the whole encoder
929     // to work in 160x192 and then just double pixels when sending blocks.
930     for (y = 0; y < INPUT_DIM_Y; y++)
931     {
932         const uint8_t *RESTRICT vid_row_i = image + y * (INPUT_DIM_X/2);
933         uint8_t *RESTRICT vid_row_o =
934             &(curr->f[y + AVI_BORD_Y_SZ][AVI_BORD_X_SZ]);
935 
936         for (x = 0; x < INPUT_DIM_X/2; x++)
937         {
938             uint8_t p = *vid_row_i++;
939             *vid_row_o++ = p;
940             *vid_row_o++ = p;
941         }
942     }
943 
944     if (key_frame)
945     {
946         *output++ = 0x01;           /* Key frame */
947         *output++ = 0x00;           /* Hi version */
948         *output++ = 0x01;           /* Lo version */
949         /* Compression (0x00 = Uncompressed, 0x01 = ZLIB) */
950         *output++ = info->compress;
951         *output++ = 0x04;           /* 4 = 8 bpp */
952         *output++ = BLOCK_WIDTH;    /* Block width */
953         *output++ = BLOCK_HEIGHT;   /* Block height */
954 
955         const uint32_t remain = AVI_VIDEO_ENCODE_BYTES - (output - base_output);
956         output = send_frame(AVI_BPP == 8 ? &video->palette[0][0] : NULL,
957                             (void *)curr->f, AVI_FRAME_BYTES, output, remain,
958                             info->compress, zstream);
959     } else
960     {
961         *output++ = 0x00;   /* Non-key frame (+2 = Delta palette) */
962         motion_vecs  = buffer_codec;
963         block_deltas = motion_vecs + NUM_X_BLOCK * NUM_Y_BLOCK * 2;
964         block = 0;
965         for (y = 0; y < AVI_DIM_Y; y += BLOCK_HEIGHT)
966         {
967             for (x = 0; x < AVI_DIM_X; x += BLOCK_WIDTH)
968             {
969                 int move_x = 0, move_y = 0;
970                 int best_dif = motion_search(prev, curr, x, y,
971                                              &move_x, &move_y);
972                 if (best_dif == 0)
973                 {
974                     motion_vecs[block++] = move_x * 2;
975                     motion_vecs[block++] = move_y * 2;
976                 } else
977                 {
978                     motion_vecs[block++] = move_x * 2 + 1;
979                     motion_vecs[block++] = move_y * 2;
980                     block_deltas = send_block_delta(prev, curr,
981                                                     x + move_x, y + move_y,
982                                                     x, y, block_deltas);
983                 }
984             }
985         }
986         assert(block == NUM_X_BLOCK * NUM_Y_BLOCK * 2);
987         assert(block % 4 == 0);
988 
989         const uint32_t remain = AVI_VIDEO_ENCODE_BYTES - (output - base_output);
990         output = send_frame(NULL, (void *)buffer_codec,
991                             block_deltas - buffer_codec,
992                             output, remain, info->compress, zstream);
993     }
994 
995     const uint32_t length = output - base_output;
996 
997     video->encode_len[video->encode_len_wr++ % AVI_VIDEO_BUF_FRAMES] = length;
998     video->encode_buf_wr += length;
999 }
1000 
avi_record_video(const avi_writer_t * const avi,const uint8_t * const image,const uint8_t border)1001 void avi_record_video
1002 (
1003     const avi_writer_t *const avi,
1004     const uint8_t      *const image,
1005     const uint8_t             border
1006 )
1007 {
1008     if (!avi->pvt || !avi->pvt->info.active)
1009         return;
1010 
1011     if (avi_time_scale == 1.0)
1012     {
1013         avi_record_video_internal(avi, image, border);
1014         return;
1015     }
1016 
1017     avi_video_t *video = &(avi->pvt->video);
1018 
1019     video->time_remainder -= 1.0;
1020 
1021     while (video->time_remainder < 0.0)
1022     {
1023         video->time_remainder += avi_time_scale;
1024         avi_record_video_internal(avi, image, border);
1025     }
1026 }
1027 
1028 /*
1029  ** End of video recording
1030  */
avi_end_video_internal(const avi_writer_t * const avi,const int leave_active)1031 LOCAL void avi_end_video_internal
1032 (
1033     const avi_writer_t *const avi,
1034     const int leave_active
1035 )
1036 {
1037     avi_pvt_t       *const pvt       = avi->pvt;
1038     avi_info_t      *const info      = &(pvt->info);
1039     avi_riff_t      *const riff      = &(pvt->riff);
1040     avi_container_t *const container = &(pvt->container);
1041     z_stream        *const zstream   = &(pvt->zstream);
1042     size_t c;
1043     size_t d;
1044 
1045     if (!riff->file)
1046         return;
1047 
1048     // Push out as much interleaved audio/video as we can
1049     while (avi_write_interleaved_stream(avi, 0))
1050         ;
1051 
1052     fseek(riff->file, container->pos[4], SEEK_SET);
1053     riff_write_int_32(riff, info->total_frames);
1054     fseek(riff->file, container->pos[5], SEEK_SET);
1055     riff_write_int_32(riff, info->total_frames);
1056     fseek(riff->file, 0, SEEK_END);
1057 
1058     // Flush the rest
1059     while (avi_write_interleaved_stream(avi, 1))
1060         ;
1061 
1062     riff_close_structure(riff);
1063     riff_start_element(riff, "idx1");
1064     c = riff->base_index - riff->index;
1065     for (d = 0; d < c; d++)
1066         riff_write_int_32(riff, riff->index[d]);
1067     riff_close_element(riff);
1068     riff_close_structure(riff);
1069     fseek(riff->file, container->pos[0], SEEK_SET);
1070     riff_write_int_32(riff,
1071         (info->max_size_frame + 20 + 20 +
1072          info->max_size_audio + 2047) & ~0x07ff);
1073     fseek(riff->file, container->pos[1], SEEK_SET);
1074     riff_write_int_32(riff, info->max_size_frame);
1075     fseek(riff->file, container->pos[2], SEEK_SET);
1076     riff_write_int_32(riff, info->total_audio);
1077     fseek(riff->file, container->pos[3], SEEK_SET);
1078     riff_write_int_32(riff, info->max_size_audio);
1079     free(riff->index);
1080     fclose(riff->file);
1081     riff->file = NULL;
1082 
1083     if (info->compress)
1084     {
1085         deflateReset(zstream);
1086         int ret = deflateEnd(zstream);
1087         if (ret != Z_OK)
1088             printf("\nWarning: deflateEnd returned %d: %s\n", ret,
1089                    zstream->msg ? zstream->msg :"");
1090     }
1091 
1092     if (!leave_active)
1093         info->active = 0;
1094 }
1095 
avi_end_video(const avi_writer_t * const avi)1096 void avi_end_video( const avi_writer_t *const avi )
1097 {
1098     if (avi->pvt && avi->pvt->info.active)
1099         avi_end_video_internal( avi, 0 );
1100 }
1101 
avi_is_active(const avi_writer_t * const avi)1102 int avi_is_active( const avi_writer_t *const avi )
1103 {
1104     return avi->pvt && avi->pvt->info.active;
1105 }
1106 
avi_start_time(const avi_writer_t * const avi)1107 uint64_t avi_start_time( const avi_writer_t *const avi )
1108 {
1109     return avi->pvt ? avi->pvt->info.start_time : 0;
1110 }
1111 
avi_info(const avi_writer_t * const avi)1112 const avi_info_t *avi_info( const avi_writer_t *const avi )
1113 {
1114     return avi->pvt ? &avi->pvt->info : (avi_info_t*)NULL;
1115 }
1116 
avi_set_time_scale(const double avi_time_scale_,const double incoming_audio_time_scale)1117 void avi_set_time_scale( const double avi_time_scale_,
1118                          const double incoming_audio_time_scale )
1119 {
1120     avi_time_scale         = avi_time_scale_ > 0.01 ? avi_time_scale_ : 1.0;
1121     audio_time_scale       = incoming_audio_time_scale;
1122     audio_time_scale_ratio = avi_time_scale / incoming_audio_time_scale;
1123     printf("AVI: %5.3f %5.3f %5.3f\n", avi_time_scale, audio_time_scale,
1124            audio_time_scale_ratio);
1125 }
1126