Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
2

How to render a composition in After Effects using ExtendScript

Explorer ,
Jun 26, 2023 Jun 26, 2023

I have a use case where I need to render specific layers in an After Effect's project, using my custom encoder.

In a high-level overview, when facing that "specific" layer, I creates a temporary composition to hold it for rendering, and then render that temporary comp.

The following is code snippet of how I do it:

var comp = app.project.activeItem; // assuming "activeItem" is a composition.
var layer = comp.layer(1); // assuming the first layer is the layer I which to render.

// creating a "dummy" comp to be sent to the render queue:
var duplicated = app.project.items.addComp(
    "".concat(layer.containingComp.name, "-duplicated"), 
    layer.containingComp.width, layer.containingComp.height, 
    layer.containingComp.pixelAspect, layer.containingComp.duration, 
    layer.containingComp.frameRate);
    layer.copyToComp(duplicated
);

var renderQueueItem = app.project.renderQueue.items.add(duplicated);
var outputModule = renderQueueItem.outputModule(1);
var path = "/path/to/my/output/file";
var file = new File(path);

outputModule.applyTemplate("my-custom-output-template");
outputModule.file = file;

app.project.renderQueue.render();

 

The issues are the following:

  1.  The function "app.project.renderQueue.render()" blocks the entire AE UI, including the render queue panel and its progress bar. Threre is indication once or ever what's going on until there rendering process terminates.
  2. I've found an undocumented function - "app.project.renderQueue.renderAsync()" which does not block the UI, and in addition AE render queue progress bar works perfectly. The problem with that approach, that I don't have any indication when the rendering process finishes, and/or if any error has occurred.

 

I tried to search for examples/answers online but found nothing usefull. I'll appriciate any help!!!

TOPICS
How to , Scripting
2.0K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 05, 2023 Jul 05, 2023

You can use aerender to render headlessley in the background which would avoid both of these issues. Just make sure you save your project first:

https://helpx.adobe.com/after-effects/using/automated-rendering-network-rendering.html

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 16, 2023 Jul 16, 2023

Is there a way to call it using ExtendScript API, or do I have to call it as a CLI tool?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 17, 2023 Jul 17, 2023

Yes, @ConstantinMaier answered it pretty well below, you would via system.callSystem()

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 16, 2023 Jul 16, 2023

In addition, in my use case I have to build a RenderQueueItem manually in order to apply custom settings and parameters, then render everything insdie the render queue. Reading the documentation mentioned by you above, I can do it by calling

$ ./aerender -project <project-path>

 it does render successfully and my terminal process executing the task above does wait until the render process is done, but there is no user indication of any progress in After Effects UI. I'm not sure this tool is meant for those kind of use-cases. Am I doing something wrong? can I use it differently?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 16, 2023 Jul 16, 2023

What you could do as well: Detach the AE Render process from AE by calling it using a .bat/.command file instead of system.callSystem(). Now you could build your own UI, then read the log file created by AE Render next to the rendered file and feed the information back to your UI about the current frame, etc. To build the UI, I'd use a palette. That way, you could use app.scheduleTask() while setting it to repeat which would allow you to continually update the UI and also to check when the process would be done. But since it's a palette, the user could still interact with AE during this process and I found this cause a lot of instabilities. So I guess the user somehow should be informed not interact with AE during rendering. However, all this is quite a complicated code to pull off, so I'm not sure whether it's the right thing for you.

 

Another thing I was just thinking: When you're just using system.callSystem() to call AE Render, it waits for the process to end which is what you want. But the downside is that you cannot update the UI since all of AE is halted until the process is done. So to give the user an indication where the rendering process is, you could potentially create a script using AutoIt/AppleScript or similar that reads the log file created by AE Render as described above and that displays a dialogue of the current render state. That way the render progress bar would be external from AE and could display something while AE is caught up with the system.callSystem() process.

 

Nevertheless, if anyone knows an easier/better way to achieve this, I'd like to hear it as well since I'm building something similar as well at the moment...

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 16, 2023 Jul 16, 2023

Oh, one thing I just found: When you use app.project.renderQueue.renderAsync() and schedule a task right after, the task will be executed after the rendering process is finished since it will be executed after the script's execution but because AE is then caught up with rendering at that point, it will only be executed after the rendering is finished. Maybe that could do the trick?:

 

 

function renderFinishedMessage(){
        alert("Rendering Finished");
}

app.project.renderQueue.renderAsync();
app.Init = renderFinishedMessage;
app.scheduleTask("app.Init();", 200, false);

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 18, 2023 Jul 18, 2023

Just tried that - it does work and the renderFinishedMessage function get's called after rendering is finished.

