Skip to content

Commit

Permalink
Better native backend for Video and Song streaming.
Browse files Browse the repository at this point in the history
  • Loading branch information
tomspilman committed Sep 29, 2024
1 parent c2e1f87 commit b975124
Show file tree
Hide file tree
Showing 13 changed files with 648 additions and 341 deletions.
2 changes: 1 addition & 1 deletion MonoGame.Framework/Media/Song.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public override bool Equals(Object obj)
/// </summary>
public TimeSpan Duration
{
get { return PlatformGetDuration(); }
get { return _duration; }
}

/// <summary>
Expand Down
7 changes: 5 additions & 2 deletions MonoGame.Framework/Platform/Native/Audio.Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public static partial void Buffer_InitializeXact(
#region Voice

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_Create", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGA_Voice* Voice_Create(MGA_System* system);
public static partial MGA_Voice* Voice_Create(MGA_System* system, int sampleRate = 0, int channels = 0);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_Destroy", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Voice_Destroy(MGA_Voice* voice);
Expand All @@ -152,7 +152,7 @@ public static partial void Buffer_InitializeXact(
public static partial void Voice_SetBuffer(MGA_Voice* voice, MGA_Buffer* buffer);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_AppendBuffer", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Voice_AppendBuffer(MGA_Voice* voice, byte[] buffer, int offset, int count, [MarshalAs(UnmanagedType.U1)] bool clear);
public static partial void Voice_AppendBuffer(MGA_Voice* voice, byte* buffer, uint size);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_Play", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Voice_Play(MGA_Voice* voice, [MarshalAs(UnmanagedType.U1)] bool looped);
Expand All @@ -169,6 +169,9 @@ public static partial void Buffer_InitializeXact(
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_GetState", StringMarshalling = StringMarshalling.Utf8)]
public static partial SoundState Voice_GetState(MGA_Voice* voice);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_GetPosition", StringMarshalling = StringMarshalling.Utf8)]
public static partial ulong Voice_GetPosition(MGA_Voice* voice);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGA_Voice_SetPan", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Voice_SetPan(MGA_Voice* voice, float pan);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed partial class DynamicSoundEffectInstance : SoundEffectInstance
{
private unsafe void PlatformCreate()
{
Voice = MGA.Voice_Create(SoundEffect.System);
Voice = MGA.Voice_Create(SoundEffect.System, _sampleRate, (int)_channels);
}

private unsafe int PlatformGetPendingBufferCount()
Expand Down Expand Up @@ -49,7 +49,10 @@ private unsafe void PlatformStop()
private unsafe void PlatformSubmitBuffer(byte[] buffer, int offset, int count)
{
if (Voice != null)
MGA.Voice_AppendBuffer(Voice, buffer, offset, count, false);
{
fixed (byte* ptr = buffer)
MGA.Voice_AppendBuffer(Voice, ptr + offset, (uint)count);
}
}

private unsafe void PlatformDispose(bool disposing)
Expand Down
153 changes: 91 additions & 62 deletions MonoGame.Framework/Platform/Native/Media.Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,93 +4,122 @@

using System;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

namespace MonoGame.Interop;


[MGHandle]
internal readonly struct MGM_Song { }
internal readonly struct MGM_AudioDecoder{ }

[MGHandle]
internal readonly struct MGM_Video { }
internal readonly struct MGM_VideoDecoder { }


internal static unsafe partial class MGM
struct MGM_AudioDecoderInfo
{
#region Song

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void NativeFinishedCallback(nint callbackData);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Create", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGM_Song* Song_Create(string mediaFilePath);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Destroy", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_Destroy(MGM_Song* song);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_GetDuration", StringMarshalling = StringMarshalling.Utf8)]
public static partial ulong Song_GetDuration(MGM_Song* song);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_GetPosition", StringMarshalling = StringMarshalling.Utf8)]
public static partial ulong Song_GetPosition(MGM_Song* song);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_GetVolume", StringMarshalling = StringMarshalling.Utf8)]
public static partial float Song_GetVolume(MGM_Song* song);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_SetVolume", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_SetVolume(MGM_Song* song, float volume);
/// <summary>
/// The audio samples per second, per channel.
/// </summary>
public int samplerate;

/// <summary>
/// The number of audio channels (typically 1 or 2).
/// </summary>
public int channels;

/// <summary>
/// The estimated duration of the audio in milliseconds.
/// </summary>
public ulong duration;
}

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Play", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_Play(MGM_Song* song, ulong startPositionMs, [MarshalAs(UnmanagedType.FunctionPtr)] NativeFinishedCallback callback, nint callbackData);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Pause", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_Pause(MGM_Song* song);
struct MGM_VideoDecoderInfo
{
/// <summary>
/// The width of the video frames.
/// </summary>
public int width;

/// <summary>
/// The height of the video frames.
/// </summary>
public int height;

/// <summary>
/// The number of frames per second.
/// </summary>
public float fps;

/// <summary>
/// The estimated duration of the video in milliseconds.
/// </summary>
public ulong duration;
}

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Resume", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_Resume(MGM_Song* song);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Song_Stop", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Song_Stop(MGM_Song* song);
internal static unsafe partial class MGM
{
#region Audio Decoder

/// <summary>
/// This returns an audio decoder that returns a stream of PCM data from an audio file.
/// </summary>
/// <param name="filepath">The absolute file path to the audio file.</param>
/// <param name="info">Returns information about the opened audio file.</param>
/// <returns>Returns the audio decoder ready to read data or null if the format is unsupported.</returns>
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_AudioDecoder_Create", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGM_AudioDecoder* AudioDecoder_Create(string filepath, out MGM_AudioDecoderInfo info);

/// <summary>
/// This releases all internal resources, closes the file, and destroys the audio decoder.
/// </summary>
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_AudioDecoder_Destroy", StringMarshalling = StringMarshalling.Utf8)]
public static partial void AudioDecoder_Destroy(MGM_AudioDecoder* decoder);

/// <summary>
/// Set the position of the audio decoder in milliseconds.
/// </summary>
/// <param name="decoder">The decoder.</param>
/// <param name="timeMS">The time in millseconds from the start of the audio file.</param>
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_AudioDecoder_SetPosition", StringMarshalling = StringMarshalling.Utf8)]
public static partial void AudioDecoder_SetPosition(MGM_AudioDecoder* decoder, ulong timeMS);

/// <summary>
/// Decode some PCM data from the audio file.
/// </summary>
/// <param name="decoder"></param>
/// <param name="buffer"></param>
/// <param name="size"></param>
/// <returns>Returns true if we've reached the end of the file.</returns>
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_AudioDecoder_Decode", StringMarshalling = StringMarshalling.Utf8)]
[return:MarshalAs(UnmanagedType.U1)]
public static partial bool AudioDecoder_Decode(MGM_AudioDecoder* decoder, out byte* buffer, out uint size);

#endregion


#region Video

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Create", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGM_Video* Video_Create(string mediaFilePath, int cachedFrameNum, out int width, out int height, out float fps, out ulong duration);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Destroy", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_Destroy(MGM_Video* video);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_GetState", StringMarshalling = StringMarshalling.Utf8)]
public static partial MediaState Video_GetState(MGM_Video* video);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_GetPosition", StringMarshalling = StringMarshalling.Utf8)]
public static partial ulong Video_GetPosition(MGM_Video* video);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_SetVolume", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_SetVolume(MGM_Video* video, float volume);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_SetLooped", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_SetLooped(MGM_Video* video, [MarshalAs(UnmanagedType.U1)] bool looped);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Play", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_Play(MGM_Video* video);
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_Create", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGM_VideoDecoder* VideoDecoder_Create(MGG_GraphicsDevice* device, string filepath, out MGM_VideoDecoderInfo info);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Pause", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_Pause(MGM_Video* video);
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_Destroy", StringMarshalling = StringMarshalling.Utf8)]
public static partial void VideoDecoder_Destroy(MGM_VideoDecoder* decoder);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Resume", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_Resume(MGM_Video* video);
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_GetAudioDecoder", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGM_AudioDecoder* VideoDecoder_GetAudioDecoder(MGM_VideoDecoder* decoder, out MGM_AudioDecoderInfo info);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_Stop", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_Stop(MGM_Video* video);
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_GetPosition", StringMarshalling = StringMarshalling.Utf8)]
public static partial ulong VideoDecoder_GetPosition(MGM_VideoDecoder* decoder);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_Video_GetFrame", StringMarshalling = StringMarshalling.Utf8)]
public static partial void Video_GetFrame(MGM_Video* video, out uint frame, out MGG_Texture* handle);
[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_SetLooped", StringMarshalling = StringMarshalling.Utf8)]
public static partial void VideoDecoder_SetLooped(MGM_VideoDecoder* decoder, [MarshalAs(UnmanagedType.U1)] bool looped);

[LibraryImport(MGP.MonoGameNativeDLL, EntryPoint = "MGM_VideoDecoder_Decode", StringMarshalling = StringMarshalling.Utf8)]
public static partial MGG_Texture* VideoDecoder_Decode(MGM_VideoDecoder* decoder);

#endregion
}
Loading

0 comments on commit b975124

Please sign in to comment.