2012년 6월 20일 수요일

vc++ 프로젝트 배포를 위한 manifest와 dll 링크

vc++ 로 작성된 프로젝트를 배포하다보면 타겟 PC에서
"응용 프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다"
라는 메시지를 출력하면서 실행에 실패하는 경우가 있다. 이것은 dll dependency 에 따른 dll 이 타겟 PC 에 존재하지 않거나 dll 버전이 맞지 않아서 생기는 현상이다.


이 문제를 해결하기 위해서는 올바른 dll 들을 같이 묶어서 배포해야한다.
올바른 버전의 dll 을 찾아내려면 manifest 파일을 봐야하는데 vc++ 프로젝트 속성에 디폴트로 embed manifest 로 되어있다. 따라서 manifest 정보를 보려면 프로젝트 output 파일(exe/dll/ocx)을 텍스트 에디터로 직접 열어서 manifest 정보를 보거나 xxx.intermediate.manifest 파일을 열어봐야한다.
manifest 정보를 보면 대게 아래와 같이 나오는데


<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level='asInvoker' uiAccess='false' />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.MFC' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
</assembly>

위와 같이 manifest 정보가 구성되어있을때 Microsoft.VC90.CRT 와 Microsoft.VC90.MFC 두개의 dll 들을 찾아야 하는데 이것은 각각 msvcr90.dll 과 mfc90.dll 이다.
어느 dll 인지 찾으려면 dependency walker 를 이용하면된다. 해당 dll 파일을 찾으려면

C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86

아래를 뒤져서 파일을 찾으면 되는데 경우에 따라 dll 버전이 또 다를수 있다. (속성->버전정보)
이럴 경우

C:\WINDOWS\WinSxS

아래를 뒤져서 정확히 버전이 일치하는 dll 파일을 찾아야한다.


2012년 6월 8일 금요일

H.264 RFC3984 NAL 패킷처리 - H.264 RFC3984 NAL Packet Handling

 class RTPSource  
 {  
      uint8_t*               fFrameBuf;    // 프레임 버퍼 - 한개 프레임 될때까지 채움
      int                    fFrameBufPos; // 프레임 버퍼 인덱스 - 현재 프레임 버퍼 사이즈 
      FrameHandlerFunc       fFrameHandlerFunc;        // 콜백함수 - 한개 프레임이 완성되면 호출
      void*                  fFrameHandlerFuncData;    // 콜백함수 데이터
      
      uint8_t*               fExtraData;        // SPS/PPS 정보
      unsigned               fExtraDataSize;
      ...  
 }

int trimStartCode(uint8_t *buf, int len)  
 {  
      uint8_t *ptr = buf;  
      if (len < 4) return 0;  
   
      // trying to find 0x00 0x00 ... 0x01 pattern  
      // find first 0x00 0x00 bytes  
      if (ptr[0] == 0x00 && ptr[1] == 0x00) {  
           // find first 0x01  
           while (*ptr == 0x00 && ptr < buf+len-1)  
                ptr++;  
   
           if (*ptr != 0x01) {     // error - invalid stream  
                DPRINTF("invalid stream, 0x%02x\n", *ptr);  
                ptr = buf;  
           } else {  
                ptr++;  
           }  
      }  
   
      return ptr-buf;  
 }  

