- got tired of str[n]casecmp not declared warnings
[blender.git] / source / blender / src / writemovie.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36
37 #ifdef __sgi
38
39 #include <unistd.h>
40 #include <movie.h>
41 #include <cdaudio.h>
42 #include <dmedia/cl.h>
43 #include <dmedia/cl_cosmo.h>
44 #include <sys/file.h>                   /* flock */
45 #include "MEM_guardedalloc.h"
46
47 #include "BLI_blenlib.h"
48
49 #include "IMB_imbuf_types.h"
50 #include "IMB_imbuf.h"
51
52 #include "BKE_global.h"
53 #include "BKE_utildefines.h"
54
55 #include "BIF_gl.h"
56 #include "BIF_writemovie.h"
57 #include "BIF_toolbox.h"
58
59 #include "render.h"
60
61 #define error(str) {perror(str) ; error("%s", str); G.afbreek= 1;}
62 #define QUIT(str) {error(str); return;}
63
64 #define DIR_UP 1
65 #define DIR_DOWN 2
66 #define DIR_BOTH (DIR_UP | DIR_DOWN)
67
68 #define MAXQUAL R.r.quality
69 #define MINQUAL 30
70
71 /* globals */
72
73 static CL_Handle        compr, soft_compr;
74 static MVid             movie, image; 
75 static DMparams *movie_params, *image_params;
76 static int                      compr_params[64];
77 static int                      myindex, qualindex, qualnow, mv_outx, mv_outy, numfields= 2;
78 static char             *comp_buf;
79 static int                      sfra, efra, first = TRUE, maxbufsize;
80 static int                      ntsc = FALSE;
81
82 #define FIRST_IMAGE "FIRST_IMAGE"
83 #define BLENDER_FIRST_IMAGE "BLENDER_1ST_IMG"
84
85
86 static void report_flock(void)
87 {
88         static int flock_reported = FALSE;
89         
90         if (flock_reported) return;
91         flock_reported = TRUE;
92         
93         error("WriteMovie: couldn't flock() moviefile. Ignoring.");
94 }
95
96
97 static void make_movie_name(char *string)
98 {
99         int len;
100         char txt[64];
101
102         if (string==0) return;
103
104         strcpy(string, G.scene->r.pic);
105         BLI_convertstringcode(string, G.sce, G.scene->r.cfra);
106         len= strlen(string);
107
108         RE_make_existing_file(string);
109
110         if (BLI_strcasecmp(string + len - 3, ".mv")) {
111                 sprintf(txt, "%04d_%04d.mv", sfra, efra);
112                 strcat(string, txt);
113         }
114 }
115
116 static int my_Compress(uint * rect, int *bufsize)
117 {
118         int err = 0;
119         
120         compr_params[qualindex] = qualnow;
121         clSetParams(compr, compr_params, myindex);
122
123         while (clCompress(compr, numfields, rect, bufsize, comp_buf) != numfields) {
124                 if (compr == soft_compr) {
125                         error("clCompress (software)");
126                         return 1;
127                 }
128                 
129                 /* hardware opnieuw initialiseren */
130                 clCloseCompressor(compr);
131                 clOpenCompressor(CL_JPEG_COSMO, &compr);
132
133                 qualnow--;
134                 compr_params[qualindex] = qualnow;
135                 clSetParams(compr, compr_params, myindex);
136                 printf("retrying at quality %d\n", qualnow);
137                 
138                 err= TRUE;
139         }
140         
141         return (err);
142 }
143
144 static void set_sfra_efra(void)
145 {
146         sfra = (G.scene->r.sfra);
147         efra = (G.scene->r.efra);
148 }
149
150 static void open_compressor(void)
151 {
152         int cosmo = FAILURE;
153         
154         /* initialiseren van de compressor */
155         
156         if (clOpenCompressor(CL_JPEG_SOFTWARE, &soft_compr) != SUCCESS) QUIT("clOpenCompressor");
157         
158         if (G.scene->r.mode & R_COSMO) {
159                 cosmo = clOpenCompressor(CL_JPEG_COSMO, &compr);
160                 if (cosmo != SUCCESS && first) error("warning: using software compression");
161                 first = FALSE;
162         }
163         
164         if (cosmo != SUCCESS) compr = soft_compr;
165         
166         myindex = 0;
167
168         compr_params[myindex++]= CL_IMAGE_WIDTH;
169         compr_params[myindex++]= mv_outx;
170
171         compr_params[myindex++]= CL_IMAGE_HEIGHT;
172         compr_params[myindex++]= mv_outy / numfields;
173         
174         compr_params[myindex++]= CL_JPEG_QUALITY_FACTOR;
175         qualindex = myindex;
176         compr_params[myindex++]= R.r.quality;
177
178         compr_params[myindex++]= CL_ORIGINAL_FORMAT;
179         compr_params[myindex++]= CL_RGBX;
180
181         compr_params[myindex++]= CL_ORIENTATION;
182         compr_params[myindex++]= CL_TOP_DOWN;
183
184         compr_params[myindex++]= CL_INTERNAL_FORMAT;
185         compr_params[myindex++]= CL_YUV422;
186
187         /* this parameter must be set for non-queueing mode */
188         compr_params[myindex++]= CL_ENABLE_IMAGEINFO;
189         compr_params[myindex++]= 1;
190
191         /* enable stream headers */
192         compr_params[myindex++]= CL_STREAM_HEADERS;
193         compr_params[myindex++]= TRUE;
194
195         clSetParams(compr, compr_params, myindex);
196         if (compr != soft_compr) clSetParams(soft_compr, compr_params, myindex);
197         
198         maxbufsize = 2 * clGetParam(compr, CL_COMPRESSED_BUFFER_SIZE);
199         comp_buf = MEM_mallocN(maxbufsize, "cosmo_buffer");
200 }
201
202 static void close_compressor(void)
203 {
204         MEM_freeN(comp_buf);
205         comp_buf = 0;
206
207         clCloseCompressor(compr);
208         if (soft_compr != compr) clCloseCompressor(soft_compr);
209 }
210
211 void end_movie(void)
212 {
213 }
214
215 static void new_movie(int fd)
216 {
217         char    string[120];
218
219         if (dmParamsCreate(&movie_params) != DM_SUCCESS) QUIT("dmParamsCreate");
220         if (dmParamsCreate(&image_params) != DM_SUCCESS) QUIT("dmParamsCreate");
221                 
222                 if (mvSetMovieDefaults(movie_params, MV_FORMAT_SGI_3) != DM_SUCCESS) QUIT("mvSetMovieDefaults");
223                 if (dmSetImageDefaults(image_params, mv_outx, mv_outy, DM_PACKING_RGBX) != DM_SUCCESS) QUIT("dmSetImageDefaults");
224                         
225                 mvAddUserParam(BLENDER_FIRST_IMAGE);
226                 sprintf(string, "%04d", sfra);
227                 dmParamsSetString(image_params, BLENDER_FIRST_IMAGE, string);
228         
229                 if (ntsc) dmParamsSetFloat(image_params, DM_IMAGE_RATE, 29.97);
230                 else dmParamsSetFloat(image_params, DM_IMAGE_RATE, 25.0);
231                 
232                 if (numfields == 2) {
233                         if (ntsc) dmParamsSetEnum(image_params, DM_IMAGE_INTERLACING, DM_IMAGE_INTERLACED_ODD);
234                         else dmParamsSetEnum(image_params, DM_IMAGE_INTERLACING, DM_IMAGE_INTERLACED_EVEN);
235                 } else dmParamsSetEnum(image_params, DM_IMAGE_INTERLACING, DM_IMAGE_NONINTERLACED);
236         
237                 dmParamsSetEnum(image_params, DM_IMAGE_ORIENTATION, DM_TOP_TO_BOTTOM);
238                 dmParamsSetString(image_params, DM_IMAGE_COMPRESSION, DM_IMAGE_JPEG);
239         
240                 if (mvCreateFD(fd, movie_params, NULL, &movie) != DM_SUCCESS) QUIT("mvCreateFile");
241                 if (mvAddTrack(movie, DM_IMAGE, image_params, NULL, &image)) QUIT("mvAddTrack");;
242                 if (mvSetLoopMode(movie, MV_LOOP_CONTINUOUSLY) != DM_SUCCESS) QUIT("mvSetMovieDefaults");
243                                                 
244                 if (mvWrite(movie) != DM_SUCCESS) QUIT("mvWrite");
245                 if (mvClose(movie) != DM_SUCCESS) QUIT("mvClose");
246         
247         dmParamsDestroy(image_params);
248         dmParamsDestroy(movie_params);
249 }
250
251
252 void start_movie(void)
253 {
254         char    name[FILE_MAXDIR+FILE_MAXFILE];
255         char    bak[sizeof(name) + 4];
256         int             fd;
257         
258         first = TRUE;
259         
260         set_sfra_efra();
261         
262         /* naam bedenken voor de movie */
263         make_movie_name(name);
264         
265         ntsc = FALSE;
266         
267         switch (R.recty) {
268                 case 480: case 360: case 240: case 120:
269                         ntsc = TRUE;
270         }
271         
272         if (ntsc) {
273                 switch (R.rectx) {
274                 case 360: case 320: case 720: case 640:
275                         mv_outx = R.rectx;
276                         break;
277                 default:
278                         if (R.rectx <= 320) mv_outx = 320;
279                         else if (R.rectx <= 640) mv_outx = 640;
280                         else mv_outx = 720;
281                 }
282         } else {
283                 switch (R.rectx) {
284                 case 360: case 384: case 720: case 768:
285                         mv_outx = R.rectx;
286                         break;
287                 default:
288                         if (R.rectx < 384) mv_outx = 384;
289                         else mv_outx = 768;
290                 }
291         }
292         
293         if (ntsc) {
294                 if (R.recty <= 240) {
295                         mv_outy = 240;
296                         numfields = 1;
297                 } else {
298                         mv_outy = 480;
299                         numfields = 2;
300                 }
301         } else {
302                 if (R.recty <= 288) {
303                         mv_outy = 288;
304                         numfields = 1;
305                 } else {
306                         mv_outy = 576;
307                         numfields = 2;
308                 }
309         }
310         
311         if(R.r.mode & R_MOVIECROP) {
312                 if (ntsc) {
313                         if (R.rectx > 640) mv_outx = 720;
314                         else mv_outx = 640;
315                         mv_outy = 480;
316                         numfields = 2;
317                 } else {
318                         if (R.rectx > 720) mv_outx = 768;
319                         else mv_outx = 720;
320                         mv_outy = 576;
321                         numfields = 2;
322                 }
323         }
324         
325         qualnow = R.r.quality;
326
327         
328         fd = open(name, O_BINARY|O_RDWR);
329         if (fd != -1) {
330                 if (flock(fd, LOCK_EX) == -1) report_flock();
331                 
332                         if (mvOpenFD(fd, &movie) == DM_SUCCESS) {
333                                 if (mvFindTrackByMedium(movie, DM_IMAGE, &image) == DM_SUCCESS) {
334                                         if (mvGetImageWidth(image) == mv_outx) {
335                                                 if (mvGetImageHeight(image) == mv_outy) {
336                                                         mvClose(movie);
337                                                         close(fd);
338                                                         return;
339                                                 }
340                                         }
341                                 }
342                                 strcpy(bak, name);
343                                 strcat(bak, ".bak");
344                                 BLI_rename(name, bak);
345                                 mvClose(movie);
346                         }
347                 
348                 close(fd);
349         }
350         fd = open(name, O_BINARY|O_RDWR | O_CREAT | O_EXCL, 0664);
351         if (fd != -1) {
352                 if (flock(fd, LOCK_EX) == -1) report_flock();
353                         new_movie(fd);
354                         printf("Created movie: %s\n", name);
355                 close(fd);
356         }
357 }
358
359 void append_movie(int cfra)
360 {
361         ImBuf           *ibuf, *tbuf;
362         int                     err, ofsx, ofsy, bufsize, rate, lastqual, qualstep, direction, first_image, num_images;
363         char            name[FILE_MAXDIR+FILE_MAXFILE];
364         const char      *string;
365         int                     fd;
366         
367         set_sfra_efra();
368         make_movie_name(name);
369         open_compressor();
370         
371         rate = 1024 * R.r.maximsize;
372         
373         /* veranderd: kopie van rectot maken */
374         ibuf= IMB_allocImBuf(R.rectx, R.recty, 32, IB_rect, 0);
375         memcpy(ibuf->rect, R.rectot, 4*R.rectx*R.recty);
376         
377         if (ibuf->x != mv_outx || ibuf->y != mv_outy) {
378                 tbuf = IMB_allocImBuf(mv_outx, mv_outy, 32, IB_rect, 0);
379                 IMB_rectoptot(tbuf, 0, IMB_rectfill, 0x00);
380                 
381                 ofsx = (tbuf->x - ibuf->x) / 2;
382                 ofsy = (tbuf->y - ibuf->y) / 2;
383                 if (numfields == 2) ofsy &= ~1;
384                 
385                 IMB_rectop(tbuf, ibuf, ofsx, ofsy, 0, 0, 32767, 32767, IMB_rectcpy, 0);
386                 IMB_freeImBuf(ibuf);
387                 strcpy(tbuf->name, ibuf->name);
388                 ibuf = tbuf;
389         }
390         IMB_convert_rgba_to_abgr(ibuf->x*ibuf->y, ibuf->rect);
391         
392         if (numfields == 2) {
393                 if (ntsc) {
394                         IMB_rectop(ibuf, ibuf, 0, 0, 0, 1, 32767, 32767, IMB_rectcpy, 0);
395                         IMB_flipy(ibuf);
396                         IMB_de_interlace(ibuf);
397                         if (ntsc) IMB_rectop(ibuf, ibuf, 0, 0, 0, 1, 32767, 32767, IMB_rectcpy, 0);
398                 } else {
399                         IMB_flipy(ibuf);
400                         IMB_rectop(ibuf, ibuf, 0, 0, 0, 1, 32767, 32767, IMB_rectcpy, 0);
401                         IMB_de_interlace(ibuf);
402                 }
403         }
404         else {
405                 /* kleine movies anders op de kop */
406                 IMB_flipy(ibuf);
407         }
408         
409         if (rate == 0) {
410                 qualnow = R.r.quality;
411                 my_Compress(ibuf->rect, &bufsize);
412         } else {
413                 qualstep = 4;
414                 direction = 0;
415                 
416                 do {
417                         if (qualnow > MAXQUAL) qualnow = MAXQUAL;
418                         if (qualnow < MINQUAL) qualnow = MINQUAL;
419
420                         compr_params[qualindex] = qualnow;
421                         clSetParams(compr, compr_params, myindex);
422
423                         lastqual = qualnow;
424                         err = my_Compress(ibuf->rect, &bufsize);
425                         
426                         printf(" tried quality: %d, size %d\n", qualnow, bufsize);
427                         
428                         if (bufsize < 0.9 * rate) {
429                                 if (err) {
430                                         /* forget about this frame, retry next frame at old quality settting */
431                                         qualnow = lastqual;
432                                         break;
433                                 }
434                                 if (qualnow == MAXQUAL) break;
435                                 direction |= DIR_UP;
436                                 if (direction == DIR_BOTH) qualstep /= 2;
437                                 qualnow += qualstep;
438                         } else if (bufsize > 1.1 * rate) {
439                                 if (qualnow == MINQUAL) break;
440                                 direction |= DIR_DOWN;
441                                 if (direction == DIR_BOTH) qualstep /= 2;
442                                 qualnow -= qualstep;
443                         } else break;
444                                                                         
445                         if (qualstep == 0) {
446                                 /* this was the last iteration. Make sure that the buffer isn't to big */
447                                 if (bufsize < 1.1 * rate) break;
448                                 else qualnow--;
449                         }
450                 } while (1);
451                 
452                 printf("used quality: %d\n", qualnow);
453                 
454                 if (bufsize < rate) qualnow++;
455                 else qualnow--;
456                 
457         }
458         
459         fd = open(name, O_BINARY|O_RDWR);
460
461         if (fd != -1) {
462                 if (flock(fd, LOCK_EX) == -1) report_flock();
463                         if (mvOpenFD(fd, &movie) == DM_SUCCESS){
464                                 if (mvFindTrackByMedium(movie, DM_IMAGE, &image) == DM_SUCCESS) {
465                                         image_params = mvGetParams(image);
466                                         
467                                         first_image = 1;
468                                         
469                                         string = dmParamsGetString(image_params, FIRST_IMAGE);
470                                         if (string) {
471                                                 first_image = atoi(string);
472                                         }
473                                         string = dmParamsGetString(image_params, BLENDER_FIRST_IMAGE);
474                                         if (string) {
475                                                 first_image = atoi(string);
476                                         }
477                                         
478                                         num_images = mvGetTrackLength(image);
479                                         
480                                         if (cfra >= first_image && cfra <= (first_image + num_images - 1)) {
481                                                 if (mvDeleteFrames(image, cfra - first_image, 1) != DM_SUCCESS) {
482                                                         mvDestroyMovie(movie);
483                                                         error("mvDeleteFrames");
484                                                         G.afbreek = 1;
485                                                 }
486                                         }
487                                         
488                                         if (G.afbreek != 1) {
489                                                 if (mvInsertCompressedImage(image, cfra - first_image, bufsize, comp_buf) == DM_SUCCESS) {
490                                                         printf("added frame %3d (frame %3d in movie): length %6d: ", cfra, cfra - first_image + 1, bufsize);
491                                                         mvClose(movie);
492                                                 } else {
493                                                         mvDestroyMovie(movie);
494                                                         error("mvInsertCompressedImage");
495                                                         G.afbreek = 1;
496                                                 }
497                                         }
498                                 } else {
499                                         mvDestroyMovie(movie);
500                                         error("mvFindTrackByMedium");
501                                         G.afbreek = 1;
502                                 }
503                         }else {
504                                 error("mvOpenFD");
505                                 G.afbreek = 1;
506                         }
507                 close(fd);
508         } else {
509                 error("open movie");
510                 G.afbreek = 1;
511         }
512         
513         IMB_freeImBuf(ibuf);
514         
515         close_compressor();     
516 }
517
518 #endif  /* __sgi */