Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. Unity 2020 LTS & Unity 2021.1 have been released.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Video Control Unity Recorder from Script

Discussion in 'Audio & Video' started by EmmetOT, Mar 5, 2020.

  1. EmmetOT

    EmmetOT

    Joined:
    Apr 25, 2016
    Posts:
    3
    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.
     
  2. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    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):
    Code (CSharp):
    1. GameObject go = GameObject.Find("TimelinePlaceholder");
    2. PlayableDirector dir = go.GetComponent<PlayableDirector>();
    3. TimelineAsset timeline = dir.playableAsset as TimelineAsset;
    4. float fps = timeline.editorSettings.fps;
    5. RecorderTrack recorderTrack = timeline.GetRootTrack(0) as RecorderTrack;
    6. var recorderClip = recorderTrack.GetClips().ToList()[0].asset as RecorderClip;
    7. var outputFile = recorderClip.settings.OutputFile;
    8. 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:
    Code (CSharp):
    1. var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
    2. var TestRecorderController = new RecorderController(controllerSettings);
    3.  
    4. var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
    5. videoRecorder.name = "My Video Recorder";
    6. videoRecorder.Enabled = true;
    7. videoRecorder.VideoBitRateMode = VideoBitrateMode.High;
    8.  
    9. videoRecorder.ImageInputSettings = new GameViewInputSettings
    10. {
    11.     OutputWidth = 640,
    12.     OutputHeight = 480
    13. };
    14.  
    15. videoRecorder.AudioInputSettings.PreserveAudio = true;
    16. //videoRecorder.OutputFile; // Change this to change the output file name (no extension)
    17.  
    18. controllerSettings.AddRecorderSettings(videoRecorder);
    19. controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
    20. controllerSettings.FrameRate = 30;
    21.  
    22. RecorderOptions.VerboseMode = false;
    23. TestRecorderController.PrepareRecording();
    24. TestRecorderController.StartRecording();
    25.  
    26. // 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
     
    Last edited: Jun 5, 2020
    hkung and ufo_zj like this.
  3. RebelNature_Daniel

    RebelNature_Daniel

    Joined:
    Jul 6, 2018
    Posts:
    4
    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.

    Code (CSharp):
    1. using UnityEditor.Recorder;
    2.      
    3. private RecorderWindow GetRecorderWindow()
    4.         {
    5.             return (RecorderWindow)EditorWindow.GetWindow(typeof(RecorderWindow));
    6.         }
    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:
    Code (CSharp):
    1.                     RecorderWindow recorderWindow = GetRecorderWindow();
    2.                     if(!recorderWindow.IsRecording())
    3.                         recorderWindow.StartRecording();
    or when I want to stop recording:
    Code (CSharp):
    1.             RecorderWindow recorderWindow = GetRecorderWindow();
    2.             if (recorderWindow.IsRecording())
    3.                 recorderWindow.StopRecording();
    I also manage the recorders in the recorder, where they save to, fileNames etc.

    Hope it helps!
    -Daniel
     
    _slash_, sarebots2, olix4242 and 3 others like this.
  4. Yuvix25

    Yuvix25

    Joined:
    Jun 11, 2019
    Posts:
    1
    RebelNature_Daniel

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

    ufo_zj

    Joined:
    Nov 7, 2018
    Posts:
    3
    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 <fbb5ed17eb6e46c680000f8910ebb50c>: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
     
  6. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    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:

    Code (CSharp):
    1. TestRecorderController.PrepareRecording();
    Let me know how this goes. I'll update my original reply as well.

    Have a great weekend,
    Bruno
     
  7. ufo_zj

    ufo_zj

    Joined:
    Nov 7, 2018
    Posts:
    3
    Thanks Bruno, this is working well right now. I should go through the RecorderController.cs before asking you.:rolleyes:
     
    unitybru likes this.
  8. Ikaro88

    Ikaro88

    Joined:
    Jun 6, 2016
    Posts:
    150
    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
     
  9. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    Yes, in the code I put above, you can set the name of the output file without the extension.
    Something like:
    Code (CSharp):
    1. videoRecorder.OutputFile = "C:\\SomeFolder\\MyOutputFileWithNoExtension";
     
    Ikaro88 likes this.
  10. fct509

    fct509

    Joined:
    Aug 15, 2018
    Posts:
    83
    Any advise on image capture with the Unity Recorder via scripting. I tried to alter this example and I used "C:/temp/img_b_<frame>" 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 "<frame>" 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.
     
    Last edited: Oct 28, 2020
  11. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    Here is some code that works without you having to resolve the wildcards yourself:

    Code (CSharp):
    1. public class TestImageSequence : MonoBehaviour
    2. {
    3.     RecorderController m_RecorderController;
    4.  
    5.     void OnEnable()
    6.     {
    7.         var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
    8.         m_RecorderController = new RecorderController(controllerSettings);
    9.  
    10.         // Image sequence
    11.         var imageRecorder = ScriptableObject.CreateInstance<ImageRecorderSettings>();
    12.         imageRecorder.name = "My Image Recorder";
    13.         imageRecorder.Enabled = true;
    14.         imageRecorder.OutputFormat = ImageRecorderSettings.ImageRecorderOutputFormat.PNG;
    15.         imageRecorder.CaptureAlpha = false;
    16.  
    17.         var mediaOutputFolder = Path.Combine(Application.dataPath, "..", "SampleRecordings");
    18.         imageRecorder.OutputFile = Path.Combine(mediaOutputFolder, "image_") + DefaultWildcard.Frame;
    19.  
    20.         imageRecorder.imageInputSettings = new GameViewInputSettings
    21.         {
    22.             OutputWidth = 3840,
    23.             OutputHeight = 2160,
    24.         };
    25.  
    26.         // Setup Recording
    27.         controllerSettings.AddRecorderSettings(imageRecorder);
    28.         controllerSettings.SetRecordModeToFrameInterval(0, 10);
    29.         m_RecorderController.PrepareRecording();
    30.         m_RecorderController.StartRecording();
    31.     }
    32. }
     
    fct509 likes this.
  12. fct509

    fct509

    Joined:
    Aug 15, 2018
    Posts:
    83
    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.
     
    unitybru likes this.
  13. mmccall

    mmccall

    Joined:
    Aug 6, 2020
    Posts:
    1
    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:

    Code (CSharp):
    1. public class TestVideoCapture : MonoBehaviour
    2. {
    3.     public RenderTexture renderTexture = null;
    4.     void Start()
    5.     {
    6.         Init();
    7.         RecorderWindow rw = GetRecorderWindow.Recorder();  // Open up the recorder window
    8.     }
    9.  
    10.     void Init()
    11.     {
    12.         RecorderOptions.VerboseMode = true;
    13.    
    14.         var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
    15.         var TestRecorderController = new RecorderController(controllerSettings);
    16.    
    17.         var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
    18.         videoRecorder.name = "My Video Recorder";
    19.         videoRecorder.Enabled = true;
    20.  
    21.         videoRecorder.ImageInputSettings = new RenderTextureInputSettings()
    22.         {
    23.             OutputWidth = renderTexture.width,
    24.             OutputHeight = renderTexture.height,
    25.             FlipFinalOutput = true,
    26.             RenderTexture = renderTexture
    27.         };
    28.    
    29.         videoRecorder.AudioInputSettings.PreserveAudio = true;
    30.    
    31.         controllerSettings.AddRecorderSettings(videoRecorder);
    32.         controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
    33.         controllerSettings.FrameRate = 60;
    34.         TestRecorderController.PrepareRecording();
    35.     }
    36.  
    37. }
    38.  
     
    Last edited: Nov 13, 2020
  14. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    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).

    Code (CSharp):
    1. using UnityEditor.Recorder;
    2. using UnityEditor.Recorder.Input;
    3. using UnityEngine;
    4.  
    5. public class TestVideoCapture : MonoBehaviour
    6. {
    7.     // You can change this variable from other places in your scripts, or manually using the GUI
    8.     public RenderTexture renderTexture = null;
    9.  
    10.     // This function gets called when entering Play Mode. We configure the Recorder and start it.
    11.     private void OnEnable()
    12.     {
    13.         if (renderTexture == null)
    14.         {
    15.             Debug.LogError($"You must assign a valid renderTexture before entering Play Mode");
    16.             return;
    17.         }
    18.  
    19.         RecorderOptions.VerboseMode = true;
    20.  
    21.         var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
    22.         var TestRecorderController = new RecorderController(controllerSettings);
    23.  
    24.         var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
    25.         videoRecorder.name = "My Video Recorder";
    26.         videoRecorder.Enabled = true;
    27.  
    28.         videoRecorder.ImageInputSettings = new RenderTextureInputSettings()
    29.         {
    30.             OutputWidth = renderTexture.width,
    31.             OutputHeight = renderTexture.height,
    32.             FlipFinalOutput = true,
    33.             RenderTexture = renderTexture
    34.         };
    35.  
    36.         videoRecorder.AudioInputSettings.PreserveAudio = true;
    37.  
    38.         controllerSettings.AddRecorderSettings(videoRecorder);
    39.         controllerSettings.SetRecordModeToFrameInterval(0, 59); // 2s @ 30 FPS
    40.         controllerSettings.FrameRate = 60;
    41.         TestRecorderController.PrepareRecording();
    42.         TestRecorderController.StartRecording();
    43.     }
    44. }
     
  15. ploink

    ploink

    Joined:
    Jun 12, 2013
    Posts:
    6
    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!

    Code (CSharp):
    1. void StartVideoRecording(int duration) {
    2.  
    3.         renderTexture = new RenderTexture(1080 , 1920 , 16);
    4.         renderTexture.Create();
    5.  
    6.         if (renderTexture == null) {
    7.             Debug.LogError($"You must assign a valid renderTexture before entering Play Mode");
    8.             return;
    9.         }
    10.  
    11.         RecorderOptions.VerboseMode = true;
    12.  
    13.         var controllerSettings = ScriptableObject.CreateInstance<RecorderControllerSettings>();
    14.         var TestRecorderController = new RecorderController(controllerSettings);
    15.  
    16.         var videoRecorder = ScriptableObject.CreateInstance<MovieRecorderSettings>();
    17.         videoRecorder.name = "My Video Recorder";
    18.         videoRecorder.Enabled = true;
    19.         videoRecorder.OutputFile = "Recordings/PicTarot_" + currentCardId;
    20.         videoRecorder.OutputFormat = MovieRecorderSettings.VideoRecorderOutputFormat.MP4;
    21.  
    22.         videoRecorder.ImageInputSettings = new RenderTextureInputSettings() {
    23.             OutputWidth = renderTexture.width ,
    24.             OutputHeight = renderTexture.height ,
    25.             FlipFinalOutput = true ,
    26.             RenderTexture = renderTexture
    27.         };
    28.  
    29.         videoRecorder.AudioInputSettings.PreserveAudio = true;
    30.  
    31.         controllerSettings.AddRecorderSettings(videoRecorder);
    32.         controllerSettings.SetRecordModeToFrameInterval(0 , duration * 30); // 2s @ 30 FPS
    33.         controllerSettings.FrameRate = 30;
    34.         TestRecorderController.PrepareRecording();
    35.         TestRecorderController.StartRecording();
    36.  
    37.     }
     
  16. cdauphin

    cdauphin

    Joined:
    Mar 16, 2018
    Posts:
    13
    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 2560*1440 everything works fine. But the movie is a bit stretched since our video format is 2560*1303 (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.
     
  17. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    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?
     
  18. unitybru

    unitybru

    Unity Technologies

    Joined:
    Jan 28, 2020
    Posts:
    91
    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?
     
  19. ploink

    ploink

    Joined:
    Jun 12, 2013
    Posts:
    6
    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! :)
     
    unitybru likes this.
unityunity