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