using Godot; using System; using System.Runtime.InteropServices; namespace VideoOrchestra.Platform { /// /// Android VP9 decoder implementation using MediaCodec /// 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 void UpdateTextures() { } 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(); } } }