Control Unity Recorder from Script

I just want to get some simple information about the Unity Recorder in my scripts, such as whether it’s recording or not, what the target framerate is, etc. I can see there’s an API but there’s no information about where to get the ‘current’ recorder.

Hi,

if you want to know what’s going on with the Recorder window, there is no public API for this.

If you are using a timeline with a playable director and a recorder clip, here is how to get its info (the FPS is set by the timeline, and there is no way of knowing if the playhead is inside the clip):

GameObject go = GameObject.Find("TimelinePlaceholder");
PlayableDirector dir = go.GetComponent<PlayableDirector>();
TimelineAsset timeline = dir.playableAsset as TimelineAsset;
float fps = timeline.editorSettings.fps;
RecorderTrack recorderTrack = timeline.GetRootTrack(0) as RecorderTrack;
var recorderClip = recorderTrack.GetClips().ToList()[0].asset as RecorderClip;
var outputFile = recorderClip.settings.OutputFile;
Debug.Log(string.Format("Timeline '{0}' track '{1}' clip '{2}' recording to file {3}.mp4 @ FPS={4}", timeline.name, recorderTrack.name, recorderClip.name, outputFile, fps));

I think your best bet would be to instantiate the recorder yourself via scripting, so that you have more control over what happens and the timing:

var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
var TestRecorderController = new RecorderController(controllerSettings);

var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
videoRecorder.name = "My Video Recorder";
videoRecorder.Enabled = true;
videoRecorder.VideoBitRateMode = VideoBitrateMode.High;

videoRecorder.ImageInputSettings = new GameViewInputSettings
{
    OutputWidth = 640,
    OutputHeight = 480
};

videoRecorder.AudioInputSettings.PreserveAudio = true;
//videoRecorder.OutputFile; // Change this to change the output file name (no extension)

controllerSettings.AddRecorderSettings(videoRecorder);
controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
controllerSettings.FrameRate = 30;

RecorderOptions.VerboseMode = false;
TestRecorderController.PrepareRecording();
TestRecorderController.StartRecording();

// Wait a while

If you start the recorder this way, make sure you wait a few frames after the expected end of the recording in case there are GPU readbacks pending.

I hope this answers your question.

Best regards,
Bruno

10 Likes

Hi EmmetOT

I just stumbled across your post looking for another issue, but I was looking at the same info you are looking for now a while back, and I came up with this solution.

using UnityEditor.Recorder;
      
private RecorderWindow GetRecorderWindow()
        {
            return (RecorderWindow)EditorWindow.GetWindow(typeof(RecorderWindow));
        }

This code snippet will retrieve the Recorderwindow for you, from here you can access all sorts of things, like Start/Stop Recording and all sorts of really cool stuff. The only drawback is that the Recorder window pops up when you do.
I use this for instance when I wanna start recording:

                    RecorderWindow recorderWindow = GetRecorderWindow();
                    if(!recorderWindow.IsRecording())
                        recorderWindow.StartRecording();

or when I want to stop recording:

            RecorderWindow recorderWindow = GetRecorderWindow();
            if (recorderWindow.IsRecording())
                recorderWindow.StopRecording();

I also manage the recorders in the recorder, where they save to, fileNames etc.

Hope it helps!
-Daniel

16 Likes

RebelNature_Daniel

Can you send the code that you use to manage the recorders in the recorder?