void RTPSource::copyToFrameBuffer(uint8_t *buf, int len)  
 {  
      if (fFrameBufPos+len >= FRAME_BUFFER_SIZE) {  
           DPRINTF("RTP Frame Buffer overflow %s\n", fCodecName);  
           fFrameBufPos = 0;  
      }  
      memmove(&fFrameBuf[fFrameBufPos], buf, len);  
      fFrameBufPos += len;  
 }  
   
 void RTPSource::resetFrameBuf()  
 {  
      fFrameBufPos = 0;  
 }  
   
 uint64_t RTPSource::getMediaTimestamp(uint32_t timestamp)  
 {  
      uint64_t msec = 1000;  
      uint64_t time_msec = timestamp*msec/fTimestampFrequency;  
      return time_msec;  
 }  
   
 void H264RTPSource::putStartCode()  
 {  
      fFrameBuf[fFrameBufPos++] = 0x00;  
      fFrameBuf[fFrameBufPos++] = 0x00;  
      fFrameBuf[fFrameBufPos++] = 0x00;  
      fFrameBuf[fFrameBufPos++] = 0x01;  
 }  
   
 void H264RTPSource::processFrame(RTPPacketBuffer *packet)  
 {  
      uint8_t *buf = (uint8_t *)packet->payload();  
      int len = packet->payloadLen();  
   
      int offset = trimStartCode(buf, len);  
      buf = &buf[offset];  
      len -= offset;  
   
      uint8_t *buf_ptr = buf;  
      bool isCompleteFrame = false;  
   
      uint32_t media_timestamp = getMediaTimestamp(packet->timestamp());  
   
      uint8_t nalUnitType = (buf[0]&0x1F);  
   
      if (RTSPCommonEnv::nDebugFlag&DEBUG_FLAG_RTP_PAYLOAD)  
           DPRINTF("nal_type: %d, size: %d\n", nalUnitType, len);  
   
      if (!fIsStartFrame) {  
           if (fExtraData) {  
                putStartCode();  
                copyToFrameBuffer(fExtraData, fExtraDataSize);  
           }  
           fIsStartFrame = true;  
      }  
   
      switch (nalUnitType)  
      {  
      case 28: {     // FU-A  
           uint8_t startBit = buf[1]&0x80;  
           uint8_t endBit = buf[1]&0x40;  
   
           if (startBit) {  
                buf_ptr++; len--;  
                buf[1] = (buf[0]&0xE0) + (buf[1]&0x1F);  
                putStartCode();  
           } else {  
                buf_ptr += 2; len -= 2;  
           }  
   
           copyToFrameBuffer(buf_ptr, len);  
           isCompleteFrame = (endBit != 0);            
           break;  
                 }  
      case 5: {     // IDR-Picture  
           putStartCode();  
           copyToFrameBuffer(buf_ptr, len);  
           isCompleteFrame = true;  
           break;  
                }  
      case 7: {     // SPS  
           putStartCode();  
           copyToFrameBuffer(buf_ptr, len);  
           isCompleteFrame = false;  
           break;  
                }  
      case 8: {     // PPS  
           putStartCode();  
           copyToFrameBuffer(buf_ptr, len);  
           isCompleteFrame = false;  
           break;  
                }  
      case 24: {     // STAP-A  
           buf_ptr++; len--;  
           while (len > 3)  
           {  
                uint16_t staplen = (buf_ptr[0]<<8) | (buf_ptr[1]);  
                if (staplen > len) {  
                     DPRINTF("STAP-A process error, staplen: %d, len\n", staplen, len);  
                     break;  
                }  
   
                buf_ptr += 2; len -= 2;  
                nalUnitType = buf_ptr[0]&0x1F;  
   
                putStartCode();  
                copyToFrameBuffer(buf_ptr, staplen);  
   
                buf_ptr += staplen; len -= staplen;  
   
                if (fFrameHandlerFunc)  
                     fFrameHandlerFunc(fFrameHandlerFuncData, fFrameType, media_timestamp, fFrameBuf, fFrameBufPos);  
                resetFrameBuf();  
           }  
           break;  
                 }  
      default:  
           putStartCode();  
           copyToFrameBuffer(buf_ptr, len);  
           isCompleteFrame = true;  
           break;  
      }  
   
      if (isCompleteFrame) {  
           if (fFrameHandlerFunc)  
                fFrameHandlerFunc(fFrameHandlerFuncData, fFrameType, media_timestamp, fFrameBuf, fFrameBufPos);  
           resetFrameBuf();  
      }  
 }  
   

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년 6월 5일 화요일

TCP 소켓 Connection 타임아웃 주기(윈도우/리눅스 공통) - TCP Socket Connection Timeout