However, code that I place after the following line:

app.scheduleTask("app.Init();", 200, false);

gets executed immediately without waiting for the rendering to finish.

My "end-goal" is having an ExtendScript function that I  can call from CEP, which does not return to CEP until rendering is finished. Is it possible? 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 18, 2023 Jul 18, 2023

Move that inside your renderFinishedMessage() function.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 20, 2023 Jul 20, 2023

Can you provide more details please?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Using a scheduleTask or setTimeout is just going to wait the specified number of ms (in this case 200) before running, if your render takes longer then that won't work. You want to check if your render is done before running your action after.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 20, 2023 Jul 20, 2023

Maybe I'm misinterpreting what you said here, @Justin Taylor-Hyper Brew but the render *can* actually take longer in this case. Generally, yes, app.scheduleTask() will wait the specified number of ms before executing. However, in this specific instance, the scheduled task can't be executed because AE is occupied at that time with the rendering that was called using app.project.renderQueue.renderAsync(). That's why the scheduled task is waiting until the render is actually done (which is what we want).

 

@avi23837866f7yk: Just as Justin said, simply do everything you want to do coding-wise in the renderFinishedMessage() function (or however you want to call it). Everything inside that function will be executed after the rendering in this case. Or is it something else you need more details for?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

A right, renderAsync() is a strange one.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 23, 2023 Jul 23, 2023

I have two problems with that approach:

  1.  I'm not a big fan of using app.scheduleTask() as one must specify a callback function as a string, that cannot be debugged.
  2.  I need to somehow be able to return the control to CEP only when rendering is done. When I use the approach you guys described above, the control is being returned to CEP side before that. So even if app.scheduleTask() does get called only after rendering is done, it's just partiallly and not fully helps me..

I'll mention that I don't really need app.project.renderQueue.renderAsync(), and app.project.renderQueue.render() would do just fine! the only problem with the last one is that it freezes the UI(including render queue progress bar) on Windows. On macOS it works just fine.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 23, 2023 Jul 23, 2023

What do you mean by return control to CEP? If you mean your function is completing before rendering is done then yes, this is expected. 

 

If you want to call some function in CEP only after rendering has completed (from inside your renderFinishedMessage() function) then register a CSXS Event and call it from ExtendScript with PlugPlug.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 23, 2023 Jul 23, 2023

I mean all of this is already quite hacky what we're doing here anyway, so I'm not sure there will be a perfect answer to this. Using app.scheduleTask() is generally not ideal but sometimes there's no way around it. Can you maybe elaborate on why you need the function to be returned only when the rendering is done? You can do whatever you want to do in your scheduled function.

 

To be honest, I myself am not familiar with CEP panels, so I can only speak from my experience with ScriptUI panels. If I'd want to reduce whatever I do in my scheduled function, I might just simply restart the panel from there by calling app.executeCommand([Panel ID]) twice and hand over a global variable or write something to the prefs before to tell the panel to execute a certain function upon opening. Not sure whether this could be something applicable in your case.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 24, 2023 Jul 24, 2023

I'm totally agree with you that all of the solutions mentioned above(including mine) feels like a total hack.

Is there any chance that Adobe devs either:

  1.  Fix app.project.renderQueue.render() so it won't freeze the UI on Windows?
  2.  Fix app.project.renderQueue.renderAsync() so it would have a built-in mechanism to indicate when rendering is done?

By "return control to CEP" I mean that I'm calling an ExtendScript function to render group of layers(by "wrapping" each one in a comp that being sent to ther render queue) lets say its name is render(). I'm using Bolt-CEP framework and call that ES function that way:

await evalTS("render");
// code that needs to execute ONLY after rendering is done

which behind the scenes calls CSInterface evalScript().

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 24, 2023 Jul 24, 2023
LATEST

Yes this is all hacky, but not sure of a better way unless you want to fire off headless renders with aerender.

 

Answer to your question would be the same if you're using Bolt or not, evalTS() is just a type-safe wrapper for the native csInterface.evalScript().

 

If you need some async call to trigger CEP space from ExtendScript, you'll need to use something like PlugPlug.

 

 

Listen on CEP Side:

 

 

 

// if using Bolt CEP, import csi ( essentially new CSInterface() )

csi.addEventListener('myEventName', function (evt) {
    // Do stuff
});

 

 

 

Call on ExtendScript Side:

 

 

 

try {
    var xLib = new ExternalObject("lib:\PlugPlugExternalObject");
} catch (e) {
    alert(e);
}
if (xLib) {
    var eventObj = new CSXSEvent();
    eventObj.type = 'myEventName';
    eventObj.data = 'myData';
    eventObj.dispatch();
}

 

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines