2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "gnashconfig.h"
24 #include "NullSoundHandler.h"
26 # include "ffmpeg/MediaHandlerFfmpeg.h"
27 #elif defined(USE_GST)
28 # include "gst/MediaHandlerGst.h"
41 #include "ClockTime.h"
42 #include "movie_definition.h"
43 #include "MovieClip.h"
44 #include "movie_root.h"
49 #include "GnashException.h"
52 #include "noseek_fd_adapter.h"
53 #include "ManualClock.h"
54 #include "StringPredicates.h"
55 #include "smart_ptr.h"
56 #include "IOChannel.h" // for proper dtor call
57 #include "GnashSleep.h" // for usleep comptibility.
58 #include "MovieFactory.h"
59 #include "RunResources.h"
68 extern int optind
, getopt(int, char *const *, const char *);
72 // How many seconds to wait for a frame advancement
73 // before kicking the movie (forcing it to next frame)
74 static const double waitforadvance
= 5;
76 // How many time do we allow for loop backs
77 // (goto frame < current frame)
78 static const size_t allowloopbacks
= 10;
80 // How many times to call 'advance' ?
81 // If 0 number of advance is unlimited
82 // (see other constraints)
83 // TODO: add a command-line switch to control this
84 static size_t limit_advances
= 0;
86 // How much time to sleep between advances ?
87 // If set to -1 it will be computed based on FPS.
88 static long int delay
= 0;
90 const char *GPROC_VERSION
= "1.0";
93 using namespace gnash
;
96 gnash::LogFile
& dbglogfile
= gnash::LogFile::getDefaultInstance();
97 gnash::RcInitFile
& rcfile
= gnash::RcInitFile::getDefaultInstance();
99 gnash::Debugger
& debugger
= gnash::Debugger::getDefaultInstance();
107 gnash::movie_definition
* m_movie
;
108 std::string m_filename
;
111 static boost::intrusive_ptr
<gnash::movie_definition
> play_movie(
112 const std::string
& filename
, const RunResources
& runResources
);
114 static bool s_do_output
= false;
115 static bool s_stop_on_errors
= true;
117 // How many time do we allow to hit the end ?
118 static size_t allowed_end_hits
= 1;
120 double lastAdvanceTimer
;
123 resetLastAdvanceTimer()
125 // clocktime::getTicks() returns milliseconds
126 lastAdvanceTimer
= static_cast<double>(clocktime::getTicks()) / 1000.0;
130 secondsSinceLastAdvance()
132 // clocktime::getTicks() returns milliseconds
133 double now
= static_cast<double>(clocktime::getTicks()) / 1000.0;
134 return ( now
- lastAdvanceTimer
);
137 // A flag which will be used to interrupt playback
138 // by effect of a "quit" fscommand
140 static int quitrequested
= false;
142 class FsCommandExecutor
: public movie_root::AbstractFsCallback
{
144 void notify(const std::string
& command
, const std::string
& args
)
146 log_debug(_("fs_callback(%p): %s %s"), command
, args
);
148 StringNoCaseEqual ncasecomp
;
150 if ( ncasecomp(command
, "quit") ) quitrequested
= true;
154 class EventCallback
: public movie_root::AbstractIfaceCallback
{
156 std::string
call(const std::string
& event
, const std::string
& arg
)
158 log_debug(_("eventCallback: %s %s"), event
, arg
);
160 static bool mouseShown
= true;
162 // These should return "true" if the mouse was visible before
164 if ( event
== "Mouse.hide" ) {
165 bool state
= mouseShown
;
167 return state
? "true" : "false";
170 if ( event
== "Mouse.show" ) {
171 bool state
= mouseShown
;
173 return state
? "true" : "false" ;
176 // Some fake values for consistent test results.
178 if ( event
== "System.capabilities.screenResolutionX" ) {
182 if ( event
== "System.capabilities.screenResolutionY" ) {
186 if ( event
== "System.capabilities.screenDPI" ) {
190 if ( event
== "System.capabilities.screenColor" ) {
194 if ( event
== "System.capabilities.playerType" ) {
202 bool yesNo(const std::string
& /*query*/)
208 EventCallback eventCallback
;
209 FsCommandExecutor execFsCommand
;
212 vm_main(int argc
, char *argv
[])
214 // Enable native language support, i.e. internationalization
216 std::setlocale (LC_ALL
, "");
217 bindtextdomain (PACKAGE
, LOCALEDIR
);
218 textdomain (PACKAGE
);
222 // scan for the two main standard GNU options
223 for (c
= 0; c
< argc
; c
++) {
224 if (strcmp("--help", argv
[c
]) == 0) {
227 if (strcmp("--version", argv
[c
]) == 0) {
228 printf (_("Gnash gprocessor version: %s, Gnash version: %s\n"),
229 GPROC_VERSION
, VERSION
);
234 std::vector
<std::string
> infiles
;
236 //RcInitFile& rcfile = RcInitFile::getDefaultInstance();
237 //rcfile.loadFiles();
239 if (rcfile
.verbosityLevel() > 0) {
240 dbglogfile
.setVerbosity(rcfile
.verbosityLevel());
243 dbglogfile
.setLogFilename(rcfile
.getDebugLog());
245 if (rcfile
.useWriteLog()) {
246 dbglogfile
.setWriteDisk(true);
249 if (rcfile
.useActionDump()) {
250 dbglogfile
.setActionDump(true);
251 dbglogfile
.setVerbosity();
254 if (rcfile
.useParserDump()) {
255 dbglogfile
.setParserDump(true);
256 dbglogfile
.setVerbosity();
259 while ((c
= getopt (argc
, argv
, ":hwvapr:gf:d:")) != -1) {
262 dbglogfile
.removeLog();
268 dbglogfile
.setVerbosity();
269 log_debug (_("Verbose output turned on"));
273 debugger
.enabled(true);
275 log_debug (_("Setting debugger ON"));
277 log_error (_("The debugger has been disabled at configuration time"));
281 dbglogfile
.setActionDump(true);
283 log_error (_("Verbose actions disabled at compile time"));
288 dbglogfile
.setParserDump(true);
290 log_error (_("Verbose parsing disabled at compile time"));
294 allowed_end_hits
= strtol(optarg
, NULL
, 0);
297 delay
= strtol(optarg
, NULL
, 0)*1000; // delay is in microseconds
298 // this will be recognized as a request to run at FPS speed
299 if ( delay
< 0 ) delay
= -1;
302 limit_advances
= strtol(optarg
, NULL
, 0);
305 fprintf(stderr
, "Missing argument for switch ``%c''\n", optopt
);
309 fprintf(stderr
, "Unknown switch ``%c''\n", optopt
);
315 // get the file name from the command line
316 while (optind
< argc
) {
317 infiles
.push_back(argv
[optind
]);
321 // No file names were supplied
322 if (infiles
.empty()) {
323 std::cerr
<< "no input files" << std::endl
;
324 dbglogfile
.removeLog();
328 if (infiles
.size() > 1) {
329 // We're not ready for multiple runs yet.
330 std::cerr
<< "Multiple input files not supported." << std::endl
;
331 dbglogfile
.removeLog();
337 std::auto_ptr
<media::MediaHandler
> handler(
338 new gnash::media::ffmpeg::MediaHandlerFfmpeg() );
339 #elif defined(USE_GST)
340 std::auto_ptr
<media::MediaHandler
> handler(
341 new gnash::media::gst::MediaHandlerGst() );
343 std::cerr
<< "Neither SOUND_SDL nor SOUND_GST defined" << std::endl
;
346 gnash::media::MediaHandler::set(handler
);
348 boost::shared_ptr
<sound::sound_handler
> soundHandler(
349 new sound::NullSoundHandler());
351 std::vector
<movie_data
> data
;
353 // Play through all the movies.
354 for (std::vector
<std::string
>::const_iterator i
= infiles
.begin(),
355 e
= infiles
.end(); i
!= e
; ++i
) {
357 RunResources
runResources(*i
);
358 runResources
.setSoundHandler(soundHandler
);
360 boost::intrusive_ptr
<gnash::movie_definition
> m
=
361 play_movie(*i
, runResources
);
363 if (s_stop_on_errors
) {
365 std::cerr
<< "error playing through movie " << *i
<< std::endl
;
366 std::exit(EXIT_FAILURE
);
371 md
.m_movie
= m
.get();
380 // Load the named movie, make an instance, and play it, virtually.
381 // I.e. run through and render all the frames, even though we are not
382 // actually doing any output (our output handlers are disabled).
384 // What this does is warm up all the cached data in the movie, so that
385 // if we save that data for later, we won't have to tesselate shapes
386 // or build font textures again.
388 // Return the movie definition.
389 boost::intrusive_ptr
<gnash::movie_definition
>
390 play_movie(const std::string
& filename
, const RunResources
& runResources
)
392 boost::intrusive_ptr
<gnash::movie_definition
> md
;
397 if (filename
== "-") {
398 std::auto_ptr
<IOChannel
> in (
399 noseek_fd_adapter::make_stream(fileno(stdin
)) );
400 md
= gnash::MovieFactory::makeMovie(in
, filename
, runResources
, false);
402 if ( url
.protocol() == "file" ) {
403 const std::string
& path
= url
.path();
404 #if 1 // add the *directory* the movie was loaded from to the local sandbox path
405 size_t lastSlash
= path
.find_last_of('/');
406 std::string dir
= path
.substr(0, lastSlash
+1);
407 rcfile
.addLocalSandboxPath(dir
);
408 log_debug(_("%s appended to local sandboxes"), dir
.c_str());
409 #else // add the *file* to be loaded to the local sandbox path
410 rcfile
.addLocalSandboxPath(path
);
411 log_debug(_("%s appended to local sandboxes"), path
.c_str());
414 md
= gnash::MovieFactory::makeMovie(url
, runResources
, NULL
, false);
417 catch (GnashException
& ge
) {
419 fprintf(stderr
, "%s\n", ge
.what());
422 std::cerr
<< "error: can't play movie: "<< filename
<< std::endl
;
423 std::exit(EXIT_FAILURE
);
426 float fps
= md
->get_frame_rate();
427 long fpsDelay
= long(1000000/fps
);
428 long clockAdvance
= fpsDelay
/1000;
429 long localDelay
= delay
== -1 ? fpsDelay
: delay
; // microseconds
431 log_debug("Will sleep %ld microseconds between iterations - "
432 "fps is %g, clockAdvance is %lu", localDelay
, fps
, clockAdvance
);
435 // Use a clock advanced at every iteration to match exact FPS speed.
437 gnash::movie_root
m(*md
, cl
, runResources
);
439 // Register processor to receive ActionScript events (Mouse, Stage
441 m
.registerEventCallback(&eventCallback
);
442 m
.registerFSCommandCallback(&execFsCommand
);
446 m
.init(md
.get(), MovieClip::MovieVariables(), MovieClip::MovieVariables());
447 if ( quitrequested
) { // setRootMovie would execute actions in first frame
448 quitrequested
= false;
452 log_debug("iteration, timer: %lu, localDelay: %ld\n",
453 cl
.elapsed(), localDelay
);
454 gnashSleep(localDelay
);
456 resetLastAdvanceTimer();
459 size_t loop_back_count
=0;
460 size_t latest_frame
=0;
461 size_t end_hitcount
=0;
463 // Run through the movie.
465 // @@ do we also have to run through all sprite frames
468 // @@ also, ActionScript can rescale things
469 // dynamically -- we can't really do much about that I
472 // @@ Maybe we should allow the user to specify some
473 // safety margin on scaled shapes.
475 size_t last_frame
= m
.get_current_frame();
476 //printf("advancing clock by %lu\n", clockAdvance);
477 cl
.advance(clockAdvance
);
482 quitrequested
= false;
486 m
.display(); // FIXME: for which reason are we calling display here ??
488 if ( limit_advances
&& nadvances
>= limit_advances
) {
489 log_debug("exiting after %d advances", nadvances
);
493 size_t curr_frame
= m
.get_current_frame();
495 // We reached the end, done !
496 if (curr_frame
>= md
->get_frame_count() - 1 ) {
497 if ( allowed_end_hits
&& ++end_hitcount
>= allowed_end_hits
)
499 log_debug("exiting after %d"
500 " times last frame was reached", end_hitcount
);
506 if (curr_frame
== last_frame
) {
507 // Max stop counts reached, kick it
508 if ( secondsSinceLastAdvance() > waitforadvance
) {
512 if ( last_frame
+ 1 > md
->get_frame_count() -1 ) {
513 fprintf(stderr
, "Exiting after %g seconds in STOP mode at last frame\n", waitforadvance
);
516 fprintf(stderr
, "Kicking movie after %g seconds in STOP mode, kick ct = %d\n", waitforadvance
, kick_count
);
518 m
.goto_frame(last_frame
+ 1);
519 m
.getRootMovie().setPlayState(gnash::MovieClip::PLAYSTATE_PLAY
);
522 if (kick_count
> 10) {
523 printf("movie is stalled; giving up on playing it through.\n");
527 resetLastAdvanceTimer(); // It's like we advanced
531 // We looped back. Skip ahead...
532 else if (m
.get_current_frame() < last_frame
) {
533 if ( last_frame
> latest_frame
) latest_frame
= last_frame
;
534 if ( ++loop_back_count
> allowloopbacks
) {
535 log_debug("%d loop backs; jumping one-after "
537 loop_back_count
, latest_frame
+1);
538 m
.goto_frame(latest_frame
+ 1);
544 resetLastAdvanceTimer();
547 log_debug("iteration, timer: %lu, localDelay: %ld\n",
548 cl
.elapsed(), localDelay
);
549 gnashSleep(localDelay
);
553 MovieFactory::clear();
562 // indent-tabs-mode: t