253 lines
8.0 KiB
C#
253 lines
8.0 KiB
C#
|
|
using Godot;
|
||
|
|
using System;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
|
||
|
|
namespace VideoOrchestra.Platform
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Android VP9 decoder implementation using MediaCodec
|
||
|
|
/// </summary>
|
||
|
|
public class AndroidVP9Decoder : IVP9PlatformDecoder
|
||
|
|
{
|
||
|
|
private const int MAX_STREAMS = 3;
|
||
|
|
|
||
|
|
private ImageTexture[] _textures = new ImageTexture[MAX_STREAMS];
|
||
|
|
private bool _initialized = false;
|
||
|
|
private bool _hardwareEnabled = true;
|
||
|
|
private int _width = 0;
|
||
|
|
private int _height = 0;
|
||
|
|
private VP9DecoderStatus _status = VP9DecoderStatus.Uninitialized;
|
||
|
|
|
||
|
|
public string PlatformName => "Android";
|
||
|
|
public bool IsHardwareDecodingSupported => CheckMediaCodecSupport();
|
||
|
|
|
||
|
|
#region Native Library P/Invoke Declarations
|
||
|
|
|
||
|
|
[DllImport("libvp9orchestra")]
|
||
|
|
private static extern bool vp9_initialize(int width, int height);
|
||
|
|
|
||
|
|
[DllImport("libvp9orchestra")]
|
||
|
|
private static extern bool vp9_decode_frame(byte[] data, int dataSize, int streamId);
|
||
|
|
|
||
|
|
[DllImport("libvp9orchestra")]
|
||
|
|
private static extern uint vp9_get_texture_id(int streamId);
|
||
|
|
|
||
|
|
[DllImport("libvp9orchestra")]
|
||
|
|
private static extern bool vp9_is_hardware_supported();
|
||
|
|
|
||
|
|
[DllImport("libvp9orchestra")]
|
||
|
|
private static extern void vp9_release();
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
public AndroidVP9Decoder()
|
||
|
|
{
|
||
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
||
|
|
{
|
||
|
|
_textures[i] = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool CheckMediaCodecSupport()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
// Check if native library is available and supports hardware decoding
|
||
|
|
return vp9_is_hardware_supported();
|
||
|
|
}
|
||
|
|
catch (DllNotFoundException)
|
||
|
|
{
|
||
|
|
GD.Print("Warning: libvp9orchestra.so not found, hardware decoding not available");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.Print($"Warning: Error checking MediaCodec support: {ex.Message}");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Initialize(int width, int height, bool enableHardware = true)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
_width = width;
|
||
|
|
_height = height;
|
||
|
|
_hardwareEnabled = enableHardware && IsHardwareDecodingSupported;
|
||
|
|
|
||
|
|
// Initialize native MediaCodec decoder
|
||
|
|
bool success = vp9_initialize(width, height);
|
||
|
|
|
||
|
|
if (success)
|
||
|
|
{
|
||
|
|
// Initialize Godot textures
|
||
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
||
|
|
{
|
||
|
|
_textures[i] = new ImageTexture();
|
||
|
|
}
|
||
|
|
|
||
|
|
_initialized = true;
|
||
|
|
_status = VP9DecoderStatus.Initialized;
|
||
|
|
|
||
|
|
GD.Print($"Android VP9 decoder initialized: {width}x{height}, Hardware: {_hardwareEnabled}");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_status = VP9DecoderStatus.Error;
|
||
|
|
GD.PrintErr("Failed to initialize Android MediaCodec VP9 decoder");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (DllNotFoundException)
|
||
|
|
{
|
||
|
|
GD.PrintErr("libvp9orchestra.so not found. Make sure the native library is included in the Android build.");
|
||
|
|
_status = VP9DecoderStatus.Error;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.PrintErr($"Error initializing Android VP9 decoder: {ex.Message}");
|
||
|
|
_status = VP9DecoderStatus.Error;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool DecodeFrame(byte[] frameData, int streamId)
|
||
|
|
{
|
||
|
|
if (!_initialized || streamId < 0 || streamId >= MAX_STREAMS)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (frameData == null || frameData.Length == 0)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
_status = VP9DecoderStatus.Decoding;
|
||
|
|
|
||
|
|
bool success = vp9_decode_frame(frameData, frameData.Length, streamId);
|
||
|
|
|
||
|
|
if (success)
|
||
|
|
{
|
||
|
|
// Update Godot texture with decoded frame
|
||
|
|
UpdateGodotTexture(streamId);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_status = VP9DecoderStatus.Error;
|
||
|
|
GD.PrintErr($"Failed to decode frame for stream {streamId}");
|
||
|
|
}
|
||
|
|
|
||
|
|
return success;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.PrintErr($"Error decoding frame for stream {streamId}: {ex.Message}");
|
||
|
|
_status = VP9DecoderStatus.Error;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateGodotTexture(int streamId)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
uint textureId = vp9_get_texture_id(streamId);
|
||
|
|
|
||
|
|
if (textureId > 0)
|
||
|
|
{
|
||
|
|
// In a full implementation, this would bind the OpenGL texture
|
||
|
|
// to the Godot ImageTexture. For now, create a placeholder.
|
||
|
|
|
||
|
|
var image = Image.CreateEmpty(_width, _height, false, Image.Format.Rgba8);
|
||
|
|
|
||
|
|
// Different color per stream for testing
|
||
|
|
var color = streamId switch
|
||
|
|
{
|
||
|
|
0 => Colors.Red,
|
||
|
|
1 => Colors.Green,
|
||
|
|
2 => Colors.Blue,
|
||
|
|
_ => Colors.White
|
||
|
|
};
|
||
|
|
|
||
|
|
image.Fill(color);
|
||
|
|
_textures[streamId].SetImage(image);
|
||
|
|
|
||
|
|
GD.Print($"Updated texture for Android stream {streamId}, native texture ID: {textureId}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.PrintErr($"Failed to update texture for stream {streamId}: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public ImageTexture GetDecodedTexture(int streamId)
|
||
|
|
{
|
||
|
|
if (!_initialized || streamId < 0 || streamId >= MAX_STREAMS)
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return _textures[streamId];
|
||
|
|
}
|
||
|
|
|
||
|
|
public uint GetNativeTextureId(int streamId)
|
||
|
|
{
|
||
|
|
if (!_initialized || streamId < 0 || streamId >= MAX_STREAMS)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return vp9_get_texture_id(streamId);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.PrintErr($"Error getting texture ID for stream {streamId}: {ex.Message}");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public VP9DecoderStatus GetStatus()
|
||
|
|
{
|
||
|
|
return _status;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Release()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
_status = VP9DecoderStatus.Released;
|
||
|
|
|
||
|
|
if (_initialized)
|
||
|
|
{
|
||
|
|
vp9_release();
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < MAX_STREAMS; i++)
|
||
|
|
{
|
||
|
|
_textures[i]?.Dispose();
|
||
|
|
_textures[i] = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
_initialized = false;
|
||
|
|
GD.Print("Android VP9 decoder released");
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
GD.PrintErr($"Error releasing Android VP9 decoder: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
Release();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|