e74f772f7593bd0e1811f658d64ee2b21c87b920
[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 #else
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 #if defined(WIN32_PIPE_TO_PROCESSES)
87                         pclose(file);
88 #elif defined(UNIX_PIPE_TO_PROCESSES)
89                         fclose(file);
90                         int status;
91                         waitpid(pid,&status,0);
92 #endif
93                 }
94
95 #if defined(WIN32_PIPE_TO_PROCESSES)
96
97                 string command;
98                 
99                 command=strprintf("ffmpeg -i \"%s\" -an -f image2pipe -vcodec ppm -\n",filename.c_str());
100                 
101                 file=popen(command.c_str(),POPEN_BINARY_READ_TYPE);
102
103 #elif defined(UNIX_PIPE_TO_PROCESSES)
104
105                 int p[2];
106           
107                 if (pipe(p)) {
108                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
109                         return false;
110                 };
111           
112                 pid = fork();
113           
114                 if (pid == -1) {
115                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
116                         return false;
117                 }
118           
119                 if (pid == 0){
120                         // Child process
121                         // Close pipein, not needed
122                         close(p[0]);
123                         // Dup pipein to stdout
124                         if( dup2( p[1], STDOUT_FILENO ) == -1 ){
125                                 cerr<<"Unable to open pipe to ffmpeg"<<endl;
126                                 return false;
127                         }
128                         // Close the unneeded pipein
129                         close(p[1]);
130                         execlp("ffmpeg", "ffmpeg", "-i", filename.c_str(), "-an", "-f", "image2pipe", "-vcodec", "ppm", "-", (const char *)NULL);
131                         // We should never reach here unless the exec failed
132                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
133                         return false;
134                 } else {
135                         // Parent process
136                         // Close pipeout, not needed
137                         close(p[1]);
138                         // Save pipein to file handle, will read from it later
139                         file = fdopen(p[0], "rb");
140                 }
141
142 #else
143         #error There are no known APIs for creating child processes
144 #endif
145
146                 if(!file)
147                 {
148                         cerr<<"Unable to open pipe to ffmpeg"<<endl;
149                         return false;
150                 }
151                 cur_frame=-1;
152         }
153
154         while(cur_frame<frame-1)
155         {
156                 cerr<<"Seeking to..."<<frame<<'('<<cur_frame<<')'<<endl;
157                 if(!grab_frame())
158                         return false;
159         }
160         return true;
161 }
162
163 bool
164 ffmpeg_mptr::grab_frame(void)
165 {
166         if(!file)
167         {
168                 cerr<<"unable to open "<<filename<<endl;
169                 return false;
170         }
171         int w,h;
172         float divisor;
173         char cookie[2];
174         cookie[0]=fgetc(file);
175         cookie[1]=fgetc(file);
176
177         if(cookie[0]!='P' || cookie[1]!='6')
178         {
179                 cerr<<"stream not in PPM format \""<<cookie[0]<<cookie[1]<<'"'<<endl;
180                 return false;
181         }
182
183         fgetc(file);
184         fscanf(file,"%d %d\n",&w,&h);
185         fscanf(file,"%f",&divisor);
186         fgetc(file);
187
188         if(feof(file))
189                 return false;
190
191         int x;
192         int y;
193         frame.set_wh(w,h);
194         for(y=0;y<frame.get_h();y++)
195                 for(x=0;x<frame.get_w();x++)
196                 {
197                         if(feof(file))
198                                 return false;
199 /*
200                         frame[y][x]=Color(
201                                 (float)(unsigned char)fgetc(file)/divisor,
202                                 (float)(unsigned char)fgetc(file)/divisor,
203                                 (float)(unsigned char)fgetc(file)/divisor,
204                                 1.0
205 */
206                         float r=gamma().r_U8_to_F32((unsigned char)fgetc(file));
207                         float g=gamma().g_U8_to_F32((unsigned char)fgetc(file));
208                         float b=gamma().b_U8_to_F32((unsigned char)fgetc(file));
209                         frame[y][x]=Color(
210                                 r,
211                                 g,
212                                 b,
213                                 1.0
214                         );
215                 }
216         cur_frame++;
217         return true;
218 }
219
220 ffmpeg_mptr::ffmpeg_mptr(const char *f)
221 {
222         pid=-1;
223 #ifdef HAVE_TERMIOS_H
224         tcgetattr (0, &oldtty);
225 #endif
226         filename=f;
227         file=NULL;
228         fps=23.98;
229         cur_frame=-1;
230 }
231
232 ffmpeg_mptr::~ffmpeg_mptr()
233 {
234         if(file)
235         {
236 #if defined(WIN32_PIPE_TO_PROCESSES)
237                 pclose(file);
238 #elif defined(UNIX_PIPE_TO_PROCESSES)
239                 fclose(file);
240                 int status;
241                 waitpid(pid,&status,0);
242 #endif
243         }
244 #ifdef HAVE_TERMIOS_H
245         tcsetattr(0,TCSANOW,&oldtty);
246 #endif
247 }
248
249 bool
250 ffmpeg_mptr::get_frame(synfig::Surface &surface,Time time, synfig::ProgressCallback *)
251 {
252         int i=(int)(time*fps);
253         if(i!=cur_frame)
254         {
255                 if(!seek_to(i))
256                         return false;
257                 if(!grab_frame())
258                         return false;
259         }
260
261         surface=frame;
262         return false;
263 }