레이블이 cpp인 게시물을 표시합니다. 모든 게시물 표시
레이블이 cpp인 게시물을 표시합니다. 모든 게시물 표시

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)를 받는 복사 생성자와 대입 연산자에서도 처리가 가능할텐데 말이다.
몇 가지 이유가 있지만 제일 큰 이유는 자원이 이동한다는 것을 명시적으로 표현함으로써 표준을 정하고 혼란을 없애기 위함이다.
일반 복사 생성자/대입 연산자에서 이것을 처리하게 되면 실제 깊은 복사가 일어나야 하는 것인지 자원이동만 일어나야하는지 구분이 어렵기 때문이다.

2023년 1월 25일 수요일

VC++ Unicode 와 MBSC 겸용으로 빌드시 MultiByteToWideChar/WideCharToMultiByte 함수 처리(리눅스 포함)

 VC++에서 Unicode/MBCS(Multi-Byte Character Set) 겸용으로 빌드시에 MultiByteToWideChar/WideCharToMultiByte 함수를 아래와 같이 처리하면 문제없이 빌드가 가능하다. 윈도우가 아닌 경우(리눅스 등)에도 빌드 처리가 된다.


#ifdef WIN32
#include <tchar.h>
#endif

#ifdef WIN32
#define STRNCPY _tcsncpy
#define STRLEN _tcslen
#ifdef UNICODE
#define MULTIBYTETOWIDECHAR MultiByteToWideChar
#define WIDECHARTOMULTIBYTE WideCharToMultiByte
#define SNPRINTF swprintf
#define SSCANF swscanf
#else
#define MULTIBYTETOWIDECHAR(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar) \
_snprintf(lpWideCharStr, cchWideChar, "%s", lpMultiByteStr)

#define WIDECHARTOMULTIBYTE(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar) \
_snprintf(lpMultiByteStr, cbMultiByte, "%s", lpWideCharStr)

#define SNPRINTF _snprintf
#define SSCANF sscanf
#endif
#else
#define MULTIBYTETOWIDECHAR(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar) \
snprintf(lpWideCharStr, cchWideChar, "%s", lpMultiByteStr)

#define WIDECHARTOMULTIBYTE(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar) \
snprintf(lpMultiByteStr, cbMultiByte, "%s", lpWideCharStr)

#define STRNCPY strncpy
#define STRLEN strlen
#define SNPRINTF snprintf
#define SSCANF sscanf

#define TCHAR char
#define TEXT(quote) quote
#define _T(quote) quote
#define __T(quote) quote
#endif

< 사용예 >
TCHAR filepathPrefix[260] = { 0 };
TCHAR fileExtension[260] = { 0 };

MULTIBYTETOWIDECHAR(CP_ACP, 0, m_szFilePathPrefix, sizeof(m_szFilePathPrefix), filepathPrefix, sizeof(filepathPrefix));

MULTIBYTETOWIDECHAR(CP_ACP, 0, m_szFileExtension, sizeof(m_szFileExtension), fileExtension, sizeof(fileExtension));

char filepathChar[256];
WIDECHARTOMULTIBYTE(CP_UTF8, 0, filepath, sizeof(filepath), filepathChar, sizeof(filepathChar), NULL, NULL);


2023년 1월 13일 금요일

vc++ 메모리 릭 감지

프로그램 시작 부분에 아래 코드 넣고 실행 후 정상종료하면 메모리릭 발생시 출력창에 메모리릭 출력 


#ifdef _DEBUG
#include <crtdbg.h>
#endif

int main()
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

간단한 strtok_s 사용법

 원본 문자열을 ',' 로 분할하고 2차원 배열에 복사하는 소스이다.


#ifdef WIN32
#define strtok_r strtok_s    // 윈도우/리눅스 빌드
#endif

char m_szRecordPath[MAX_RECORD_PATH][MAX_PATH];
int m_nRecordPathCount;

void setRecordPaths(char *filepaths)
{
memset(&m_szRecordPath, 0, sizeof(m_szRecordPath));
m_nRecordPathCount = 0;
char *ret_ptr, *next_ptr;

ret_ptr = strtok_r(filepaths, ",", &next_ptr);
while (ret_ptr) {
memcpy(&m_szRecordPath[m_nRecordPathCount], ret_ptr, strlen(ret_ptr));
m_nRecordPathCount++;
ret_ptr = strtok_r(NULL, ",", &next_ptr);
}

for (int i=0; i<m_nRecordPathCount; i++)
printf("record path[%d] : %s\n", i, m_szRecordPath[i]);
}

2022년 7월 12일 화요일

c++ 간단한 endian check 함수

int checkEndian()
{
    int i = 0x00000001;
    if( ((char *)&i)[0] ) return 0;
    else return 1;
}


리턴값이 0 이면 리틀엔디안, 1이면 빅엔디안

2015년 12월 3일 목요일

윈도우/리눅스/안드로이드 공통 ffmpeg 디코더 라이브러리 소스 - ffmpeg decoder library source code for window/linux/android

ffmpeg 비디오/오디오 디코더 라이브러리 - 윈도우/리눅스/안드로이드 환경에서 빌드 및 테스트

뮤텍스 소스
오디오 리샘플러 소스

< FFMPEGUtil.h >
 #ifndef __FFMPEG_UTIL_H__  
 #define __FFMPEG_UTIL_H__  
   
 extern "C" {  
 #include "libavformat/avformat.h"  
 #include "libavutil/pixdesc.h"  
 }  
   
 void InitFFmpegLib();  
 enum AVCodecID GetCodecID(const char *codecName);  
 enum AVSampleFormat GetSampleFormat(enum AVCodecID codec_id);  
 unsigned int GetCodecTag(const AVCodecTag *tags, enum AVCodecID id);  
 enum AVCodecID GetCodecID(const AVCodecTag *tags, unsigned int tag);   
 char* GetCodecName(enum AVCodecID codec_id);  
 int GetBitPerPixel(AVPixelFormat pix_fmt);  
   
 #endif     

< FFMPEGUtil.cpp >
 #include "util.h"  
 #include "FFMPEGUtil.h"  
 #include "GlobalEnv.h"  
 #include "Mutex.h"  
 #include <ctype.h>  
   
 typedef struct AVCodecTag {  
   enum AVCodecID id;  
   unsigned int tag;  
 } AVCodecTag;  
   
 static bool isInit = false;  
 static MUTEX hMutex = PTHREAD_MUTEX_INITIALIZER;  
   
 static int lockmgr(void **mtx, enum AVLockOp op)  
 {  
   switch(op) {  
    case AV_LOCK_CREATE:  
            MUTEX_INIT((MUTEX *)mtx);  
      if(!*mtx)  
        return 1;  
      return 0;  
    case AV_LOCK_OBTAIN:  
            return !!MUTEX_LOCK((MUTEX *)mtx);  
    case AV_LOCK_RELEASE:  
            return !!MUTEX_UNLOCK((MUTEX *)mtx);  
    case AV_LOCK_DESTROY:  
            MUTEX_DESTROY((MUTEX *)mtx);  
      return 0;  
   }  
   return 1;  
 }  
   
 void InitFFmpegLib()  
 {  
      MUTEX_LOCK(&hMutex);  
   
      if (!isInit) {  
           av_register_all();  
   
           if (av_lockmgr_register(lockmgr)) {  
                DXPRINTF("Could not initialize lock manager!\n");  
                exit(1);  
           }  
           isInit = true;  
      }  
   
      MUTEX_UNLOCK(&hMutex);  
 }  
   
 enum AVCodecID GetCodecID(const char *codecName)  
 {       
      if (!codecName) return AV_CODEC_ID_NONE;  
   
      if (strcmp(codecName, "H264") == 0)  
           return AV_CODEC_ID_H264;  
      else if (strcmp(codecName, "MP4V-ES") == 0)  
           return AV_CODEC_ID_MPEG4;  
      else if (strcmp(codecName, "MPEG4-GENERIC") == 0)  
           return AV_CODEC_ID_AAC;  
      else if (strcmp(codecName, "JPEG") == 0)  
           return AV_CODEC_ID_MJPEG;  
      else if (strcmp(codecName, "AC3") == 0)  
           return AV_CODEC_ID_AC3;  
      else if (strcmp(codecName, "L16") == 0)  
           return AV_CODEC_ID_PCM_S16BE;  
      else if (strcmp(codecName, "PCMU") == 0)  
           return AV_CODEC_ID_PCM_MULAW;  
      else if (strcmp(codecName, "PCMA") == 0)  
           return AV_CODEC_ID_PCM_ALAW;  
      else DXPRINTF("cannot find %s codec id\n", codecName);  
   
      return AV_CODEC_ID_NONE;  
 }  
   
 char* GetCodecName(AVCodecID codec_id)  
 {  
      char *temp = NULL;  
   
      switch (codec_id) {  
           case AV_CODEC_ID_H264: { temp = "H264"; break; }  
           case AV_CODEC_ID_MPEG4: { temp = "MP4V-ES"; break; }  
           case AV_CODEC_ID_AAC: { temp = "MPEG4-GENERIC"; break; }  
           case AV_CODEC_ID_MJPEG: { temp = "JPEG"; break; }  
           case AV_CODEC_ID_AC3: { temp = "AC3"; break; }  
           case AV_CODEC_ID_PCM_S16BE: { temp = "L16"; break; }  
           case AV_CODEC_ID_PCM_MULAW: { temp = "PCMU"; break; }  
           case AV_CODEC_ID_PCM_ALAW: { temp = "PCMA"; break; }  
      }  
   
      return strDup(temp);  
 }  
   
 enum AVSampleFormat GetSampleFormat(enum AVCodecID codec_id)  
 {  
      if (codec_id == AV_CODEC_ID_PCM_MULAW)  
           return AV_SAMPLE_FMT_U8;  
      else if (codec_id == AV_CODEC_ID_AAC)  
           return AV_SAMPLE_FMT_S16;  
      else if (codec_id == AV_CODEC_ID_AC3)  
           return AV_SAMPLE_FMT_S16;  
      else if (codec_id == AV_CODEC_ID_PCM_S16BE)  
           return AV_SAMPLE_FMT_S16;  
      else DXPRINTF("cannot find codec id %d sample format\n", codec_id);  
   
      return AV_SAMPLE_FMT_NONE;  
 }  
  
 unsigned int GetCodecTag(const AVCodecTag *tags, enum AVCodecID id)  
 {  
   while (tags->id != AV_CODEC_ID_NONE) {  
     if (tags->id == id)  
       return tags->tag;  
     tags++;  
   }  
   return 0;  
 }  
   
 static unsigned int avpriv_toupper4(unsigned int x)  
 {  
   return toupper(x & 0xFF) +  
      (toupper((x >> 8) & 0xFF) << 8) +  
      (toupper((x >> 16) & 0xFF) << 16) +  
      (toupper((x >> 24) & 0xFF) << 24);  
 }  
   
 enum AVCodecID GetCodecID(const AVCodecTag *tags, unsigned int tag)  
 {  
   int i;  
   for(i=0; tags[i].id != AV_CODEC_ID_NONE;i++) {  
     if(tag == tags[i].tag)  
       return tags[i].id;  
   }  
   for(i=0; tags[i].id != AV_CODEC_ID_NONE; i++) {  
     if (avpriv_toupper4(tag) == avpriv_toupper4(tags[i].tag))  
       return tags[i].id;  
   }  
   return AV_CODEC_ID_NONE;  
 }  
   
 int GetBitPerPixel(AVPixelFormat pix_fmt)  
 {  
      const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);  
      return av_get_bits_per_pixel(desc);  
 }     

