Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
34136ae
added linux build tools to project
ericjohnson97 Jun 1, 2024
1f562cc
added cesium-unity as a submodule
ericjohnson97 Jun 1, 2024
3b4c33b
made submodule relative
ericjohnson97 Jun 1, 2024
082b42c
added logger to generate text log relative to project
ericjohnson97 Jun 1, 2024
42186c2
updated submodule
ericjohnson97 Jun 2, 2024
64cdd7c
fixed bug with jsb interface
ejohnson-darkhive Jul 8, 2024
2b1a3d2
merged main to linux-support
ejohnson-darkhive Jul 8, 2024
edd8292
updated ffmpeg to support streaming on linux
ejohnson-darkhive Jul 8, 2024
ffd222f
added MJPEG stream
ejohnson-darkhive Jul 8, 2024
1718463
added script to show FPS in overlay
ejohnson-darkhive Jul 16, 2024
0d03147
fixed jsbsim interpolation bug
ejohnson-darkhive Jul 16, 2024
8c5491d
general streaming improvements
ejohnson-darkhive Jul 16, 2024
0d68162
implemented god mode
ejohnson-darkhive Jul 19, 2024
c9b7884
create dummy pipeline
Jul 20, 2024
220c85f
improved follow camera
ejohnson-darkhive Jul 31, 2024
a41c4af
added ability to load asset bundles and spawn them in the scene
ejohnson-darkhive Jul 31, 2024
7fe0baa
added ability to stream greyscale
ejohnson-darkhive Jul 31, 2024
bb9cd3a
add support for clipping game objects to terrain
ejohnson-darkhive Aug 1, 2024
bbd2062
fixed issue where game doesn't load if AssetBundles are not found
ejohnson-darkhive Aug 1, 2024
6974ec9
fixed IG always stealing mouse
ejohnson-darkhive Aug 1, 2024
1a73a73
updated project settings
ejohnson-darkhive Aug 1, 2024
5b77457
updated git ignore
ejohnson-darkhive Aug 1, 2024
c7fbcab
Merge branch 'develop' of gitlab.com:darkhiveai/darkhive-private/simu…
ejohnson-darkhive Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
/[Bb]uild/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/

**/AssetBundles
ig/
.vscode
*.blob
# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/
Expand Down
9 changes: 9 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
image: ${NEXUS_IMAGES_PRIVATE_URL}/devops-tools-base:latest

stages:
- success

substitute:
stage: success
script:
- echo "dummy pipeline only needed for repos that have no pipeline configured. this is used to trigger a successful pipeline and meet the requirements to merge"
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "Packages/com.cesium.unity"]
path = Packages/com.cesium.unity
url = ../cesium-unity.git
40 changes: 34 additions & 6 deletions Assets/FFmpegOut/Runtime/CameraCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using UnityEngine;
using System.Collections;
using System.Threading.Tasks;

