Minor security fix: the unused mod_ffmpeg importer used popen to run convert, change...
[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 #include <sys/wait.h>
39 #include <unistd.h>
40 #include <iostream>
41 #include <algorithm>
42 #include <functional>
43 #include <ETL/stringf>
44 #endif
45
46 /* === M A C R O S ========================================================= */
47
48 using namespace synfig;
49 using namespace std;
50 using namespace etl;
51
52 /* === G L O B A L S ======================================================= */
53
54 SYNFIG_IMPORTER_INIT(ffmpeg_mptr);
55 SYNFIG_IMPORTER_SET_NAME(ffmpeg_mptr,"ffmpeg");
56 SYNFIG_IMPORTER_SET_EXT(ffmpeg_mptr,"avi");
57 SYNFIG_IMPORTER_SET_VERSION(ffmpeg_mptr,"0.1");
58 SYNFIG_IMPORTER_SET_CVS_ID(ffmpeg_mptr,"$Id$");
59
60 /* === M E T H O D S ======================================================= */
61
62 bool
63 ffmpeg_mptr::seek_to(int frame)
64 {
65         if(frame<cur_frame || !file)
66         {
67                 if(file)
68                 {
69                         fclose(file);
70                         int status;
71                         waitpid(pid,&status,0);
72                 }
73
74                 int p[2];
75           
76                 if (pipe(p)) {
77                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
78                         return false;
79                 };
80           
81                 pid = fork();
82           
83                 if (pid == -1) {
84                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
85                         return false;
86                 }
87           
88                 if (pid == 0){
89                         // Child process
90                         // Close pipein, not needed
91                         close(p[0]);
92                         // Dup pipein to stdout
93                         if( dup2( p[1], STDOUT_FILENO ) == -1 ){
94                                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
95                                 return false;
96                         }
97                         // Close the unneeded pipein
98                         close(p[1]);
99                         execlp("ffmpeg", "ffmpeg", "-i", filename.c_str(), "-an", "-f", "image2pipe", "-vcodec", "ppm", "-", (const char *)NULL);
100                         // We should never reach here unless the exec failed
101                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
102                         return false;
103                 } else {
104                         // Parent process
105                         // Close pipeout, not needed
106                         close(p[1]);
107                         // Save pipeout to file handle, will write to it later
108                         file = fdopen(p[0], "wb");
109                 }
110
111                 if(!file)
112                 {
113                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
114                         return false;
115                 }
116                 cur_frame=-1;
117         }
118
119         while(cur_frame<frame-1)
120         {
121                 cerr<<"Seeking to..."<<frame<<'('<<cur_frame<<')'<<endl;
122                 if(!grab_frame())
123                         return false;
124         }
125         return true;
126 }
127
128 bool
129 ffmpeg_mptr::grab_frame(void)
130 {
131         if(!file)
132         {
133                 cerr<<"unable to open "<<filename<<endl;
134                 return false;
135         }
136         int w,h;
137         float divisor;
138         char cookie[2];
139         cookie[0]=fgetc(file);
140         cookie[1]=fgetc(file);
141
142         if(cookie[0]!='P' || cookie[1]!='6')
143         {
144                 cerr<<"stream not in PPM format \""<<cookie[0]<<cookie[1]<<'"'<<endl;
145                 return false;
146         }
147
148         fgetc(file);
149         fscanf(file,"%d %d\n",&w,&h);
150         fscanf(file,"%f",&divisor);
151         fgetc(file);
152
153         if(feof(file))
154                 return false;
155
156         int x;
157         int y;
158         frame.set_wh(w,h);
159         for(y=0;y<frame.get_h();y++)
160                 for(x=0;x<frame.get_w();x++)
161                 {
162                         if(feof(file))
163                                 return false;
164 /*
165                         frame[y][x]=Color(
166                                 (float)(unsigned char)fgetc(file)/divisor,
167                                 (float)(unsigned char)fgetc(file)/divisor,
168                                 (float)(unsigned char)fgetc(file)/divisor,
169                                 1.0
170 */
171                         float r=gamma().r_U8_to_F32((unsigned char)fgetc(file));
172                         float g=gamma().g_U8_to_F32((unsigned char)fgetc(file));
173                         float b=gamma().b_U8_to_F32((unsigned char)fgetc(file));
174                         frame[y][x]=Color(
175                                 r,
176                                 g,
177                                 b,
178                                 1.0
179                         );
180                 }
181         cur_frame++;
182         return true;
183 }
184
185 ffmpeg_mptr::ffmpeg_mptr(const char *f)
186 {
187         pid=-1;
188 #ifdef HAVE_TERMIOS_H
189         tcgetattr (0, &oldtty);
190 #endif
191         filename=f;
192         file=NULL;
193         fps=23.98;
194         cur_frame=-1;
195 }
196
197 ffmpeg_mptr::~ffmpeg_mptr()
198 {
199         if(file)
200         {
201                 fclose(file);
202                 int status;
203                 waitpid(pid,&status,0);
204         }
205 #ifdef HAVE_TERMIOS_H
206         tcsetattr(0,TCSANOW,&oldtty);
207 #endif
208 }
209
210 bool
211 ffmpeg_mptr::get_frame(synfig::Surface &surface,Time time, synfig::ProgressCallback *)
212 {
213         int i=(int)(time*fps);
214         if(i!=cur_frame)
215         {
216                 if(!seek_to(i))
217                         return false;
218                 if(!grab_frame());
219                         return false;
220         }
221
222         surface=frame;
223         return false;
224 }