< Decoder.h >
 #ifndef __DECODER_H__  
 #define __DECODER_H__  
   
 #ifdef WIN32  
 #include <windows.h>  
 #endif  
 #include "Mutex.h"  
   
 extern "C" {  
 #include "libavcodec/avcodec.h"  
 }  
   
 #define MAX_DECODE_BUFFER_SIZE               (1024*1024)  
   
 class Decoder  
 {  
 public:  
      Decoder();  
      virtual ~Decoder();  
   
      static void initCodec();  
   
      virtual int open(enum AVCodecID codecId, int channel);  
      virtual int open(AVCodecContext *codecCtx, int channel);  
      virtual void close();  
   
      virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame) = 0;  
      void flush();  
   
      enum AVCodecID codecID();  
      AVFrame* frame() { return m_pFrame; }  
   
      int width();  
      int height();  
      enum AVPixelFormat pixelFormat();  
   
 protected:  
      virtual int open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel) = 0;  
      int prepareOpen(enum AVCodecID codecId, AVCodecContext *codecCtx);  
      void prepareDecBuffer(unsigned char *inBuf, int inLen);  
   
 protected:  
      AVCodec*          m_pCodec;  
      AVCodecContext*     m_pCodecCtx;  
      AVFrame*          m_pFrame;  
      AVPacket          m_avPacket;  
   
      unsigned char*     m_pDecBuffer;  
      int                    m_nDecBufferSize;  
      bool               m_bCodecCtxAlloc;  
   
      static MUTEX     m_hMutex;  
      static bool          m_bInit;  
      static int          m_nCPU;  
   
      // dump  
      FILE               *m_pFile;  
 };  
   
 #endif     

< Decoder.cpp >
 #include "Decoder.h"  
 #include "GlobalEnv.h"  
 #include "FFMPEGUtil.h"  
   
 bool Decoder::m_bInit = false;  
 int Decoder::m_nCPU = 1;  
 MUTEX Decoder::m_hMutex = PTHREAD_MUTEX_INITIALIZER;  
   
 Decoder::Decoder() : m_pCodec(NULL), m_pCodecCtx(NULL), m_pFrame(NULL), m_bCodecCtxAlloc(false), m_pFile(NULL)  
 {  
      initCodec();  
   
      m_pDecBuffer = new unsigned char[MAX_DECODE_BUFFER_SIZE];  
      m_nDecBufferSize = MAX_DECODE_BUFFER_SIZE;  
 }  
   
 Decoder::~Decoder()  
 {  
      if (m_pDecBuffer) {  
           delete[] m_pDecBuffer;  
           m_pDecBuffer = NULL;  
      }  
 }  
   
 void Decoder::initCodec()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (!m_bInit) {  
           InitFFmpegLib();  
 #ifdef WIN32  
           SYSTEM_INFO sysinfo;  
           GetSystemInfo(&sysinfo);  
           m_nCPU = sysinfo.dwNumberOfProcessors;  
 #else  
           int ret = sysconf(_SC_NPROCESSORS_ONLN);  
           if (ret != -1) m_nCPU = ret;            
 #endif  
           DXPRINTF("CPU : %d\n", m_nCPU);  
           m_bInit = true;  
      }  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 int Decoder::open(AVCodecID codecId, int channel)  
 {  
      return open(codecId, NULL, channel);  
 }  
   
 int Decoder::open(AVCodecContext *codecCtx, int channel)  
 {  
      return open(codecCtx->codec_id, codecCtx, channel);  
 }  
   
 int Decoder::prepareOpen(enum AVCodecID codecId, AVCodecContext *codecCtx)  
 {  
      av_init_packet(&m_avPacket);  
   
      m_pCodec = avcodec_find_decoder(codecId);  
      if (!m_pCodec) {  
           DXPRINTF("avcodec_find_decoder failed to find codec %d\n", codecId);  
           return -1;  
      }  
   
      if (codecCtx) {  
           m_pCodecCtx = codecCtx;  
           m_bCodecCtxAlloc = false;  
      } else {  
           m_pCodecCtx = avcodec_alloc_context3(m_pCodec);  
           if (!m_pCodecCtx) {  
                DXPRINTF("avcodec_alloc_context3 failed\n");  
                return -1;  
           }  
           m_bCodecCtxAlloc = true;  
      }  
   
      m_pFrame = avcodec_alloc_frame();  
      if (!m_pFrame) {  
           DXPRINTF("avcodec_alloc_frame failed\n");            
           return -1;  
      }  
   
      return 0;  
 }  
   
 void Decoder::close()  
 {  
      if (m_pCodecCtx)  
           avcodec_close(m_pCodecCtx);  
   
      if (m_bCodecCtxAlloc) {  
           av_free(m_pCodecCtx);  
           m_bCodecCtxAlloc = false;  
      }   
   
      m_pCodecCtx = NULL;  
   
      avcodec_free_frame(&m_pFrame);  
   
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
   
 enum AVCodecID Decoder::codecID()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->codec_id;  
   
      return AV_CODEC_ID_NONE;  
 }  
   
 int Decoder::width()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->width;  
      return 0;  
 }  
   
 int Decoder::height()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->height;  
      return 0;  
 }  
   
 enum AVPixelFormat Decoder::pixelFormat()  
 {  
      if (m_pCodecCtx)  
           return m_pCodecCtx->pix_fmt;  
      return AV_PIX_FMT_NONE;  
 }  
   
 void Decoder::prepareDecBuffer(unsigned char *inBuf, int inLen)  
 {  
      if (inLen+FF_INPUT_BUFFER_PADDING_SIZE > m_nDecBufferSize) {  
           delete[] m_pDecBuffer;  
           m_pDecBuffer = new unsigned char[inLen+FF_INPUT_BUFFER_PADDING_SIZE];  
           m_nDecBufferSize = inLen+FF_INPUT_BUFFER_PADDING_SIZE;  
      }  
   
      memset(&m_pDecBuffer[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);  
      memcpy(m_pDecBuffer, inBuf, inLen);  
   
      av_init_packet(&m_avPacket);  
      m_avPacket.data = m_pDecBuffer;  
      m_avPacket.size = inLen;  
 }  
   
 void Decoder::flush()  
 {  
      if (m_pCodecCtx)  
           avcodec_flush_buffers(m_pCodecCtx);  
 }     

< VideoDecoder.h >
 #ifndef __VIDEO_DECODER_H__  
 #define __VIDEO_DECODER_H__  
   
 #include "Decoder.h"  
   
 class VideoDecoder : public Decoder  
 {  
 public:  
      VideoDecoder();  
      virtual ~VideoDecoder();  
   
      virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame);  
   
 protected:  
      virtual int open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel);  
   
 protected:  
      int writeYUVStream(FILE *fp, unsigned char *buf, int wrap, int xsize, int ysize);  
 };  
   
 #endif  

