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