TCP 소켓통신에서 서버로 연결할때 상대서버가 동작하지 않거나 연결에 문제가 생겼을때
connect 함수에서 블러킹이 걸려 한참 동안 빠져나오지 못할때가 있다.
이런 현상을 방지하려면 소켓을 non-blocking 으로 만든다음 타임아웃을 주고 connect 후
select 함수에서 연결결과를 감지하면 된다.

 #ifdef WIN32  
   
 #include <WinSock2.h>  
 #include <ws2tcpip.h>  
   
 #define closeSocket     closesocket  
 #define EWOULDBLOCK     WSAEWOULDBLOCK  
 #define EINPROGRESS WSAEWOULDBLOCK  
 #define EINTR          WSAEINTR  
     
 #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>  
 #endif  
   
 #ifdef WIN32  
 #define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/  
 #define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/  
 int initializeWinsockIfNecessary(void) {  
      /* We need to call an initialization routine before  
      * we can do anything with winsock. (How fucking lame!):  
      */  
      static int _haveInitializedWinsock = 0;  
      WSADATA     wsadata;  
   
      if (!_haveInitializedWinsock) {  
           if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0)  
                && ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) {  
                     return 0; /* error in initialization */  
           }  
           if ((wsadata.wVersion != WS_VERSION_CHOICE1)  
                && (wsadata.wVersion != WS_VERSION_CHOICE2)) {  
                     WSACleanup();  
                     return 0; /* desired Winsock version was not available */  
           }  
           _haveInitializedWinsock = 1;  
      }  
   
      return 1;  
 }  
 #else  
 #define initializeWinsockIfNecessary()     1  
 #endif  
    
 int makeSocketNonBlocking(int sock)  
 {  
 #ifdef WIN32  
      unsigned long arg = 1;  
      return ioctlsocket(sock, FIONBIO, &arg) == 0;  
 #else  
      int curFlags = fcntl(sock, F_GETFL, 0);  
      return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;  
 #endif  
 }  
   
 static int reuseFlag = 1;  
   
 int setupStreamSock(short port, int makeNonBlocking)  
 {  
      if (!initializeWinsockIfNecessary()) {  
           socketErr("[%s] Failed to initialize 'winsock': ", __FUNCTION__);  
           return -1;  
      }  
   
      int newSocket = socket(AF_INET, SOCK_STREAM, 0);  
      if (newSocket < 0) {  
           DPRINTF("%s:%d\n",__FUNCTION__,__LINE__);  
           socketErr("[%s] unable to create stream socket: ", __FUNCTION__);  
           return newSocket;  
      }  
  
      if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,  
           (const char*)&reuseFlag, sizeof reuseFlag) != 0) {  
                socketErr("[%s] setsockopt(SO_REUSEADDR) error: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
      }  
 
      struct sockaddr_in c_addr;  
      memset(&c_addr, 0, sizeof(c_addr));  
      c_addr.sin_addr.s_addr = INADDR_ANY;  
      c_addr.sin_family = AF_INET;  
      c_addr.sin_port = htons(port);  
   
      if (bind(newSocket, (struct sockaddr*)&c_addr, sizeof c_addr) != 0) {  
           socketErr("[%s] bind() error (port number: %d): ", __FUNCTION__, port);  
           closeSocket(newSocket);  
           return -1;  
      }  
   
      if (makeNonBlocking) {  
           if (!makeSocketNonBlocking(newSocket)) {  
                socketErr("[%s] failed to make non-blocking: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
           }  
      }  
   
      return newSocket;  
 }  
   
 int connectToServer(char *svrIP, int svrPort)  
 {  
      int sock = setupStreamSocket(0, 1);  
      int ret, err;  
   
      struct sockaddr_in svr_addr;  
      memset(&svr_addr, 0, sizeof(svr_addr));  
      svr_addr.sin_addr.s_addr = inet_addr(svrIP);  
      svr_addr.sin_family = AF_INET;  
      svr_addr.sin_port = htons(svrPort);  
   
      fd_set set;  
      FD_ZERO(&set);  
      timeval tvout = {2, 0};  // 2 seconds timeout  
   
      FD_SET(sock, &set);  
   
      if ((ret=connect(sock, (struct sockaddr *)&svr_addr, sizeof(svr_addr))) != 0) {  
           err = WSAGetLastError();  
           if (err != EINPROGRESS && err != EWOULDBLOCK) {  
                printf("connect() failed : %d\n", err);  
                return -1;  
           }  
           if (select(sock+1, NULL, &set, NULL, &tvout) <= 0) {  
                printf("select/connect() failed : %d\n", WSAGetLastError());  
                return -1;  
           } 
           err = 0;
	   socklen_t len = sizeof(err);
	   if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0 ) {
		printf("getsockopt() error: %d\n", err);
		return -1;
	   }
      }  
      return 0;  
 }  

c# 소스코드