< VideoDecoder.cpp >
 #include "VideoDecoder.h"  
 #include "GlobalEnv.h"  
   
 VideoDecoder::VideoDecoder() : Decoder()  
 {  
 }  
   
 VideoDecoder::~VideoDecoder()  
 {  
 }  
   
 int VideoDecoder::open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel)  
 {       
      int err;  
      char errbuf[128];  
   
      err = prepareOpen(codecId, codecCtx);  
      if (err < 0) return -1;  
   
      m_pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;  
      m_pCodecCtx->thread_type = FF_THREAD_SLICE;  
      m_pCodecCtx->thread_count = m_nCPU;  
   
      if (codecId == CODEC_ID_H264) {  
           if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)  
                m_pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;  
      }  
   
      if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codecId), err, errbuf);  
           goto exit;  
      }  
   
      DXPRINTF("video decoder opened %s\n", avcodec_get_name(codecId));  
   
      //m_pFile = fopen("c:/video.yuv", "wb");  
   
 exit:  
      return err;  
 }  
   
 int VideoDecoder::decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame)  
 {  
      if (!m_pCodecCtx || !inBuf || inLen <= 0) return 0;  
   
      int retLen = 0, got_picture;  
   
      prepareDecBuffer(inBuf, inLen);  
   
      retLen = avcodec_decode_video2(m_pCodecCtx, frame, &got_picture, &m_avPacket);  
   
      if (frame->pict_type == AV_PICTURE_TYPE_NONE)  
           return 0;  
   
      if (m_pFile) {  
           writeYUVStream(m_pFile, frame->data[0], frame->linesize[0], m_pCodecCtx->width, m_pCodecCtx->height);  
           writeYUVStream(m_pFile, frame->data[1], frame->linesize[0]/2, m_pCodecCtx->width/2, m_pCodecCtx->height/2);  
           writeYUVStream(m_pFile, frame->data[2], frame->linesize[0]/2, m_pCodecCtx->width/2, m_pCodecCtx->height/2);  
      }  
   
      return retLen;  
 }  
   
 int VideoDecoder::writeYUVStream(FILE *fp, unsigned char *buf, int wrap, int xsize, int ysize)  
 {  
      for(int i=0;i<ysize;i++) {  
           if (fp)  
                fwrite(buf + i * wrap, 1, xsize, fp);  
      }  
      return 0;  
 }  
   

< AudioDecoder.h >
 #ifndef __AUDIO_DECODER_H__  
 #define __AUDIO_DECODER_H__  
   
 #include "Decoder.h"  
 #include "Resampler.h"  
   
 class AudioDecoder : public Decoder  
 {  
 public:  
      AudioDecoder();  
      virtual ~AudioDecoder();  
   
      virtual void close();  
      virtual int decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame);  
   
      unsigned char* outBuf() { return m_pOutBuf; }  
      int outBufSize() { return m_nOutBufSize; }  
   
      void setExtraData(unsigned char const *extradata, unsigned extradatasize);  
   
      int sampleRate();  
      int channels();  
      uint64_t channelLayout();  
   
 protected:  
      virtual int open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel);  
      int initResampler(uint64_t channel_layout, int sample_rate, enum AVSampleFormat sample_fmt);  
      int checkResampler();  
   
 protected:  
      Resampler*          m_pResampler;  
   
      unsigned char*     m_pOutBuf;  
      int                    m_nOutBufSize;  
      int                    m_nOutBufMaxSize;  
   
      unsigned char*     m_pExtraData;  
      int                    m_nExtraDataSize;  
 };  
   
 #endif  
   

< AudioDecoder.cpp >
 #include "AudioDecoder.h"  
 #include "GlobalEnv.h"  
   
 AudioDecoder::AudioDecoder() : Decoder(),  
      m_pResampler(NULL), m_pOutBuf(NULL), m_nOutBufSize(0), m_nOutBufMaxSize(0), m_pExtraData(NULL), m_nExtraDataSize(0)  
 {  
 }  
   
 AudioDecoder::~AudioDecoder()  
 {  
      DX_DELETE_OBJECT(m_pResampler);  
 }  
   
 int AudioDecoder::open(enum AVCodecID codecId, AVCodecContext *codecCtx, int channel)  
 {  
      int err;  
      char errbuf[128];  
   
      err = prepareOpen(codecId, codecCtx);  
      if (err < 0) return -1;  
   
      if (m_bCodecCtxAlloc) {  
           m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;  
           m_pCodecCtx->channels = channel;  
   
           if (codecId == AV_CODEC_ID_PCM_MULAW) {  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;  
           } else if (codecId == AV_CODEC_ID_AAC) {  
                if (m_pExtraData) {  
                     m_pCodecCtx->extradata = m_pExtraData;  
                     m_pCodecCtx->extradata_size = m_nExtraDataSize;  
                }  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;  
           } else {  
                m_pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;  
           }  
      }  
   
      if (err=avcodec_open2(m_pCodecCtx, m_pCodec, NULL) < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avcodec_open2 %s open failed, err: %d %s\n", avcodec_get_name(codecId), err, errbuf);  
           return err;  
      }  
   
      if (m_pCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16 || m_pCodecCtx->channels > 2) {  
           m_pResampler = new Resampler();  
           err = m_pResampler->open(  
                m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt,  
                m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16);  
   
           if (err < 0) {  
                DXPRINTF("failed to open resampler %d %d %d %d\n",   
                     m_pCodecCtx->sample_rate,  
                     m_pCodecCtx->channels,  
                     m_pCodecCtx->channel_layout,  
                     AV_SAMPLE_FMT_S16);  
                return err;  
           }  
      }  
   
      DXPRINTF("audio decoder opened %s\n", avcodec_get_name(codecId));  
   
 #if 0  
      m_pFile = fopen("audio_decoder.wav", "wb");  
 #endif  
   
      return err;  
 }  
   
 void AudioDecoder::close()  
 {  
      Decoder::close();  
   
      if (m_pResampler) {  
           m_pResampler->close();  
           DX_DELETE_OBJECT(m_pResampler);  
      }  
   
      if (m_pExtraData) {  
           delete[] m_pExtraData;  
           m_pExtraData = NULL;  
           m_nExtraDataSize = 0;  
      }  
   
      if (m_pOutBuf) {  
           delete[] m_pOutBuf;  
           m_pOutBuf = NULL;  
           m_nOutBufMaxSize = m_nOutBufSize = 0;  
      }  
   
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
   
 int AudioDecoder::decodeFrame(unsigned char *inBuf, int inLen, AVFrame *frame)  
 {  
      if (!m_pCodecCtx || !inBuf || inLen <= 0) return 0;  
   
      int retLen = 0, got_frame;  
   
      prepareDecBuffer(inBuf, inLen);  
   
      retLen = avcodec_decode_audio4(m_pCodecCtx, m_pFrame, &got_frame, &m_avPacket);  
   
      if (retLen <= 0) {  
           DXPRINTF("audio decode error : %d\n", retLen);  
           return retLen;  
      }  
        
      int out_size = av_samples_get_buffer_size(NULL, m_pCodecCtx->channels, m_pFrame->nb_samples,  
           m_pCodecCtx->sample_fmt, 1);  
   
      if (out_size <= 0) {  
           return -1;  
      }  
   
      if (out_size > m_nOutBufMaxSize) {  
           if (m_pOutBuf) delete[] m_pOutBuf;  
           m_pOutBuf = new unsigned char[out_size];  
           m_nOutBufMaxSize = out_size;  
      }  
   
      if (m_pResampler) {  
           if (m_pResampler->checkResampler(m_pCodecCtx->sample_rate, m_pCodecCtx->channels,   
                m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt) < 0)   
           {  
                m_pResampler->close();  
   
                int err = m_pResampler->open(  
                     m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, m_pCodecCtx->sample_fmt,  
                     m_pCodecCtx->sample_rate, m_pCodecCtx->channels, m_pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16);  
   
                if (err < 0) {  
                     DXPRINTF("failed to open resampler %d %d %d %d\n",   
                          m_pCodecCtx->sample_rate,  
                          m_pCodecCtx->channels,  
                          m_pCodecCtx->channel_layout,  
                          AV_SAMPLE_FMT_S16);  
                     return err;  
                }  
           }  
           retLen = m_pResampler->resample(m_pFrame, out_size);  
           out_size = m_pResampler->outBufIndex();  
           memcpy(m_pOutBuf, m_pResampler->outBuf(), out_size);  
           m_pResampler->resetOutBufIndex();  
      } else {  
           if (m_pCodecCtx->channels == 1) {  
                memcpy(m_pOutBuf, m_pFrame->extended_data[0], out_size);  
           } else {  
                uint8_t *data0 = m_pFrame->extended_data[0];  
                uint8_t *data1 = m_pFrame->extended_data[1];  
                int c = 0;  
                for (int i=0; i<out_size/2; i+=2) {  
                     m_pOutBuf[c++] = data0[i]; m_pOutBuf[c++] = data0[i+1];  
                     m_pOutBuf[c++] = data1[i]; m_pOutBuf[c++] = data1[i+1];  
                }  
                out_size = c;  
           }  
           out_size = retLen*m_pCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);  
      }  
        
      m_nOutBufSize = out_size;  
   
      if (m_pFile)  
           fwrite(m_pOutBuf, m_nOutBufSize, 1, m_pFile);  
   
      return retLen;  
 }  
   
 void AudioDecoder::setExtraData(unsigned char const *extradata, unsigned extradatasize)  
 {  
      if (m_pExtraData) {  
           delete[] m_pExtraData;  
           m_pExtraData = NULL;  
           m_nExtraDataSize = 0;  
      }  
   
      if (!extradatasize)  
           return;  
   
      m_pExtraData = new unsigned char[extradatasize];  
      memset(m_pExtraData, 0, extradatasize);  
      memcpy(m_pExtraData, extradata, extradatasize);  
      m_nExtraDataSize = extradatasize;  
 }  
   
 int AudioDecoder::sampleRate()  
 {  
      if (m_pCodecCtx) return m_pCodecCtx->sample_rate;  
      return 0;  
 }  
   
 int AudioDecoder::channels()  
 {  
      if (m_pCodecCtx) return m_pCodecCtx->channels;  
      return 0;  
 }  
   
 uint64_t AudioDecoder::channelLayout()  
 {  
      if (m_pCodecCtx) return m_pCodecCtx->channel_layout;  
      return 0;  
 }  
   

