Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 42 additions & 7 deletions Runtime/Scripts/AudioStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,57 @@ private void OnAudioStreamEvent(AudioStreamEvent e)
if (e.MessageCase != AudioStreamEvent.MessageOneofCase.FrameReceived)
return;

var frame = new AudioFrame(e.FrameReceived.Frame);

lock (_lock)
{
if (_numChannels == 0)
return;

unsafe
{
var uFrame = _resampler.RemixAndResample(frame, _numChannels, _sampleRate);
if (uFrame != null)
// Change deal with issue in newer LiveKit where channels are returned incorrectly
// -https://github.com/livekit/client-sdk-unity/issues/169
// (plus some new changes to reduce garbage creation)
if (e.FrameReceived.Frame.Info.NumChannels == 1 && _numChannels == 2)
{
var data = new Span<byte>(uFrame.Data.ToPointer(), uFrame.Length);
_buffer?.Write(data);
}

int samplesPerChannel = (int)e.FrameReceived.Frame.Info.SamplesPerChannel;
int monoLengthBytes =
(int)(e.FrameReceived.Frame.Info.SamplesPerChannel *
e.FrameReceived.Frame.Info.NumChannels *
sizeof(short));

// Span over the incoming mono audio bytes
var monoByteSpan = new ReadOnlySpan<byte>(
(void*)e.FrameReceived.Frame.Info.DataPtr,
monoLengthBytes);

// Treat them as 16-bit samples
var monoSamples = MemoryMarshal.Cast<byte, short>(monoByteSpan);

// Allocate stereo buffer on the stack: 2 channels * samples * sizeof(short)
Span<short> stereoSamples = stackalloc short[samplesPerChannel * 2];

for (int i = 0; i < samplesPerChannel; i++)
{
short sample = monoSamples[i]; // mono
int dstIndex = i * 2;

stereoSamples[dstIndex] = sample; // Left
stereoSamples[dstIndex + 1] = sample; // Right
}

// Cast the stereo short span to bytes and write to the buffer
_buffer?.Write(MemoryMarshal.AsBytes(stereoSamples));
}
else
{
//TODO add support here if they fix the above bug
}
}
}
// Change - need to drop handle here because it would normally be done
// within AudioFrame, without memory will be leaked on the plugin side
NativeMethods.FfiDropHandle((IntPtr)e.FrameReceived.Frame.Handle.Id);
}

public void Dispose()
Expand All @@ -126,8 +159,10 @@ private void Dispose(bool disposing)
{
if (!_disposed && disposing)
{
FfiClient.Instance.AudioStreamEventReceived -= OnAudioStreamEvent;
_audioSource.Stop();
UnityEngine.Object.Destroy(_audioSource.GetComponent<AudioProbe>());
_buffer?.Dispose();
}
_disposed = true;
}
Expand Down
17 changes: 13 additions & 4 deletions Runtime/Scripts/Internal/FFIClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public void Release(FfiResponse response)
ffiResponsePool.Release(response);
}

public FfiResponse SendRequest(FfiRequest request)
public FfiResponse SendRequest(FfiRequest request, bool requiresResponse = true)
{
try
{
Expand All @@ -203,9 +203,18 @@ public FfiResponse SendRequest(FfiRequest request)
out UIntPtr dataLen
);
var dataSpan = new Span<byte>(dataPtr, (int)dataLen.ToUInt64());
var response = responseParser.ParseFrom(dataSpan)!;
NativeMethods.FfiDropHandle(handle);
return response;

if (requiresResponse)
{
var response = responseParser.ParseFrom(dataSpan)!;
NativeMethods.FfiDropHandle(handle);
return response;
}
else
{
NativeMethods.FfiDropHandle(handle);
return null;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Scripts/Internal/FFIClients/IFFIClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace LiveKit.Internal.FFIClients
/// </summary>
public interface IFFIClient : IDisposable
{
FfiResponse SendRequest(FfiRequest request);
FfiResponse SendRequest(FfiRequest request, bool requireResponse = true);

void Release(FfiResponse response);
}
Expand Down
4 changes: 2 additions & 2 deletions Runtime/Scripts/Internal/FFIClients/Requests/FFIBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class FFIBridge : IFFIBridge

public static FFIBridge Instance => instance.Value;

private readonly IFFIClient ffiClient;
private readonly IMultiPool multiPool;
public readonly IFFIClient ffiClient;
public readonly IMultiPool multiPool;

public FFIBridge(IFFIClient client, IMultiPool multiPool)
{
Expand Down
34 changes: 26 additions & 8 deletions Runtime/Scripts/RtcAudioSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,33 @@ static short FloatToS16(float v)
for (int i = 0; i < data.Length; i++)
_frameData[i] = FloatToS16(data[i]);

//Change - hand to make FFIBridge properties public to do this which isn't great
// Capture the frame.
using var request = FFIBridge.Instance.NewRequest<CaptureAudioFrameRequest>();
using var audioFrameBufferInfo = request.TempResource<AudioFrameBufferInfo>();
CaptureAudioFrameRequest pushFrame = FFIBridge.Instance.multiPool.Get<CaptureAudioFrameRequest>();
AudioFrameBufferInfo audioFrameBufferInfo = FFIBridge.Instance.multiPool.Get<AudioFrameBufferInfo>();

FfiRequest ffiRequest = FFIBridge.Instance.multiPool.Get<FfiRequest>();

var pushFrame = request.request;
pushFrame.SourceHandle = (ulong)Handle.DangerousGetHandle();
pushFrame.Buffer = audioFrameBufferInfo;
unsafe
{
pushFrame.Buffer.DataPtr = (ulong)NativeArrayUnsafeUtility
.GetUnsafePtr(_frameData);
pushFrame.Buffer.DataPtr = (ulong)NativeArrayUnsafeUtility
.GetUnsafePtr(_frameData);
}
pushFrame.Buffer.NumChannels = (uint)channels;
pushFrame.Buffer.SampleRate = (uint)sampleRate;
pushFrame.Buffer.SamplesPerChannel = (uint)data.Length / (uint)channels;

using var response = request.Send();
FfiResponse res = response;

// Wait for async callback, log an error if the capture fails.
ffiRequest.CaptureAudioFrame = pushFrame;
FFIBridge.Instance.ffiClient.SendRequest(ffiRequest, false);


// Changes - this was creating memory because of the callback being created,
// might be able to make a call back and cache the asyncID to get around that problem
// TODO: Investigate further and optimize if needed
/*
var asyncId = res.CaptureAudioFrame.AsyncId;
void Callback(CaptureAudioFrameCallback callback)
{
Expand All @@ -155,6 +162,17 @@ void Callback(CaptureAudioFrameCallback callback)
FfiClient.Instance.CaptureAudioFrameReceived -= Callback;
}
FfiClient.Instance.CaptureAudioFrameReceived += Callback;
*/

ffiRequest.CaptureAudioFrame = null;
pushFrame.Buffer.ClearDataPtr();
pushFrame.Buffer = null;
ffiRequest.ClearMessage();


FFIBridge.Instance.multiPool.Release(ffiRequest);
FFIBridge.Instance.multiPool.Release(pushFrame);
FFIBridge.Instance.multiPool.Release(audioFrameBufferInfo);
}

/// <summary>
Expand Down
Loading