HeavenStudio/Assets/FFmpegOut/Runtime/CameraCapture.cs
colorplease 404ba1d981 release v0.0.1
uh it works guys!
2024-01-31 19:43:04 -08:00

201 lines
5.6 KiB
C#

// 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
}
}