doxygen: add newline after \file
[blender.git] / source / blender / blenkernel / intern / writeavi.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  * Functions for writing avi-format files.
19  * Added interface for generic movie support (ton)
20  */
21
22 /** \file
23  * \ingroup bke
24  */
25
26
27 #include <string.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_scene_types.h"
32
33 #include "BLI_utildefines.h"
34
35 #include "BKE_report.h"
36 #ifdef WITH_AVI
37 #  include "BLI_blenlib.h"
38
39 #  include "BKE_main.h"
40 #endif
41
42 #include "BKE_writeavi.h"
43
44 /* ********************** general blender movie support ***************************** */
45
46 static int start_stub(void *UNUSED(context_v), Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty),
47                       ReportList *UNUSED(reports), bool UNUSED(preview), const char *UNUSED(suffix))
48 { return 0; }
49
50 static void end_stub(void *UNUSED(context_v))
51 {}
52
53 static int append_stub(void *UNUSED(context_v), RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels),
54                        int UNUSED(rectx), int UNUSED(recty), const char *UNUSED(suffix), ReportList *UNUSED(reports))
55 { return 0; }
56
57 static void *context_create_stub(void)
58 { return NULL; }
59
60 static void context_free_stub(void *UNUSED(context_v))
61 {}
62
63 #ifdef WITH_AVI
64 #  include "AVI_avi.h"
65
66 /* callbacks */
67 static int start_avi(void *context_v, Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *suffix);
68 static void end_avi(void *context_v);
69 static int append_avi(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels,
70                       int rectx, int recty, const char *suffix, ReportList *reports);
71 static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix);
72 static void *context_create_avi(void);
73 static void context_free_avi(void *context_v);
74 #endif  /* WITH_AVI */
75
76 #ifdef WITH_FFMPEG
77 #  include "BKE_writeffmpeg.h"
78 #endif
79
80 bMovieHandle *BKE_movie_handle_get(const char imtype)
81 {
82         static bMovieHandle mh = {NULL};
83         /* stub callbacks in case none of the movie formats is supported */
84         mh.start_movie = start_stub;
85         mh.append_movie = append_stub;
86         mh.end_movie = end_stub;
87         mh.get_next_frame = NULL;
88         mh.get_movie_path = NULL;
89         mh.context_create = context_create_stub;
90         mh.context_free = context_free_stub;
91
92         /* set the default handle, as builtin */
93 #ifdef WITH_AVI
94         mh.start_movie = start_avi;
95         mh.append_movie = append_avi;
96         mh.end_movie = end_avi;
97         mh.get_movie_path = filepath_avi;
98         mh.context_create = context_create_avi;
99         mh.context_free = context_free_avi;
100 #endif
101
102         /* do the platform specific handles */
103 #ifdef WITH_FFMPEG
104         if (ELEM(imtype, R_IMF_IMTYPE_FFMPEG, R_IMF_IMTYPE_H264, R_IMF_IMTYPE_XVID, R_IMF_IMTYPE_THEORA)) {
105                 mh.start_movie = BKE_ffmpeg_start;
106                 mh.append_movie = BKE_ffmpeg_append;
107                 mh.end_movie = BKE_ffmpeg_end;
108                 mh.get_movie_path = BKE_ffmpeg_filepath_get;
109                 mh.context_create = BKE_ffmpeg_context_create;
110                 mh.context_free = BKE_ffmpeg_context_free;
111         }
112 #endif
113
114         /* in case all above are disabled */
115         (void)imtype;
116
117         return (mh.append_movie != append_stub) ? &mh : NULL;
118 }
119
120 /* ****************************************************************** */
121
122
123 #ifdef WITH_AVI
124
125 static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix)
126 {
127         int sfra, efra;
128
129         if (string == NULL) return;
130
131         if (preview) {
132                 sfra = rd->psfra;
133                 efra = rd->pefra;
134         }
135         else {
136                 sfra = rd->sfra;
137                 efra = rd->efra;
138         }
139
140         strcpy(string, rd->pic);
141         BLI_path_abs(string, BKE_main_blendfile_path_from_global());
142
143         BLI_make_existing_file(string);
144
145         if (rd->scemode & R_EXTENSION) {
146                 if (!BLI_path_extension_check(string, ".avi")) {
147                         BLI_path_frame_range(string, sfra, efra, 4);
148                         strcat(string, ".avi");
149                 }
150         }
151         else {
152                 if (BLI_path_frame_check_chars(string)) {
153                         BLI_path_frame_range(string, sfra, efra, 4);
154                 }
155         }
156
157         BLI_path_suffix(string, FILE_MAX, suffix, "");
158 }
159
160 static int start_avi(void *context_v, Scene *UNUSED(scene), RenderData *rd, int rectx, int recty,
161                      ReportList *reports, bool preview, const char *suffix)
162 {
163         int x, y;
164         char name[256];
165         AviFormat format;
166         int quality;
167         double framerate;
168         AviMovie *avi = context_v;
169
170         filepath_avi(name, rd, preview, suffix);
171
172         x = rectx;
173         y = recty;
174
175         quality = rd->im_format.quality;
176         framerate = (double) rd->frs_sec / (double) rd->frs_sec_base;
177
178         if (rd->im_format.imtype != R_IMF_IMTYPE_AVIJPEG) format = AVI_FORMAT_AVI_RGB;
179         else format = AVI_FORMAT_MJPEG;
180
181         if (AVI_open_compress(name, avi, 1, format) != AVI_ERROR_NONE) {
182                 BKE_report(reports, RPT_ERROR, "Cannot open or start AVI movie file");
183                 return 0;
184         }
185
186         AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &x);
187         AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &y);
188         AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &quality);
189         AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &framerate);
190
191         avi->interlace = 0;
192         avi->odd_fields = 0;
193
194         printf("Created avi: %s\n", name);
195         return 1;
196 }
197
198 static int append_avi(void *context_v, RenderData *UNUSED(rd), int start_frame, int frame, int *pixels,
199                       int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports))
200 {
201         unsigned int *rt1, *rt2, *rectot;
202         int x, y;
203         char *cp, rt;
204         AviMovie *avi = context_v;
205
206         if (avi == NULL)
207                 return 0;
208
209         /* note that libavi free's the buffer... stupid interface - zr */
210         rectot = MEM_mallocN(rectx * recty * sizeof(int), "rectot");
211         rt1 = rectot;
212         rt2 = (unsigned int *)pixels + (recty - 1) * rectx;
213         /* flip y and convert to abgr */
214         for (y = 0; y < recty; y++, rt1 += rectx, rt2 -= rectx) {
215                 memcpy(rt1, rt2, rectx * sizeof(int));
216
217                 cp = (char *)rt1;
218                 for (x = rectx; x > 0; x--) {
219                         rt = cp[0];
220                         cp[0] = cp[3];
221                         cp[3] = rt;
222                         rt = cp[1];
223                         cp[1] = cp[2];
224                         cp[2] = rt;
225                         cp += 4;
226                 }
227         }
228
229         AVI_write_frame(avi, (frame - start_frame), AVI_FORMAT_RGB32, rectot, rectx * recty * 4);
230 //      printf("added frame %3d (frame %3d in avi): ", frame, frame-start_frame);
231
232         return 1;
233 }
234
235 static void end_avi(void *context_v)
236 {
237         AviMovie *avi = context_v;
238
239         if (avi == NULL) return;
240
241         AVI_close_compress(avi);
242 }
243
244 static void *context_create_avi(void)
245 {
246         AviMovie *avi = MEM_mallocN(sizeof(AviMovie), "avimovie");
247         return avi;
248 }
249
250 static void context_free_avi(void *context_v)
251 {
252         AviMovie *avi = context_v;
253         if (avi) {
254                 MEM_freeN(avi);
255         }
256 }
257
258 #endif  /* WITH_AVI */
259
260 /* similar to BKE_image_path_from_imformat() */
261 void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix)
262 {
263         bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype);
264         if (mh && mh->get_movie_path) {
265                 mh->get_movie_path(string, rd, preview, suffix);
266         }
267         else {
268                 string[0] = '\0';
269         }
270 }