< 라이브러리 사용 >

Decoder *pVideoDecoder = new VideoDecoder();
Decoder *pAudioDecoder = new AudioDecoder();
...
// 코덱열기
pVideoDecoder->open(AV_CODEC_ID_H264, 0);    // 비디오 코덱은 채널 사용X
pAudioDecoder->open(AV_CODEC_ID_AAC, 2);

...
// 디코딩
unsigned char *buf => encoded data buffer
int size => encoded data buffer size

if (pVideoDecoder->decodeFrame(buf, size, pVideoDecoder->frame()) > 0) {
    // now pVideoDecoder->frame() has decoded picture
}
...
if (pAudioDecoder->decodeFrame(buf, size, pAudioDecoder->frame()) > 0) {
    // now pAudioDecoder->outBuf() has decoded audio pcm data &&
    // pAudioDecoder->outBufSize() has decoded audio pcm data size
}
...

// 코덱닫기
pVideoDecoder->close();
pAudioDecoder->close();

2015년 11월 26일 목요일

live555 TaskScheduler 수정소스

live555 의 TaskScheduler 를 사용하기 쉽게 수정한 소스이다. 윈도우/리눅스 둘 다 적용가능하며 소켓통신 프로그램 개발시 간편하게 사용할 수 있다.
Mutex/Thread 관련 소스는 http://greenday96.blogspot.com/2015/08/blog-post.html 포스트 참조

< NetCommon.h >
 #ifndef __NETCOMMON_H__  
 #define __NETCOMMON_H__  
   
 #ifdef WIN32  
   
 #include <WinSock2.h>  
 #include <ws2tcpip.h>  
   
 #define closeSocket     closesocket  
 #define EWOULDBLOCK     WSAEWOULDBLOCK  
 #define EINPROGRESS WSAEWOULDBLOCK  
 #define EINTR          WSAEINTR  
   
 #define _strcasecmp     _strnicmp  
 #define snprintf     _snprintf  
   
 #else  
   
 #include <sys/socket.h>  
 #include <netinet/in.h>  
 #include <netinet/tcp.h>  
 #include <arpa/inet.h>  
 #include <unistd.h>  
 #include <fcntl.h>  
 #include <errno.h>  
   
 #define closeSocket               close  
 #define WSAGetLastError()     errno  
   
 #include <ctype.h>  
 #include <stdlib.h>  
 #define _strcasecmp strncasecmp  
 #endif  
   
 #endif  
   


< TaskScheduler.h >
 #ifndef __TASK_SCHEDULER_H__  
 #define __TASK_SCHEDULER_H__  
   
 #include "NetCommon.h"  
 #include "Mutex.h"  
 #include "Thread.h"  
   
 #define SOCKET_READABLE  (1<<1)  
 #define SOCKET_WRITABLE  (1<<2)  
 #define SOCKET_EXCEPTION  (1<<3)  
   
 class HandlerSet;  
   
 class TaskScheduler   
 {  
 public:       
      TaskScheduler();  
      virtual ~TaskScheduler();  
   
      typedef void BackgroundHandlerProc(void* clientData, int mask);  
   
      void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void *clientData);  
      void turnOffBackgroundReadHandling(int socketNum);       
   
      int startEventLoop();  
      void stopEventLoop();  
      void doEventLoop();  
   
      int isRunning() { return fTaskLoop; }  
   
 protected:            
      virtual void SingleStep();  
      void taskLock();  
      void taskUnlock();  
   
 protected:  
      int                         fTaskLoop;  
      MUTEX                    fMutex;  
      THREAD                    fThread;  
   
      HandlerSet     *fReadHandlers;  
      int               fLastHandledSocketNum;  
   
      int          fMaxNumSockets;  
      fd_set     fReadSet;  
 };  
   
 class HandlerDescriptor {  
      HandlerDescriptor(HandlerDescriptor* nextHandler);  
      virtual ~HandlerDescriptor();  
   
 public:  
      int socketNum;  
      TaskScheduler::BackgroundHandlerProc* handlerProc;  
      void* clientData;  
   
 private:  
      // Descriptors are linked together in a doubly-linked list:  
      friend class HandlerSet;  
      friend class HandlerIterator;  
      HandlerDescriptor* fNextHandler;  
      HandlerDescriptor* fPrevHandler;  
 };  
   
 class HandlerSet {  
 public:  
      HandlerSet();  
      virtual ~HandlerSet();  
   
      void assignHandler(int socketNum, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);  
      void removeHandler(int socketNum);  
      void moveHandler(int oldSocketNum, int newSocketNum);  
   
 private:  
      HandlerDescriptor* lookupHandler(int socketNum);  
   
 private:  
      friend class HandlerIterator;  
      HandlerDescriptor fHandlers;  
 };  
   
 class HandlerIterator {  
 public:  
      HandlerIterator(HandlerSet& handlerSet);  
      virtual ~HandlerIterator();  
   
      HandlerDescriptor* next(); // returns NULL if none  
      void reset();  
   
 private:  
      HandlerSet& fOurSet;  
      HandlerDescriptor* fNextPtr;  
 };  
   
 #endif  
   


