release v0.0.1

uh it works guys!
This commit is contained in:
colorplease 2024-01-31 19:43:04 -08:00
parent eb76f406fe
commit 404ba1d981
49 changed files with 4346 additions and 1894 deletions

9
Assets/FFmpegOut.meta Normal file
View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cc572eb7215289d4998e2ebd1f2a2dbc
folderAsset: yes
timeCreated: 1488810210
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,183 @@
using UnityEngine;
using System;
using System.IO;
public class AudioRenderer : MonoBehaviour
{
#region Fields, Properties, and Inner Classes
// constants for the wave file header
private const int HEADER_SIZE = 44;
private const short BITS_PER_SAMPLE = 16;
private const int SAMPLE_RATE = 44100;
// the number of audio channels in the output file
private int channels = 2;
// the audio stream instance
private MemoryStream outputStream;
private BinaryWriter outputWriter;
// should this object be rendering to the output stream?
public bool Rendering = false;
/// The status of a render
public enum Status
{
UNKNOWN,
SUCCESS,
FAIL,
ASYNC
}
/// The result of a render.
public class Result
{
public Status State;
public string Message;
public Result(Status newState = Status.UNKNOWN, string newMessage = "")
{
this.State = newState;
this.Message = newMessage;
}
}
#endregion
public AudioRenderer()
{
this.Clear();
}
// reset the renderer
public void Clear()
{
this.outputStream = new MemoryStream();
this.outputWriter = new BinaryWriter(outputStream);
}
/// Write a chunk of data to the output stream.
public void Write(float[] audioData)
{
// Convert numeric audio data to bytes
for (int i = 0; i < audioData.Length; i++)
{
// write the short to the stream
this.outputWriter.Write((short)(audioData[i] * (float)Int16.MaxValue));
}
}
// write the incoming audio to the output string
void OnAudioFilterRead(float[] data, int channels)
{
if( this.Rendering )
{
// store the number of channels we are rendering
this.channels = channels;
// store the data stream
this.Write(data);
}
}
#region File I/O
public AudioRenderer.Result Save(string filename)
{
Result result = new AudioRenderer.Result();
if (outputStream.Length > 0)
{
// add a header to the file so we can send it to the SoundPlayer
this.AddHeader();
// if a filename was passed in
if (filename.Length > 0)
{
// Save to a file. Print a warning if overwriting a file.
if (File.Exists(filename))
Debug.LogWarning("Overwriting " + filename + "...");
// reset the stream pointer to the beginning of the stream
outputStream.Position = 0;
// write the stream to a file
FileStream fs = File.OpenWrite(filename);
this.outputStream.WriteTo(fs);
fs.Close();
// for debugging only
Debug.Log("Finished saving to " + filename + ".");
}
result.State = Status.SUCCESS;
}
else
{
Debug.LogWarning("There is no audio data to save!");
result.State = Status.FAIL;
result.Message = "There is no audio data to save!";
}
return result;
}
/// This generates a simple header for a canonical wave file,
/// which is the simplest practical audio file format. It
/// writes the header and the audio file to a new stream, then
/// moves the reference to that stream.
///
/// See this page for details on canonical wave files:
/// http://www.lightlink.com/tjweber/StripWav/Canon.html
private void AddHeader()
{
// reset the output stream
outputStream.Position = 0;
// calculate the number of samples in the data chunk
long numberOfSamples = outputStream.Length / (BITS_PER_SAMPLE / 8);
// create a new MemoryStream that will have both the audio data AND the header
MemoryStream newOutputStream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(newOutputStream);
writer.Write(0x46464952); // "RIFF" in ASCII
// write the number of bytes in the entire file
writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * channels / 8)) - 8);
writer.Write(0x45564157); // "WAVE" in ASCII
writer.Write(0x20746d66); // "fmt " in ASCII
writer.Write(16);
// write the format tag. 1 = PCM
writer.Write((short)1);
// write the number of channels.
writer.Write((short)channels);
// write the sample rate. 44100 in this case. The number of audio samples per second
writer.Write(SAMPLE_RATE);
writer.Write(SAMPLE_RATE * channels * (BITS_PER_SAMPLE / 8));
writer.Write((short)(channels * (BITS_PER_SAMPLE / 8)));
// 16 bits per sample
writer.Write(BITS_PER_SAMPLE);
// "data" in ASCII. Start the data chunk.
writer.Write(0x61746164);
// write the number of bytes in the data portion
writer.Write((int)(numberOfSamples * BITS_PER_SAMPLE * channels / 8));
// copy over the actual audio data
this.outputStream.WriteTo(newOutputStream);
// move the reference to the new stream
this.outputStream = newOutputStream;
}
#endregion
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1150ae7143196054bba8378d288fc1c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ca9c13867ad2c7142a339af129c0f7d7
folderAsset: yes
timeCreated: 1491148560
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,63 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using UnityEditor;
using System.Linq;
namespace FFmpegOut
{
[CanEditMultipleObjects]
[CustomEditor(typeof(CameraCapture))]
public class CameraCaptureEditor : Editor
{
SerializedProperty _width;
SerializedProperty _height;
SerializedProperty _preset;
SerializedProperty _frameRate;
GUIContent[] _presetLabels;
int[] _presetOptions;
// It shows the render format options when:
// - Editing multiple objects.
// - No target texture is specified in the camera.
bool ShouldShowFormatOptions
{
get {
if (targets.Length > 1) return true;
var camera = ((Component)target).GetComponent<Camera>();
return camera.targetTexture == null;
}
}
void OnEnable()
{
_width = serializedObject.FindProperty("_width");
_height = serializedObject.FindProperty("_height");
_preset = serializedObject.FindProperty("_preset");
_frameRate = serializedObject.FindProperty("_frameRate");
var presets = FFmpegPreset.GetValues(typeof(FFmpegPreset));
_presetLabels = presets.Cast<FFmpegPreset>().
Select(p => new GUIContent(p.GetDisplayName())).ToArray();
_presetOptions = presets.Cast<int>().ToArray();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (ShouldShowFormatOptions)
{
EditorGUILayout.PropertyField(_width);
EditorGUILayout.PropertyField(_height);
}
EditorGUILayout.IntPopup(_preset, _presetLabels, _presetOptions);
EditorGUILayout.PropertyField(_frameRate);
serializedObject.ApplyModifiedProperties();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5e125d1c67e59c444a1e722899c8a950
timeCreated: 1491148560
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
{
"name": "FFmpegOut.Editor",
"references": [
"FFmpegOut"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 34c6485430b2eb948b8840a09f7c2fbf
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,68 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using UnityEditor;
namespace FFmpegOut
{
[CanEditMultipleObjects]
[CustomEditor(typeof(FrameRateController))]
public class FrameRateControllerEditor : Editor
{
SerializedProperty _frameRate;
SerializedProperty _offlineMode;
void OnEnable()
{
_frameRate = serializedObject.FindProperty("_frameRate");
_offlineMode = serializedObject.FindProperty("_offlineMode");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_frameRate);
EditorGUILayout.PropertyField(_offlineMode);
if (!Application.isPlaying &&
!_frameRate.hasMultipleDifferentValues &&
!_offlineMode.hasMultipleDifferentValues)
{
if (_offlineMode.boolValue)
{
EditorGUILayout.HelpBox(
"Offline mode enabled: Time interval will be fixed " +
"to the specified value to keep exact speed on " +
"recorded videos. This stops synchronizing game " +
"time to wall clock time.", MessageType.None
);
}
else
{
var vSyncCount =
((FrameRateController)target).CalculateVSyncCount();
if (vSyncCount == 0)
{
EditorGUILayout.HelpBox(
"V-sync will be disabled because the specified " +
"frame rate is not divisible by the screen " +
"refresh rate.", MessageType.None
);
}
else
{
EditorGUILayout.HelpBox(
"V-sync count will be set to " + vSyncCount,
MessageType.None
);
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08d008f9408507d4886445ae32b751b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c36e64b6a30f4a43abc488dc63a3323
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
Shader "Hidden/FFmpegOut/Blitter"
{
Properties
{
_MainTex("", 2D) = "gray" {}
}
HLSLINCLUDE
sampler2D _MainTex;
void Vertex(
uint vid : SV_VertexID,
out float4 position : SV_Position,
out float2 texcoord : TEXCOORD
)
{
float x = (vid == 1) ? 1 : 0;
float y = (vid == 2) ? 1 : 0;
position = float4(x * 4 - 1, y * 4 - 1, 1, 1);
texcoord = float2(x * 2, 1 - y * 2);
}
half4 Fragment(
float4 position : SV_Position,
float2 texcoord : TEXCOORD
) : SV_Target
{
return tex2D(_MainTex, texcoord);
}
ENDHLSL
SubShader
{
Tags { "Queue" = "Transparent+100" }
Cull Off ZWrite Off ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex Vertex
#pragma fragment Fragment
ENDHLSL
}
}
}

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7c75f8f2de6a25741be0e5fa986fc435
timeCreated: 1488901510
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,37 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
Shader "Hidden/FFmpegOut/Preprocess"
{
Properties
{
_MainTex("", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 frag_flip(v2f_img i) : SV_Target
{
float2 uv = i.uv;
uv.y = 1 - uv.y;
return tex2D(_MainTex, uv);
}
ENDCG
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag_flip
ENDCG
}
}
}

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a4bd3b00ad1ed53458ec6ff07694985e
timeCreated: 1488901510
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5654631662aea5c4294d86ddd75eab98
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,6 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("FFmpegOut.Editor")]

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 535920348c7aa054cb3f13e8b09d6dc6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,200 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using System.Collections;
namespace FFmpegOut
{
[AddComponentMenu("FFmpegOut/Camera Capture")]
public sealed class CameraCapture : MonoBehaviour
{
#region Public properties
[SerializeField] int _width = 1920;
public int width {
get { return _width; }
set { _width = value; }
}
[SerializeField] int _height = 1080;
public int height {
get { return _height; }
set { _height = value; }
}
[SerializeField] FFmpegPreset _preset;
public FFmpegPreset preset {
get { return _preset; }
set { _preset = value; }
}
[SerializeField] float _frameRate = 60;
public float frameRate {
get { return _frameRate; }
set { _frameRate = value; }
}
#endregion
#region Private members
FFmpegSession _session;
RenderTexture _tempRT;
GameObject _blitter;
RenderTextureFormat GetTargetFormat(Camera camera)
{
return camera.allowHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
}
int GetAntiAliasingLevel(Camera camera)
{
return camera.allowMSAA ? QualitySettings.antiAliasing : 1;
}
#endregion
#region Time-keeping variables
int _frameCount;
float _startTime;
int _frameDropCount;
float FrameTime {
get { return _startTime + (_frameCount - 0.5f) / _frameRate; }
}
void WarnFrameDrop()
{
if (++_frameDropCount != 10) return;
Debug.LogWarning(
"Significant frame droppping was detected. This may introduce " +
"time instability into output video. Decreasing the recording " +
"frame rate is recommended."
);
}
#endregion
#region MonoBehaviour implementation
void OnValidate()
{
_width = Mathf.Max(8, _width);
_height = Mathf.Max(8, _height);
}
void OnDisable()
{
if (_session != null)
{
// Close and dispose the FFmpeg session.
_session.Close();
_session.Dispose();
_session = null;
}
if (_tempRT != null)
{
// Dispose the frame texture.
GetComponent<Camera>().targetTexture = null;
Destroy(_tempRT);
_tempRT = null;
}
if (_blitter != null)
{
// Destroy the blitter game object.
Destroy(_blitter);
_blitter = null;
}
}
IEnumerator Start()
{
// Sync with FFmpeg pipe thread at the end of every frame.
for (var eof = new WaitForEndOfFrame();;)
{
yield return eof;
_session?.CompletePushFrames();
}
}
void Update()
{
var camera = GetComponent<Camera>();
// Lazy initialization
if (_session == null)
{
// Give a newly created temporary render texture to the camera
// if it's set to render to a screen. Also create a blitter
// object to keep frames presented on the screen.
if (camera.targetTexture == null)
{
_tempRT = new RenderTexture(_width, _height, 24, GetTargetFormat(camera));
_tempRT.antiAliasing = GetAntiAliasingLevel(camera);
camera.targetTexture = _tempRT;
_blitter = Blitter.CreateInstance(camera);
}
// Start an FFmpeg session.
_session = FFmpegSession.Create(
gameObject.name,
camera.targetTexture.width,
camera.targetTexture.height,
_frameRate, preset
);
_startTime = Time.time;
_frameCount = 0;
_frameDropCount = 0;
}
var gap = Time.time - FrameTime;
var delta = 1 / _frameRate;
if (gap < 0)
{
// Update without frame data.
_session.PushFrame(null);
}
else if (gap < delta)
{
// Single-frame behind from the current time:
// Push the current frame to FFmpeg.
_session.PushFrame(camera.targetTexture);
_frameCount++;
}
else if (gap < delta * 2)
{
// Two-frame behind from the current time:
// Push the current frame twice to FFmpeg. Actually this is not
// an efficient way to catch up. We should think about
// implementing frame duplication in a more proper way. #fixme
_session.PushFrame(camera.targetTexture);
_session.PushFrame(camera.targetTexture);
_frameCount += 2;
}
else
{
// Show a warning message about the situation.
WarnFrameDrop();
// Push the current frame to FFmpeg.
_session.PushFrame(camera.targetTexture);
// Compensate the time delay.
_frameCount += Mathf.FloorToInt(gap * _frameRate);
}
}
#endregion
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: a66fbd5ffe9b9d64da304996f1919f40
timeCreated: 1488901810
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences:
- _shader: {fileID: 4800000, guid: a4bd3b00ad1ed53458ec6ff07694985e, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
{
"name": "FFmpegOut"
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2d927f977f87e2f4497af759c3426e00
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,88 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
namespace FFmpegOut
{
public enum FFmpegPreset
{
H264Default,
H264Nvidia,
H264Lossless420,
H264Lossless444,
HevcDefault,
HevcNvidia,
ProRes422,
ProRes4444,
VP8Default,
VP9Default,
Hap,
HapAlpha,
HapQ
}
static public class FFmpegPresetExtensions
{
public static string GetDisplayName(this FFmpegPreset preset)
{
switch (preset)
{
case FFmpegPreset.H264Default: return "H.264 Default (MP4)";
case FFmpegPreset.H264Nvidia: return "H.264 NVIDIA (MP4)";
case FFmpegPreset.H264Lossless420: return "H.264 Lossless 420 (MP4)";
case FFmpegPreset.H264Lossless444: return "H.264 Lossless 444 (MP4)";
case FFmpegPreset.HevcDefault: return "HEVC Default (MP4)";
case FFmpegPreset.HevcNvidia: return "HEVC NVIDIA (MP4)";
case FFmpegPreset.ProRes422: return "ProRes 422 (QuickTime)";
case FFmpegPreset.ProRes4444: return "ProRes 4444 (QuickTime)";
case FFmpegPreset.VP8Default: return "VP8 (WebM)";
case FFmpegPreset.VP9Default: return "VP9 (WebM)";
case FFmpegPreset.Hap: return "HAP (QuickTime)";
case FFmpegPreset.HapAlpha: return "HAP Alpha (QuickTime)";
case FFmpegPreset.HapQ: return "HAP Q (QuickTime)";
}
return null;
}
public static string GetSuffix(this FFmpegPreset preset)
{
switch (preset)
{
case FFmpegPreset.H264Default:
case FFmpegPreset.H264Nvidia:
case FFmpegPreset.H264Lossless420:
case FFmpegPreset.H264Lossless444:
case FFmpegPreset.HevcDefault:
case FFmpegPreset.HevcNvidia: return ".mp4";
case FFmpegPreset.ProRes422:
case FFmpegPreset.ProRes4444: return ".mov";
case FFmpegPreset.VP9Default:
case FFmpegPreset.VP8Default: return ".webm";
case FFmpegPreset.Hap:
case FFmpegPreset.HapQ:
case FFmpegPreset.HapAlpha: return ".mov";
}
return null;
}
public static string GetOptions(this FFmpegPreset preset)
{
switch (preset)
{
case FFmpegPreset.H264Default: return "-pix_fmt yuv420p";
case FFmpegPreset.H264Nvidia: return "-c:v h264_nvenc -pix_fmt yuv420p";
case FFmpegPreset.H264Lossless420: return "-pix_fmt yuv420p -preset ultrafast -crf 0";
case FFmpegPreset.H264Lossless444: return "-pix_fmt yuv444p -preset ultrafast -crf 0";
case FFmpegPreset.HevcDefault: return "-c:v libx265 -pix_fmt yuv420p";
case FFmpegPreset.HevcNvidia: return "-c:v hevc_nvenc -pix_fmt yuv420p";
case FFmpegPreset.ProRes422: return "-c:v prores_ks -pix_fmt yuv422p10le";
case FFmpegPreset.ProRes4444: return "-c:v prores_ks -pix_fmt yuva444p10le";
case FFmpegPreset.VP8Default: return "-c:v libvpx -pix_fmt yuv420p";
case FFmpegPreset.VP9Default: return "-c:v libvpx-vp9";
case FFmpegPreset.Hap: return "-c:v hap";
case FFmpegPreset.HapAlpha: return "-c:v hap -format hap_alpha";
case FFmpegPreset.HapQ: return "-c:v hap -format hap_q";
}
return null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2b56c2e4fdc0fd4f9b1f81dd6bd785b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,196 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
namespace FFmpegOut
{
public sealed class FFmpegSession : System.IDisposable
{
#region Factory methods
public static FFmpegSession Create(
string name,
int width, int height, float frameRate,
FFmpegPreset preset
)
{
name += System.DateTime.Now.ToString(" yyyy MMdd HHmmss");
var path = name.Replace(" ", "_") + preset.GetSuffix();
return CreateWithOutputPath(path, width, height, frameRate, preset);
}
public static FFmpegSession CreateWithOutputPath(
string outputPath,
int width, int height, float frameRate,
FFmpegPreset preset
)
{
return new FFmpegSession(
"-y -f rawvideo -vcodec rawvideo -pixel_format rgba"
+ " -colorspace bt709"
+ " -video_size " + width + "x" + height
+ " -framerate " + frameRate
+ " -loglevel warning -i - " + preset.GetOptions()
+ " \"" + "C:/Users/pikmi/Downloads/temp.mp4" + "\"" //hijacked this. basically i dont want some fancy output file name (which is what the old code did) so it's just temp now lol
);
}
public static FFmpegSession CreateWithArguments(string arguments)
{
return new FFmpegSession(arguments);
}
#endregion
#region Public properties and members
public void PushFrame(Texture source)
{
if (_pipe != null)
{
ProcessQueue();
if (source != null) QueueFrame(source);
}
}
public void CompletePushFrames()
{
_pipe?.SyncFrameData();
}
public void Close()
{
if (_pipe != null)
{
var error = _pipe.CloseAndGetOutput();
if (!string.IsNullOrEmpty(error))
Debug.LogWarning(
"FFmpeg returned with warning/error messages. " +
"See the following lines for details:\n" + error
);
_pipe.Dispose();
_pipe = null;
}
if (_blitMaterial != null)
{
UnityEngine.Object.Destroy(_blitMaterial);
_blitMaterial = null;
}
}
public void Dispose()
{
Close();
}
#endregion
#region Private objects and constructor/destructor
FFmpegPipe _pipe;
Material _blitMaterial;
FFmpegSession(string arguments)
{
if (!FFmpegPipe.IsAvailable)
Debug.LogWarning(
"Failed to initialize an FFmpeg session due to missing " +
"executable file. Please check FFmpeg installation."
);
else if (!UnityEngine.SystemInfo.supportsAsyncGPUReadback)
Debug.LogWarning(
"Failed to initialize an FFmpeg session due to lack of " +
"async GPU readback support. Please try changing " +
"graphics API to readback-enabled one."
);
else
_pipe = new FFmpegPipe(arguments);
}
~FFmpegSession()
{
if (_pipe != null)
Debug.LogError(
"An unfinalized FFmpegCapture object was detected. " +
"It should be explicitly closed or disposed " +
"before being garbage-collected."
);
}
#endregion
#region Frame readback queue
List<AsyncGPUReadbackRequest> _readbackQueue =
new List<AsyncGPUReadbackRequest>(4);
void QueueFrame(Texture source)
{
if (_readbackQueue.Count > 6)
{
Debug.LogWarning("Too many GPU readback requests.");
return;
}
// Lazy initialization of the preprocessing blit shader
if (_blitMaterial == null)
{
var shader = Shader.Find("Hidden/FFmpegOut/Preprocess");
_blitMaterial = new Material(shader);
}
// Blit to a temporary texture and request readback on it.
var rt = RenderTexture.GetTemporary
(source.width, source.height, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(source, rt, _blitMaterial, 0);
_readbackQueue.Add(AsyncGPUReadback.Request(rt));
RenderTexture.ReleaseTemporary(rt);
}
void ProcessQueue()
{
while (_readbackQueue.Count > 0)
{
// Check if the first entry in the queue is completed.
if (!_readbackQueue[0].done)
{
// Detect out-of-order case (the second entry in the queue
// is completed before the first entry).
if (_readbackQueue.Count > 1 && _readbackQueue[1].done)
{
// We can't allow the out-of-order case, so force it to
// be completed now.
_readbackQueue[0].WaitForCompletion();
}
else
{
// Nothing to do with the queue.
break;
}
}
// Retrieve the first entry in the queue.
var req = _readbackQueue[0];
_readbackQueue.RemoveAt(0);
// Error detection
if (req.hasError)
{
Debug.LogWarning("GPU readback error was detected.");
continue;
}
// Feed the frame to the FFmpeg pipe.
_pipe.PushFrameData(req.GetData<byte>());
}
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8ab25b2c3553cd446afb35cf75341bbc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,74 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using System.Collections;
namespace FFmpegOut
{
[AddComponentMenu("FFmpegOut/Frame Rate Controller")]
public sealed class FrameRateController : MonoBehaviour
{
[SerializeField] float _frameRate = 60;
[SerializeField] bool _offlineMode = true;
int _originalFrameRate;
int _originalVSyncCount;
internal int CalculateVSyncCount()
{
// Determine the display refresh rate.
// We assume 59=59.95Hz, 23=23.976Hz and so on.
// Is it the right way to get fractional-number rate? Who knows.
var i_rate = Screen.currentResolution.refreshRate;
var f_rate = (float)i_rate;
switch (i_rate)
{
case 23: f_rate = 23.976f; break;
case 29: f_rate = 29.970f; break;
case 47: f_rate = 47.952f; break;
case 59: f_rate = 59.940f; break;
case 71: f_rate = 71.928f; break;
case 119: f_rate = 119.88f; break;
}
// Return a positive value if it's divisible by the frame rate.
if (Mathf.Approximately(f_rate % _frameRate, 0))
return Mathf.RoundToInt(f_rate / _frameRate);
else
return 0; // Don't use v-sync.
}
void OnEnable()
{
var ifps = Mathf.RoundToInt(_frameRate);
if (_offlineMode)
{
_originalFrameRate = Time.captureFramerate;
Time.captureFramerate = ifps;
}
else
{
_originalFrameRate = Application.targetFrameRate;
_originalVSyncCount = QualitySettings.vSyncCount;
Application.targetFrameRate = ifps;
QualitySettings.vSyncCount = CalculateVSyncCount();
}
}
void OnDisable()
{
if (_offlineMode)
{
Time.captureFramerate = _originalFrameRate;
}
else
{
Application.targetFrameRate = _originalFrameRate;
QualitySettings.vSyncCount = _originalVSyncCount;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e55bddfe16e69224c8c4b4eb165695d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 88f8cafe5d48d4a4f89f22b10bbc64a4
folderAsset: yes
timeCreated: 1491313768
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,111 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using UnityEngine;
using UnityEngine.Rendering;
namespace FFmpegOut
{
sealed class Blitter : MonoBehaviour
{
#region Factory method
static System.Type[] _initialComponents =
{ typeof(Camera), typeof(Blitter) };
public static GameObject CreateInstance(Camera source)
{
var go = new GameObject("Blitter", _initialComponents);
go.hideFlags = HideFlags.HideInHierarchy;
var camera = go.GetComponent<Camera>();
camera.cullingMask = 1 << UILayer;
camera.targetDisplay = source.targetDisplay;
var blitter = go.GetComponent<Blitter>();
blitter._sourceTexture = source.targetTexture;
return go;
}
#endregion
#region Private members
// Assuming that the 5th layer is "UI". #badcode
const int UILayer = 5;
Texture _sourceTexture;
Mesh _mesh;
Material _material;
void PreCull(Camera camera)
{
if (_mesh == null || camera != GetComponent<Camera>()) return;
Graphics.DrawMesh(
_mesh, transform.localToWorldMatrix,
_material, UILayer, camera
);
}
#if UNITY_2019_2_OR_NEWER
void BeginCameraRendering(ScriptableRenderContext context, Camera camera)
{
PreCull(camera);
}
#endif
#endregion
#region MonoBehaviour implementation
void Update()
{
if (_mesh == null)
{
// Index-only triangle mesh
_mesh = new Mesh();
_mesh.vertices = new Vector3[3];
_mesh.triangles = new int [] { 0, 1, 2 };
_mesh.bounds = new Bounds(Vector3.zero, Vector3.one);
_mesh.UploadMeshData(true);
// Blitter shader material
var shader = Shader.Find("Hidden/FFmpegOut/Blitter");
_material = new Material(shader);
_material.SetTexture("_MainTex", _sourceTexture);
// Register the camera render callback.
#if UNITY_2019_2_OR_NEWER
RenderPipelineManager.beginCameraRendering += BeginCameraRendering; // SRP
#else
UnityEngine.Experimental.Rendering.RenderPipeline.beginCameraRendering += PreCull; // SRP
#endif
Camera.onPreCull += PreCull; // Legacy
}
}
void OnDisable()
{
if (_mesh != null)
{
// Unregister the camera render callback.
#if UNITY_2019_2_OR_NEWER
RenderPipelineManager.beginCameraRendering -= BeginCameraRendering; // SRP
#else
UnityEngine.Experimental.Rendering.RenderPipeline.beginCameraRendering -= PreCull; // SRP
#endif
Camera.onPreCull -= PreCull; // Legacy
// Destroy temporary objects.
Destroy(_mesh);
Destroy(_material);
_mesh = null;
_material = null;
}
}
#endregion
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6dc134cd7da61a9438d65f309789d0d3
timeCreated: 1491312926
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,234 @@
// FFmpegOut - FFmpeg video encoding plugin for Unity
// https://github.com/keijiro/KlakNDI
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Unity.Collections;
namespace FFmpegOut
{
public sealed class FFmpegPipe : System.IDisposable
{
#region Public methods
public static bool IsAvailable {
get { return System.IO.File.Exists(ExecutablePath); }
}
public FFmpegPipe(string arguments)
{
// Start FFmpeg subprocess.
_subprocess = Process.Start(new ProcessStartInfo {
FileName = ExecutablePath,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true
});
// Start copy/pipe subthreads.
_copyThread = new Thread(CopyThread);
_pipeThread = new Thread(PipeThread);
_copyThread.Start();
_pipeThread.Start();
}
public void PushFrameData(NativeArray<byte> data)
{
// Update the copy queue and notify the copy thread with a ping.
lock (_copyQueue) _copyQueue.Enqueue(data);
_copyPing.Set();
}
public void SyncFrameData()
{
// Wait for the copy queue to get emptied with using pong
// notification signals sent from the copy thread.
while (_copyQueue.Count > 0) _copyPong.WaitOne();
// When using a slower codec (e.g. HEVC, ProRes), frames may be
// queued too much, and it may end up with an out-of-memory error.
// To avoid this problem, we wait for pipe queue entries to be
// comsumed by the pipe thread.
while (_pipeQueue.Count > 4) _pipePong.WaitOne();
}
public string CloseAndGetOutput()
{
// Terminate the subthreads.
_terminate = true;
_copyPing.Set();
_pipePing.Set();
_copyThread.Join();
_pipeThread.Join();
// Close FFmpeg subprocess.
_subprocess.StandardInput.Close();
_subprocess.WaitForExit();
var outputReader = _subprocess.StandardError;
var error = outputReader.ReadToEnd();
_subprocess.Close();
_subprocess.Dispose();
outputReader.Close();
outputReader.Dispose();
// Nullify members (just for ease of debugging).
_subprocess = null;
_copyThread = null;
_pipeThread = null;
_copyQueue = null;
_pipeQueue = _freeBuffer = null;
return error;
}
#endregion
#region IDisposable implementation
public void Dispose()
{
if (!_terminate) CloseAndGetOutput();
}
~FFmpegPipe()
{
if (!_terminate)
UnityEngine.Debug.LogError(
"An unfinalized FFmpegPipe object was detected. " +
"It should be explicitly closed or disposed " +
"before being garbage-collected."
);
}
#endregion
#region Private members
Process _subprocess;
Thread _copyThread;
Thread _pipeThread;
AutoResetEvent _copyPing = new AutoResetEvent(false);
AutoResetEvent _copyPong = new AutoResetEvent(false);
AutoResetEvent _pipePing = new AutoResetEvent(false);
AutoResetEvent _pipePong = new AutoResetEvent(false);
bool _terminate;
Queue<NativeArray<byte>> _copyQueue = new Queue<NativeArray<byte>>();
Queue<byte[]> _pipeQueue = new Queue<byte[]>();
Queue<byte[]> _freeBuffer = new Queue<byte[]>();
public static string ExecutablePath
{
get {
var basePath = UnityEngine.Application.streamingAssetsPath;
var platform = UnityEngine.Application.platform;
if (platform == UnityEngine.RuntimePlatform.OSXPlayer ||
platform == UnityEngine.RuntimePlatform.OSXEditor)
return basePath + "/FFmpegOut/macOS/ffmpeg";
if (platform == UnityEngine.RuntimePlatform.LinuxPlayer ||
platform == UnityEngine.RuntimePlatform.LinuxEditor)
return basePath + "/FFmpegOut/Linux/ffmpeg";
return basePath + "/FFmpegOut/Windows/ffmpeg.exe";
}
}
#endregion
#region Subthread entry points
// CopyThread - Copies frames given from the readback queue to the pipe
// queue. This is required because readback buffers are not under our
// control -- they'll be disposed before being processed by us. They
// have to be buffered by end-of-frame.
void CopyThread()
{
while (!_terminate)
{
// Wait for ping from the main thread.
_copyPing.WaitOne();
// Process all entries in the copy queue.
while (_copyQueue.Count > 0)
{
// Retrieve an copy queue entry without dequeuing it.
// (We don't want to notify the main thread at this point.)
NativeArray<byte> source;
lock (_copyQueue) source = _copyQueue.Peek();
// Try allocating a buffer from the free buffer list.
byte[] buffer = null;
if (_freeBuffer.Count > 0)
lock (_freeBuffer) buffer = _freeBuffer.Dequeue();
// Copy the contents of the copy queue entry.
if (buffer == null || buffer.Length != source.Length)
buffer = source.ToArray();
else
source.CopyTo(buffer);
// Push the buffer entry to the pipe queue.
lock (_pipeQueue) _pipeQueue.Enqueue(buffer);
_pipePing.Set(); // Ping the pipe thread.
// Dequeue the copy buffer entry and ping the main thread.
lock (_copyQueue) _copyQueue.Dequeue();
_copyPong.Set();
}
}
}
// PipeThread - Receives frame entries from the copy thread and push
// them into the FFmpeg pipe.
void PipeThread()
{
var pipe = _subprocess.StandardInput.BaseStream;
while (!_terminate)
{
// Wait for the ping from the copy thread.
_pipePing.WaitOne();
// Process all entries in the pipe queue.
while (_pipeQueue.Count > 0)
{
// Retrieve a frame entry.
byte[] buffer;
lock (_pipeQueue) buffer = _pipeQueue.Dequeue();
// Write it into the FFmpeg pipe.
try
{
pipe.Write(buffer, 0, buffer.Length);
pipe.Flush();
}
catch
{
// Pipe.Write could raise an IO exception when ffmpeg
// is terminated for some reason. We just ignore this
// situation and assume that it will be resolved in the
// main thread. #badcode
}
// Add the buffer to the free buffer list to reuse later.
lock (_freeBuffer) _freeBuffer.Enqueue(buffer);
_pipePong.Set();
}
}
}
#endregion
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fffc00cbe6345c64a8d3898031c46d8a
timeCreated: 1491121325
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -7526,6 +7526,51 @@ MonoBehaviour:
m_XAdvance: 0 m_XAdvance: 0
m_YAdvance: 0 m_YAdvance: 0
m_FeatureLookupFlags: -11456224 m_FeatureLookupFlags: -11456224
- m_FirstAdjustmentRecord:
m_GlyphIndex: 57
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0.90000004
m_YAdvance: 0
m_SecondAdjustmentRecord:
m_GlyphIndex: 10
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0
m_YAdvance: 0
m_FeatureLookupFlags: -2056572640
- m_FirstAdjustmentRecord:
m_GlyphIndex: 57
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0.90000004
m_YAdvance: 0
m_SecondAdjustmentRecord:
m_GlyphIndex: 62
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0
m_YAdvance: 0
m_FeatureLookupFlags: -2056572640
- m_FirstAdjustmentRecord:
m_GlyphIndex: 57
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0.90000004
m_YAdvance: 0
m_SecondAdjustmentRecord:
m_GlyphIndex: 122
m_GlyphValueRecord:
m_XPlacement: 0
m_YPlacement: 0
m_XAdvance: 0
m_YAdvance: 0
m_FeatureLookupFlags: -2056572640
m_MarkToBaseAdjustmentRecords: [] m_MarkToBaseAdjustmentRecords: []
m_MarkToMarkAdjustmentRecords: [] m_MarkToMarkAdjustmentRecords: []
m_ShouldReimportFontFeatures: 0 m_ShouldReimportFontFeatures: 0

View file

@ -14,8 +14,8 @@ RenderTexture:
m_DownscaleFallback: 0 m_DownscaleFallback: 0
m_IsAlphaChannelOptional: 0 m_IsAlphaChannelOptional: 0
serializedVersion: 5 serializedVersion: 5
m_Width: 947 m_Width: 974
m_Height: 533 m_Height: 547
m_AntiAliasing: 2 m_AntiAliasing: 2
m_MipCount: -1 m_MipCount: -1
m_DepthStencilFormat: 92 m_DepthStencilFormat: 92

View file

@ -14,8 +14,8 @@ RenderTexture:
m_DownscaleFallback: 0 m_DownscaleFallback: 0
m_IsAlphaChannelOptional: 0 m_IsAlphaChannelOptional: 0
serializedVersion: 5 serializedVersion: 5
m_Width: 1420 m_Width: 1461
m_Height: 799 m_Height: 820
m_AntiAliasing: 1 m_AntiAliasing: 1
m_MipCount: -1 m_MipCount: -1
m_DepthStencilFormat: 92 m_DepthStencilFormat: 92

View file

@ -83,6 +83,6 @@ Material:
- _ZWrite: 1 - _ZWrite: 1
m_Colors: m_Colors:
- _AddColor: {r: 0, g: 0, b: 0, a: 1} - _AddColor: {r: 0, g: 0, b: 0, a: 1}
- _Color: {r: 0.9528302, g: 0.87130237, b: 0.7775454, a: 1} - _Color: {r: 0.9529412, g: 0.87058824, b: 0.7764706, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: [] m_BuildTextureStacks: []

View file

@ -8906,7 +8906,7 @@ MonoBehaviour:
m_faceColor: m_faceColor:
serializedVersion: 2 serializedVersion: 2
rgba: 4294967295 rgba: 4294967295
m_fontSize: 14 m_fontSize: 9.65
m_fontSizeBase: 14.3 m_fontSizeBase: 14.3
m_fontWeight: 400 m_fontWeight: 400
m_enableAutoSizing: 1 m_enableAutoSizing: 1
@ -18420,6 +18420,36 @@ GameObject:
m_CorrespondingSourceObject: {fileID: 6052331674369862802, guid: 720073bc7682b1441bece7116681f72c, type: 3} m_CorrespondingSourceObject: {fileID: 6052331674369862802, guid: 720073bc7682b1441bece7116681f72c, type: 3}
m_PrefabInstance: {fileID: 631525709} m_PrefabInstance: {fileID: 631525709}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
--- !u!114 &631525720
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 631525712}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5ebf035b10ec113418cebc7dff510693, type: 3}
m_Name:
m_EditorClassIdentifier:
recorder: {fileID: 631525723}
audioRenderer: {fileID: 1699860728}
--- !u!114 &631525723
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 631525712}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a66fbd5ffe9b9d64da304996f1919f40, type: 3}
m_Name:
m_EditorClassIdentifier:
_width: 1920
_height: 1080
_preset: 0
_frameRate: 60
--- !u!1 &632795495 --- !u!1 &632795495
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -32707,7 +32737,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5} m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5} m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 142.32655} m_AnchoredPosition: {x: 0, y: 90.48979}
m_SizeDelta: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 1} m_Pivot: {x: 0.5, y: 1}
--- !u!222 &1154875945 --- !u!222 &1154875945
@ -43455,8 +43485,8 @@ MonoBehaviour:
m_TargetGraphic: {fileID: 1220118245} m_TargetGraphic: {fileID: 1220118245}
m_HandleRect: {fileID: 1220118244} m_HandleRect: {fileID: 1220118244}
m_Direction: 2 m_Direction: 2
m_Value: 1.0000008 m_Value: 1.0000002
m_Size: 0.78099126 m_Size: 0.35148686
m_NumberOfSteps: 0 m_NumberOfSteps: 0
m_OnValueChanged: m_OnValueChanged:
m_PersistentCalls: m_PersistentCalls:
@ -44971,6 +45001,19 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1695786878} m_GameObject: {fileID: 1695786878}
m_CullTransparentMesh: 1 m_CullTransparentMesh: 1
--- !u!114 &1699860728
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 631525713}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1150ae7143196054bba8378d288fc1c3, type: 3}
m_Name:
m_EditorClassIdentifier:
Rendering: 0
--- !u!1 &1700641149 --- !u!1 &1700641149
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View file

@ -1,8 +1,8 @@
using System; using System;
public static class AppInfo { public static class AppInfo {
public const string Version = "0.0.1018"; public const string Version = "1.0.8";
public static readonly DateTime Date = new DateTime(2024, 01, 15, 19, 39, 36, 317, DateTimeKind.Utc); public static readonly DateTime Date = new DateTime(2024, 02, 01, 03, 33, 35, 379, DateTimeKind.Utc);
} }

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 84f50102f80683a4db6a365fefd779cf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,68 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FFmpegOut;
using System.IO;
namespace FFmpegOut
{
public class HeavenRecorderController : MonoBehaviour
{
public CameraCapture recorder;
public AudioRenderer audioRenderer;
bool exporting = true;
// Start is called before the first frame update
void Start()
{
recorder.enabled = false;
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.L))
{
if(recorder.enabled)
{
print("recording stopped!");
recorder.enabled = false;
audioRenderer.Rendering = false;
if(audioRenderer.Save("C:/Users/pikmi/Downloads/temp.wav").State == AudioRenderer.Status.SUCCESS && exporting == true)
{
Merge();
audioRenderer.Clear();
}
}
else
{
if(File.Exists("C:/Users/pikmi/Downloads/temp.wav"))
{
File.Delete("C:/Users/pikmi/Downloads/temp.wav");
}
if(File.Exists("C:/Users/pikmi/Downloads/temp.mp4"))
{
File.Delete("C:/Users/pikmi/Downloads/temp.mp4");
}
audioRenderer.Rendering = true;
print("recording started!");
recorder.enabled = true;
exporting = true;
}
}
}
void Merge()
{
exporting = false;
FFmpegSession _session;
_session = FFmpegSession.CreateWithArguments("-y -i C:/Users/pikmi/Downloads/temp.mp4 -i C:/Users/pikmi/Downloads/temp.wav -c:v copy -c:a aac C:/Users/pikmi/Downloads/output.mp4");
_session.Close();
_session.Dispose();
_session = null;
File.Delete("C:/Users/pikmi/Downloads/temp.wav");
File.Delete("C:/Users/pikmi/Downloads/temp.mp4");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ebf035b10ec113418cebc7dff510693
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -134,7 +134,7 @@ PlayerSettings:
16:10: 1 16:10: 1
16:9: 1 16:9: 1
Others: 1 Others: 1
bundleVersion: 1.0.1 bundleVersion: 1.0.8
preloadedAssets: preloadedAssets:
- {fileID: 102900000, guid: 5348c08b82446e0478cee8bda6c02cfc, type: 3} - {fileID: 102900000, guid: 5348c08b82446e0478cee8bda6c02cfc, type: 3}
metroInputSource: 0 metroInputSource: 0
@ -158,11 +158,11 @@ PlayerSettings:
applicationIdentifier: applicationIdentifier:
Standalone: com.RHeavenStudio.Heaven-Studio Standalone: com.RHeavenStudio.Heaven-Studio
buildNumber: buildNumber:
Standalone: 1018 Standalone: 100008
iPhone: 0 iPhone: 0
tvOS: 0 tvOS: 0
overrideDefaultApplicationIdentifier: 0 overrideDefaultApplicationIdentifier: 0
AndroidBundleVersionCode: 1018 AndroidBundleVersionCode: 100008
AndroidMinSdkVersion: 22 AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 0 AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1 AndroidPreferredInstallLocation: 1

View file

@ -17,275 +17,9 @@ MonoBehaviour:
DenseViewWidthThreshold: 512 DenseViewWidthThreshold: 512
_disableAutoReloadInBackground: 0 _disableAutoReloadInBackground: 0
ImportedScriptPaths: ImportedScriptPaths:
- Assets/Scripts/Minigames.cs - Assets/Scripts/LevelEditor/HeavenRecorder/HeavenRecorderController.cs
- Assets/Editor/CreateAssetBundles.cs - Assets/FFmpegOut/Runtime/FFmpegSession.cs
- Assets/Editor/CreateMinigameScriptTemplate.cs - Assets/FFmpegOut/AudioRenderer.cs
- Assets/Scripts/Games/Minigame.cs
- Assets/Scripts/Games/FanClub/FanClub.cs
- Assets/Scripts/GameManager.cs
- Assets/Editor/SpritesheetScaler.cs
- Assets/Scripts/USG.g/LoadMinigames.Minigames.MinigameLoaderGenerator.g.cs
- Assets/Scripts/Games/TheDazzles/TheDazzles.cs
- Assets/Scripts/Games/FirstContact/FirstContact.cs
- Assets/Scripts/SourceGenerators/ControllerLoaderGenerator.cs
- Assets/Scripts/InputSystem/PlayerInput.cs
- Assets/Scripts/JudgementManager.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/RatingScreenPropertyDialog.cs
- Assets/Scripts/GlobalGameManager.cs
- Assets/Scripts/Games/KarateMan/KarateMan.cs
- Assets/Scripts/Games/KarateMan/KarateManJoe.cs
- Assets/Scripts/Games/KarateMan/KarateManNoriController.cs
- Assets/Scripts/Util/BeatAction.cs
- Assets/Scripts/Games/TossBoys/TossBoys.cs
- Assets/Scripts/Games/DoubleDate/DoubleDate.cs
- Assets/Scripts/Games/AirRally/AirRally.cs
- Assets/Scripts/LevelEditor/Editor.cs
- Assets/Scripts/Common/MemRenderer.cs
- Assets/Scripts/Games/BoardMeeting/BoardMeeting.cs
- Assets/Scripts/PersistentDataManager.cs
- Assets/Scripts/UI/Overlays/OverlaysManager.cs
- Assets/Scripts/TitleManager.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/SectionDialog.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/TimelineObjs/SectionTimelineObj.cs
- Assets/Scripts/Games/DJSchool/Student.cs
- Assets/Scripts/UI/PauseMenu.cs
- Assets/Scripts/Util/Sound.cs
- Assets/Scripts/Conductor.cs
- Assets/Scripts/EventCaller.cs
- Assets/Scripts/Games/TrickClass/TrickClass.cs
- Assets/Scripts/GameInitializer.cs
- Assets/Scripts/LevelEditor/Timeline/Timeline.cs
- Assets/Scripts/Games/PlayerActionEvent.cs
- Assets/Scripts/Games/KarateMan/KarateManPot.cs
- Assets/Scripts/Games/DJSchool/DJSchool.cs
- Assets/Scripts/LevelEditor/Commands/TestCommand.cs
- Assets/Scripts/Games/ClappyTrio/ClappyTrioPlayer.cs
- Assets/Scripts/Games/GleeClub/GleeClub.cs
- Assets/Scripts/Games/DoubleDate/Basketball.cs
- Assets/Scripts/Games/TotemClimb/TCBackgroundManager.cs
- Assets/Scripts/Games/Spaceball/SpaceballBall.cs
- Assets/Scripts/Games/QuizShow/QuizShow.cs
- Assets/Scripts/InputSystem/ControllerTypes/InputJoyshock.cs
- Assets/Scripts/Games/TotemClimb/TCEndTotem.cs
- Assets/Editor/BuildScript.cs
- Assets/Scripts/LevelEditor/Selections.cs
- Assets/Scripts/Games/NightWalkRvl/RvlNightWalk.cs
- Assets/Plugins/Starpelly/Math.cs
- Assets/Scripts/Games/GleeClub/ChorusKid.cs
- Assets/Scripts/LevelEditor/EventSelector/GridGameSelector.cs
- Assets/Scripts/LevelEditor/Timeline/TimelineZoom.cs
- Assets/Scripts/Games/CheerReaders/RvlCharacter.cs
- Assets/Scripts/Games/MrUpbeat/MrUpbeat.cs
- Assets/Scripts/Games/CatchyTune/CatchyTune.cs
- Assets/GUIWindows/Scripts/GUIWindow.cs
- Assets/Scripts/Games/AirRally/CloudsManager.cs
- Assets/Scripts/Games/DogNinja/ThrowObject.cs
- Assets/Scripts/Games/RhythmTweezers/Hair.cs
- Assets/Scripts/Games/Rockers/RockersInput.cs
- Assets/Scripts/UI/Overlays/GoForAPerfect.cs
- Assets/Scripts/Games/SpaceSoccer/Ball.cs
- Assets/Scripts/Games/TossBoys/TossKid.cs
- Assets/Scripts/Games/CropStomp/Farmer.cs
- Assets/Scripts/Games/SpaceSoccer/SpaceSoccer.cs
- Assets/Scripts/UI/LeftClickTMP_Dropdown.cs
- Assets/Scripts/Games/RhythmTweezers/NoPeekingSign.cs
- Assets/Scripts/Games/Fireworks/FireworksBomb.cs
- Assets/Scripts/Transform/ScaleByVelocity.cs
- Assets/Scripts/Games/BuiltToScaleDS/BuiltToScaleDS.cs
- Assets/Scripts/Games/Global/Textbox.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/ImageChartResourcePrefab.cs
- Assets/Scripts/Games/CropStomp/Veggie.cs
- Assets/Scripts/OpeningManager.cs
- Assets/Scripts/Games/BlueBear/BlueBear.cs
- Assets/Scripts/Games/DoubleDate/Football.cs
- Assets/Plugins/JoyShockLibrary/JoyShockLibrary.cs
- Assets/Scripts/StretchCameraVFX.cs
- Assets/Scripts/LevelEditor/Timeline/TimelineEventObj.cs
- Assets/Scripts/Games/TapTrial/TapTrial.cs
- Assets/Scripts/Games/AirRally/RvlIsland.cs
- Assets/Scripts/LevelEditor/EventSelector/PropertyPrefabs/StringPropertyPrefab.cs
- Assets/Scripts/Games/DoubleDate/DoubleDateWeasels.cs
- Assets/Scripts/Games/Rockers/RockersRocker.cs
- Assets/Scripts/Games/TapTroupe/TapTroupe.cs
- Assets/Scripts/Games/TotemClimb/TCTotem.cs
- Assets/Scripts/LevelEditor/Timeline/BlockDeleteFX.cs
- Assets/Scripts/UI/Overlays/SectionMedalsManager.cs
- Assets/Scripts/Games/AirRally/IslandsManager.cs
- Assets/Scripts/Games/CallAndResponseHandler.cs
- Assets/Scripts/Games/RhythmTweezers/LongHair.cs
- Assets/Scripts/Games/SneakySpirits/SneakySpiritsGhost.cs
- Assets/Scripts/Games/ForkLifter/Pea.cs
- Assets/Scripts/LevelEditor/WaveformVisual.cs
- Assets/Scripts/Games/CatchyTune/Fruit.cs
- Assets/Scripts/Games/FanClub/NtrIdolFan.cs
- Assets/Scripts/Games/PajamaParty/PajamaParty.cs
- Assets/Scripts/Games/SneakySpirits/SneakySpirits.cs
- Assets/Scripts/LevelEditor/Commands/Block.cs
- Assets/Scripts/LevelEditor/EventSelector/PropertyPrefabs/ColorPropertyPrefab.cs
- Assets/Scripts/Games/LaunchParty/LaunchParty.cs
- Assets/Scripts/Games/Rockers/Rockers.cs
- Assets/Scripts/Games/SamuraiSliceNtr/NtrSamuraiObject.cs
- Assets/Scripts/Games/ForkLifter/ForkLifterHand.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/EnumChartPropertyPrefab.cs
- Assets/Scripts/Games/Global/Filter.cs
- Assets/Scripts/Games/NightWalkAgb/AgbPlatform.cs
- Assets/Scripts/Games/PajamaParty/CtrPillowMonkey.cs
- Assets/Scripts/Games/MarchingOrders/MarchingOrders.cs
- Assets/Scripts/LevelEditor/SnapDialog/SnapChangeButton.cs
- Assets/Plugins/StandaloneFileBrowser/StandaloneFileBrowserWindows.cs
- Assets/Scripts/Games/WorkingDough/NPCDoughBall.cs
- Assets/Scripts/ScreenTiling.cs
- Assets/Scripts/Games/AirRally/Shuttlecock.cs
- Assets/Scripts/Games/WizardsWaltz/WizardsWaltz.cs
- Assets/Scripts/Games/NightWalkAgb/AgbPlayYan.cs
- Assets/Scripts/Games/CoinToss/CoinToss.cs
- Assets/Scripts/Games/BuiltToScaleDS/Blocks.cs
- Assets/Scripts/LevelEditor/EventSelector/PropertyPrefabs/EnumPropertyPrefab.cs
- Assets/Scripts/Games/TapTrial/TapTrialPlayer.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/NumberChartPropertyPrefab.cs
- Assets/Plugins/Starpelly/Colors.cs
- Assets/Scripts/Games/AirRally/RvlBirds.cs
- Assets/Scripts/Games/GleeClub/GleeClubSingInput.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/StringChartPropertyPrefab.cs
- Assets/Scripts/Games/Splashdown/NtrSplash.cs
- Assets/Scripts/Games/WorkingDough/PlayerEnterDoughBall.cs
- Assets/Scripts/Games/FlipperFlop/FlipperFlopFlipper.cs
- Assets/Scripts/Games/NightWalkAgb/AgbNightWalk.cs
- Assets/Scripts/Games/TramAndPauline/AgbAnimalKid.cs
- Assets/Scripts/AppInfo.cs - Assets/Scripts/AppInfo.cs
- Assets/Scripts/Games/SeeSaw/SeeSaw.cs
- Assets/Scripts/Util/AnimationHelpers.cs
- Assets/Scripts/Games/TossBoys/TossBoysBall.cs
- Assets/Scripts/LevelEditor/BPMText.cs
- Assets/Scripts/Games/NightWalkAgb/AgbPlatformHandler.cs
- Assets/Scripts/Games/CheerReaders/CheerReaders.cs
- Assets/Scripts/Games/SpaceSoccer/Kicker.cs
- Assets/Scripts/InputSystem/ControllerTypes/InputMouse.cs
- Assets/Scripts/Games/DogNinja/DogNinja.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/TempoDialog.cs
- Assets/Scripts/LevelEditor/EventSelector/PropertyPrefabs/NumberPropertyPrefab.cs
- Assets/Scripts/Games/TotemClimb/TCDragon.cs
- Assets/Scripts/LevelEditor/BoxSelection.cs
- Assets/Scripts/Games/ForkLifter/ForkLifter.cs
- Assets/Scripts/Games/DrummingPractice/DrummingPractice.cs
- Assets/Scripts/LevelEditor/DisableSelectOnHover.cs
- Assets/Scripts/LevelEditor/Theme.cs
- Assets/Scripts/Games/BoardMeeting/BMExecutive.cs
- Assets/Scripts/Common/CanvasScroll.cs
- Assets/Scripts/GameCamera.cs
- Assets/Scripts/Games/TotemClimb/TCJumper.cs
- Assets/Scripts/Games/MeatGrinder/MeatGrinder.cs
- Assets/Scripts/UI/RightClickDropdownObject.cs
- Assets/Scripts/StaticCamera.cs
- Assets/Scripts/Games/Spaceball/SpaceballPlayer.cs
- Assets/Scripts/Util/SoundByte.cs
- Assets/Scripts/LevelEditor/EventSelector/PropertyPrefabs/BoolPropertyPrefab.cs
- Assets/Scripts/Games/Splashdown/NtrSynchrette.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/TimelineObjs/VolumeTimelineObj.cs
- Assets/Scripts/LevelEditor/DialogHelpers/Dialog.cs
- Assets/Scripts/UI/SettingsDialog/Tabs/EditorSettings.cs
- Assets/Scripts/StudioDance/ChoreographyInfo.cs
- Assets/Scripts/Games/CropStomp/CropStomp.cs
- Assets/Scripts/Games/Global/Flash.cs
- Assets/Scripts/LevelEditor/Commands/ICommand.cs
- Assets/Scripts/StudioDance/StudioDanceManager.cs
- Assets/Scripts/Games/SeeSaw/SeeSawGuy.cs
- Assets/Scripts/Games/DrummingPractice/Drummer.cs
- Assets/Scripts/Games/RhythmRally/Paddlers.cs
- Assets/Scripts/UI/SettingsDialog/Tabs/DispAudioSettings.cs
- Assets/Scripts/Util/SavWav.cs
- Assets/Scripts/Util/EntityTypes.cs
- Assets/Scripts/Games/PajamaParty/CtrPillowPlayer.cs
- Assets/Scripts/Games/Ringside/Ringside.cs
- Assets/Scripts/Games/LaunchParty/LaunchPartyRocket.cs
- Assets/Scripts/Games/Spaceball/Spaceball.cs
- Assets/Scripts/Games/TotemClimb/TCGroundManager.cs
- Assets/Scripts/Games/TramAndPauline/TramAndPauline.cs
- Assets/Scripts/Games/BlueBear/Treat.cs
- Assets/Scripts/PostProcessingVFX.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/SpecialTimeline.cs
- Assets/Scripts/SourceGenerators/MinigameLoaderGenerator.cs
- Assets/Scripts/InputSystem/ControllerTypes/InputKeyboard.cs
- Assets/Scripts/LevelEditor/EditorTheme.cs
- Assets/Scripts/Games/TrickClass/MobTrickObj.cs
- Assets/Scripts/Games/OctopusMachine/Octopus.cs
- Assets/Scripts/Games/WizardsWaltz/Plant.cs
- Assets/Scripts/UI/SettingsDialog/Tabs/CreditsLegalSettings.cs
- Assets/Scripts/Games/TapTroupe/TapTroupeZoomOut.cs
- Assets/Scripts/Games/MeatGrinder/Meat.cs
- Assets/Plugins/StandaloneFileBrowser/StandaloneFileBrowser.cs
- Assets/Scripts/Games/TheDazzles/TheDazzlesGirl.cs
- Assets/Scripts/LevelEditor/Commands/SpecialMarker.cs
- Assets/Scripts/UI/SettingsDialog/SettingsDialog.cs
- Assets/Scripts/UI/Overlays/ChartSectionDisplay.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/PropertyPrefabs/ColorChartPropertyPrefab.cs
- Assets/GUIWindows/Scripts/GUIWindowHandle.cs
- Assets/Scripts/Games/Kitties/Kitties.cs
- Assets/Scripts/Games/QuizShow/QSTimer.cs
- Assets/Scripts/CircleCursor.cs
- Assets/Scripts/Games/TotemClimb/TCBirdManager.cs
- Assets/Plugins/StandaloneFileBrowser/StandaloneFileBrowserLinux.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/TimelineObjs/SpecialTimelineObj.cs
- Assets/Scripts/Games/RhythmTweezers/RhythmTweezers.cs
- Assets/Scripts/Games/SamuraiSliceNtr/SamuraiSliceNtr.cs
- Assets/Scripts/LevelEditor/Timeline/TimelineBlockManager.cs
- Assets/Scripts/Games/Rockers/RockerBendInput.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/Tabs/ChartInfoProperties.cs
- Assets/Plugins/StandaloneFileBrowser/StandaloneFileBrowserEditor.cs
- Assets/Scripts/Games/Kitties/CtrTeppanPlayer.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/TimelineObjs/TempoTimelineObj.cs
- Assets/Scripts/LevelEditor/Timeline/SpecialTmeline/VolumeDialog.cs
- Assets/Scripts/Games/Splashdown/Splashdown.cs
- Assets/Plugins/StandaloneFileBrowser/StandaloneFileBrowserMac.cs
- Assets/Scripts/Games/AirRally/Cloud.cs
- Assets/Scripts/InputSystem/InputController.cs
- Assets/Scripts/UI/Overlays/SkillStarManager.cs
- Assets/Scripts/LevelEditor/BeatGrid.cs
- Assets/Scripts/Games/RhythmRally/RhythmRally.cs
- Assets/Scripts/Games/SamuraiSliceNtr/NtrSamurai.cs
- Assets/Scripts/Games/TapTroupe/TapTroupeCorner.cs
- Assets/Scripts/Games/FanClub/NtrIdolAmie.cs
- Assets/GUIWindows/Scripts/GUIWindowUtils.cs
- Assets/Scripts/Games/Tambourine/Tambourine.cs
- Assets/Scripts/Util/MultiSound.cs
- Assets/Scripts/Games/Fireworks/Rocket.cs
- Assets/Scripts/Games/RhythmSomen/RhythmSomen.cs
- Assets/Scripts/Games/Tunnel/Tunnel.cs
- Assets/Scripts/Games/ClappyTrio/ClappyTrio.cs
- Assets/Scripts/LevelEditor/RemixPropertiesDialog/RemixPropertiesDialog.cs
- Assets/Scripts/Games/TotemClimb/TotemClimb.cs
- Assets/Plugins/StandaloneFileBrowser/IStandaloneFileBrowser.cs
- Assets/Scripts/LevelEditor/Commands/CommandManager.cs
- Assets/Scripts/Games/FirstContact/Translator.cs
- Assets/Scripts/LevelEditor/TooltipGiver.cs
- Assets/Scripts/InputSystem/USG.g/InitInputControllers.PlayerInput.ControllerLoaderGenerator.g.cs
- Assets/Scripts/Games/DrummingPractice/DrummerHit.cs
- Assets/Scripts/LevelEditor/TempoFinder/TempoFinder.cs
- Assets/Scripts/LevelEditor/EventSelector/EventParameterManager.cs
- Assets/Scripts/StudioDance/Dancer.cs
- Assets/Scripts/LevelEditor/EventSelector/EventPropertyPrefab.cs
- Assets/Scripts/Games/Lockstep/Lockstep.cs
- Assets/Scripts/Games/TotemClimb/TCTotemManager.cs
- Assets/Scripts/UI/Overlays/TimingAccuracyDisplay.cs
- Assets/Scripts/Games/MunchyMonk/MunchyMonk.cs
- Assets/Scripts/Games/ForkLifter/ForkLifterPlayer.cs
- Assets/Scripts/Games/DoubleDate/SoccerBall.cs
- Assets/Scripts/Games/RhythmTweezers/Tweezers.cs
- Assets/Scripts/Games/WorkingDough/WorkingDough.cs
- Assets/Scripts/Games/TotemClimb/TCFrog.cs
- Assets/Scripts/UI/SettingsDialog/Tabs/ControllerSettings.cs
- Assets/Scripts/Games/SpaceDance/SpaceDance.cs
- Assets/Scripts/Games/MrUpbeat/UpbeatMan.cs
- Assets/Scripts/Games/OctopusMachine/OctopusMachine.cs
- Assets/Scripts/Games/TotemClimb/TCPillarManager.cs
- Assets/Scripts/Games/Fireworks/Fireworks.cs
- Assets/Scripts/Games/FlipperFlop/FlipperFlop.cs
- Assets/Scripts/Games/WizardsWaltz/Wizard.cs
- Assets/Scripts/Games/MonkeyWatch/MonkeyWatch.cs
- Assets/Scripts/Games/MonkeyWatch/WatchMonkeyHandler.cs
- Assets/Scripts/Games/MonkeyWatch/WatchBackgroundHandler.cs
- Assets/Scripts/Games/MonkeyWatch/MonkeyClockArrow.cs
- Assets/Scripts/Games/MonkeyWatch/BalloonHandler.cs
- Assets/Scripts/Games/MonkeyWatch/WatchMonkey.cs
PathsToSkipImportEvent: [] PathsToSkipImportEvent: []
PathsToIgnoreOverwriteSettingOnAttribute: [] PathsToIgnoreOverwriteSettingOnAttribute: []

BIN
output.mp4 Normal file

Binary file not shown.

BIN
temp.mp4 Normal file

Binary file not shown.

BIN
temp.wav Normal file

Binary file not shown.