Jump to content
Search Community

Timing issue when rendering with phantomjs

robincsamuel test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I'm trying to export a timelineMax animation as mp4 video using phantomjs.  Everything works fine, but the animation speed is different on phantomjs. It's running too fast, 10-second animation completes in 2 seconds. If I timescale to 0.2, it works. 

 

Framerate is set to 24. 

 

Does anyone have an idea on what's happening?

 

Thanks.

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums,

 

I don't have any experience with PhantomJS but there may be one or two around here who may be able to help you. 

GSAP animations update at 60fps so I suspect that could cause some discrepancy.

 

You can change the ticker fps in GSAP: 

 

TweenLite.ticker.fps(24);

 

https://greensock.com/docs/TweenLite/static.ticker

 

 

 

 

  • Like 1
Link to comment
Share on other sites

HI @PointC :)

 

I was following @swampthang on the GSAP forums for a while, and tried the options he said. I was able to export the video successfully using

Electron Recorder. But it won't run headless, and what I need is a headless solution to export the video. So I ended up in phantomjs, which seems to work but with some issues as I mentioned above. 

 

When I don't set a frame rate (60 is the default I think), on phantomjs I get around 27fps, and since I was expecting 60fps, the animation on video was running faster. What's weird is, If I set fps to 24 using,

 

TweenMax.ticker.fps(24);

 

Then I got 11 fps on phantomjs and FFmpeg is expecting 24 in this case. So I don't think it's lack of performance on phantomjs, but something tricky with the ticks. Because whatever framerate I set, I get almost half the fps on phantomjs. Seems like phantomjs is working like `useFrames:true` mode. 

 

I was able to match it by time scaling to 0.2, but not sure how reliable it is.

 

 

EDIT: I just tried with a 16fps setting, and got 5fps on phantom. And I forgot to mention, I'm capturing screens on 'onUpdate' event, so frame count is the number of times onupdate is triggered.

Edited by robincsamuel
Link to comment
Share on other sites

Why do you need a headless browser?

 

If you must use one, I wouldn't use onUpdate to do the screen captures. That's probably what's messing up your timing as animation performance is really bad in phantomjs. I would step through the progress of your animation just like @swampthang does in his code. This will ensure that every frame is evenly spaced out.

 

var fps = 24;
var duration = animation.duration();
var frames   = Math.ceil(duration / 1 * fps);
var current  = 0;

captureScreen();

function captureScreen() {
   
  animation.progress(current++ / frames);
    
  // capture the screen
  
  if (current <= frames) {
    captureScreen();
  }
}

 

 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Hey @OSUblake,

 

Big thanks! That was it.  It worked perfectly with @swampthang's code. 

 

Actually, I tried many ways like setInterval and all referring some tutorials related to phantomjs. But all those were messing up things in my case, and the onUpdate went well on chrome. Since a newbie, I wasn't aware of the animation progress method.

 

I want headless browser because I'm doing the video conversion on the server side. 

 

Thanks everyone for helping out :) 

  • Like 2
Link to comment
Share on other sites

Awesome, robincsamuel. Sorry I haven't been in here lately. I must have turned off notifications because I'm just now seeing this. Let me know if you have any other questions regarding video stuff. 

 

As for creating video, I'm using FFMPEG for creating video. FFMPEG can be installed on your server so you might want to look in to that. Not sure if you're using nodejs or not but there's also a node library that connects to all the FFMPEG commands called fluent-ffmpeg. It's really saved my bacon. One of the things that's really nice about FFMPEG is you can use an input of an image sequence and output to an mp4 or whatever you like. 

  • Thanks 1
Link to comment
Share on other sites

Hey @swampthang 

 

I really wanted to thank you for posting all the information on the forum. I've been going through your post on the forum for a while.

 

For creating the video, I'm using FFmpeg itself and yes, on a node.js environment. 

 

 

As of now, I'm running from command line, But I want to do this on an HTTP request, as a child process.

 

phantomjs runner.js | ffmpeg -y -c:v png -f image2pipe -r 24 -i - -c:v libx264 -pix_fmt yuv420p -movflags +faststart output.mp4

 

This is how I render now in runner.js

page.render('/dev/stdout', { format: "png" });

 

Do you know how I can input the images from `page.render` of phantom to FFmpeg?    

 

EDIT:  I was able to solve the issue by piping the phantom process output to FFmpeg stdin.

 

Link to comment
Share on other sites

Glad you got it figured out robincsamuel. My situation is a bit different in that I'm using Electron to build a desktop application. My FFMPEG function using fluent-ffmpeg looks like this...

 

// pngFolder is a temp folder to place all the transparent png images in a sequence like img00001.png, img00002.png, etc
// obj contains FPS: frames-per-second, WD: width, HT: height
function createMovOverlay(obj) {
  // the command line ends up looking something like this...
  // ffmpeg -y -r 60 -f image2 -s 1280x720 -i slower/img%05d.png -vcodec png -q:v 15 -pix_fmt rgba animation.mov

  var proc = new ffmpeg(`${pngFolder}img%05d.png`);
  proc.addInputOption(`-r ${obj.FPS}`)
    .addInputOption(`-f image2`)
    .addInputOption(`-s ${obj.WD}x${obj.HT}`)
    .on('start', function(ffmpegCommand) {
      // console.log(ffmpegCommand);
    })
    .on('progress', function(data) {
      // you can use data object to post progress meters...
      $('#processing-overlay progress').attr('value',data.frames);
    })
    .on('end', function() {
      // after creating this video (as a transparent MOV), I run a createVideo function that overlays it on any other videos that need to be used as backgrounds, etc.
      createVideo(obj);
    })
    .on('error', function(err, stdout, stderr) {
      // could probably be a little more detailed with this section
      console.log('error: ' + err.message);
      console.log('stderr:' + stderr);
      proc.kill();
    })
    .addOutputOptions(['-vcodec png', '-q:v 15', '-pix_fmt rgba', `-r ${obj.FPS}`])
    .output(`${tempFolder}text-anim-rendered.mov`)
    .run();
}

 

In case you're curious, I'm connecting the FFMPEG static binaries (that are embedded in the app) like this...
 

const ffmpeg = require('fluent-ffmpeg');

ffmpeg.setFfmpegPath(path.join(__dirname, '..', '..', 'libs', process.platform, process.arch, 'ffmpeg' + ext));
ffmpeg.setFfprobePath(path.join(__dirname, '..', '..', 'libs', process.platform, process.arch, 'ffprobe' + ext));

 

They are in a libs folder according to platform (windows or mac)

ffmpeg-folders.png?dl=1

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...