< TaskScheduler.cpp >
 #include "TaskScheduler.h"  
 #include "RTSPCommonEnv.h"  
 #include <stdio.h>  
   
 THREAD_FUNC DoEventThread(void* lpParam)  
 {  
      TaskScheduler *scheduler = (TaskScheduler *)lpParam;  
      scheduler->doEventLoop();  
      return 0;  
 }  
   
 TaskScheduler::TaskScheduler()  
 {  
      fTaskLoop = 0;  
      MUTEX_INIT(&fMutex);  
      FD_ZERO(&fReadSet);  
      fMaxNumSockets = 0;  
      fThread = NULL;  
      fReadHandlers = new HandlerSet();  
 }  
   
 TaskScheduler::~TaskScheduler()  
 {  
      stopEventLoop();  
   
      delete fReadHandlers;  
   
      THREAD_DESTROY(&fThread);  
   
      MUTEX_DESTROY(&fMutex);  
 }  
   
 void TaskScheduler::taskLock()  
 {  
      MUTEX_LOCK(&fMutex);  
 }  
   
 void TaskScheduler::taskUnlock()  
 {  
      MUTEX_UNLOCK(&fMutex);  
 }  
   
 void TaskScheduler::turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void *clientData)   
 {  
	taskLock();

	if (socketNum < 0) goto exit;

	FD_SET((unsigned)socketNum, &fReadSet);
	fReadHandlers->assignHandler(socketNum, handlerProc, clientData);

	if (socketNum+1 > fMaxNumSockets) {
		fMaxNumSockets = socketNum+1;
	}

exit:
	taskUnlock();
 }  
   
 void TaskScheduler::turnOffBackgroundReadHandling(int socketNum)   
 {  
	taskLock();

	if (socketNum < 0) goto exit;

	FD_CLR((unsigned)socketNum, &fReadSet);
	fReadHandlers->removeHandler(socketNum);

    HandlerIterator iter(*fReadHandlers);
	HandlerDescriptor* handler;

	int maxSocketNum = 0;
	while ((handler = iter.next()) != NULL) {
		if (handler->socketNum+1 > maxSocketNum) {
			maxSocketNum = handler->socketNum+1;
		}
	}
	fMaxNumSockets = maxSocketNum;	

exit:
	taskUnlock();
 }  
   
 int TaskScheduler::startEventLoop()  
 {  
      if (fTaskLoop != 0)  
           return -1;  
   
      fTaskLoop = 1;  
      THREAD_CREATE(&fThread, DoEventThread, this);  
      if (!fThread) {  
           DPRINTF("failed to create event loop thread\n");  
           fTaskLoop = 0;  
           return -1;  
      }  
   
      return 0;  
 }  
   
 void TaskScheduler::stopEventLoop()  
 {  
      fTaskLoop = 0;  
   
      THREAD_JOIN(&fThread);  
      THREAD_DESTROY(&fThread);  
 }  
   
 void TaskScheduler::doEventLoop()   
 {  
      while (fTaskLoop)  
      {  
           SingleStep();  
      }  
 }  
   
 void TaskScheduler::SingleStep()  
 {  
	taskLock();

	fd_set readSet = fReadSet;

	struct timeval timeout;
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	int selectResult = select(fMaxNumSockets, &readSet, NULL, NULL, &timeout);
	if (selectResult < 0) {
		int err = WSAGetLastError();
		DPRINTF("TaskScheduler::SingleStep(): select() fails : %d\n", err);
		taskUnlock();
		return;
	}

	HandlerIterator iter(*fReadHandlers);
	HandlerDescriptor* handler;

	while ((handler = iter.next()) != NULL) {
		if (FD_ISSET(handler->socketNum, &readSet) && handler->handlerProc != NULL) {
			(*handler->handlerProc)(handler->clientData, SOCKET_READABLE);
		}
	}

	taskUnlock();
 }  
   
   
 HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)  
 : handlerProc(NULL) {  
      // Link this descriptor into a doubly-linked list:  
      if (nextHandler == this) { // initialization  
           fNextHandler = fPrevHandler = this;  
      } else {  
           fNextHandler = nextHandler;  
           fPrevHandler = nextHandler->fPrevHandler;  
           nextHandler->fPrevHandler = this;  
           fPrevHandler->fNextHandler = this;  
      }  
 }  
   
 HandlerDescriptor::~HandlerDescriptor() {  
      // Unlink this descriptor from a doubly-linked list:  
      fNextHandler->fPrevHandler = fPrevHandler;  
      fPrevHandler->fNextHandler = fNextHandler;  
 }  
   
 HandlerSet::HandlerSet()  
 : fHandlers(&fHandlers) {  
      fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...  
 }  
   
 HandlerSet::~HandlerSet() {  
      // Delete each handler descriptor:  
      while (fHandlers.fNextHandler != &fHandlers) {  
           delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler  
      }  
 }  
   
 void HandlerSet  
 ::assignHandler(int socketNum, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData) {  
      // First, see if there's already a handler for this socket:  
      HandlerDescriptor* handler = lookupHandler(socketNum);  
      if (handler == NULL) { // No existing handler, so create a new descr:  
           handler = new HandlerDescriptor(fHandlers.fNextHandler);  
           handler->socketNum = socketNum;  
      }  
   
      handler->handlerProc = handlerProc;  
      handler->clientData = clientData;  
 }  
   
 void HandlerSet::removeHandler(int socketNum) {  
      HandlerDescriptor* handler = lookupHandler(socketNum);  
      delete handler;  
 }  
   
 void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) {  
      HandlerDescriptor* handler = lookupHandler(oldSocketNum);  
      if (handler != NULL) {  
           handler->socketNum = newSocketNum;  
      }  
 }  
   
 HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) {  
      HandlerDescriptor* handler;  
      HandlerIterator iter(*this);  
      while ((handler = iter.next()) != NULL) {  
           if (handler->socketNum == socketNum) break;  
      }  
      return handler;  
 }  
   
 HandlerIterator::HandlerIterator(HandlerSet& handlerSet)  
 : fOurSet(handlerSet) {  
      reset();  
 }  
   
 HandlerIterator::~HandlerIterator() {  
 }  
   
 void HandlerIterator::reset() {  
      fNextPtr = fOurSet.fHandlers.fNextHandler;  
 }  
   
 HandlerDescriptor* HandlerIterator::next() {  
      HandlerDescriptor* result = fNextPtr;  
      if (result == &fOurSet.fHandlers) { // no more  
           result = NULL;  
      } else {  
           fNextPtr = fNextPtr->fNextHandler;  
      }  
   
      return result;  
 }  
   


< TaskScheduler 사용 >

TaskScheduler* fTask = new TaskScheduler();
...
fTask->turnOnBackgroundReadHandling(sock, &incomingHandler, this); // 소켓 핸들러 등록
...
fTask->turnOffBackgroundReadHandling(sock); // 소켓 핸들러 해제
delete fTask;

static void incomingHandler(void *data, int)
{
    recvfrom();
    ...
}

2015년 11월 25일 수요일

android JNI 를 이용한 c/c++ -> java 메소드 호출 - call method from c/c++ to java with android jni

1. java 에서 호출 메소드 정의
package com.kimdh.dxmediaplayer;
... 
public class DXMediaPlayer {  
   static {  
     System.loadLibrary("DXMediaPlayer");  
   }  
    
   private native void createPlayer();  
   private native void destroyPlayer();  
    
   protected void onDXEvent(int event_type, String strEvent) {  
     ...  
   }  
   ...  
   protected int writeAudioOut(byte[] buffer) {  
     ...  
   }  
 }  

2. java -> c/c++ 함수 호출에서 JavaVM 등 확보
 static JavaVM*  m_pJVM = NULL;  
 jobject    m_object;  
 ...  
 jmethodID    m_method_onDXEvent;  
 ...  
 jmethodID    m_method_writeAudioOut;  
   
 JNIEXPORT void JNICALL Java_com_kimdh_dxmediaplayer_DXMediaPlayer_createPlayer(JNIEnv *env, jobject obj)  
 {  
   m_object = env->NewGlobalRef(obj);  
   
   jclass cls = env->GetObjectClass(m_object);  
   if (cls == NULL) DXPRINTF("Failed to find class\n");  
   
   m_method_onDXEvent = env->GetMethodID(cls, "onDXEvent", "(ILjava/lang/String;)V");  
   if (m_method_onDXEvent == NULL) DXPRINTF("Unable to get method ref : onDXEvent\n");  
   
   m_method_writeAudioOut = env->GetMethodID(cls, "writeAudioOut", "([B)I");
   if (m_method_writeAudioOut == NULL) DXPRINTF("Unable to get method ref : writeAudioOut\n"); 

   if (m_pJVM == NULL)  
     env->GetJavaVM(&m_pJVM);  
 }  
   

3. 호출 함수 정의
 int OnDXEvent(WPARAM wParam, LPARAM lParam)  
 {  
   ...  
   int type = 123;  
   char strEvent[512] = {0};  
   jstring jstrEvent;  
   
   JNIEnv *env;  
    
   int getEnvStat = m_pJVM->GetEnv((void **)&env, JNI_VERSION_1_6);  
   if (getEnvStat == JNI_EDETACHED) {  
     //DXPRINTF("GetEnv: not attached\n");  
     if (m_pJVM->AttachCurrentThread(&env, NULL) != 0) {  
       DXPRINTF("Failed to attach\n");  
       return -1;  
     }  
   } else if (getEnvStat == JNI_OK) {  
     //  
   } else if (getEnvStat == JNI_EVERSION) {  
     DXPRINTF("GetEnv: version not supported\n");  
     return -1;  
   }  
   
   sprintf(strEvent, "event string");  
   
   jstrEvent = env->NewStringUTF(strEvent);  
   
   if (m_method_onDXEvent)  
     env->CallVoidMethod(m_object, m_method_onDXEvent, type, jstrEvent);  
   
   if (env->ExceptionCheck()) {  
     env->ExceptionDescribe();  
   }  
   
   if (getEnvStat == JNI_EDETACHED)  
     m_pJVM->DetachCurrentThread();  
   
   return 0;  
 }  
   
 int WriteAudioOut(unsigned char *buffer, int size)  
 {  
     JNIEnv *env;  
   
     int getEnvStat = m_pJVM->GetEnv((void **)&env, JNI_VERSION_1_6);  
     if (getEnvStat == JNI_EDETACHED) {  
        //DXPRINTF("GetEnv: not attached\n");  
        if (m_pJVM->AttachCurrentThread(&env, NULL) != 0) {  
            DXPRINTF("Failed to attach\n");  
            return -1;  
        }  
     } else if (getEnvStat == JNI_OK) {  
       //  
     } else if (getEnvStat == JNI_EVERSION) {  
        DXPRINTF("GetEnv: version not supported\n");  
        return -1;  
     }  
   
     int ret = -1;  
     if (m_method_writeAudioOut) {  
         jbyteArray jarr = env->NewByteArray(size);  
         jbyte* jbytes = env->GetByteArrayElements(jarr, NULL);  
   
         memcpy(jbytes, buffer, size);  
         env->SetByteArrayRegion(jarr, 0, size, jbytes);  
    
         ret = env->CallIntMethod(m_object, m_method_writeAudioOut, jarr);  
   
         env->ReleaseByteArrayElements(jarr, jbytes, JNI_ABORT);  
     }  
   
     if (env->ExceptionCheck())  
         env->ExceptionDescribe();  
   
     if (getEnvStat == JNI_EDETACHED)  
         m_pJVM->DetachCurrentThread();  
    
     return ret;  
 }  