namespace FFmpegOut
{
Expand Down Expand Up @@ -61,7 +62,7 @@ int GetAntiAliasingLevel(Camera camera)

#region Public members

protected virtual FFmpegSession GetSession( int texWidth, int texHeight )
protected virtual FFmpegSession GetSession(int texWidth, int texHeight)
{
return FFmpegSession.Create(
gameObject.name,
Expand All @@ -88,7 +89,7 @@ void WarnFrameDrop()
if (++_frameDropCount != 10) return;

Debug.LogWarning(
"Significant frame droppping was detected. This may introduce " +
"Significant frame dropping was detected. This may introduce " +
"time instability into output video. Decreasing the recording " +
"frame rate is recommended."
);
Expand Down Expand Up @@ -133,10 +134,35 @@ void OnDisable()
IEnumerator Start()
{
// Sync with FFmpeg pipe thread at the end of every frame.
for (var eof = new WaitForEndOfFrame();;)
for (var eof = new WaitForEndOfFrame(); ;)
{
yield return eof;
_session?.CompletePushFrames();
if (_session != null)
{
// Asynchronously complete push frames
yield return CompletePushFramesCoroutine();
}
}
}

IEnumerator CompletePushFramesCoroutine()
{
var task = CompletePushFramesAsync();
while (!task.IsCompleted)
{
yield return null;
}
if (task.Exception != null)
{
Debug.LogError(task.Exception);
}
}

async Task CompletePushFramesAsync()
{
if (_session != null)
{
await _session.CompletePushFramesAsync();
}
}

Expand Down Expand Up @@ -190,12 +216,14 @@ void Update()
// 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
// TODO: clean this up
// _session.PushFrame(camera.targetTexture); removing I would rather drop a frame than have the same frame show twice
_session.PushFrame(camera.targetTexture);
_session.PushFrame(camera.targetTexture);
_frameCount += 2;
_frameCount++;
}
else
{
Debug.Log("Dropping Frame");
// Show a warning message about the situation.
WarnFrameDrop();

Expand Down
8 changes: 6 additions & 2 deletions Assets/FFmpegOut/Runtime/FFmpegPreset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace FFmpegOut
{
public enum FFmpegPreset
{
MJPEG,
H264Default,
H264Nvidia,
H264Lossless420,
Expand All @@ -26,6 +27,7 @@ public static string GetDisplayName(this FFmpegPreset preset)
{
switch (preset)
{
case FFmpegPreset.MJPEG: return "MJPEG";
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)";
Expand All @@ -47,6 +49,7 @@ public static string GetSuffix(this FFmpegPreset preset)
{
switch (preset)
{
case FFmpegPreset.MJPEG:
case FFmpegPreset.H264Default:
case FFmpegPreset.H264Nvidia:
case FFmpegPreset.H264Lossless420:
Expand All @@ -69,8 +72,9 @@ public static string GetOptions(this FFmpegPreset preset)
switch (preset)
{
// case FFmpegPreset.H264Default: return "-pix_fmt yuv420p";
case FFmpegPreset.H264Default: return "-c:v libx264 -preset fast -tune zerolatency -b:v 10M";
case FFmpegPreset.H264Nvidia: return "-c:v h264_nvenc -preset fast -b:v 10M";
case FFmpegPreset.MJPEG: return "-flush_packets 0 -max_delay 100000";
case FFmpegPreset.H264Default: return "-c:v libx264 -preset fast -b:v 10M";
case FFmpegPreset.H264Nvidia: return "-c:v h264_nvenc -preset fast -b:v 10M";
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";
Expand Down
7 changes: 6 additions & 1 deletion Assets/FFmpegOut/Runtime/FFmpegSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace FFmpegOut
{
Expand Down Expand Up @@ -39,7 +40,6 @@ FFmpegPreset preset
+ " -b:v 2M" // Bitrate
+ " " + outputPath
);

}

public static FFmpegSession CreateWithArguments(string arguments)
Expand All @@ -65,6 +65,11 @@ public void CompletePushFrames()
_pipe?.SyncFrameData();
}

public async Task CompletePushFramesAsync()
{
await Task.Run(() => CompletePushFrames());
}

public void Close()
{
if (_pipe != null)
Expand Down
5 changes: 3 additions & 2 deletions Assets/FFmpegOut/Runtime/LiveStream/StreamCameraCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ namespace FFmpegOut.LiveStream
{
public class StreamCameraCapture : CameraCapture
{
[SerializeField] protected StreamPreset _streamPreset;
[SerializeField] public StreamPreset _streamPreset;
[SerializeField] public string streamAddress;
[SerializeField] public bool isGreyScale;

protected override FFmpegSession GetSession(int texWidth, int texHeight)
{
Expand All @@ -15,7 +16,7 @@ protected override FFmpegSession GetSession(int texWidth, int texHeight)
frameRate,
preset,
_streamPreset,
streamAddress);
streamAddress, isGreyScale);
}
}
}
101 changes: 71 additions & 30 deletions Assets/FFmpegOut/Runtime/LiveStream/StreamFFmpegSession.cs
Original file line number Diff line number Diff line change
@@ -1,51 +1,92 @@
using System;
using System;
using System.Threading;
using System.Diagnostics;
using UnityEngine;

namespace FFmpegOut.LiveStream
{
/// <summary>
/// Represents a session for streaming video from Unity to an RTP endpoint using FFmpeg.
/// </summary>
public sealed class StreamFFmpegSession : FFmpegSession
{
// Defines the format and codec for the raw video data from Unity.
private Thread ffmpegThread;
private Process ffmpegProcess;
private string ffmpegArguments;

private const string UNITY_CAM_TEX_BYTE_FORMAT =
"-pixel_format rgba -colorspace bt709 -f rawvideo -vcodec rawvideo";
// private const string UNITY_CAM_TEX_BYTE_FORMAT =
// "-pixel_format rgba";

// Sets the FFmpeg logging level to warning.
private const string FFMPEG_LOGLEVEL = "-loglevel warning";

// Private constructor to enforce the use of the static Create method.
private StreamFFmpegSession(string arguments) : base(arguments) { }

/// <summary>
/// Creates a new FFmpeg session for streaming.
/// </summary>
/// <param name="width">The width of the video.</param>
/// <param name="height">The height of the video.</param>
/// <param name="frameRate">The frame rate of the video.</param>
/// <param name="encodingPreset">The encoding preset to use.</param>
/// <param name="streamPreset">The streaming preset to use.</param>
/// <param name="address">The address to stream to.</param>
/// <returns>A new instance of StreamFFmpegSession.</returns>
private StreamFFmpegSession(string arguments) : base(arguments)
{
this.ffmpegArguments = arguments;
}

public static StreamFFmpegSession Create(
int width, int height, float frameRate,
FFmpegPreset encodingPreset, StreamPreset streamPreset,
string address)
string address, bool isGreyScale)
{
// Constructs the FFmpeg command-line arguments for streaming.
string colorConversion = "";
if (isGreyScale)
{
colorConversion = "-vf format=gray -pix_fmt gray ";
}

string ffmpegArguments =
$"{UNITY_CAM_TEX_BYTE_FORMAT} {FFMPEG_LOGLEVEL} -framerate {frameRate} -video_size {width}x{height} "
+ $"-re -i pipe:0 {encodingPreset.GetOptions()} "
+ $"-re -i pipe:0 {encodingPreset.GetOptions()} {colorConversion}"
+ $"{streamPreset.GetOptions()} {address}";
UnityEngine.Debug.Log($"FFmpeg Arguments: {ffmpegArguments}");
return new StreamFFmpegSession(ffmpegArguments);
}

// Logs the constructed FFmpeg arguments for debugging purposes.
Debug.Log($"FFmpeg Arguments: {ffmpegArguments}");
public void Start()
{
if (ffmpegThread == null || !ffmpegThread.IsAlive)
{
ffmpegThread = new Thread(ExecuteFFmpeg);
ffmpegThread.IsBackground = true;
ffmpegThread.Start();
}
}

// Returns a new FFmpeg session with the constructed arguments.
return new StreamFFmpegSession(ffmpegArguments);
private void ExecuteFFmpeg()
{
ProcessStartInfo startInfo = new ProcessStartInfo("ffmpeg", ffmpegArguments)
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};

ffmpegProcess = new Process { StartInfo = startInfo };
ffmpegProcess.Start();

ffmpegProcess.BeginOutputReadLine();
ffmpegProcess.BeginErrorReadLine();
ffmpegProcess.OutputDataReceived += (sender, args) => UnityEngine.Debug.Log("FFmpeg output: " + args.Data);
ffmpegProcess.ErrorDataReceived += (sender, args) => UnityEngine.Debug.Log("FFmpeg error: " + args.Data);
}

public void Stop()
{
if (ffmpegProcess != null)
{
if (!ffmpegProcess.HasExited)
{
ffmpegProcess.Kill();
}
ffmpegProcess.Dispose();
}
if (ffmpegThread != null)
{
if (ffmpegThread.IsAlive)
{
ffmpegThread.Join(); // Wait for the thread to finish
}
ffmpegThread = null;
}
}
}
}
}
3 changes: 3 additions & 0 deletions Assets/FFmpegOut/Runtime/LiveStream/StreamPreset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public enum StreamPreset
{
Udp,
UdpMJPEG,
Rtp,
Rtsp,
Hls,
Expand All @@ -18,6 +19,8 @@ public static string GetOptions(this StreamPreset preset)
{
case StreamPreset.Udp:
return "-f rtp";
case StreamPreset.UdpMJPEG:
return "-vcodec mjpeg -q:v 2 -f mjpeg";
case StreamPreset.Rtp:
return "-f rtp_mpegts";
case StreamPreset.Rtsp:
Expand Down
Loading