Avalonia 영상재생 플레이어를 만들어봤습니다

Avalonia 처음 써봤는데 힘드네요. WPF랑 비슷하다고 생각했는데 구조가 꽤 많이 다른거같습니다.
자잘한 버그성 문제들이 있는거같고 윈도우랑 리눅스가 약간의 차이를 보이긴합니다.

구현 참고 : 주관적 판단을 배제하라 - 매일 전진하자: Rendering Video in Avalonia's NativeControlHost with SDL2.

7 Likes

간단한 샘플을 봤을땐 비슷하다 생각했었는데…
막상 들어가보려고하니…이것저것 공부해둬야할게 많더라구요~

3 Likes

"자잘한 버그성 문제들이"라고 하셨는데 구체적으로 말씀해주실 수 있나요? Avalonia 를 좀 깊이 있게 공부하고 있어서 살펴보고 싶습니다. 다른 분들도 Avalonia 가 버그가 있다고 말씀하시는데, 저는 아직 발견을 못해서 궁금합니다.

3 Likes

내용이 좀 복잡한데 최대한 간단하게 설명하자면

    public class NativeEmbeddingControl : NativeControlHost
    {
        public IntPtr Handle { get; private set; }

        protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
        {
            var handle = base.CreateNativeControlCore(parent);
            Handle = handle.Handle;
            Console.WriteLine($"Handle : {Handle}");
            return handle;
        }
}

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:nec="clr-namespace:AvaloniaDXPlayer"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
        x:Class="AvaloniaDXPlayer.MainWindow"
        Width="800" Height="600" Title="AvaloniaDXPlayer">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*"/>
      <RowDefinition Height="100"/>
    </Grid.RowDefinitions>
    <Grid Grid.Row="0" Background="Black">
      <TextBlock Text="테스트입니다" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" />
      <nec:NativeEmbeddingControl x:Name="host" Grid.Row="0" IsVisible="True" SizeChanged="Host_SizeChanged" />
    </Grid>
    <Canvas Grid.Row="1">
      <TextBox x:Name="textURL" Margin="10" Width="300" Height="20" FontSize="14" Text="rtsp://admin:!QAZants@192.168.140.46/0"/>
      <Button x:Name="btnConnect" Content="Connect" Margin="320,10,0,0" />
      <Button x:Name="btnClose" Content="Close" Margin="400,10,0,0" />
      <Button x:Name="btnTest" Content="Test" Margin="460,10,0,0" />
    </Canvas>
  </Grid>
</Window>

이렇게 해서 NativeEmbeddingControl 을 MainWindow 에 삽입했습니다.
이후에 host 에 SDL2 로 렌더링을 하기위해 NativeEmbeddingControl의 Handle 을 사용했습니다.
아래는 SDL2 를 사용하는 c++ 코드입니다.

m_pWindow = SDL_CreateWindowFrom(hwnd); // hwnd == host.Handle(NativeEmbeddingControl.Handle)

m_pWindow 는 SDL_Window* 타입입니다.
이때 렌더링 하기전 m_pWindow의 크기를 얻어오기 위해
SDL_GetWindowSize(m_pWindow, &targetWidth, &targetHeight);
하는데 targetWidth/targetHeight 값이 m_pWindow 즉, host 의 크기여야 하는데 유저가 수동으로 화면을 변경시키기 전까지 엉뚱한 값이 들어옵니다.

똑같은 코드를 윈도우에서 실행하면 잘되는데 리눅스에서는 크기값을 제대로 못 얻어오는 문제가 있습니다.
그리고 host 의 크기를 변경시키면 이전 마지막 변경값이 적용되는 문제도 있는거같습니다.(역시 윈도우에서는 괜찮습니다)

SDL2 버전 문제인가 싶어 최신 SDL2 를 적용해봤는데 같은 현상이네요.

2 Likes

이건 Avalonia 의 버그로 보기 힘들 거 같은데…
일단 제 생각은, CreateNativeControlCore 의 호출 시점 즉, handle 을 넘겨줘서 SDL2 에서 사용하는 시점의 차이에서 발생하는 문제점일 거 같습니다.

즉, 윈도우에서는 초기화가 먼저 일어나서 handle 이 안정적으로 가져올 수 있었고 그 이후에 SDL2 에서 사용하니 문제가 없었지만, 리눅스에서는 그렇게 되지 않는 문제가 발생한 것이라 생각이 듭니다.

이건 사용자 이벤트를 하나더 만들어서, 처리하면 어떨까 생각이 듭니다.

public event EventHandler HandleCreated;

protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
    var handle = base.CreateNativeControlCore(parent);
    Handle = handle.Handle;
    **HandleCreated?.Invoke(this, EventArgs.Empty);**
    return handle;
}

nativeEmbeddingControl.HandleCreated += (sender, args) =>
{
** // Handle이 할당된 후에 실행할 로직**
};

2 Likes