4. 함수 호출
...
OnDXEvent(NULL, NULL);
...
WriteAudioOut(buf, size);
...

5. JNI 변수 해제
 JNIEXPORT void JNICALL Java_com_kimdh_dxmediaplayer_DXMediaPlayer_destroyPlayer(JNIEnv *env, jobject obj)  
 {  
   env->DeleteGlobalRef(m_object);  
 }  

2015년 8월 31일 월요일

윈도우/리눅스 공통 뮤텍스/쓰레드/이벤트/세마포어 라이브러리

< Mutex.h >
 #ifndef __MUTEX_H__  
 #define __MUTEX_H__  
   
 #ifdef WIN32  
 #include <windows.h>  
 #include <process.h>  
   
 #define MUTEX     HANDLE  
 #define PTHREAD_MUTEX_INITIALIZER     CreateMutex(NULL, FALSE, NULL)  
 #else  
 #include <pthread.h>  
   
 #define MUTEX     pthread_mutex_t  
 #endif  
   
 int MUTEX_INIT(MUTEX *mutex);  
 int MUTEX_LOCK(MUTEX *mutex);  
 int MUTEX_UNLOCK(MUTEX *mutex);  
 int MUTEX_DESTROY(MUTEX *mutex);  
   
 #endif  
   

< Mutex.cpp >
 #include "Mutex.h"  
   
 int MUTEX_INIT(MUTEX *mutex)  
 {  
 #ifdef WIN32  
      *mutex = CreateMutex(NULL, FALSE, NULL);  
      return *mutex == NULL ? -1 : 0;  
 #else  
      pthread_mutexattr_t attr;  
      pthread_mutexattr_init(&attr);  
      pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  
      return pthread_mutex_init(mutex, &attr);  
 #endif  
 }  
   
 int MUTEX_DESTROY(MUTEX *mutex)  
 {  
 #ifdef WIN32  
      if (*mutex) {  
           CloseHandle(*mutex);  
           *mutex = NULL;  
      }  
      return 0;  
 #else  
      return pthread_mutex_destroy(mutex);  
 #endif  
 }  
   
 int MUTEX_LOCK(MUTEX *mutex)  
 {  
 #ifdef WIN32  
      return WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED ? -1 : 0;  
 #else  
      return pthread_mutex_lock(mutex);  
 #endif  
 }  
   
 int MUTEX_UNLOCK(MUTEX *mutex)  
 {  
 #ifdef WIN32  
      return ReleaseMutex(*mutex) == 0 ? -1 : 1;  
 #else  
      return pthread_mutex_unlock(mutex);  
 #endif  
 }  
   

< Mutex 사용 >
 MUTEX mutex;  
 ...  
 MUTEX_INIT(&mutex);  
 ...  
 MUTEX_LOCK(&mutex);  
 ...  
 MUTEX_UNLOCK(&mutex);  
 ...  
 MUTEX_DESTROY(&mutex);  
 static MUTEX static_mutex = PTHREAD_MUTEX_INITIALIZER;  
 ...  
 MUTEX_LOCK(&static_mutex );  
 ...  
 MUTEX_UNLOCK(&static_mutex );  
 ...  

< Thread.h >
 #ifndef __THREAD_H__  
 #define __THREAD_H__  
   
 #ifdef WIN32  
 #include <windows.h>  
 #include <process.h>  
   
 #define THREAD          HANDLE  
 #define THREAD_FUNC     unsigned __stdcall  
 #else  
 #include <pthread.h>  
   
 #define THREAD          pthread_t  
 #define THREAD_FUNC     void*  
 #endif  
   
 int THREAD_CREATE(THREAD *thread, THREAD_FUNC func(void *), void *param);  
 int THREAD_JOIN(THREAD *thread);  
 void THREAD_DESTROY(THREAD *thread);  
   
 #endif  
   

< Thread.cpp >

 #include "Thread.h"  
   
 int THREAD_CREATE(THREAD *thread, THREAD_FUNC func(void *), void *param)  
 {  
 #ifdef WIN32  
      *thread = (HANDLE)_beginthreadex(NULL, 0, func, param, 0, NULL);  
      return *thread == NULL ? -1 : 0;  
 #else  
      return pthread_create(thread, NULL, func, param);  
 #endif  
 }  
   
 int THREAD_JOIN(THREAD *thread)  
 {  
 #ifdef WIN32  
      return WaitForSingleObject(*thread, INFINITE) == WAIT_FAILED ? -1 : 0;  
 #else  
      int status;  
      return pthread_join(*thread, (void **)&status);  
 #endif  
 }  
   
 void THREAD_DESTROY(THREAD *thread)  
 {  
 #ifdef WIN32  
      if (*thread)  
           CloseHandle(*thread);  
 #endif  
      *thread = NULL;  
 }  
   

< Thread 사용 >
 THREAD thread;  
 ...  
 static THREAD_FUNC ThreadFunc(void* lpParam) {  
 ...  
   return 0;  
 }  
   
 THREAD_CREATE(&thread, ThreadFunc, this);  
 ...  
 THREAD_JOIN(&thread);  
 THREAD_DESTROY(&thread);  
   

< Event.h>
 #ifndef __EVENT_H__  
 #define __EVENT_H__  
   
 #ifdef WIN32  
 #include <windows.h>  
 #define     EVENT     HANDLE  
 #else  
 #include <pthread.h>  
   
 typedef struct {  
   pthread_mutex_t mutex;  
   pthread_cond_t cond;  
   bool triggered;  
 } mrevent;  
   
 #define EVENT     mrevent  
 #endif  
   
 void EVENT_INIT(EVENT *event);  
 void EVENT_DESTROY(EVENT *event);  
 void EVENT_WAIT(EVENT *event);  
 void EVENT_SET(EVENT *event);  
 void EVENT_RESET(EVENT *event);  
   
 #endif  
   

< Event.cpp>
 #include "Event.h"  
   
 void EVENT_INIT(EVENT *event)  
 {  
 #ifdef WIN32  
      *event = CreateEvent(NULL, TRUE, FALSE, NULL);  
 #else  
      pthread_mutex_init(&event->mutex, 0);  
      pthread_cond_init(&event->cond, 0);  
      event->triggered = false;  
 #endif  
 }  
   
 void EVENT_DESTROY(EVENT *event)  
 {  
 #ifdef WIN32  
      CloseHandle(*event);  
 #else  
      pthread_mutex_destroy(&event->mutex);  
      pthread_cond_destroy(&event->cond);  
 #endif  
 }  
   
 void EVENT_WAIT(EVENT *event)  
 {  
 #ifdef WIN32  
      WaitForSingleObject(*event, INFINITE);  
 #else  
      pthread_mutex_lock(&event->mutex);  
      while (!event->triggered)  
           pthread_cond_wait(&event->cond, &event->mutex);  
      pthread_mutex_unlock(&event->mutex);  
 #endif  
 }  
   
 void EVENT_SET(EVENT *event)  
 {  
 #ifdef WIN32  
      SetEvent(*event);  
 #else  
   pthread_mutex_lock(&event->mutex);  
   event->triggered = true;  
   pthread_cond_signal(&event->cond);  
   pthread_mutex_unlock(&event->mutex);  
 #endif  
 }  
   
 void EVENT_RESET(EVENT *event)  
 {  
 #ifdef WIN32  
      ResetEvent(*event);  
 #else  
      pthread_mutex_lock(&event->mutex);  
      event->triggered = false;  
      pthread_mutex_unlock(&event->mutex);  
 #endif  
 }  
   