Hi Bruno,
I have tried with your code, but it always came up with an error:
ArgumentNullException: Value cannot be null.
Parameter name: source
System.Linq.Enumerable.Any[TSource] (System.Collections.Generic.IEnumerable`1[T] source) (at :0)
UnityEditor.Recorder.RecorderController.StartRecording () (at Library/PackageCache/com.unity.recorder@2.2.0-preview.4/Editor/Sources/RecorderController.cs:133)

Can you help me with this?

Kind regards,
Jun

Hi Jun!

Sorry for the delay. I just tried it and reproduced your issue. I forgot to add this line before the call to StartRecording:

TestRecorderController.PrepareRecording();

Let me know how this goes. I’ll update my original reply as well.

Have a great weekend,
Bruno

Thanks Bruno, this is working well right now. I should go through the RecorderController.cs before asking you.:roll_eyes:

1 Like

HI, there is a way to set the path of the file?

because if i not configure the recorder window to .mp4 BEFORE the script it simply do anything

Yes, in the code I put above, you can set the name of the output file without the extension.
Something like:

videoRecorder.OutputFile = "C:\\SomeFolder\\MyOutputFileWithNoExtension";
1 Like

Any advise on image capture with the Unity Recorder via scripting. I tried to alter this example and I used “C:/temp/img_b_” for the value passed to OutputFile, but it just created single image file named “C:/temp/img_b_frame.png” and kept overwriting the image each time it captured a new frame.

Edit:
I figured it out.
I have to grab the FileNameGenerator of the RecorderSettings that we are using and call the AddWildcard(). This method takes two parameters. It take the name of the wildcard and a lambda expression (with a RecordingSession parameter) that returns/generates the sting that I want to replace “” with.

_recorderSettings.FileNameGenerator.AddWildcard("frame", (sess) => { return sess.frameIndex.ToString(); });

I’m going to have to add some 0 padding to the numbers to keep things clean for my Users, but that code isn’t needed for answering the question that I had.

Here is some code that works without you having to resolve the wildcards yourself:

public class TestImageSequence : MonoBehaviour
{
    RecorderController m_RecorderController;

    void OnEnable()
    {
        var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
        m_RecorderController = new RecorderController(controllerSettings);

        // Image sequence
        var imageRecorder = ScriptableObject.CreateInstance<ImageRecorderSettings>();
        imageRecorder.name = "My Image Recorder";
        imageRecorder.Enabled = true;
        imageRecorder.OutputFormat = ImageRecorderSettings.ImageRecorderOutputFormat.PNG;
        imageRecorder.CaptureAlpha = false;

        var mediaOutputFolder = Path.Combine(Application.dataPath, "..", "SampleRecordings");
        imageRecorder.OutputFile = Path.Combine(mediaOutputFolder, "image_") + DefaultWildcard.Frame;

        imageRecorder.imageInputSettings = new GameViewInputSettings
        {
            OutputWidth = 3840,
            OutputHeight = 2160,
        };

        // Setup Recording
        controllerSettings.AddRecorderSettings(imageRecorder);
        controllerSettings.SetRecordModeToFrameInterval(0, 10);
        m_RecorderController.PrepareRecording();
        m_RecorderController.StartRecording();
    }
}
1 Like

Thanks. I was actually wondering about the correct usage of the DefaultWildcard. Unfortunately, I’m going to have to stick to resolving the wildcards myself because it gives my Users greater flexibility when setting the values of the automation that we’re doing.

1 Like

Hi Bruno
I need to assign a RenderTexture that’s generated at runtime as the Render Texture Asset for the capture source in the Recorder. I can get a handle to the recorder window but I can’t use it to change the RenderTexture property

I tried using your code to instantiate a recorder at runtime but there’s no UI associated with the object… Can this be done? Or is there a way to change the UI input RenderTexture from script?

Any suggestions?

Thank you!!

Here’s my code:

public class TestVideoCapture : MonoBehaviour
{
    public RenderTexture renderTexture = null;
    void Start()
    {
        Init();
        RecorderWindow rw = GetRecorderWindow.Recorder();  // Open up the recorder window
    }

    void Init()
    {
        RecorderOptions.VerboseMode = true;
    
        var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
        var TestRecorderController = new RecorderController(controllerSettings);
    
        var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
        videoRecorder.name = "My Video Recorder";
        videoRecorder.Enabled = true;

        videoRecorder.ImageInputSettings = new RenderTextureInputSettings()
        {
            OutputWidth = renderTexture.width,
            OutputHeight = renderTexture.height,
            FlipFinalOutput = true,
            RenderTexture = renderTexture
        };
    
        videoRecorder.AudioInputSettings.PreserveAudio = true;
    
        controllerSettings.AddRecorderSettings(videoRecorder);
        controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
        controllerSettings.FrameRate = 60;
        TestRecorderController.PrepareRecording();
    }

}

Hi,

you shouldn’t be trying to manipulate the Recorder Window in script. Instead, rely entirely on a scripted Recorder using a Recorder Controller. You have to think about Recorder Window and scripted Recorder Controller as two separate approaches. I modified your MonoBehaviour, see below. I think you forgot to start the Recording.
In this example the Recording is started when entering Play Mode. You can also add a button to trigger it manually instead (see the CaptureScreenShotExample sample in the package).

using UnityEditor.Recorder;
using UnityEditor.Recorder.Input;
using UnityEngine;

public class TestVideoCapture : MonoBehaviour
{
    // You can change this variable from other places in your scripts, or manually using the GUI
    public RenderTexture renderTexture = null;

    // This function gets called when entering Play Mode. We configure the Recorder and start it.
    private void OnEnable()
    {
        if (renderTexture == null)
        {
            Debug.LogError($"You must assign a valid renderTexture before entering Play Mode");
            return;
        }

        RecorderOptions.VerboseMode = true;

        var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
        var TestRecorderController = new RecorderController(controllerSettings);

        var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
        videoRecorder.name = "My Video Recorder";
        videoRecorder.Enabled = true;

        videoRecorder.ImageInputSettings = new RenderTextureInputSettings()
        {
            OutputWidth = renderTexture.width,
            OutputHeight = renderTexture.height,
            FlipFinalOutput = true,
            RenderTexture = renderTexture
        };

        videoRecorder.AudioInputSettings.PreserveAudio = true;

        controllerSettings.AddRecorderSettings(videoRecorder);
        controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
        controllerSettings.FrameRate = 60;
        TestRecorderController.PrepareRecording();
        TestRecorderController.StartRecording();
    }
}

Hi,

I am using this script to record a series of videos and save them to my HD. I set the name of the file with videoRecorder.OutputFile. It works, it save the files, they have the right length and they contain the audio, but the video is just black. Am I doing something wrong with the renderTexture?

Thanks!

void StartVideoRecording(int duration) {

        renderTexture = new RenderTexture(1080 , 1920 , 16);
        renderTexture.Create();

        if (renderTexture == null) {
            Debug.LogError($"You must assign a valid renderTexture before entering Play Mode");
            return;
        }

        RecorderOptions.VerboseMode = true;

        var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
        var TestRecorderController = new RecorderController(controllerSettings);

        var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
        videoRecorder.name = "My Video Recorder";
        videoRecorder.Enabled = true;
        videoRecorder.OutputFile = "Recordings/PicTarot_" + currentCardId;
        videoRecorder.OutputFormat = MovieRecorderSettings.VideoRecorderOutputFormat.MP4;

        videoRecorder.ImageInputSettings = new RenderTextureInputSettings() {
            OutputWidth = renderTexture.width ,
            OutputHeight = renderTexture.height ,
            FlipFinalOutput = true ,
            RenderTexture = renderTexture
        };

        videoRecorder.AudioInputSettings.PreserveAudio = true;

        controllerSettings.AddRecorderSettings(videoRecorder);
        controllerSettings.SetRecordModeToFrameInterval(0 , duration * 30); // 2s @ 30 FPS
        controllerSettings.FrameRate = 30;
        TestRecorderController.PrepareRecording();
        TestRecorderController.StartRecording();

    }

Hello,

I’m also using a script to control the recorder and I face some issues.
I’m using a CameraInputSettings to only record the necessary thing. When I use an output width and height of 25601440 everything works fine. But the movie is a bit stretched since our video format is 25601303 (there is a control bar of 137px height on the footer).

When I try to use this output height, the recorder does not works and throw an “ObjectDisposedException: Cannot access a disposed object. Object name: ‘Encoder is disposed or invalid’.”

Is there anything I should do with the recorder to make it accept non standard ratios ?

Thanks.

Well, you need to write stuff to that RenderTexture if you want it to appear in the recording. Your script just initializes the RenderTexture and feeds it, blank, to the video recorder. Do you have a camera that you can use instead? Or the GameView as in my first post of this thread?

What output format are you using? MP4 does not support odd width or height. Unity Recorder should warn you about that.
Try using another format, like ProRes.
If you really want to have a final file without those extra 137px, do this in some video editing software OR use ffmpeg to crop.
I’m curious about the exception because it shouldn’t happen in normal usage. Do you have exact reproduction steps, including the version of Unity/the Recorder package?

Thanks! Yes, I have a camera. I had to dig a little but adding this worked for me then:

mainCamera.targetTexture = renderTexture;

So rather than using the camera output the camera is told to render to the renderTexture that’s used for recording video. It’s a bit counterintuitive to me, but it works! :slight_smile:

1 Like

Hello,
I also wanted to ask something concerning the Unity Recorder. Is there a way to access the Codec Format of the Media File Format by Script? There are options for it in the Recorder Window (for MOV files), but I didn’t find anything in the Scripting API.