2024년 7월 11일 목요일

SDL2 리눅스 빌드

 ./configure --prefix=/oem/app/kimdh/DXMediaPlayer/build/SDL2-2.28.5/
make
make install

export SDL2_CONFIG=/oem/app/kimdh/DXMediaPlayer/build/SDL2-2.28.5/bin/sdl2-config

./configure --prefix=/oem/app/kimdh/DXMediaPlayer/build/SDL2_image-2.6.3/
make
make install

./configure --prefix=/oem/app/kimdh/DXMediaPlayer/build/SDL2_ttf-2.20.2/
make
make install

2024년 3월 26일 화요일

Avalonia Weston 지원 확인

X11 이 아닌 Weston(Wayland reference compositor) 에서 Avalonia 로 빌드한 프로그램이 실행가능함을 확인하였다. 

실행환경 : i.MX8 시리즈 CPU, Debian 11, Weston

sudo apt install xwayland => 반드시 설치
export DISPLAY=:0 => 환경변수에 추가








2024년 2월 28일 수요일

C++ 우측값 참조와 std::move 의 실제 동작 원리

 우측값 참조에 대해 검색해보면 보통 아래와 같이 설명한다.

1. 좌측값/우측값에 대한 설명
2. 좌측값 참조/우측값 참조에 대한 설명
3. std::move 를 통한 자원 이동에 대한 설명

설명을 읽어보고 대략적인 의미와 적용방법은 알 수 있었으나 실체적인 동작원리를 알 수 없었다.
예를 들어 std::move 를 사용하면 해당 변수를 좌측값->우측값 으로 변환한다 고 설명하는데 구체적인 설명은 보통 없다.

먼저 우측값 참조를 간단하게 설명하자면,

int a = 10;
int b = 20;
int&& c = a + b;

위와같은 코드가 있을때 마지막 줄에서 메모리 상에 어떤 일이 발생하는가하면
CPU가 a+b(30)를 계산한 후 해당 값을 임시 메모리 변수에 저장을 하게되고 우측값 참조변수 c가 이 메모리를 참조하게된다.
int c = a + b; 였으면 컴파일러에 따라 생성되지않거나 혹은 바로 사라질 임시 변수(우측값)를 해제하지 않고 c가 참조하게 함으로써 메모리에서 해제를 하지않게된다.
사실 int같은 기본타입에서는 별다른 사용 용도가 없으며 실제로는 클래스(구조체)에서 사용된다.

먼저 std::move 함수를 보자.
std::move(변수) 는 static_cast<변수타입&&>(변수) 와 같은 의미이다. 그러니까 그냥 우측값 참조형으로 타입 캐스팅을 하는것이지
실제로 메모리 상에 뭐가 이동하고 어쩌고 그런것과 아무 상관이 없다.

그리고 우측값 참조는 int 같은 기본형에서는 메모리 이동의 효과가 전혀 없다.(당연하게도)
아래 코드와 같이 클래스(구조체)인 경우에 우측값 참조를 받는 이동 생성자/이동 대입 연산자에 우측값 참조를 받아서
깊은 복사없이 포인터만 변경하게 해주는 것으로 그 용도가 전부이다.

 class MoveTest  
 {  
 private:  
   char* str = nullptr;  
   
 public:  
   MoveTest()  
   {  
     str = nullptr;  
   }  
   
   MoveTest(const char* str1)  
   {  
     int len = strlen(str1);  
     str = new char[len + 1];  
     strcpy_s(str, len+1, str1);  
   }  
   
   MoveTest(const MoveTest& lhs)  
   {  
     int len = strlen(lhs.str);  
     str = new char[len + 1];  
     strcpy_s(str, len+1, lhs.str);  
   }  
   
   // 이동 생성자  
   MoveTest(MoveTest&& rhs) noexcept : str(rhs.str)  
   {  
     rhs.str = nullptr;  
   }  
   
   MoveTest& operator=(const MoveTest& lhs)  
   {  
     int len = strlen(lhs.str);  
     str = new char[len + 1];  
     strcpy_s(str, len + 1, lhs.str);  
     return *this;  
   }  
   
   // 이동 대입 연산자  
   MoveTest& operator=(MoveTest&& rhs) noexcept  
   {  
     str = rhs.str;  
     rhs.str = nullptr;  
     return *this;  
   }  
   
   ~MoveTest()  
   {  
     delete[] str;  
   }  
 };  
위 코드에서 

MoveTest m1("abc");
MoveTest m2(std::move(m1)); // 이동 생성자 호출, m1->m2 로 이동
// MoveTest m2(static_cast<MoveTest&&>(m1)); 와 같음

MoveTest m3;
m3 = std::move(m2); // 이동 대입 연산자 호출, m2->m3 로 이동
// m3 = static_cast<MoveTest&&>(m2); 와 같음

와 같이 해당 이동 생성자/이동 대입 연산자를 호출하게되고 호출을 받은쪽은 포인터 이동을 하면 되는것이다.