< Event 사용 >
 EVENT event;  
 ...  
 EVENT_INIT(&event);  
 ...  
 EVENT_SET(&event);  
 ...  
 EVENT_RESET(&event);  
 ...  
 EVENT_WAIT(&event);  
 ...  
 EVENT_DESTROY(&event);  

< MySemaphore.h >
 #ifndef __MY_SEMAPHORE_H__  
 #define __MY_SEMAPHORE_H__  
   
 #ifdef WIN32  
 #include <windows.h>  
 #define SEMAPHORE     HANDLE  
 #else  
 #include <semaphore.h>  
 #define SEMAPHORE     sem_t  
 #endif  
   
 int SEM_INIT(SEMAPHORE *sem, int init, int max);  
 int SEM_WAIT(SEMAPHORE *sem);  
 int SEM_POST(SEMAPHORE *sem);  
 int SEM_DESTROY(SEMAPHORE *sem);  
   
 #endif  
   

< MySemaphore.cpp >
 #include "MySemaphore.h"  
   
 int SEM_INIT(SEMAPHORE *sem, int init, int max)  
 {  
 #ifdef WIN32  
      *sem = CreateSemaphore(NULL, init, max, NULL);  
      return *sem == NULL ? -1 : 0;  
 #else  
      return sem_init(sem, 0, init);  
 #endif  
 }  
   
 int SEM_DESTROY(SEMAPHORE *sem)  
 {  
 #ifdef WIN32  
      if (*sem) {  
           CloseHandle(*sem);  
           *sem = NULL;  
      }  
      return 0;  
 #else  
      return sem_destroy(sem);  
 #endif  
 }  
   
 int SEM_WAIT(SEMAPHORE *sem)  
 {  
 #ifdef WIN32  
      return WaitForSingleObject(*sem, INFINITE);  
 #else  
      return sem_wait(sem);  
 #endif  
 }  
   
 int SEM_POST(SEMAPHORE *sem)  
 {  
 #ifdef WIN32  
      return ReleaseSemaphore(*sem, 1, NULL) == TRUE ? 0 : -1;  
 #else  
      return sem_post(sem);  
 #endif  
 }  
   

< MySemaphore 사용 >
 SEMAPHORE sem;  
 ...  
 SEM_INIT(&sem, 0, 10);  
 ...  
 SEM_WAIT(&sem);  
 ...  
 SEM_POST(&sem);  
 ...  
 SEM_DESTROY(&sem);  

2013년 3월 28일 목요일

C++ double linked-list Template Queue

< BufferQueue.h - cpp 만들지 말것, 빌드시 링크에러 발생 >
 #ifndef __BUFFER_QUEUE_H__  
 #define __BUFFER_QUEUE_H__  
   
 #include "Mutex.h"  
 #include "CommonType.h"  
   
 template <typename T>  
 class BufferQueue  
 {  
 public:  
      BufferQueue(int maxQueCount);  
      virtual ~BufferQueue();  
   
      int push_back(T *data);  
      T* pop_front();  
   
      int push_front(T *data);  
      T* pop_back();  
   
      void clear();  
      int count() { return m_nCount; }  
      int maxCount() { return m_nMaxQueCount; }  
      bool isFull();  
   
 protected:  
      int                         m_nMaxQueCount;  
      int                         m_nCount;  
      MUTEX                    m_hMutex;  
   
      template <typename DT>  
      class Element  
      {  
           friend class BufferQueue<DT>;  
   
      private:  
           Element(DT *inData) : data(inData), next(NULL), prev(NULL)  
           {}  
   
           DT *data;  
           Element *next, *prev;  
      };  
   
      Element<T> *m_pHead, *m_pTail;  
 };  
   
 // 템플릿은 미리 컴파일이 되어있어야 함  
 template <typename T>  
 BufferQueue<T>::BufferQueue(int maxQueCount)  
 {  
      m_nMaxQueCount = maxQueCount;  
      m_nCount = 0;  
      m_pHead = m_pTail = NULL;  
      MUTEX_INIT(&m_hMutex);  
 }  
   
 template <typename T>  
 BufferQueue<T>::~BufferQueue()  
 {  
      clear();  
      MUTEX_DESTROY(&m_hMutex);  
 }  
   
 template <typename T>  
 void BufferQueue<T>::clear()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *tmp;  
      T *data;  
   
      while (m_pHead != NULL)  
      {  
           tmp = m_pHead;  
           m_pHead = tmp->next;  
           data = tmp->data;  
           delete data;  
           delete tmp;  
      }  
   
      m_pHead = m_pTail = NULL;  
      m_nCount = 0;  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 template <typename T>  
 int BufferQueue<T>::push_back(T *data)  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (m_nMaxQueCount > 0) {  
           if (m_nCount >= m_nMaxQueCount) {  
                MUTEX_UNLOCK(&m_hMutex);  
                return -1;  
           }  
      }       
   
      Element<T> *elm = new Element<T>(data);  
   
      if (m_pHead == NULL)  
      {  
           m_pHead = elm;  
           m_pTail = elm;  
      }  
      else  
      {  
           elm->prev = m_pTail;  
           m_pTail->next = elm;  
           m_pTail = elm;  
      }  
   
      m_nCount++;  
   
      MUTEX_UNLOCK(&m_hMutex);  
   
      return m_nCount;  
 }  
   
 template <typename T>  
 T* BufferQueue<T>::pop_front()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *elm;  
      T *data = NULL;  
   
      if (m_nCount == 0)  
           goto exit;  
   
      if (m_pHead == NULL)  
           goto exit;  
   
      elm = m_pHead;  
      data = m_pHead->data;  
      m_pHead = m_pHead->next;  
      if (m_pHead) m_pHead->prev = NULL;  
      delete elm;  
   
      if (!m_pHead)  
           m_pTail = NULL;  
   
      m_nCount--;  
   
 exit:  
      MUTEX_UNLOCK(&m_hMutex);  
   
      return data;  
 }  
   
 template <typename T>  
 int BufferQueue<T>::push_front(T *data)  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (m_nMaxQueCount > 0) {  
           if (m_nCount >= m_nMaxQueCount) {  
                MUTEX_UNLOCK(&m_hMutex);  
                return -1;  
           }  
      }       
   
      Element<T> *elm = new Element<T>(data);  
   
      if (m_pHead == NULL)  
      {  
           m_pHead = elm;  
           m_pTail = elm;  
      }  
      else  
      {  
           elm->next = m_pHead;  
           m_pHead->prev = elm;  
           m_pHead = elm;  
      }  
   
      m_nCount++;  
   
      MUTEX_UNLOCK(&m_hMutex);  
   
      return m_nCount;  
 }  
   
 template <typename T>  
 T* BufferQueue<T>::pop_back()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *elm;  
      T *data = NULL;  
   
      if (m_nCount == 0)  
           goto exit;  
   
      if (m_pTail == NULL)  
           goto exit;  
   
      elm = m_pTail;  
      data = m_pTail->data;  
      m_pTail = m_pTail->prev;  
      if (m_pTail) m_pTail->next = NULL;  
      delete elm;  
   
      if (!m_pTail)  
           m_pHead = NULL;  
   
      m_nCount--;  
   
 exit:  
      MUTEX_UNLOCK(&m_hMutex);  
   
      return data;  
 }  
   
 template <typename T>  
 bool BufferQueue<T>::isFull()  
 {  
      if (m_nMaxQueCount > 0) {  
           return m_nCount>=m_nMaxQueCount;  
      }  
   
      return false;  
 }  
   
 #endif     
< BufferQueue 사용 >

class MediaBuffer
{
public:
enum MediaType mediaType;
char *data;
int size;
int streamIndex;
int keyFrame;
__int64 timestamp;
__int64 pos;
}
...
BufferQueue<MediaBuffer> *m_pVideoQue = new BufferQueue<MediaBuffer>(30);
...
MediaBuffer *pBuffer = new MediaBuffer();
if (m_pVideoQue->push_back(pBuffer) < 0)
    delete pBuffer;
...
MediaBuffer *pBuffer = m_pVideoQue->pop_front();
...

* linked-list 로 할 수도 있지만 향후 확장성을 고려하여 double linked-list 로 구현

2012년 6월 8일 금요일

base64 디코딩 - base64 decoding


int b64_decode( char *dest, char *src )
{
const char *dest_start = dest;
int  i_level;
int  last = 0;
int  b64[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
};

for( i_level = 0; *src != '\0'; src++ )
{
int  c;

c = b64[(unsigned int)*src];
if( c == -1 )
{
continue;
}

switch( i_level )
{
case 0:
i_level++;
break;
case 1:
*dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
i_level++;
break;
case 2:
*dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
i_level++;
break;
case 3:
*dest++ = ( ( last &0x03 ) << 6 ) | c;
i_level = 0;
}
last = c;
}

*dest = '\0';

return dest - dest_start;
}

