2017년 11월 17일 금요일

c# 윈폼 사용시 주의할 점 - 메모리릭 방지

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년 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비트
./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);
    }
    // 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 사용권을 빼앗아 강제종료 시키기때문에 함수 호출시 어떤일이 발생할지 알 수가 없다.
예를 들어 쓰레드가 전역변수 뮤텍스를 사용중인데 강제종료 되었다면 해당 뮤텍스를 사용하는 다른 쓰레드는 데드락에 빠져버린다.
굳이 이런 상황이 아니더라도 쓰레드는 반드시 정상종료 시켜야 프로그램의 완성도를 높이고 오동작을 방지할 수 있다.

쓰레드 시작과 종료는 보통 아래코드와 같이하면 문제가 없다.

     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-서버/클라이언트 구분없이 모두 사용할 수 있다.

 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 >
 #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;  
 }