Removed typo'ed semicolon.
[synfig.git] / synfig-core / trunk / src / modules / mod_ffmpeg / mptr_ffmpeg.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file mptr_ffmpeg.cpp
3 **      \brief ppm Target Module
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 **
21 ** === N O T E S ===========================================================
22 **
23 ** ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include <ETL/stringf>
35 #include "mptr_ffmpeg.h"
36 #include <stdio.h>
37 #include <sys/types.h>
38 #if HAVE_SYS_WAIT_H
39  #include <sys/wait.h>
40 #endif
41 #if HAVE_IO_H
42  #include <io.h>
43 #endif
44 #if HAVE_PROCESS_H
45  #include <process.h>
46 #endif
47 #if HAVE_FCNTL_H
48  #include <fcntl.h>
49 #endif
50 #include <unistd.h>
51 #include <iostream>
52 #include <algorithm>
53 #include <functional>
54 #include <ETL/stringf>
55 #endif
56
57 /* === M A C R O S ========================================================= */
58
59 using namespace synfig;
60 using namespace std;
61 using namespace etl;
62
63 #if defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID)
64  #define UNIX_PIPE_TO_PROCESSES
65 #elif defined(HAVE__SPAWNLP) && defined(HAVE__PIPE) && defined(HAVE_CWAIT)
66  #define WIN32_PIPE_TO_PROCESSES
67 #endif
68
69 /* === G L O B A L S ======================================================= */
70
71 SYNFIG_IMPORTER_INIT(ffmpeg_mptr);
72 SYNFIG_IMPORTER_SET_NAME(ffmpeg_mptr,"ffmpeg");
73 SYNFIG_IMPORTER_SET_EXT(ffmpeg_mptr,"avi");
74 SYNFIG_IMPORTER_SET_VERSION(ffmpeg_mptr,"0.1");
75 SYNFIG_IMPORTER_SET_CVS_ID(ffmpeg_mptr,"$Id$");
76
77 /* === M E T H O D S ======================================================= */
78
79 bool
80 ffmpeg_mptr::seek_to(int frame)
81 {
82         if(frame<cur_frame || !file)
83         {
84                 if(file)
85                 {
86                         fclose(file);
87                         int status;
88 #if defined(WIN32_PIPE_TO_PROCESSES)
89                 cwait(&status,pid,0);
90 #elif defined(UNIX_PIPE_TO_PROCESSES)
91                 waitpid(pid,&status,0);
92 #endif
93                 }
94
95 #if defined(WIN32_PIPE_TO_PROCESSES)
96
97         int p[2];
98         int stdin_fileno, stdout_fileno;
99
100         if(_pipe(p, 512, O_BINARY | O_NOINHERIT) < 0) {
101                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
102                 return false;
103         }
104
105         // Save stdin/stdout so we can restore them later
106         stdin_fileno  = _dup(_fileno(stdin));
107         stdout_fileno = _dup(_fileno(stdout));
108
109         // ffmpeg should write to the pipe
110         if(_dup2(p[1], _fileno(stdout)) != 0) {
111                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
112                 return false;
113         }
114
115         /*
116         ffmpeg accepts the input filename on the command-line
117         if(_dup2(_fileno(input), _fileno(stdin)) != 0) {
118                 synfig::error(_("Unable to open pipe to ffmpeg"));
119                 return false;
120         }
121         */
122
123         pid = _spawnlp(_P_NOWAIT, "ffmpeg", "ffmpeg", "-i", filename.c_str(), "-an", "-f", "image2pipe", "-vcodec", "ppm", "-", (const char *)NULL);
124         if( pid < 0) {
125                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
126                 return false;
127         }
128
129         // Restore stdin/stdout
130         if(_dup2(stdin_fileno, _fileno(stdin)) != 0) {
131                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
132                 return false;
133         }
134         if(_dup2(stdout_fileno, _fileno(stdout)) != 0) {
135                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
136                 return false;
137         }
138         close(stdin_fileno);
139         close(stdout_fileno);
140
141         // Close the pipe write end - ffmpeg uses it
142         close(p[1]);
143         
144         // We read data from the read end of the pipe
145         file = fdopen(p[0], "rb");
146
147 #elif defined(UNIX_PIPE_TO_PROCESSES)
148
149                 int p[2];
150           
151                 if (pipe(p)) {
152                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
153                         return false;
154                 };
155           
156                 pid = fork();
157           
158                 if (pid == -1) {
159                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
160                         return false;
161                 }
162           
163                 if (pid == 0){
164                         // Child process
165                         // Close pipein, not needed
166                         close(p[0]);
167                         // Dup pipein to stdout
168                         if( dup2( p[1], STDOUT_FILENO ) == -1 ){
169                                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
170                                 return false;
171                         }
172                         // Close the unneeded pipein
173                         close(p[1]);
174                         execlp("ffmpeg", "ffmpeg", "-i", filename.c_str(), "-an", "-f", "image2pipe", "-vcodec", "ppm", "-", (const char *)NULL);
175                         // We should never reach here unless the exec failed
176                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
177                         return false;
178                 } else {
179                         // Parent process
180                         // Close pipeout, not needed
181                         close(p[1]);
182                         // Save pipein to file handle, will read from it later
183                         file = fdopen(p[0], "rb");
184                 }
185
186 #else
187         #error There are no known APIs for creating child processes
188 #endif
189
190                 if(!file)
191                 {
192                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
193                         return false;
194                 }
195                 cur_frame=-1;
196         }
197
198         while(cur_frame<frame-1)
199         {
200                 cerr<<"Seeking to..."<<frame<<'('<<cur_frame<<')'<<endl;
201                 if(!grab_frame())
202                         return false;
203         }
204         return true;
205 }
206
207 bool
208 ffmpeg_mptr::grab_frame(void)
209 {
210         if(!file)
211         {
212                 cerr<<"unable to open "<<filename<<endl;
213                 return false;
214         }
215         int w,h;
216         float divisor;
217         char cookie[2];
218         cookie[0]=fgetc(file);
219         cookie[1]=fgetc(file);
220
221         if(cookie[0]!='P' || cookie[1]!='6')
222         {
223                 cerr<<"stream not in PPM format \""<<cookie[0]<<cookie[1]<<'"'<<endl;
224                 return false;
225         }
226
227         fgetc(file);
228         fscanf(file,"%d %d\n",&w,&h);
229         fscanf(file,"%f",&divisor);
230         fgetc(file);
231
232         if(feof(file))
233                 return false;
234
235         int x;
236         int y;
237         frame.set_wh(w,h);
238         for(y=0;y<frame.get_h();y++)
239                 for(x=0;x<frame.get_w();x++)
240                 {
241                         if(feof(file))
242                                 return false;
243 /*
244                         frame[y][x]=Color(
245                                 (float)(unsigned char)fgetc(file)/divisor,
246                                 (float)(unsigned char)fgetc(file)/divisor,
247                                 (float)(unsigned char)fgetc(file)/divisor,
248                                 1.0
249 */
250                         float r=gamma().r_U8_to_F32((unsigned char)fgetc(file));
251                         float g=gamma().g_U8_to_F32((unsigned char)fgetc(file));
252                         float b=gamma().b_U8_to_F32((unsigned char)fgetc(file));
253                         frame[y][x]=Color(
254                                 r,
255                                 g,
256                                 b,
257                                 1.0
258                         );
259                 }
260         cur_frame++;
261         return true;
262 }
263
264 ffmpeg_mptr::ffmpeg_mptr(const char *f)
265 {
266         pid=-1;
267 #ifdef HAVE_TERMIOS_H
268         tcgetattr (0, &oldtty);
269 #endif
270         filename=f;
271         file=NULL;
272         fps=23.98;
273         cur_frame=-1;
274 }
275
276 ffmpeg_mptr::~ffmpeg_mptr()
277 {
278         if(file)
279         {
280                 fclose(file);
281                 int status;
282 #if defined(WIN32_PIPE_TO_PROCESSES)
283                 cwait(&status,pid,0);
284 #elif defined(UNIX_PIPE_TO_PROCESSES)
285                 waitpid(pid,&status,0);
286 #endif
287         }
288 #ifdef HAVE_TERMIOS_H
289         tcsetattr(0,TCSANOW,&oldtty);
290 #endif
291 }
292
293 bool
294 ffmpeg_mptr::get_frame(synfig::Surface &surface,Time time, synfig::ProgressCallback *)
295 {
296         int i=(int)(time*fps);
297         if(i!=cur_frame)
298         {
299                 if(!seek_to(i))
300                         return false;
301                 if(!grab_frame())
302                         return false;
303         }
304
305         surface=frame;
306         return false;
307 }