c#에서 간단하게 모달 다이얼로그 폼을 띄울때 보통 아래와 같이 한다.
MyForm form = new MyForm();
if (form.ShowDialog() == DialogResult.OK)
{
...
}
위 코드는 별 문제 없어보이지만 사실은 메모리릭이 발생하는 코드이다.
폼 클래스는 IDisposable 을 구현하기 때문에 자원을 해제하려면 반드시 Dispose() 를
호출해줘야한다.
폼 내부에서 Dispose()를 호출하는 방법도 있지만 아래와같이 using 으로 묶어주면 깔끔하다.
using (MyForm form = new MyForm())
{
if (form.ShowDialog() == DialogResult.OK)
{
...
}
}
MyForm 객체를 멤버변수로 선언해서 재사용해서 쓰는것도 방법이겠지만 간단한 다이얼로그를 멤버변수로 두면 코드가 불필요하게 복잡해진다. 따라서 간단하게 using 으로 묶어주는것이 좋다.
위와같이 Dispose 를 해주지않고 반복해서 폼을 생성하면 "Win32Exception - error creating window handle" (윈도우 핸들을 생성할 수 없습니다) 와 같은 예외를 만나게된다.
2017년 11월 17일 금요일
2017년 10월 5일 목요일
ffmpeg+x264 윈도우/안드로이드 빌드(32/64비트)
ffmpeg을 빌드하는 방법 중 가장 확실하고 안정적인 방법은 리눅스(우분투)에서 빌드하는 것이다. 윈도우에서 mingw+msys 조합으로 빌드하는 방법도 있는데 여러모로 우분투에서 하는것이 정신건강에 좋다.
우분투는 실제 PC에 설치하지않고 VirtualBox 에 설치하면 간편하게 쓸수있다.
리눅스에서 mingw 설치
리눅스에서 아래 경로의 git 소스를 clone 받는다.
https://github.com/Zeranoe/mingw-w64-build.git
./mingw-w64-build 실행
위 스크립트를 실행하면 필요한 툴과 mingw 소스코드를 다운받은 다음 gcc로 빌드해서 윈도우용 32/64비트 크로스컴파일 툴채인을 만들어준다.
i686-w64-mingw32 아래에 32비트
x86_64-w64-mingw32 아래에 64비트 빌드 툴채인이 만들어진다.
따라서 쉘 환경의 경로설정에 따라 32/64비트 빌드 환경을 결정할 수 있다.
export PATH=$PATH:/home/ubuntu/work/mingw/mingw-w64-build/i686-w64-mingw32/bin => 32비트
export PATH=$PATH:/home/ubuntu/work/mingw/mingw-w64-build/x86_64-w64-mingw32/bin => 64비트
x264 빌드
./configure --cross-prefix=i686-w64-mingw32- --host=i686-w64-mingw32 => 32비트
./configure --cross-prefix=x86_64-w64-mingw32- --host=x86_64-w64-mingw32 => 64비트
make
빌드에 성공하면 libx264.a 파일이 생성된다.
ffmpeg 빌드
위에서 빌드한 libx264.a 파일을 정적으로 링크해서 빌드해주면된다.
※ x264 소스코드 : /home/ubuntu/work/mingw/x264
ffmpeg 소스코드 : /home/ubuntu/work/mingw/ffmpeg
output : /home/ubuntu/work/mingw/output
32비트
64비트
make
make install 하면 /home/ubuntu/work/mingw/output/ 아래에 빌드파일들 복사됨
안드로이드 NDK 빌드
안드로이드 NDK 를 다운받은 다음 툴채인을 만든다. 툴채인을 꼭 만들필요는 없는데 안만드는 경우 해당 아키텍쳐의 빌드경로만 설정해주면 된다. 여기서는 툴채인을 만들도록 하겠다.(툴채인 만드는법은 다른 포스트 참조 - http://greenday96.blogspot.kr/2015/01/android-ffmpeg-build.html)
툴채인 경로 : /home/ubuntu/work/my-android-toolchain-18
x264 빌드
make
빌드를 하면 안드로이드용 libx264.a 라이브러리가 생성된다.
ffmpeg 빌드
make
make install
하면 /home/ubuntu/work/mingw/output/android 아래에 빌드된 파일들이 복사된다
Android.mk 파일
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavfilter.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := postproc
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libpostproc.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := x264
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libx264.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DXMediaPlayer
LOCAL_SRC_FILES := DXMediaPlayer.cpp \
DXMediaPlayerCtrl.cpp
LOCAL_LDLIBS := -llog -lz -ljnigraphics -landroid -Wl,--no-warn-shared-textrel
LOCAL_STATIC_LIBRARIES := avfilter avformat avcodec avutil swscale swresample x264 postproc
LOCAL_CFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
include $(BUILD_SHARED_LIBRARY)
콘솔창에서 빌드
ndk-build
빌드에 성공하면 x264+ffmpeg 포함된 so 파일 생성
우분투는 실제 PC에 설치하지않고 VirtualBox 에 설치하면 간편하게 쓸수있다.
리눅스에서 mingw 설치
리눅스에서 아래 경로의 git 소스를 clone 받는다.
https://github.com/Zeranoe/mingw-w64-build.git
./mingw-w64-build 실행
위 스크립트를 실행하면 필요한 툴과 mingw 소스코드를 다운받은 다음 gcc로 빌드해서 윈도우용 32/64비트 크로스컴파일 툴채인을 만들어준다.
i686-w64-mingw32 아래에 32비트
x86_64-w64-mingw32 아래에 64비트 빌드 툴채인이 만들어진다.
따라서 쉘 환경의 경로설정에 따라 32/64비트 빌드 환경을 결정할 수 있다.
export PATH=$PATH:/home/ubuntu/work/mingw/mingw-w64-build/i686-w64-mingw32/bin => 32비트
export PATH=$PATH:/home/ubuntu/work/mingw/mingw-w64-build/x86_64-w64-mingw32/bin => 64비트
x264 빌드
./configure --cross-prefix=i686-w64-mingw32- --host=i686-w64-mingw32 => 32비트
./configure --cross-prefix=x86_64-w64-mingw32- --host=x86_64-w64-mingw32 => 64비트
make
빌드에 성공하면 libx264.a 파일이 생성된다.
ffmpeg 빌드
위에서 빌드한 libx264.a 파일을 정적으로 링크해서 빌드해주면된다.
※ x264 소스코드 : /home/ubuntu/work/mingw/x264
ffmpeg 소스코드 : /home/ubuntu/work/mingw/ffmpeg
output : /home/ubuntu/work/mingw/output
32비트
./configure --arch=x86 --target-os=mingw32 --cross-prefix=i686-w64-mingw32- --pkg-config=pkg-config --enable-w32threads --prefix=../output/ffmpeg_x86/ --enable-shared --disable-static --enable-gpl --enable-libx264 --extra-cflags=-I/home/ubuntu/work/mingw/x264 --extra-ldflags=-L/home/ubuntu/work/mingw/x264
64비트
./configure --arch=x86_64 --target-os=mingw32 --cross-prefix=x86_64-w64-mingw32- --pkg-config=pkg-config --enable-w32threads --prefix=../output/ffmpeg_x64/ --enable-shared --disable-static --enable-gpl --enable-libx264 --extra-cflags=-I/home/ubuntu/work/mingw/x264 --extra-ldflags=-L/home/ubuntu/work/mingw/x264
make
make install 하면 /home/ubuntu/work/mingw/output/ 아래에 빌드파일들 복사됨
안드로이드 NDK 빌드
안드로이드 NDK 를 다운받은 다음 툴채인을 만든다. 툴채인을 꼭 만들필요는 없는데 안만드는 경우 해당 아키텍쳐의 빌드경로만 설정해주면 된다. 여기서는 툴채인을 만들도록 하겠다.(툴채인 만드는법은 다른 포스트 참조 - http://greenday96.blogspot.kr/2015/01/android-ffmpeg-build.html)
툴채인 경로 : /home/ubuntu/work/my-android-toolchain-18
x264 빌드
./configure --cross-prefix=/home/ubuntu/work/my-android-toolchain-18/bin/arm-linux-androideabi- --host=arm-linux --extra-cflags='-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -D__ANDROID_API__=18' --extra-ldflags='-Wl,--fix-cortex-a8' --enable-pic
make
빌드를 하면 안드로이드용 libx264.a 라이브러리가 생성된다.
ffmpeg 빌드
./configure --target-os=linux --arch=arm --enable-cross-compile --cc=/home/ubuntu/work/my-android-toolchain-18/bin/arm-linux-androideabi-gcc --cross-prefix=/home/ubuntu/work/my-android-toolchain-18/bin/arm-linux-androideabi- --prefix=../output/android --extra-cflags='-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -D__ANDROID_API__=18' --extra-ldflags='-Wl,--fix-cortex-a8 -llog' --enable-shared --enable-gpl --enable-libx264 --extra-cflags=-I/home/ubuntu/work/mingw/x264 --extra-ldflags=-L/home/ubuntu/work/mingw/x264
make
make install
하면 /home/ubuntu/work/mingw/output/android 아래에 빌드된 파일들이 복사된다
Android.mk 파일
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavfilter.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := postproc
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libpostproc.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := x264
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libx264.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DXMediaPlayer
LOCAL_SRC_FILES := DXMediaPlayer.cpp \
DXMediaPlayerCtrl.cpp
LOCAL_LDLIBS := -llog -lz -ljnigraphics -landroid -Wl,--no-warn-shared-textrel
LOCAL_STATIC_LIBRARIES := avfilter avformat avcodec avutil swscale swresample x264 postproc
LOCAL_CFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
include $(BUILD_SHARED_LIBRARY)
콘솔창에서 빌드
ndk-build
빌드에 성공하면 x264+ffmpeg 포함된 so 파일 생성
2017년 9월 28일 목요일
ActiveX <-> C# 포인터 매개변수 전달 - Passing pointer parameters between activex and c#
vc++로 작성된 activex 와 c#으로 작성된 호스트 프로그램 간에 포인터 값을 전달해야하는 경우는
1. c# -> activex 메소드 호출
2. activex -> c# 이벤트 호출
두 가지 경우가 있다. activex가 c#과 연동하기 위해서는 activex 에서 적절한 메소드를 만든다음 커맨드창에서
aximp /source MyActiveX.ocx
해서 만들어진 인터페이스용 cs 소스파일과 dll 을 사용한다.
이때 메소드의 매개변수를 c++의 BYTE* 와같이 포인터로해도 c#에서는 포인터를 매개변수로 사용할 수 없으므로 만들어진 cs 파일에는 ref byte 타입으로 변경되어 포인터값을 전달할 수 없다.
2의 경우도 마찬가지로 aximp 커맨드를 통해 생성되는 인터페이스용 cs 파일에는 포인터 타입이 들어가지않는다.
activex가 아닌 dll 인 경우 c# 함수 프로토타입 선언에서 byte[] 타입으로 매개변수를 지정해서 쓰면되지만 activex(ocx)는 aximp 로 생성된 소스를 사용해야하므로 난감한 상황이 발생한다.
결론적으로 포인터는 결국 32(혹은 64)비트 정수값일 뿐이며 사용하기에 따라 BYTE*, LONG* 등으로 캐스팅해서 사용할 뿐이다. (결국 해석의 차이)
따라서 매개변수를 굳이 포인터 타입으로 할 이유가 없다. activex 쪽에서 LONGLONG 타입(64비트 프로세스인 경우 대비해서 LONG보다 LONGLONG 권장)으로 매개변수를 지정한다음 포인터값(주소)을 넘겨주거나 넘겨받으면 그만이다.
1. 포인터 매개변수 전달 예 - activex
void MyActiveXCtrl::SetIntArrayData(LONGLONG pData)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
int *data = (int *)pData;
...
}
aximp /source MyActiveX.ocx => cs 파일 생성
1. 포인터 매개변수 전달 예 - c#
int[] data = new int[10];
for (int i = 0; i < data.Length; i++) data[i] = i;
IntPtr ptr = Marshal.AllocHGlobal(sizeof(int) * data.Length);
Marshal.Copy(data, 0, ptr, data.Length);
myAxCtrl.SetIntArrayData((long)ptr); => c# long은 vc++ LONGLONG 이므로
Marshal.FreeHGlobal(ptr);
2. 포인터 매개변수 전달 예 - activex
void MyActiveXCtrl::OnDataEvent(LONG size, LONGLONG pData)
{
FireEvent(eventidOnDataEvent, ...., pData);
}
2. 포인터 매개변수 전달 예 - c#
myAxCtrl.OnDataEvent += axAxCtrl_OnDataEvent;
private void axAxCtrl_OnDataEvent(_AXMyActiveXCtrlEvents_OnDataEvent e)
{
byte[] btaImage = new byte[e.size];
unsafe
{
byte* p = (byte*)e.pData;
Marshal.Copy((IntPtr)p, btaImage, 0, e.size);
}
1번의 경우 c# -> activex 로 포인터를 전달하는데 원하는 데이터(여기서는 int 배열)를 IntPtr 변수에 복사한다음 포인터값을 전달한다. 이때 포인터를 받는 c++에서는 LONGLONG 타입을 int* 로 타입캐스팅해서 사용하면된다.
2번의 경우 activex -> c# 으로 포인터를 전달하는데 포인터 size를 지정한다음 LONGLONG 타입(64비트) 정수에 포인터 주소값을 넣어서 c# 쪽으로 전달한다.
c#에서는 전달받은 long 타입(64비트) 데이터를 byte* 으로 캐스팅한다음 c#에서 쉽게 사용하도록 byte 배열에 복사한다.
1. c# -> activex 메소드 호출
2. activex -> c# 이벤트 호출
두 가지 경우가 있다. activex가 c#과 연동하기 위해서는 activex 에서 적절한 메소드를 만든다음 커맨드창에서
aximp /source MyActiveX.ocx
해서 만들어진 인터페이스용 cs 소스파일과 dll 을 사용한다.
이때 메소드의 매개변수를 c++의 BYTE* 와같이 포인터로해도 c#에서는 포인터를 매개변수로 사용할 수 없으므로 만들어진 cs 파일에는 ref byte 타입으로 변경되어 포인터값을 전달할 수 없다.
2의 경우도 마찬가지로 aximp 커맨드를 통해 생성되는 인터페이스용 cs 파일에는 포인터 타입이 들어가지않는다.
activex가 아닌 dll 인 경우 c# 함수 프로토타입 선언에서 byte[] 타입으로 매개변수를 지정해서 쓰면되지만 activex(ocx)는 aximp 로 생성된 소스를 사용해야하므로 난감한 상황이 발생한다.
결론적으로 포인터는 결국 32(혹은 64)비트 정수값일 뿐이며 사용하기에 따라 BYTE*, LONG* 등으로 캐스팅해서 사용할 뿐이다. (결국 해석의 차이)
따라서 매개변수를 굳이 포인터 타입으로 할 이유가 없다. activex 쪽에서 LONGLONG 타입(64비트 프로세스인 경우 대비해서 LONG보다 LONGLONG 권장)으로 매개변수를 지정한다음 포인터값(주소)을 넘겨주거나 넘겨받으면 그만이다.
1. 포인터 매개변수 전달 예 - activex
void MyActiveXCtrl::SetIntArrayData(LONGLONG pData)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
int *data = (int *)pData;
...
}
aximp /source MyActiveX.ocx => cs 파일 생성
1. 포인터 매개변수 전달 예 - c#
int[] data = new int[10];
for (int i = 0; i < data.Length; i++) data[i] = i;
IntPtr ptr = Marshal.AllocHGlobal(sizeof(int) * data.Length);
Marshal.Copy(data, 0, ptr, data.Length);
myAxCtrl.SetIntArrayData((long)ptr); => c# long은 vc++ LONGLONG 이므로
Marshal.FreeHGlobal(ptr);
2. 포인터 매개변수 전달 예 - activex
void MyActiveXCtrl::OnDataEvent(LONG size, LONGLONG pData)
{
FireEvent(eventidOnDataEvent, ...., pData);
}
2. 포인터 매개변수 전달 예 - c#
myAxCtrl.OnDataEvent += axAxCtrl_OnDataEvent;
private void axAxCtrl_OnDataEvent(_AXMyActiveXCtrlEvents_OnDataEvent e)
{
byte[] btaImage = new byte[e.size];
unsafe
{
byte* p = (byte*)e.pData;
Marshal.Copy((IntPtr)p, btaImage, 0, e.size);
}
// now you can use btaImage data
...
}1번의 경우 c# -> activex 로 포인터를 전달하는데 원하는 데이터(여기서는 int 배열)를 IntPtr 변수에 복사한다음 포인터값을 전달한다. 이때 포인터를 받는 c++에서는 LONGLONG 타입을 int* 로 타입캐스팅해서 사용하면된다.
2번의 경우 activex -> c# 으로 포인터를 전달하는데 포인터 size를 지정한다음 LONGLONG 타입(64비트) 정수에 포인터 주소값을 넣어서 c# 쪽으로 전달한다.
c#에서는 전달받은 long 타입(64비트) 데이터를 byte* 으로 캐스팅한다음 c#에서 쉽게 사용하도록 byte 배열에 복사한다.
2017년 8월 24일 목요일
C# 쓰레드 올바른 사용법
C# Thread 관련 API 중 왠만하면 절대 사용하면 안되는 함수
Thread.Abort()
Thread.Interrupt()
위 두 함수는 극단적인 상황이 아니면 절대 사용해서는 안된다. 대신 모든 쓰레드의 종료는
Thread.Join() 함수로 정상종료를 확인해야한다.
쓰레드는 자체 메모리 공간을 가지고있고 프로세스의 전역변수를 사용할 수 있다.
Abort/Interrupt 함수는 해당 쓰레드가 어떤 동작을 하는중인지 상관없이 CPU 사용권을 빼앗아 강제종료 시키기때문에 함수 호출시 어떤일이 발생할지 알 수가 없다.
예를 들어 쓰레드가 전역변수 뮤텍스를 사용중인데 강제종료 되었다면 해당 뮤텍스를 사용하는 다른 쓰레드는 데드락에 빠져버린다.
굳이 이런 상황이 아니더라도 쓰레드는 반드시 정상종료 시켜야 프로그램의 완성도를 높이고 오동작을 방지할 수 있다.
쓰레드 시작과 종료는 보통 아래코드와 같이하면 문제가 없다.
추가적으로 Suspend/Resume 같은 함수도 사용을 권장하지않는다.
두 함수 역시 해당 쓰레드의 동작여부에 상관없이 중지/재시작을 하기때문에 어떤 상황이 발생할지 알 수가 없다.
Thread.Abort()
Thread.Interrupt()
위 두 함수는 극단적인 상황이 아니면 절대 사용해서는 안된다. 대신 모든 쓰레드의 종료는
Thread.Join() 함수로 정상종료를 확인해야한다.
쓰레드는 자체 메모리 공간을 가지고있고 프로세스의 전역변수를 사용할 수 있다.
Abort/Interrupt 함수는 해당 쓰레드가 어떤 동작을 하는중인지 상관없이 CPU 사용권을 빼앗아 강제종료 시키기때문에 함수 호출시 어떤일이 발생할지 알 수가 없다.
예를 들어 쓰레드가 전역변수 뮤텍스를 사용중인데 강제종료 되었다면 해당 뮤텍스를 사용하는 다른 쓰레드는 데드락에 빠져버린다.
굳이 이런 상황이 아니더라도 쓰레드는 반드시 정상종료 시켜야 프로그램의 완성도를 높이고 오동작을 방지할 수 있다.
쓰레드 시작과 종료는 보통 아래코드와 같이하면 문제가 없다.
private Thread threadDoWork;
private bool bThreadDoWorkRun = false;
private void StartDoWork()
{
if (!bThreadDoWorkRun)
{
bThreadDoWorkRun= true;
threadDoWork = new Thread(new ThreadStart(ThreadDoWork));
threadDoWork.Start();
}
}
private void StopDoWork()
{
if (bThreadDoWorkRun)
{
bThreadDoWorkRun = false;
threadDoWork.Join();
}
}
private void ThreadDoWork()
{
while (bThreadDoWorkRun)
{
...
}
}
추가적으로 Suspend/Resume 같은 함수도 사용을 권장하지않는다.
두 함수 역시 해당 쓰레드의 동작여부에 상관없이 중지/재시작을 하기때문에 어떤 상황이 발생할지 알 수가 없다.
2017년 1월 23일 월요일
C# 유용한 소켓 Select 함수 핸들러 소스코드 - useful socket select method handler source code
소켓 수신 이벤트 처리에 유용한 코드이다.
소켓 이벤트 핸들러를 등록하면 수신 이벤트 발생시 콜백으로 호출해준다.
TCP/UDP-서버/클라이언트 구분없이 모두 사용할 수 있다.
< 사용법 >
Socket clientSock;
SocketTaskScheduler task = new SocketTaskScheduler();
...
clientSock.Blocking = false;
clientSock.Connect(serverIP, serverPort);
task.RegisterSocketHandler(clientSock, IncomingPacketHandler, this);
task.StartEventLoop();
...
private void IncomingPacketHandler(object data)
{
int ret = clientSock.Receive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None);
if (ret <= 0)
{
task.UnregisterSocketHandler(clientSock);
CloseSocket();
task.StopEventLoop();
}
else
{
// process recvBuffer
}
}
소켓 이벤트 핸들러를 등록하면 수신 이벤트 발생시 콜백으로 호출해준다.
TCP/UDP-서버/클라이언트 구분없이 모두 사용할 수 있다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
namespace MySocketLib
{
public delegate void SocketReadHandlerCallback(Object data);
public class SocketHandler
{
public Socket sock;
public SocketReadHandlerCallback handler;
public object data;
}
public class SocketTaskScheduler
{
protected Dictionary<Socket, SocketHandler> sockHandlerTable = new Dictionary<Socket, SocketHandler>();
protected bool isRunning = false;
protected Thread thread;
protected int TIMEOUT = 1000000;
public SocketTaskScheduler()
{
}
public void RegisterSocketHandler(Socket sock, SocketReadHandlerCallback handler, object data)
{
lock (sockHandlerTable)
{
if (sock != null)
{
SocketHandler sockHandler = new SocketHandler();
sockHandler.sock = sock;
sockHandler.handler = handler;
sockHandler.data = data;
sockHandlerTable.Add(sockHandler.sock, sockHandler);
}
}
}
public void UnregisterSocketHandler(Socket sock)
{
lock (sockHandlerTable)
{
if (sock != null && sockHandlerTable.ContainsKey(sock))
{
sockHandlerTable.Remove(sock);
}
}
}
protected void SingleStep()
{
lock (sockHandlerTable)
{
try
{
ArrayList selectList = new ArrayList();
foreach (var handler in sockHandlerTable)
{
selectList.Add(handler.Key);
}
if (selectList.Count == 0)
{
Thread.Sleep(10);
return;
}
Socket.Select(selectList, null, null, TIMEOUT);
foreach (Socket sock in selectList)
{
var handler = sockHandlerTable[sock];
if (handler != null && handler.handler != null) handler.handler(handler.data);
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
}
}
protected void DoEventLoop()
{
while (isRunning == true)
{
SingleStep();
}
}
public void StartEventLoop()
{
if (isRunning == true) return;
thread = new Thread(new ThreadStart(DoEventLoop));
thread.IsBackground = true;
isRunning = true;
thread.Start();
}
public void StopEventLoop()
{
isRunning = false;
if (thread != null)
{
if (Thread.CurrentThread != thread)
{
thread.Join();
thread = null;
}
}
sockHandlerTable.Clear();
}
}
}
< 사용법 >
Socket clientSock;
SocketTaskScheduler task = new SocketTaskScheduler();
...
clientSock.Blocking = false;
clientSock.Connect(serverIP, serverPort);
task.RegisterSocketHandler(clientSock, IncomingPacketHandler, this);
task.StartEventLoop();
...
private void IncomingPacketHandler(object data)
{
int ret = clientSock.Receive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None);
if (ret <= 0)
{
task.UnregisterSocketHandler(clientSock);
CloseSocket();
task.StopEventLoop();
}
else
{
// process recvBuffer
}
}
2017년 1월 18일 수요일
CUDA 코덱 비디오 인코더 사용 소스코드 - CUDA Codec Video Encoder source code
NvEncoder 샘플소스를 수정한 소스코드, QT 기반으로 작성되었으며 yuv420 포멧으로 입력받은 영상 데이터를 h264로 인코딩한다.
인코딩된 프레임은 m_pEncodeBuffer 버퍼에 저장
< encodethread.h >
< encodethread.cpp >
인코딩된 프레임은 m_pEncodeBuffer 버퍼에 저장
< encodethread.h >
#ifndef ENCODETHREAD_H
#define ENCODETHREAD_H
#include <QThread>
#include <opencv2/imgproc/imgproc_c.h>
#include "imagequeue.h"
#include "./common/inc/NvHWEncoder.h"
#define MAX_ENCODE_QUEUE 32
#define FRAME_QUEUE 240
#define SET_VER(configStruct, type) {configStruct.version = type##_VER;}
template<class T>
class CNvQueue {
T** m_pBuffer;
unsigned int m_uSize;
unsigned int m_uPendingCount;
unsigned int m_uAvailableIdx;
unsigned int m_uPendingndex;
public:
CNvQueue(): m_pBuffer(NULL), m_uSize(0), m_uPendingCount(0), m_uAvailableIdx(0),
m_uPendingndex(0)
{
}
~CNvQueue()
{
delete[] m_pBuffer;
}
bool Initialize(T *pItems, unsigned int uSize)
{
m_uSize = uSize;
m_uPendingCount = 0;
m_uAvailableIdx = 0;
m_uPendingndex = 0;
m_pBuffer = new T *[m_uSize];
for (unsigned int i = 0; i < m_uSize; i++)
{
m_pBuffer[i] = &pItems[i];
}
return true;
}
T * GetAvailable()
{
T *pItem = NULL;
if (m_uPendingCount == m_uSize)
{
return NULL;
}
pItem = m_pBuffer[m_uAvailableIdx];
m_uAvailableIdx = (m_uAvailableIdx+1)%m_uSize;
m_uPendingCount += 1;
return pItem;
}
T* GetPending()
{
if (m_uPendingCount == 0)
{
return NULL;
}
T *pItem = m_pBuffer[m_uPendingndex];
m_uPendingndex = (m_uPendingndex+1)%m_uSize;
m_uPendingCount -= 1;
return pItem;
}
};
typedef struct _EncodeFrameConfig
{
uint8_t *yuv[3];
uint32_t stride[3];
uint32_t width;
uint32_t height;
}EncodeFrameConfig;
typedef enum
{
NV_ENC_DX9 = 0,
NV_ENC_DX11 = 1,
NV_ENC_CUDA = 2,
NV_ENC_DX10 = 3,
} NvEncodeDeviceType;
class EncodeThread : public QThread
{
Q_OBJECT
public:
explicit EncodeThread(QThread *parent = 0, ImageQueue<EncodeFrame> *queue = NULL);
virtual ~EncodeThread();
void start();
void stop();
protected:
virtual void run();
signals:
void started();
void finished();
private:
bool openEncoder(NV_ENC_BUFFER_FORMAT format, int width, int height);
void closeEncoder();
NVENCSTATUS deinitialize(uint32_t devicetype);
NVENCSTATUS encodeFrame(EncodeFrameConfig *pEncodeFrame, bool bFlush, uint32_t width, uint32_t height);
NVENCSTATUS initCuda(uint32_t deviceID = 0);
NVENCSTATUS allocateIOBuffers(uint32_t uInputWidth, uint32_t uInputHeight, NV_ENC_BUFFER_FORMAT inputFormat);
NVENCSTATUS releaseIOBuffers();
NVENCSTATUS flushEncoder();
private:
bool m_bRun;
private:
ImageQueue<EncodeFrame>* m_pQueue;
EncodeConfig m_stEncodeConfig;
CNvHWEncoder* m_pNvHWEncoder;
uint32_t m_uEncodeBufferCount;
uint32_t m_uPicStruct;
void* m_pDevice;
CUcontext m_cuContext;
EncodeConfig m_stEncoderInput;
EncodeBuffer m_stEncodeBuffer[MAX_ENCODE_QUEUE];
CNvQueue<EncodeBuffer> m_EncodeBufferQueue;
EncodeOutputBuffer m_stEOSOutputBfr;
uint8_t* m_pEncodeBuffer;
int m_nEncodeBufferSize;
FILE* m_pFile;
};
#endif // ENCODETHREAD_H
< encodethread.cpp >
#include "encodethread.h"
#include <QDebug>
#include "DXMediaPlayerCtrl.h"
#include "DXUtil.h"
#include "MediaBuffer.h"
#include "GlobalTimer.h"
#define BITSTREAM_BUFFER_SIZE 2 * 1024 * 1024
EncodeThread::EncodeThread(QThread *parent, ImageQueue<EncodeFrame> *queue) : QThread(parent)
{
m_bRun = false;
m_pQueue = queue;
m_pNvHWEncoder = new CNvHWEncoder;
m_cuContext = NULL;
m_uEncodeBufferCount = 0;
memset(&m_stEncoderInput, 0, sizeof(m_stEncoderInput));
memset(&m_stEOSOutputBfr, 0, sizeof(m_stEOSOutputBfr));
memset(&m_stEncodeBuffer, 0, sizeof(m_stEncodeBuffer));
m_pEncodeBuffer = new uint8_t[BITSTREAM_BUFFER_SIZE];
m_nEncodeBufferSize = 0;
m_pFile = NULL;
}
EncodeThread::~EncodeThread()
{
stop();
if (m_pNvHWEncoder) {
delete m_pNvHWEncoder;
m_pNvHWEncoder = NULL;
}
if (m_pEncodeBuffer) {
delete[] m_pEncodeBuffer;
m_pEncodeBuffer = NULL;
}
RTSPServer::destroy();
GlobalTimer::destroy();
}
void EncodeThread::start()
{
m_bRun = true;
QThread::start();
}
void EncodeThread::stop()
{
m_bRun = false;
wait();
}
void EncodeThread::run()
{
emit started();
bool bInit = false;
int count = 0;
EncodeFrameConfig stEncodeFrame;
CDXMediaPlayerCtrl *player = new CDXMediaPlayerCtrl(NULL, NULL);
player->openCaptureServerSession("stream1", AV_CODEC_ID_H264, AV_CODEC_ID_NONE);
player->playCaptureServerSession();
uint16_t port = 8554;
if (player->startServer(port) < 0)
qDebug() << "failed to start server, port : " << port;
uint64_t timestamp = 0;
MediaBuffer *pBuffer = NULL;
while (m_bRun) {
EncodeFrame *frame = m_pQueue->pop();
if (frame == NULL) {
QThread::usleep(1);
continue;
}
count = m_pQueue->count();
if (count > 0) qDebug() << "decode queue : " << count;
if (!bInit) {
NV_ENC_BUFFER_FORMAT format = NV_ENC_BUFFER_FORMAT_UNDEFINED;
if (frame->format == 0) format = NV_ENC_BUFFER_FORMAT_NV12;
else format = NV_ENC_BUFFER_FORMAT_YUV444;
if (openEncoder(format, frame->width, frame->height)) {
//m_pFile = fopen("output.264", "wb");
bInit = true;
} else {
closeEncoder();
}
}
if (bInit) {
memset(&stEncodeFrame, 0, sizeof(stEncodeFrame));
stEncodeFrame.yuv[0] = frame->yuv[0];
stEncodeFrame.yuv[1] = frame->yuv[1];
stEncodeFrame.yuv[2] = frame->yuv[2];
stEncodeFrame.stride[0] = frame->stride[0];
stEncodeFrame.stride[1] = frame->stride[1];
stEncodeFrame.stride[2] = frame->stride[2];
stEncodeFrame.width = frame->width;
stEncodeFrame.height = frame->height;
if (encodeFrame(&stEncodeFrame, false, frame->width, frame->height) == NV_ENC_SUCCESS) {
if (m_pFile) fwrite(m_pEncodeBuffer, 1, m_nEncodeBufferSize, m_pFile);
timestamp = GetTimeOfDay();
pBuffer = MediaBuffer::createBuffer(VideoMedia, m_pEncodeBuffer, m_nEncodeBufferSize, timestamp, timestamp);
if (player->pushCaptureInput(pBuffer) < 0) {
qDebug() << "cannot push capture input";
delete pBuffer;
}
}
}
delete frame;
}
if (bInit) encodeFrame(NULL, true, m_stEncodeConfig.width, m_stEncodeConfig.height);
player->close();
player->stopServer();
delete player;
closeEncoder();
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
emit finished();
}
bool EncodeThread::openEncoder(NV_ENC_BUFFER_FORMAT format, int width, int height)
{
memset(&m_stEncodeConfig, 0, sizeof(EncodeConfig));
m_stEncodeConfig.endFrameIdx = INT_MAX;
m_stEncodeConfig.bitrate = 5000000;
m_stEncodeConfig.rcMode = NV_ENC_PARAMS_RC_CONSTQP;
//m_stEncodeConfig.gopLength = NVENC_INFINITE_GOPLENGTH;
m_stEncodeConfig.deviceType = NV_ENC_CUDA;
m_stEncodeConfig.codec = NV_ENC_H264;
//m_stEncodeConfig.fps = 30;
m_stEncodeConfig.qp = 28;
m_stEncodeConfig.i_quant_factor = DEFAULT_I_QFACTOR;
m_stEncodeConfig.b_quant_factor = DEFAULT_B_QFACTOR;
m_stEncodeConfig.i_quant_offset = DEFAULT_I_QOFFSET;
m_stEncodeConfig.b_quant_offset = DEFAULT_B_QOFFSET;
m_stEncodeConfig.presetGUID = NV_ENC_PRESET_DEFAULT_GUID;
m_stEncodeConfig.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
m_stEncodeConfig.inputFormat = format;
m_stEncodeConfig.repeatSPSPPS = 1;
m_stEncodeConfig.width = width;
m_stEncodeConfig.height = height;
m_stEncodeConfig.gopLength = 15;
m_stEncodeConfig.fps = 15;
//m_stEncodeConfig.encoderPreset = "hq";
switch (m_stEncodeConfig.deviceType)
{
#if defined(NV_WINDOWS)
case NV_ENC_DX9:
InitD3D9(m_stEncodeConfig.deviceID);
break;
case NV_ENC_DX10:
InitD3D10(m_stEncodeConfig.deviceID);
break;
case NV_ENC_DX11:
InitD3D11(m_stEncodeConfig.deviceID);
break;
#endif
case NV_ENC_CUDA:
initCuda(m_stEncodeConfig.deviceID);
break;
}
NVENCSTATUS nvStatus = NV_ENC_SUCCESS;
if (m_stEncodeConfig.deviceType != NV_ENC_CUDA)
nvStatus = m_pNvHWEncoder->Initialize(m_pDevice, NV_ENC_DEVICE_TYPE_DIRECTX);
else
nvStatus = m_pNvHWEncoder->Initialize(m_pDevice, NV_ENC_DEVICE_TYPE_CUDA);
if (nvStatus != NV_ENC_SUCCESS)
return false;
m_stEncodeConfig.presetGUID = m_pNvHWEncoder->GetPresetGUID(m_stEncodeConfig.encoderPreset, m_stEncodeConfig.codec);
#if 0
m_stEncodeConfig.fOutput = fopen("output.264", "wb");
if (!m_stEncodeConfig.fOutput)
qDebug() << "failed to open output file";
#endif
nvStatus = m_pNvHWEncoder->CreateEncoder(&m_stEncodeConfig);
if (nvStatus != NV_ENC_SUCCESS)
return false;
m_stEncodeConfig.maxWidth = m_stEncodeConfig.maxWidth ? m_stEncodeConfig.maxWidth : m_stEncodeConfig.width;
m_stEncodeConfig.maxHeight = m_stEncodeConfig.maxHeight ? m_stEncodeConfig.maxHeight : m_stEncodeConfig.height;
m_stEncoderInput.enableAsyncMode = m_stEncodeConfig.enableAsyncMode;
if (m_stEncodeConfig.numB > 0)
{
m_uEncodeBufferCount = m_stEncodeConfig.numB + 4; // min buffers is numb + 1 + 3 pipelining
}
else
{
int numMBs = ((m_stEncodeConfig.maxHeight + 15) >> 4) * ((m_stEncodeConfig.maxWidth + 15) >> 4);
int NumIOBuffers;
if (numMBs >= 32768) //4kx2k
NumIOBuffers = MAX_ENCODE_QUEUE / 8;
else if (numMBs >= 16384) // 2kx2k
NumIOBuffers = MAX_ENCODE_QUEUE / 4;
else if (numMBs >= 8160) // 1920x1080
NumIOBuffers = MAX_ENCODE_QUEUE / 2;
else
NumIOBuffers = MAX_ENCODE_QUEUE;
m_uEncodeBufferCount = NumIOBuffers;
}
m_uPicStruct = m_stEncodeConfig.pictureStruct;
nvStatus = allocateIOBuffers(m_stEncodeConfig.width, m_stEncodeConfig.height, m_stEncodeConfig.inputFormat);
if (nvStatus != NV_ENC_SUCCESS)
return 1;
unsigned int preloadedFrameCount = FRAME_QUEUE;
if (m_stEncodeConfig.preloadedFrameCount >= 2)
{
preloadedFrameCount = m_stEncodeConfig.preloadedFrameCount;
}
qDebug() << "encoder " << width << "x" << height << " opened";
#if 0
GUID guids[10];
uint32_t count;
m_pNvHWEncoder->NvEncGetEncodeGUIDs(guids, 10, &count);
NV_ENC_BUFFER_FORMAT fmt[20];
memset(fmt, 0, sizeof(fmt));
m_pNvHWEncoder->NvEncGetInputFormats(guids[0], fmt, 10, &count);
#endif
return true;
}
void EncodeThread::closeEncoder()
{
if (m_stEncodeConfig.fOutput) {
fclose(m_stEncodeConfig.fOutput);
m_stEncodeConfig.fOutput = NULL;
}
deinitialize(m_stEncodeConfig.deviceType);
}
void convertYUVpitchtoNV12( unsigned char *yuv_luma, unsigned char *yuv_cb, unsigned char *yuv_cr,
unsigned char *nv12_luma, unsigned char *nv12_chroma,
int width, int height , int srcStride, int dstStride)
{
int y;
int x;
if (srcStride == 0)
srcStride = width;
if (dstStride == 0)
dstStride = width;
for ( y = 0 ; y < height ; y++)
{
memcpy( nv12_luma + (dstStride*y), yuv_luma + (srcStride*y) , width );
}
for ( y = 0 ; y < height/2 ; y++)
{
for ( x= 0 ; x < width; x=x+2)
{
nv12_chroma[(y*dstStride) + x] = yuv_cb[((srcStride/2)*y) + (x >>1)];
nv12_chroma[(y*dstStride) +(x+1)] = yuv_cr[((srcStride/2)*y) + (x >>1)];
}
}
}
void convertYUV10pitchtoP010PL(unsigned short *yuv_luma, unsigned short *yuv_cb, unsigned short *yuv_cr,
unsigned short *nv12_luma, unsigned short *nv12_chroma, int width, int height, int srcStride, int dstStride)
{
int x, y;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
nv12_luma[(y*dstStride / 2) + x] = yuv_luma[(srcStride*y) + x] << 6;
}
}
for (y = 0; y < height / 2; y++)
{
for (x = 0; x < width; x = x + 2)
{
nv12_chroma[(y*dstStride / 2) + x] = yuv_cb[((srcStride / 2)*y) + (x >> 1)] << 6;
nv12_chroma[(y*dstStride / 2) + (x + 1)] = yuv_cr[((srcStride / 2)*y) + (x >> 1)] << 6;
}
}
}
void convertYUVpitchtoYUV444(unsigned char *yuv_luma, unsigned char *yuv_cb, unsigned char *yuv_cr,
unsigned char *surf_luma, unsigned char *surf_cb, unsigned char *surf_cr, int width, int height, int srcStride, int dstStride)
{
int h;
for (h = 0; h < height; h++)
{
memcpy(surf_luma + dstStride * h, yuv_luma + srcStride * h, width);
memcpy(surf_cb + dstStride * h, yuv_cb + srcStride * h, width);
memcpy(surf_cr + dstStride * h, yuv_cr + srcStride * h, width);
}
}
void convertYUV10pitchtoYUV444(unsigned short *yuv_luma, unsigned short *yuv_cb, unsigned short *yuv_cr,
unsigned short *surf_luma, unsigned short *surf_cb, unsigned short *surf_cr,
int width, int height, int srcStride, int dstStride)
{
int x, y;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
surf_luma[(y*dstStride / 2) + x] = yuv_luma[(srcStride*y) + x] << 6;
surf_cb[(y*dstStride / 2) + x] = yuv_cb[(srcStride*y) + x] << 6;
surf_cr[(y*dstStride / 2) + x] = yuv_cr[(srcStride*y) + x] << 6;
}
}
}
NVENCSTATUS EncodeThread::encodeFrame(EncodeFrameConfig *pEncodeFrame, bool bFlush, uint32_t width, uint32_t height)
{
NVENCSTATUS nvStatus = NV_ENC_SUCCESS;
uint32_t lockedPitch = 0;
EncodeBuffer *pEncodeBuffer = NULL;
if (bFlush)
{
flushEncoder();
return NV_ENC_SUCCESS;
}
if (!pEncodeFrame)
{
return NV_ENC_ERR_INVALID_PARAM;
}
pEncodeBuffer = m_EncodeBufferQueue.GetAvailable();
if(!pEncodeBuffer)
{
m_pNvHWEncoder->ProcessOutput(m_EncodeBufferQueue.GetPending());
pEncodeBuffer = m_EncodeBufferQueue.GetAvailable();
}
unsigned char *pInputSurface;
nvStatus = m_pNvHWEncoder->NvEncLockInputBuffer(pEncodeBuffer->stInputBfr.hInputSurface, (void**)&pInputSurface, &lockedPitch);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
if (pEncodeBuffer->stInputBfr.bufferFmt == NV_ENC_BUFFER_FORMAT_NV12_PL)
{
unsigned char *pInputSurfaceCh = pInputSurface + (pEncodeBuffer->stInputBfr.dwHeight*lockedPitch);
convertYUVpitchtoNV12(pEncodeFrame->yuv[0], pEncodeFrame->yuv[1], pEncodeFrame->yuv[2], pInputSurface, pInputSurfaceCh, width, height, width, lockedPitch);
}
else if (pEncodeBuffer->stInputBfr.bufferFmt == NV_ENC_BUFFER_FORMAT_YUV444)
{
unsigned char *pInputSurfaceCb = pInputSurface + (pEncodeBuffer->stInputBfr.dwHeight * lockedPitch);
unsigned char *pInputSurfaceCr = pInputSurfaceCb + (pEncodeBuffer->stInputBfr.dwHeight * lockedPitch);
convertYUVpitchtoYUV444(pEncodeFrame->yuv[0], pEncodeFrame->yuv[1], pEncodeFrame->yuv[2], pInputSurface, pInputSurfaceCb, pInputSurfaceCr, width, height, width, lockedPitch);
}
else if (pEncodeBuffer->stInputBfr.bufferFmt == NV_ENC_BUFFER_FORMAT_YUV420_10BIT)
{
unsigned char *pInputSurfaceCh = pInputSurface + (pEncodeBuffer->stInputBfr.dwHeight*lockedPitch);
convertYUV10pitchtoP010PL((uint16_t *)pEncodeFrame->yuv[0], (uint16_t *)pEncodeFrame->yuv[1], (uint16_t *)pEncodeFrame->yuv[2], (uint16_t *)pInputSurface, (uint16_t *)pInputSurfaceCh, width, height, width, lockedPitch);
}
else //if (pEncodeBuffer->stInputBfr.bufferFmt == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)
{
unsigned char *pInputSurfaceCb = pInputSurface + (pEncodeBuffer->stInputBfr.dwHeight * lockedPitch);
unsigned char *pInputSurfaceCr = pInputSurfaceCb + (pEncodeBuffer->stInputBfr.dwHeight * lockedPitch);
convertYUV10pitchtoYUV444((uint16_t *)pEncodeFrame->yuv[0], (uint16_t *)pEncodeFrame->yuv[1], (uint16_t *)pEncodeFrame->yuv[2], (uint16_t *)pInputSurface, (uint16_t *)pInputSurfaceCb, (uint16_t *)pInputSurfaceCr, width, height, width, lockedPitch);
}
nvStatus = m_pNvHWEncoder->NvEncUnlockInputBuffer(pEncodeBuffer->stInputBfr.hInputSurface);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
nvStatus = m_pNvHWEncoder->NvEncEncodeFrame(pEncodeBuffer, NULL, width, height, (NV_ENC_PIC_STRUCT)m_uPicStruct);
if (nvStatus == NV_ENC_SUCCESS) {
NV_ENC_LOCK_BITSTREAM lockBitstreamData;
memset(&lockBitstreamData, 0, sizeof(lockBitstreamData));
SET_VER(lockBitstreamData, NV_ENC_LOCK_BITSTREAM);
lockBitstreamData.outputBitstream = pEncodeBuffer->stOutputBfr.hBitstreamBuffer;
lockBitstreamData.doNotWait = false;
if (m_pNvHWEncoder->NvEncLockBitstream(&lockBitstreamData) == NV_ENC_SUCCESS) {
memcpy(m_pEncodeBuffer, lockBitstreamData.bitstreamBufferPtr, lockBitstreamData.bitstreamSizeInBytes);
m_nEncodeBufferSize = lockBitstreamData.bitstreamSizeInBytes;
m_pNvHWEncoder->NvEncUnlockBitstream(pEncodeBuffer->stOutputBfr.hBitstreamBuffer);
}
}
return nvStatus;
}
NVENCSTATUS EncodeThread::initCuda(uint32_t deviceID)
{
CUresult cuResult;
CUdevice device;
CUcontext cuContextCurr;
int deviceCount = 0;
int SMminor = 0, SMmajor = 0;
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
typedef HMODULE CUDADRIVER;
#else
typedef void *CUDADRIVER;
#endif
CUDADRIVER hHandleDriver = 0;
cuResult = cuInit(0, __CUDA_API_VERSION, hHandleDriver);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuInit error:0x%x\n", cuResult);
assert(0);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
cuResult = cuDeviceGetCount(&deviceCount);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuDeviceGetCount error:0x%x\n", cuResult);
assert(0);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
// If dev is negative value, we clamp to 0
if ((int)deviceID < 0)
deviceID = 0;
if (deviceID >(unsigned int)deviceCount - 1)
{
PRINTERR("Invalid Device Id = %d\n", deviceID);
return NV_ENC_ERR_INVALID_ENCODERDEVICE;
}
cuResult = cuDeviceGet(&device, deviceID);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuDeviceGet error:0x%x\n", cuResult);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
cuResult = cuDeviceComputeCapability(&SMmajor, &SMminor, deviceID);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuDeviceComputeCapability error:0x%x\n", cuResult);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
if (((SMmajor << 4) + SMminor) < 0x30)
{
PRINTERR("GPU %d does not have NVENC capabilities exiting\n", deviceID);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
cuResult = cuCtxCreate((CUcontext*)(&m_pDevice), 0, device);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuCtxCreate error:0x%x\n", cuResult);
assert(0);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
cuResult = cuCtxPopCurrent(&cuContextCurr);
if (cuResult != CUDA_SUCCESS)
{
PRINTERR("cuCtxPopCurrent error:0x%x\n", cuResult);
assert(0);
return NV_ENC_ERR_NO_ENCODE_DEVICE;
}
return NV_ENC_SUCCESS;
}
NVENCSTATUS EncodeThread::allocateIOBuffers(uint32_t uInputWidth, uint32_t uInputHeight, NV_ENC_BUFFER_FORMAT inputFormat)
{
NVENCSTATUS nvStatus = NV_ENC_SUCCESS;
m_EncodeBufferQueue.Initialize(m_stEncodeBuffer, m_uEncodeBufferCount);
for (uint32_t i = 0; i < m_uEncodeBufferCount; i++)
{
nvStatus = m_pNvHWEncoder->NvEncCreateInputBuffer(uInputWidth, uInputHeight, &m_stEncodeBuffer[i].stInputBfr.hInputSurface, inputFormat);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
m_stEncodeBuffer[i].stInputBfr.bufferFmt = inputFormat;
m_stEncodeBuffer[i].stInputBfr.dwWidth = uInputWidth;
m_stEncodeBuffer[i].stInputBfr.dwHeight = uInputHeight;
nvStatus = m_pNvHWEncoder->NvEncCreateBitstreamBuffer(BITSTREAM_BUFFER_SIZE, &m_stEncodeBuffer[i].stOutputBfr.hBitstreamBuffer);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
m_stEncodeBuffer[i].stOutputBfr.dwBitstreamBufferSize = BITSTREAM_BUFFER_SIZE;
if (m_stEncoderInput.enableAsyncMode)
{
nvStatus = m_pNvHWEncoder->NvEncRegisterAsyncEvent(&m_stEncodeBuffer[i].stOutputBfr.hOutputEvent);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
m_stEncodeBuffer[i].stOutputBfr.bWaitOnEvent = true;
}
else
m_stEncodeBuffer[i].stOutputBfr.hOutputEvent = NULL;
}
m_stEOSOutputBfr.bEOSFlag = TRUE;
if (m_stEncoderInput.enableAsyncMode)
{
nvStatus = m_pNvHWEncoder->NvEncRegisterAsyncEvent(&m_stEOSOutputBfr.hOutputEvent);
if (nvStatus != NV_ENC_SUCCESS)
return nvStatus;
}
else
m_stEOSOutputBfr.hOutputEvent = NULL;
return NV_ENC_SUCCESS;
}
NVENCSTATUS EncodeThread::releaseIOBuffers()
{
for (uint32_t i = 0; i < m_uEncodeBufferCount; i++)
{
m_pNvHWEncoder->NvEncDestroyInputBuffer(m_stEncodeBuffer[i].stInputBfr.hInputSurface);
m_stEncodeBuffer[i].stInputBfr.hInputSurface = NULL;
m_pNvHWEncoder->NvEncDestroyBitstreamBuffer(m_stEncodeBuffer[i].stOutputBfr.hBitstreamBuffer);
m_stEncodeBuffer[i].stOutputBfr.hBitstreamBuffer = NULL;
if (m_stEncoderInput.enableAsyncMode)
{
m_pNvHWEncoder->NvEncUnregisterAsyncEvent(m_stEncodeBuffer[i].stOutputBfr.hOutputEvent);
nvCloseFile(m_stEncodeBuffer[i].stOutputBfr.hOutputEvent);
m_stEncodeBuffer[i].stOutputBfr.hOutputEvent = NULL;
}
}
if (m_stEOSOutputBfr.hOutputEvent)
{
if (m_stEncoderInput.enableAsyncMode)
{
m_pNvHWEncoder->NvEncUnregisterAsyncEvent(m_stEOSOutputBfr.hOutputEvent);
nvCloseFile(m_stEOSOutputBfr.hOutputEvent);
m_stEOSOutputBfr.hOutputEvent = NULL;
}
}
return NV_ENC_SUCCESS;
}
NVENCSTATUS EncodeThread::flushEncoder()
{
NVENCSTATUS nvStatus = m_pNvHWEncoder->NvEncFlushEncoderQueue(m_stEOSOutputBfr.hOutputEvent);
if (nvStatus != NV_ENC_SUCCESS)
{
assert(0);
return nvStatus;
}
EncodeBuffer *pEncodeBufer = m_EncodeBufferQueue.GetPending();
while (pEncodeBufer)
{
m_pNvHWEncoder->ProcessOutput(pEncodeBufer);
pEncodeBufer = m_EncodeBufferQueue.GetPending();
}
#if defined(NV_WINDOWS)
if (m_stEncoderInput.enableAsyncMode)
{
if (WaitForSingleObject(m_stEOSOutputBfr.hOutputEvent, 500) != WAIT_OBJECT_0)
{
assert(0);
nvStatus = NV_ENC_ERR_GENERIC;
}
}
#endif
return nvStatus;
}
NVENCSTATUS EncodeThread::deinitialize(uint32_t devicetype)
{
NVENCSTATUS nvStatus = NV_ENC_SUCCESS;
releaseIOBuffers();
nvStatus = m_pNvHWEncoder->NvEncDestroyEncoder();
if (m_pDevice)
{
switch (devicetype)
{
#if defined(NV_WINDOWS)
case NV_ENC_DX9:
((IDirect3DDevice9*)(m_pDevice))->Release();
break;
case NV_ENC_DX10:
((ID3D10Device*)(m_pDevice))->Release();
break;
case NV_ENC_DX11:
((ID3D11Device*)(m_pDevice))->Release();
break;
#endif
case NV_ENC_CUDA:
CUresult cuResult = CUDA_SUCCESS;
cuResult = cuCtxDestroy((CUcontext)m_pDevice);
if (cuResult != CUDA_SUCCESS)
PRINTERR("cuCtxDestroy error:0x%x\n", cuResult);
}
m_pDevice = NULL;
}
#if defined (NV_WINDOWS)
if (m_pD3D)
{
m_pD3D->Release();
m_pD3D = NULL;
}
#endif
return nvStatus;
}
피드 구독하기:
글 (Atom)