2012년 5월 18일 금요일

ANSI <-> UTF-8 <-> Unicode 변환


int UnicodetoUTF8( LPWSTR pUniStr, int iLength, LPBYTE pUtf8Str )
{
WCHAR wChar;
BYTE szBytes[4] = { 0 };
int nbytes;
int i, j;
int iTotelLen = 0;

for( i = 0; i < iLength; i++ )
{
wChar = pUniStr[i];

if( 0x80 > wChar )
{
nbytes = 1;
szBytes[0] = (BYTE)wChar;
}
else if( 0x800 > wChar )
{
nbytes = 2;
szBytes[1] = ( wChar & 0x3f ) | 0x80;
szBytes[0] = ( ( wChar << 2 ) & 0xcf00 | 0xc000) >> 8;
}
else
{
nbytes = 3;
szBytes[2] = ( wChar & 0x3f ) | 0x80;
szBytes[1] = ( ( wChar << 2 ) & 0x3f00 | 0x8000) >> 8;
szBytes[0] = ( ( wChar << 4 ) & 0x3f0000 | 0xe00000) >> 16;
}

for( j = 0; j < nbytes; j++ )
{
pUtf8Str[iTotelLen] = szBytes[j];
iTotelLen++;
}
}

pUtf8Str[iTotelLen] = '\0';

return iTotelLen;
}

int Utf8ToUnicode( LPBYTE pUtf8Str, LPWSTR pUniStr )
{
int iIndex = 0;
int iCount = 0;
WCHAR wChar;

while( 0 != pUtf8Str[iIndex] )
{
if( ( 0xE0 == ( pUtf8Str[iIndex] & 0xE0 ) ) )
{
wChar = ( ( pUtf8Str[iIndex] & 0x0f ) << 12 ) |
( ( pUtf8Str[iIndex+1]&0x3F ) << 6 ) |
( pUtf8Str[iIndex+2] & 0x3F );

iIndex += 3;
}
else if( 0xC0 == ( pUtf8Str[iIndex] & 0xC0 ) )
{
wChar = ( ( pUtf8Str[iIndex] & 0x1F ) << 6 ) |
( pUtf8Str[iIndex+1] & 0x3F );

iIndex += 2;
}
else
{
wChar = pUtf8Str[iIndex] & 0x7F;

iIndex++;
}

pUniStr[iCount] = wChar;

iCount++;
}

pUniStr[iCount] = 0;

return iCount;
}

//char* UTF8ToANSI(char *pszCode)
int UTF8ToANSI(char *pszCode, char *pszAnsi)
{
BSTR    bstrWide;
//char*   pszAnsi;
int     nLength;

nLength = MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen(pszCode) + 1, NULL, NULL);
bstrWide = SysAllocStringLen(NULL, nLength);

MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen(pszCode) + 1, bstrWide, nLength);

nLength = WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, NULL, 0, NULL, NULL);
//pszAnsi = new char[nLength];

WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, pszAnsi, nLength, NULL, NULL);
SysFreeString(bstrWide);
//return pszAnsi;
return nLength;
}


char * ANSIToUTF8(char * pszCode)
{
int nLength, nLength2;
BSTR bstrCode;
char *pszUTFCode = NULL;

nLength = MultiByteToWideChar(CP_ACP, 0, pszCode, lstrlen(pszCode), NULL, NULL);
bstrCode = SysAllocStringLen(NULL, nLength);
MultiByteToWideChar(CP_ACP, 0, pszCode, lstrlen(pszCode), bstrCode, nLength);


nLength2 = WideCharToMultiByte(CP_UTF8, 0, bstrCode, -1, pszUTFCode, 0, NULL, NULL);
pszUTFCode = (char*)malloc(nLength2+1);
WideCharToMultiByte(CP_UTF8, 0, bstrCode, -1, pszUTFCode, nLength2, NULL, NULL);

return pszUTFCode;
}

< 출처 - http://blog.kangsoo.com/14http://skorea.tistory.com/43 >

2011년 9월 29일 목요일

c++ 에서 객체 delete 했는데 소멸자 호출안되는 경우

c++ 에서 클래스를 동적으로 생성/소멸해서 쓰는 때가 많은데 delete 로 객체를 메모리에서 해제 시켜도 해당 객체의 소멸자가 호출되지않는 귀신이 곡할때가 있다.
바로 아래의 경우에 해당한다.


< MyClass.h 파일 >

class MyClass {
public:
   MyClass();
   ~MyClass();
};

< MyClass.cpp 파일 >

MyClass::MyClass()
{
   printf("contructor\n");
}

MyClass::~MyClass()
{
   printf("destructor\n");
}

< MainClass.h 파일 >

class MyClass;

class MainClass {
...
   MyClass *myClass;
   void free();
};

< MainClass.cpp 파일 >

#include "MainClass.h"

MainClass::MainClass()
{
   myClass = new MyClass();
}
...
void MainClass::free()
{
   delete myClass;   => myClass 객체변수는 분명히 메모리해제되지만 MyClass::~MyClass() 소멸자 함수는 호출되지않는다
}

위와 같은 상황은 MainClass 쪽에서 MyClass 헤더파일을 include 해주지않고
MainClass.h 에 class MyClass; 와 같이 선언만 해주었기때문에 발생한다.
MainClass 에서 myClass 인스턴스의 어떤 변수/함수 필드도 엑세스하지않기때문에
컴파일 에러도 발생하지않으며 delete 시 메모리에서도 정상해제된다. 단지 소멸자 함수만
호출되지않는다.
이런 경우 반드시 MyClass.h 파일을 include 해주어야한다.

< MainClass.cpp >

#include "MyClass.h"   <= !!!

2011년 8월 4일 목요일

미리할당된 버퍼큐(Buffer Queue) 사용하기

링크드리스트로 구성된 메모리가 미리 할당된 버퍼큐
push() 하면 memcpy 가 일어나지만 getBuffer() 함수를 사용하면 push할 버퍼의 포인터를 가져와 엑세스하므로 퍼포먼스를 향상시킬수 있다.

< BufferQueue.h >


#ifndef __BUFFER_QUEUE_H__
#define __BUFFER_QUEUE_H__

#include <windows.h>

#define BUF_SIZE (1024*1024*8)
#define BUF_ELEM_SIZE (30)

typedef struct BufferElement_t {
int len;
char buffer[BUF_SIZE];
BufferElement_t *next;
} BufferElement;

class BufferQueue
{
public:
BufferQueue();
~BufferQueue();

int count;

int push(char *buffer, int len);
BufferElement* pop();
void clear();
BufferElement* getBuffer();
void releaseBuffer();

protected:
BufferElement *head, *tail;
HANDLE hBufferLock;
};

#endif


< BufferQueue.cpp >
#include "BufferQueue.h"

BufferQueue::BufferQueue()
{
count = 0;

hBufferLock = CreateMutex(NULL, FALSE, NULL);

BufferElement *prev = new BufferElement;
prev->len = 0;
head = tail = prev;

for (int i=0; i < BUF_ELEM_SIZE; i++)
{
BufferElement *elem = new BufferElement;
elem->len = 0;
prev->next = elem;
prev = elem;
}

prev->next = head;
}

BufferQueue::~BufferQueue()
{
WaitForSingleObject(hBufferLock, INFINITE);

BufferElement *tmp;
BufferElement *elem = head;

for (int i=0; i < BUF_ELEM_SIZE; i++) {
tmp = elem;
elem = elem->next;
if (tmp)
delete tmp;
}

if (elem)
delete elem;

count = 0;

ReleaseMutex(hBufferLock);

CloseHandle(hBufferLock);
}

BufferElement* BufferQueue::getBuffer()
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count >= BUF_ELEM_SIZE) {
ReleaseMutex(hBufferLock);
return NULL;
}

BufferElement *elem = tail;
tail = tail->next;
count++;

//ReleaseMutex(hBufferLock);

return elem;
}

void BufferQueue::releaseBuffer()
{
ReleaseMutex(hBufferLock);
}

int BufferQueue::push(char *buffer, int len)
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count >= BUF_ELEM_SIZE) {
ReleaseMutex(hBufferLock);
return -1;
}

if (len >= BUF_SIZE) {
ReleaseMutex(hBufferLock);
return -2;
}
tail->len = len;
memcpy(tail->buffer, buffer, len);
tail = tail->next;

count++;

ReleaseMutex(hBufferLock);

return count;
}

BufferElement* BufferQueue::pop()
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count <= 0) {
ReleaseMutex(hBufferLock);
return NULL;
}

BufferElement *elem = head;
head = head->next;
count--;

ReleaseMutex(hBufferLock);

return elem;
}

void BufferQueue::clear()
{
WaitForSingleObject(hBufferLock, INFINITE);
count = 0;
tail = head;
ReleaseMutex(hBufferLock);
}