Files
video-orchestra/godot-project/scripts/Platform/Android/AndroidVP9Decoder.cs
2025-09-15 00:17:01 +09:00

255 lines
8.1 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 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();
}
}
}