1 #include <config.h>
2 #ifdef SIMAGE_AVIENC_SUPPORT
3 
4 #include "avi_encode.h"
5 
6 #define HAVE_VFW
7 
8 #ifdef HAVE_VFW
9 
10 #include <windows.h>
11 #include <vfw.h>
12 #include <stdio.h>
13 
14 /* fixme 20020227 thammer: are there any problems with specifying this library
15    here as opposed to in the makefile? */
16 #pragma comment(lib, "vfw32.lib")
17 
18 typedef struct {
19   PAVIFILE pfile;
20   PAVISTREAM ps;
21   PAVISTREAM pscomp;
22   int framenumber;
23   int width;
24   int height;
25 } avi_encode_context;
26 
avi_init_context(avi_encode_context * context)27 static void avi_init_context(avi_encode_context *context)
28 {
29   context->pfile = NULL;
30   context->ps = NULL;
31   context->pscomp = NULL;
32   context->framenumber = 0;
33   context->width = 0;
34   context->height = 0;
35 }
36 
avi_cleanup_context(avi_encode_context * context)37 static void avi_cleanup_context(avi_encode_context *context)
38 {
39   if (context->pscomp)
40     AVIStreamRelease(context->pscomp);
41   context->pscomp = NULL;
42 
43   if (context->ps)
44     AVIStreamRelease(context->ps);
45   context->ps = NULL;
46 
47   if (context->pfile)
48     AVIFileRelease(context->pfile);
49   context->pfile = NULL;
50 }
51 
52 #endif /* HAVE_VFW */
53 
54 
55 void *
avi_begin_encode(const char * filename,int width,int height,int fps,const char * preferences_filename)56 avi_begin_encode(const char *filename, int width, int height, int fps, const char *preferences_filename)
57 {
58 #ifdef HAVE_VFW
59   avi_encode_context *context;
60   HRESULT hr;
61   BOOL ret;
62   AVISTREAMINFO strhdr;
63   BITMAPINFO bi;
64   AVICOMPRESSOPTIONS opts;
65   AVICOMPRESSOPTIONS * aopts[1];
66   int rowsize;
67   int imagesize;
68   int numbits;
69   int prefsReadFromFile;
70 
71   if ( (width % 4 != 0) || (height % 4 != 0) )
72     return NULL; /* width and height must be divisible by 4 (several codecs crashes if this is not true) */
73 
74   context = (avi_encode_context *) malloc(sizeof(avi_encode_context));
75   avi_init_context(context);
76 
77   context->width = width;
78   context->height = height;
79 
80   AVIFileInit();
81 
82   /* Open file */
83   hr = AVIFileOpen(&context->pfile , filename, OF_WRITE | OF_CREATE, NULL);
84   if (hr != AVIERR_OK) {
85     avi_cleanup_context(context);
86     free(context);
87     return NULL;
88   }
89 
90   /*
91   fixme 20020304 thammer: Investigate what happens if the file allready exists.
92   Preliminary tests indicate that the new stream is just added to the existing
93   file (increasing the file size), instead of truncating the file first, as the
94   documentation for AVIFileOpen states.
95   */
96 
97   numbits = 24;
98   rowsize = (width * numbits + 31) / 32 * 4; /* aligned to 32 bits */
99   imagesize = rowsize * height;
100 
101   memset(&strhdr, 0, sizeof(strhdr));
102   strhdr.fccType = streamtypeVIDEO;
103   strhdr.fccHandler = 0;
104   strhdr.dwScale = 1;
105   strhdr.dwRate = fps;
106   strhdr.dwSuggestedBufferSize = imagesize;
107   strhdr.rcFrame.left = 0;
108   strhdr.rcFrame.top = 0;
109   strhdr.rcFrame.right = width;
110   strhdr.rcFrame.bottom = height;
111 
112   /* Create stream */
113   hr = AVIFileCreateStream(context->pfile, &context->ps, &strhdr);
114   if (hr != AVIERR_OK) {
115     avi_cleanup_context(context);
116     free(context);
117     return NULL;
118   }
119 
120   aopts[0] = &opts;
121   memset(&opts, 0, sizeof(opts));
122 
123   prefsReadFromFile = 0;
124   if ( (preferences_filename != NULL) && (strlen(preferences_filename)>0) ) {
125     FILE *file;
126     int size;
127     file = fopen(preferences_filename, "rb");
128     if (file==NULL) {
129       /* file doesn't exist, must pop up GUI to get options */
130       ret = AVISaveOptions(NULL, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE, 1, &context->ps, (LPAVICOMPRESSOPTIONS *) &aopts);
131       if (!ret) {
132         /* User pressed [Cancel] */
133         avi_cleanup_context(context);
134         free(context);
135         return NULL;
136       }
137       /* Save options to file*/
138       file = fopen(preferences_filename, "wb");
139       if (file == NULL) {
140         avi_cleanup_context(context);
141         free(context);
142         return NULL;
143       }
144 
145       /* write AVICOMPRESSOPTIONS struct */
146       size = fwrite(&opts, sizeof(AVICOMPRESSOPTIONS), 1, file);
147 
148       /* write AVICOMPRESSOPTIONS.cbFormat */
149       size = fwrite(&opts.cbFormat, 4, 1, file);
150 
151       /* write AVICOMPRESSOPTIONS.lpFormat */
152       size = fwrite(opts.lpFormat, opts.cbFormat, 1, file);
153 
154       /* write AVICOMPRESSOPTIONS.cbParms */
155       size = fwrite(&opts.cbParms, 4, 1, file);
156 
157       /* write AVICOMPRESSOPTIONS.lpParms */
158       size = fwrite(opts.lpParms, opts.cbParms, 1, file);
159 
160       fclose(file);
161     } else {
162       /* Read options from file */
163       file = fopen(preferences_filename, "rb");
164       if (file == NULL) {
165         avi_cleanup_context(context);
166         free(context);
167         return NULL;
168       }
169 
170       /* read AVICOMPRESSOPTIONS struct */
171       size = fread(&opts, sizeof(AVICOMPRESSOPTIONS), 1, file);
172 
173       /* read AVICOMPRESSOPTIONS.cbFormat */
174       size = fread(&opts.cbFormat, 4, 1, file);
175 
176       /* read AVICOMPRESSOPTIONS.lpFormat */
177       opts.lpFormat = (void *) malloc(opts.cbFormat);
178       size = fread(opts.lpFormat, opts.cbFormat, 1, file);
179 
180       /* read AVICOMPRESSOPTIONS.cbParms */
181       size = fread(&opts.cbParms, 4, 1, file);
182 
183       /* read AVICOMPRESSOPTIONS.lpParms */
184       opts.lpParms = (void *) malloc(opts.cbParms);
185       size = fread(opts.lpParms, opts.cbParms, 1, file);
186 
187       fclose(file);
188 
189       prefsReadFromFile = 1;
190     }
191   }
192   else {
193     ret = AVISaveOptions(NULL, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE, 1, &context->ps, (LPAVICOMPRESSOPTIONS *) &aopts);
194     if (!ret) {
195       /* User pressed [Cancel] */
196       avi_cleanup_context(context);
197       free(context);
198       return NULL;
199     }
200   };
201 
202   hr = AVIMakeCompressedStream( &context->pscomp, context->ps, &opts, NULL);
203   if (hr != AVIERR_OK) {
204     avi_cleanup_context(context);
205     free(context);
206     return NULL;
207   }
208 
209   if (prefsReadFromFile)
210   {
211     /* Since we don't know if our method of allocating memory (malloc) differs from
212        whatever AVISaveOptions() uses, we free what we created ourselves.
213     */
214     free(opts.lpFormat);
215     opts.lpFormat = NULL;
216     free(opts.lpParms);
217     opts.lpParms = NULL;
218   }
219   else
220     AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS *) &aopts);
221 
222 
223   memset(&bi, 0, sizeof(BITMAPINFO));
224 
225   bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) ;
226   bi.bmiHeader.biWidth = width ;
227   bi.bmiHeader.biHeight = height ;
228   bi.bmiHeader.biPlanes = 1 ;
229   bi.bmiHeader.biBitCount = numbits ;
230   bi.bmiHeader.biCompression = BI_RGB ;
231   bi.bmiHeader.biSizeImage = imagesize ;
232   bi.bmiHeader.biXPelsPerMeter = 0 ;
233   bi.bmiHeader.biYPelsPerMeter = 0 ;
234   bi.bmiHeader.biClrUsed = (numbits <= 8) ? 1 << numbits : 0;
235   bi.bmiHeader.biClrImportant = 0 ;
236 
237   hr = AVIStreamSetFormat(context->pscomp, 0, &bi, bi.bmiHeader.biSize + bi.bmiHeader.biClrUsed * sizeof(RGBQUAD));
238 
239   if (hr != AVIERR_OK) {
240     avi_cleanup_context(context);
241     free(context);
242     return NULL;
243   }
244 
245   return (void *)context;
246 
247 #else /* HAVE_VFW */
248   return NULL;
249 #endif /* HAVE_VFW*/
250 
251 }
252 
253 int
avi_encode_bitmap(void * handle,unsigned char * buf,int rgb2bgr)254 avi_encode_bitmap(void *handle, unsigned char *buf, int rgb2bgr)
255 {
256   /* Note: buf must be a 24-bit RGB (or BGR) image with the same size as
257      given to avi_begin_encode. It is the caller's responsibility to
258      check this.
259 
260      If rgb2bgr, the color ordering in the buffer will be modified from RGB to BGR.
261      NB: this means that the buffer will be modified.
262 
263      2003-03-14 thammer.
264 */
265 #ifdef HAVE_VFW
266   HRESULT hr;
267   avi_encode_context *context;
268 
269   context = (avi_encode_context *)handle;
270 
271   /* For some reason, AVIStreamWrite requires the color components to
272   be ordered BGR instead of RGB */
273   if (rgb2bgr) {
274     int x, y, r, nc, w, h;
275     nc = 3;
276     w = context->width;
277     h = context->height;
278     for (x=0; x<w; x++)
279       for (y=0; y<h; y++) {
280         r = buf[x * nc + y * nc * w + 0];
281         buf[x * nc + y * nc * w + 0] = buf[x * nc + y * nc * w + 2];
282         buf[x * nc + y * nc * w + 2] = r;
283       }
284   }
285 
286   hr = AVIStreamWrite(context->pscomp,
287                       context->framenumber,
288                       1,
289                       (LPBYTE) buf,
290                       context->width * context->height * 3, /* nc = 3 (24 bit) */
291                       AVIIF_KEYFRAME,
292                       NULL,
293                       NULL);
294 
295   /*
296   fixme 20020227 thammer: Is it correct / smart to let every frame be
297   a keyframe? Check avi doc and virtualdub.  */
298 
299   if (hr != AVIERR_OK)
300     return 0;
301 
302   context->framenumber++;
303 
304   return 1;
305 #else /* HAVE_VFW */
306   return 0;
307 #endif /* HAVE_VFW */
308 };
309 
310 int
avi_end_encode(void * handle)311 avi_end_encode(void *handle)
312 {
313 #ifdef HAVE_VFW
314   avi_encode_context *context;
315 
316   context = (avi_encode_context *)handle;
317   avi_cleanup_context(context);
318   free(context);
319   AVIFileExit();
320   return 1;
321 #else /* HAVE_VFW */
322   return 0
323 #endif /* HAVE_VFW */
324 
325 };
326 
327 #endif /* SIMAGE_AVIENC_SUPPORT */
328