그러면 왜 하필 굳이 std::move 를 통해 우측값 참조 타입으로 이 기능을 구현하는가?
그냥 위 코드에서 참조(const MoveTest& lhs)를 받는 복사 생성자와 대입 연산자에서도 처리가 가능할텐데 말이다.
몇 가지 이유가 있지만 제일 큰 이유는 자원이 이동한다는 것을 명시적으로 표현함으로써 표준을 정하고 혼란을 없애기 위함이다.
일반 복사 생성자/대입 연산자에서 이것을 처리하게 되면 실제 깊은 복사가 일어나야 하는 것인지 자원이동만 일어나야하는지 구분이 어렵기 때문이다.

2024년 2월 5일 월요일

윈도우 x265 빌드

* nasm 설치
nasm 설치 후 환경변수에 경로 추가 (https://www.nasm.us/) (구글에서 nasm download 검색)

* cmake 설치
구글에서 windows cmake 검색(https://cmake.org/download/)

* x265 소스 다운로드
git clone https://bitbucket.org/multicoreware/x265_git.git

* 32 비트 빌드
Visual Studio 메뉴에서 x86 Native Tools Command Prompt for VS 2022 실행

x265_git\build\vc15-x86 에서 아래실행
cmake -G "Visual Studio 17 2022" -T v143 -A Win32 ..\..\source && cmake-gui ..\..\source

cmake-gui 뜨면 아래 Configure 클릭

x265.sln VS2022 로 열어서 빌드하면 x265_git\build\vc15-x86\Release(Debug) 아래에 빌드 파일 생성됨

* 64 비트 빌드
Visual Studio 메뉴에서 x64 Native Tools Command Prompt for VS 2022 실행

x265_git\build\vc15-x86_64 에서 아래실행
cmake -G "Visual Studio 17 2022" -T v143 ..\..\source && cmake-gui ..\..\source

cmake-gui 뜨면 아래 Configure 클릭

x265.sln VS2022 로 열어서 빌드하면 x265_git\build\vc15-x86_64\Release(Debug) 아래에 빌드 파일 생성됨

2024년 2월 1일 목요일

윈도우에서 ffmpeg NVIDIA CUDA Codec 연동 build

 * msys 설치



* Visual Studio 메뉴에서 x64 Native Tools Command Prompt for VS 2022 실행

c:\msys64\msys2_shell.cmd -use-full-path 실행

빌드에 필요한 패키지들을 설치한다.

pacman -Syu
pacman -S make diffutils yasm nasm git pkg-config

where link 하면 아래 두개가 나온다.

C:\msys64\usr\bin\link.exe
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\<버전>\bin\Hostx64\x64\link.exe

mv /usr/bin/link.exe /usr/bin/link.exe.org => 겹치지 않게 이름 변경


* 소스 다운로드

git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git

nv-codec-headers 폴더 들어가서 아래 실행
make install PREFIX=/usr

ffmpeg 소스 다운로드
git clone https://git.ffmpeg.org/ffmpeg.git


* CUDA Toolkit 설치
https://developer.nvidia.com/cuda-toolkit 에서 툴킷 받아서 설치한다.(혹은 구글에서 cuda toolkit 검색)

홈 디렉토리에서 아래 폴더 생성
mkdir build
mkdir nv_sdk

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\<버전> 아래에 include 폴더 복사 후 nv_sdk 아래에 불여넣기
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\<버전>\lib 아래에 x64 폴더 복사 후 nv_sdk 아래에 붙여넣기


* 빌드

export PATH="/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/<버전>/bin/":$PATH

ffmpeg 폴더에 들어가서 아래 실행

./configure --prefix=../build --toolchain=msvc --arch=x86_64 --enable-yasm --enable-shared --enable-gpl --enable-nonfree --enable-w32threads --enable-cuda-nvcc --enable-libnpp --extra-cflags=-I../nv_sdk/include --extra-ldflags=-libpath:../nv_sdk/x64

make
make install



* 32비트 빌드

Visual Studio 메뉴에서 x86 Native Tools Command Prompt for VS 2022 실행 후 c:\msys64\msys2_shell.cmd -use-full-path 실행

./configure --prefix=../build --toolchain=msvc --arch=x86_32 --enable-yasm --enable-shared --enable-gpl --enable-nonfree --enable-w32threads

* x264 빌드 후 ffmpeg 연동 빌드

32/64 비트는 각각 
32비트 => x86 Native Tools Command Prompt for VS 2022 실행 후 c:\msys64\msys2_shell.cmd -use-full-path 실행
64비트 => x64 Native Tools Command Prompt for VS 2022 실행 후 c:\msys64\msys2_shell.cmd -use-full-path 실행

소스 다운로드
git clone http://git.videolan.org/git/x264.git

x264 폴더에서 아래 실행 - 32/64비트 공통
CC=cl ./configure --prefix=../build --enable-static
 
make
make install

export PKG_CONFIG_PATH=../build/lib/pkgconfig/

ffmpeg 폴더에서 아래 실행
64비트
./configure --prefix=../build --toolchain=msvc --arch=x86_64 --enable-yasm --enable-shared --enable-gpl --enable-nonfree --enable-w32threads --enable-cuda-nvcc --enable-libnpp --extra-cflags="-I../nv_sdk/include -I../build/include" --extra-ldflags="-libpath:../nv_sdk/x64 -libpath:../build/lib" --enable-libx264

32비트
./configure --prefix=../build --toolchain=msvc --arch=x86_32 --enable-yasm --enable-shared --enable-gpl --enable-nonfree --enable-w32threads  --extra-cflags="-I../build/include" --extra-ldflags="-libpath:../build/lib" --enable-libx264

make
make install



* 참고 : 리눅스 빌드

cd ~
git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
cd nv-codec-headers
make
sudo make install

./configure --prefix=../build/ffmpeg-5.1.2 --extra-cflags=-I/usr/local/cuda/include --extra-ldflags=-L/usr/local/cuda/lib64 \
--enable-cuda-nvcc --enable-libnpp --enable-shared --enable-gpl --enable-nonfree




2023년 12월 8일 금요일

Avalonia ImageButton source code

 github link : https://github.com/kim-dong-hyun/AvaloniaImageButton








2023년 11월 24일 금요일

Rendering Video in Avalonia's NativeControlHost with SDL2.

 Add a NativeEmbeddingControl class that inherits from NativeControlHost in MainWindow.axaml.cs

 namespace AvaloniaDXPlayer  
 {  
   public partial class MainWindow : Window  
   {  
     public MainWindow()  
     {  
       InitializeComponent();  
     }  
     ...  
   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;  
     }  
   }     
 }  


Add a NativeEmbeddingControl class that inherits from NativeControlHost in MainWindow.axaml.cs

 <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">  
    <nec:NativeEmbeddingControl x:Name="host" Grid.Row="0" SizeChanged="Host_SizeChanged" />  
   </Grid>  
   <Canvas Grid.Row="1">  
    <TextBox x:Name="textURL" Margin="10" Width="300" Height="20" FontSize="14" Text=""/>  
    <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>  
   


Initialize SDL2. During this, insert the Handle of NativeEmbeddingControl as an argument into SDL_CreateWindowFrom

 
#if defined(LINUX)
typedef void*              HWND;  
#endif
 ...  
 int VideoSDLDraw::init(HWND hwnd, int targetWidth, int targetHeight, int srcWidth, int srcHeight)  
 {  
      release();  
   
      DXPRINTF("VideoSDLDraw::init %dx%d, %dx%d\n", targetWidth, targetHeight, srcWidth, srcHeight);  
   
      MUTEX_LOCK(&m_mutex);  
   
      Uint32 pixelFormat = SDL_PIXELFORMAT_UNKNOWN;  
   
      m_pWindow = SDL_CreateWindowFrom(hwnd);  
      if (m_pWindow == NULL) {  
           DXPRINTF("Window could not be created! SDL Error: %s\n", SDL_GetError());  
           goto fail;  
      }  
   
      m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);  
      if (m_pRenderer == NULL) {  
           DXPRINTF("Renderer could not be created! SDL Error: %s\n", SDL_GetError());  
           m_nTextureWidth = m_nTextureHeight = 0;  
           goto fail;  
      }  
      else {  
           //Initialize renderer color  
           SDL_SetRenderDrawColor(m_pRenderer, 0x00, 0x00, 0x00, 0xFF);  
           for (int i = 0; i < m_nTextDraw; i++) m_pTextDraw[i]->setSDLRenderer(m_pRenderer);  
      }  
 #if defined(LINUX)  
      if (targetWidth == 0 && targetHeight == 0) {  
           SDL_GetWindowSize(m_pWindow, &targetWidth, &targetHeight);  
      }  
 #endif  
      pixelFormat = SDL_GetWindowPixelFormat(m_pWindow);  
   
      m_pTexture = SDL_CreateTexture(m_pRenderer, pixelFormat, SDL_TEXTUREACCESS_STREAMING, targetWidth, targetHeight);  
      if (m_pTexture == NULL) {  
           DXPRINTF("Unable to create streamable texture! SDL Error: %s\n", SDL_GetError());  
           m_nTextureWidth = m_nTextureHeight = 0;  
           goto fail;  
      }  
   
      m_nTextureWidth = targetWidth;  
      m_nTextureHeight = targetHeight;  
   
      SetRectEmpty(&m_rectTargetLast);  
   
      SDL_RendererInfo rendererInfo;  
      SDL_GetRendererInfo(m_pRenderer, &rendererInfo);  
   
      DXPRINTF("SDL Renderer : %s\n", rendererInfo.name);  
   
      MUTEX_UNLOCK(&m_mutex);  
      return 0;  
   
 fail:  
      MUTEX_UNLOCK(&m_mutex);  
      return -1;  
 }  

After initialization as described above, render the decoded video data on m_pTexture with SDL2 rendering function. 
The result is as shown in the figure below.

Linux







Windows