2015년 12월 10일 목요일

java/안드로이드 넌블러킹 소켓을 사용하는 TCP 클라이언트/서버 소스 - java/android nonblocking socket tcp server/client source code

자바/안드로이드 환경에서 TCP 서버는 ServerSocketChannel 클래스를 사용해서 넌블러킹 TCP 서버소켓을 생성후 Selector 를 통해 소켓 select 함수 기능을 수행한다.(소켓 다중화)
TCP 클라이언트는 SocketChannel 클래스를 사용해서 넌블러킹 소켓을 구현한다.

* 서버 종료시 현재 연결된 클라이언트들에 대해 3-way handshake 를 통해 자연스럽게 연결종료 시키는 것에 주목(stopServer 메소드)

< TCPServer.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.ServerSocketChannel;  
 import java.nio.channels.SocketChannel;  
 import java.util.ArrayList;  
 import java.util.Iterator;  
 import java.util.List;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpServer {  
      protected ServerSocketChannel mChannel;  
      protected ServerThread mThread;  
        
      private static final int BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isOpened() {  
           if (mChannel == null) return false;  
           return mChannel.isOpen();  
      }       
   
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
        
      public boolean startServer(int port, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
             
           try {  
                if (mChannel != null) return false;  
                  
                mChannel = ServerSocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().bind(new InetSocketAddress(port));  
                  
                Selector selector = Selector.open();  
                mChannel.register(selector, SelectionKey.OP_ACCEPT);  
                  
                mThread = new ServerThread(mChannel, selector, handler);                                
                mThread.start();                 
                  
                return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }            
      }  
        
      public void stopServer() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {       
                     mThread.closeAllClient();  
                       
                     while (mThread.getClientCount() > 0)  
                          Thread.sleep(100);  
                       
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }            
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp server channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }            
      }  
        
      public void closeAllClient() {  
           setNetworkThreadPolicy();  
           if (mChannel != null)       
                mThread.closeAllClient();            
      }  
        
      public boolean send(SocketChannel client, ByteBuffer buffer) {  
           try {  
                if (client.write(buffer) == buffer.limit())   
                     return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
           return false;  
      }       
        
      public interface ReceiveEventHandler {  
           public void onClientConnected(SocketChannel client);  
           public void onReceived(SocketChannel client, ByteBuffer buffer, int len);  
           public void onClientDisconnected(SocketChannel client);  
      }       
        
      protected class ServerThread extends Thread {  
           private ServerSocketChannel mChannel;  
           private Selector mSelector;  
           private List<SocketChannel> mClientList = new ArrayList<SocketChannel>();  
             
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           public ServerThread(ServerSocketChannel channel, Selector selector, ReceiveEventHandler handler) {  
                mChannel = channel;  
                mSelector = selector;  
                mIsRunning = true;  
                mHandler = handler;  
           }  
             
           public void closeAllClient() {  
                synchronized (mClientList) {  
                     for (int i=0; i<mClientList.size(); i++) {  
                          try {  
                               mClientList.get(i).socket().shutdownOutput();  
                          } catch (Exception ex) {  
                               ex.printStackTrace();  
                          }  
                     }                      
                }  
           }  
             
           public int getClientCount() {   
                synchronized (mClientList) {  
                     return mClientList.size();                      
                }                 
           }  
             
           @Override  
           public void run() {  
                System.out.println("server thread start");  
                  
                try {  
                     while (mIsRunning) {  
                          mSelector.select(2*1000);  
                            
                          Set keys = mSelector.selectedKeys();  
                          Iterator i = keys.iterator();  
                            
                          while (i.hasNext()) {  
                               SelectionKey key = (SelectionKey)i.next();  
                               i.remove();  
                                 
                               if (key.isAcceptable()) {  
                                    SocketChannel client = mChannel.accept();  
                                    client.configureBlocking(false);  
                                    client.register(mSelector, SelectionKey.OP_READ);  
                                    synchronized (mClientList) {  
                                         mClientList.add(client);                                          
                                    }                                     
                                    System.out.println("client connected : " + client.socket().getRemoteSocketAddress().toString());  
                                    if (mHandler != null) mHandler.onClientConnected(client);  
                                    continue;  
                               }  
                                 
                               if (key.isReadable()) {  
                                    SocketChannel client = (SocketChannel)key.channel();  
                                    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                    buffer.clear();  
                                      
                                    int ret;  
                                    try {  
                                         ret = client.read(buffer);  
                                    } catch (Exception ex) {  
                                         ex.printStackTrace();  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                          
                                         System.out.println("client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    if (ret <= 0) {  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                                                                       
                                         System.out.println("socket read : " + ret + ", client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    buffer.rewind();  
                                    if (mHandler != null) mHandler.onReceived(client, buffer, ret);  
                               }  
                          }  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("server thread end");  
           }            
      }  
 }  
   

< TCPClient.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.io.IOException;  
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.SocketChannel;  
 import java.util.Iterator;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpClient {  
      protected SocketChannel mChannel;  
      protected ReceiveThread mThread;  
        
      public int RECV_BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isConnected() {  
           if (mChannel == null) return false;  
           return mChannel.isConnected();  
      }  
        
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
             
      public boolean connect(String ipAddress, short port, int timeout, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
                                 
           try     {  
                if (mChannel != null && mChannel.isConnected() == true)  
                     return false;  
                  
                mChannel = SocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().setReceiveBufferSize(RECV_BUFFER_SIZE);  
                  
                mChannel.connect(new InetSocketAddress(ipAddress, port));  
                  
                Selector selector = Selector.open();                 
                SelectionKey clientKey = mChannel.register(selector, SelectionKey.OP_CONNECT);  
                                 
                if (selector.select(timeout*1000) > 0) {  
                     if (clientKey.isConnectable()) {  
                          if (mChannel.finishConnect()) {  
                               mThread = new ReceiveThread(mChannel, handler);  
                               mThread.start();  
                               return true;  
                          }  
                     }                      
                     mChannel.close();  
                     mChannel = null;  
                     return false;                      
                } else {  
                     return false;  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }       
      }  
        
      public void close() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {                      
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }  
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp client channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
      }  
             
      public int send(ByteBuffer buffer) {  
           setNetworkThreadPolicy();  
           if (mChannel == null || !mChannel.isConnected()) return -1;            
           try {  
                return mChannel.write(buffer);  
           } catch (IOException ex) {  
                ex.printStackTrace();  
                return -1;  
           }            
      }  
                  
      public interface ReceiveEventHandler {  
           public void onReceived(ByteBuffer buffer, int len);  
           public void onClosed();  
           public void onThreadEvent();  
      }  
             
      protected class ReceiveThread extends Thread {  
           private SocketChannel mChannel;  
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           private static final int BUFFER_SIZE = 1024 * 4;  
             
           public ReceiveThread(SocketChannel channel, ReceiveEventHandler handler) {  
                mChannel = channel;                 
                mHandler = handler;  
                mIsRunning = true;  
           }  
             
           @Override  
           public void run() {  
                System.out.println("receive thread start");  
                  
                try {  
                     Selector selector = Selector.open();  
                     mChannel.register(selector, SelectionKey.OP_READ);  
                  
                     while (mIsRunning) {                 
                          if (selector.select(2*1000) > 0) {  
                               Set<SelectionKey> selectedKey = selector.selectedKeys();  
                               Iterator<SelectionKey> iterator = selectedKey.iterator();  
                                 
                               while (iterator.hasNext()) {  
                                    SelectionKey key = iterator.next();  
                                    iterator.remove();  
                                      
                                    if (key.isReadable()) {  
                                         SocketChannel channel = (SocketChannel)key.channel();  
                                         ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                         buffer.clear();  
                                           
                                         int ret;  
                                         try {  
                                              ret = channel.read(buffer);  
                                         } catch (IOException ex) {  
                                              ex.printStackTrace();  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         if (ret <= 0) {  
                                              System.out.println("SocketChannel.read returned " + ret);  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         buffer.rewind();                                                                             
                                                                                   
                                         if (mHandler != null) mHandler.onReceived(buffer, ret);  
                                    }                                     
                               }  
                          }  
                          if (mHandler != null) mHandler.onThreadEvent();  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("receive thread end");  
           }            
      }  
 }  
   


< TCP 서버 사용 >

public class MainActivity extends Activity implements TcpServer.ReceiveEventHandler {
private TcpServer mServer = new TcpServer();

private byte[] mReceiveBuffer = new byte[1024*4];
private int mReceiveBufferIndex = 0;
        ...
public void onClick(View v) {
        ...
mServer.startServer(8112, this);
    }

protected void onDestroy() {
        ...
mServer.stopServer();
    }

    @Override
public void onClientConnected(SocketChannel client) {
            // do something when client connected

    @Override
public void onReceived(SocketChannel client, ByteBuffer buffer, int len) {
System.out.println("onReceived : " + len);
buffer.get(mReceiveBuffer, mReceiveBufferIndex, len);
mReceiveBufferIndex += len;
// process receive buffer
         ...
         ByteBuffer response = ByteBuffer.allocate(len+4);
         buffer.putInt(len);
         buffer.put(4, payload);
         buffer.rewind();
         mServer.send(client, response);
}

    @Override
public void onClientDisconnected(SocketChannel client) {
// do something when client disconnected
}
}

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년 12월 2일 수요일

윈도우/리눅스/안드로이드 공통 소켓 라이브러리 소스 - common socket library source code for window/linux/android

live555 의 소켓관련 소스를 기본으로 윈도우/리눅스/안드로이드 환경에서 공통으로 사용가능한 소켓 라이브러리 소스이다. 자신의 환경에 맞게 약간의 수정만 하면 각 플랫폼에서 쉽게 사용가능하다.

< 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  
   

< SockCommon.h >
 #ifndef __SOCK_COMMON_H__  
 #define __SOCK_COMMON_H__  
   
 #include "NetCommon.h"  
   
 int setupStreamSock(short port, int makeNonBlocking);  
 int setupDatagramSock(short port, int makeNonBlocking);  
 int setupServerSock(short port, int makeNonBlocking);  
 int setupClientSock(int serverSock, int makeNonBlocking, struct sockaddr_in& clientAddr);  
 int makeSocketNonBlocking(int sock);  
   
 int makeTCP_NoDelay(int sock);  
   
 unsigned setSendBufferTo(int sock, unsigned requestedSize);  
 unsigned setReceiveBufferTo(int sock, unsigned requestedSize);  
 unsigned getSendBufferSize(int sock);  
 unsigned getReceiveBufferSize(int sock);  
   
 unsigned getBufferSize(int bufOptName, int sock);  
 unsigned setBufferSizeTo(int bufOptName, int sock, int requestedSize);  
   
 int blockUntilReadable(int sock, struct timeval* timeout);  
   
 int readSocket1(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in &fromAddress);  
 int readSocket(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in &fromAddress, struct timeval *timeout = NULL);  
 int readSocketExact(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in &fromAddress, struct timeval *timeout = NULL);  
   
 int writeSocket(int sock, char *buffer, unsigned bufferSize);  
 int writeSocket(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in& toAddress);  
   
 int sendRTPOverTCP(int sock, char *buffer, int len, unsigned char streamChannelId);  
   
 void shutdown(int sock);  
   
 bool isMulticastAddress(unsigned int address);  
 bool socketJoinGroupSSM(int sock, unsigned int groupAddress, unsigned int sourceFilterAddr);  
 bool socketLeaveGroupSSM(int sock, unsigned int groupAddress, unsigned int sourceFilterAddr);  
 bool socketJoinGroup(int sock, unsigned int groupAddress);  
 bool socketLeaveGroup(int sock, unsigned int groupAddress);  
   
 unsigned int ourIPAddress();  
   
 extern unsigned int ReceivingInterfaceAddr;  
 #endif  
   

< SockCommon.cpp >
 #include "SockCommon.h"  
 #include "RTSPCommonEnv.h"  
 #include <stdio.h>  
   
 #ifdef WIN32  
 #pragma comment(lib, "ws2_32.lib")  
 #elif defined(LINUX)  
 #include <string.h>  
 #endif  
   
 #define MAKE_SOCKADDR_IN(var,adr,prt) /*adr,prt must be in network order*/\  
   struct sockaddr_in var;\  
   var.sin_family = AF_INET;\  
   var.sin_addr.s_addr = (adr);\  
   var.sin_port = htons(prt);\  
   
 #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  
   
 void socketErr(char *lpszFormat,...)  
 {  
      va_list args;  
      int len;  
      char *buffer;  
   
      va_start(args, lpszFormat);  
   
      len = _vscprintf(lpszFormat, args) + 32;  
      buffer = (char *)malloc(len * sizeof(char));  
   
      vsprintf(buffer, lpszFormat, args);  
   
 #ifdef WIN32  
      if (RTSPCommonEnv::nDebugPrint == 0) {  
           fprintf(stdout, buffer);  
           fprintf(stdout, "%d\n", WSAGetLastError());  
      } else if (RTSPCommonEnv::nDebugPrint == 1) {  
           OutputDebugString(buffer);  
           char tmp[16] = {0};  
           sprintf(tmp, "%d\n", WSAGetLastError());  
           OutputDebugString(tmp);  
      }  
 #elif defined(ANDROID)  
      DPRINTF0(buffer);  
      char tmp[16] = {0};  
      sprintf(tmp, "%d\n", WSAGetLastError());  
      DPRINTF0(tmp);  
 #else  
      fprintf(stdout, buffer);  
      fprintf(stdout, "%d\n", WSAGetLastError());  
 #endif  
   
      free(buffer);  
 }  
   
 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 0  
      if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,  
           (const char*)&reuseFlag, sizeof reuseFlag) != 0) {  
                socketErr("[%s] setsockopt(SO_REUSEADDR) error: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
      }  
 #endif  
      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 setupDatagramSock(short port, int makeNonBlocking)  
 {  
      if (!initializeWinsockIfNecessary()) {  
           socketErr("[%s] Failed to initialize 'winsock': ", __FUNCTION__);  
           return -1;  
      }  
   
      int newSocket = socket(AF_INET, SOCK_DGRAM, 0);  
      if (newSocket < 0) {  
           socketErr("[%s] unable to create datagram socket: ", __FUNCTION__);  
           return newSocket;  
      }  
 #if 0  
      if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,  
           (const char*)&reuseFlag, sizeof reuseFlag) < 0) {  
                socketErr("setsockopt(SO_REUSEADDR) error: ", __FUNCTION__);  
                closeSocket(newSocket);  
                return -1;  
      }  
 #endif  
      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;  
 }  
   
 #define LISTEN_BACKLOG_SIZE 20  
   
 int setupServerSock(short port, int makeNonBlocking)  
 {  
      int sock = setupStreamSock(port, makeNonBlocking);  
      if (sock < 0) return sock;  
   
      if (listen(sock, LISTEN_BACKLOG_SIZE) != 0) {  
           socketErr("[%s] failed to listen sock: ", __FUNCTION__);  
           closeSocket(sock);  
           return -1;  
      }  
   
      return sock;  
 }  
   
 int setupClientSock(int serverSock, int makeNonBlocking, struct sockaddr_in& clientAddr)  
 {  
      socklen_t clientAddrLen = sizeof clientAddr;  
      int clientSock = accept(serverSock, (struct sockaddr*)&clientAddr, &clientAddrLen);  
      if (clientSock < 0) {  
           int err = WSAGetLastError();  
           if (err != EWOULDBLOCK) {  
                socketErr("[%s] accept() failed: ", __FUNCTION__);  
                closeSocket(clientSock);  
                return -1;  
           }  
           return 0;  
      }  
      makeSocketNonBlocking(clientSock);  
   
      return clientSock;  
 }  
   
 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  
 }  
   
 int makeTCP_NoDelay(int sock)  
 {  
      int flag = 1;  
      int err = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));  
   
      if(err != 0)  
           socketErr("[%s] setsocket TCPNODELAY error: ", __FUNCTION__);  
   
      return 0;  
 }  
   
 unsigned setSendBufferTo(int sock, unsigned requestedSize)  
 {  
      return setBufferSizeTo(SO_SNDBUF, sock, requestedSize);  
 }  
   
 unsigned setReceiveBufferTo(int sock, unsigned requestedSize)  
 {  
      return setBufferSizeTo(SO_RCVBUF, sock, requestedSize);  
 }  
   
 unsigned getSendBufferSize(int sock)  
 {  
      return getBufferSize(SO_SNDBUF, sock);  
 }  
   
 unsigned getReceiveBufferSize(int sock)  
 {  
      return getBufferSize(SO_RCVBUF, sock);  
 }  
   
 unsigned getBufferSize(int bufOptName, int sock)  
 {  
      unsigned curSize;  
      socklen_t sizeSize = sizeof curSize;  
      if (getsockopt(sock, SOL_SOCKET, bufOptName,  
           (char*)&curSize, &sizeSize) < 0) {  
                socketErr("getBufferSize() error: ", __FUNCTION__);  
                return 0;  
      }  
   
      return curSize;  
 }  
   
 unsigned setBufferSizeTo(int bufOptName, int sock, int requestedSize)  
 {  
      socklen_t sizeSize = sizeof requestedSize;  
      if (setsockopt(sock, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize) != 0)  
           socketErr("setBufferSizeTo() error: ", __FUNCTION__);  
      return getBufferSize(bufOptName, sock);  
 }  
   
 int blockUntilReadable(int sock, timeval *timeout)  
 {  
      int result = -1;  
   
      do {  
           fd_set rd_set;  
           FD_ZERO(&rd_set);  
           if (sock < 0) break;  
   
           FD_SET((unsigned) sock, &rd_set);  
           const unsigned numFds = sock+1;  
   
           result = select(numFds, &rd_set, NULL, NULL, timeout);  
           if (timeout != NULL && result == 0) {  
                break; // this is OK - timeout occurred  
           } else if (result <= 0) {  
                int err = WSAGetLastError();  
                if (err == EINTR || err == EWOULDBLOCK) continue;  
                socketErr("[%s] select() error: ", __FUNCTION__);  
                break;  
           }  
   
           if (!FD_ISSET(sock, &rd_set)) {  
                socketErr("[%s] select() error - !FD_ISSET", __FUNCTION__);  
                break;  
           }  
      } while (0);  
   
      return result;  
 }  
   
 int readSocket1(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in &fromAddress)  
 {  
      int bytesRead;  
      socklen_t addressSize = sizeof fromAddress;  
   
      bytesRead = recvfrom(sock, buffer, bufferSize, 0, (struct sockaddr*)&fromAddress, &addressSize);  
   
      return bytesRead;  
 }  
   
 int readSocket(int sock, char *buffer, unsigned int bufferSize, sockaddr_in &fromAddress, timeval *timeout)  
 {  
      int bytesRead = -1;  
   
      do {  
           int result = blockUntilReadable(sock, timeout);  
           if (timeout != NULL && result == 0) {  
                bytesRead = 0;  
                break;  
           } else if (result <= 0) {  
                break;  
           }  
   
           socklen_t addressSize = sizeof fromAddress;  
           bytesRead = recvfrom(sock, buffer, bufferSize, 0,  
                (struct sockaddr*)&fromAddress,  
                &addressSize);  
           if (bytesRead < 0) {  
                int err = WSAGetLastError();  
                if (err == 111 /*ECONNREFUSED (Linux)*/  
                     // What a piece of crap Windows is. Sometimes  
                     // recvfrom() returns -1, but with an 'errno' of 0.  
                     // This appears not to be a real error; just treat  
                     // it as if it were a read of zero bytes, and hope  
                     // we don't have to do anything else to 'reset'  
                     // this alleged error:  
                     || err == 0 || err == EWOULDBLOCK  
                     || err == 113 /*EHOSTUNREACH (Linux)*/) {  
                     //Why does Linux return this for datagram sock?  
                     fromAddress.sin_addr.s_addr = 0;  
                     return 0;  
                }  
                socketErr("[%s] recvfrom() error: ", __FUNCTION__);  
                break;  
           }  
      } while (0);  
   
      return bytesRead;  
 }  
   
 int readSocketExact(int sock, char *buffer, unsigned bufferSize, struct sockaddr_in& fromAddress, struct timeval* timeout)  
 {  
      int bsize = bufferSize;  
      int bytesRead = 0;  
      int totBytesRead = 0;  
      do   
      {  
           bytesRead = readSocket (sock, buffer + totBytesRead, bsize, fromAddress, timeout);  
           if (bytesRead <= 0) break;  
           totBytesRead += bytesRead;  
           bsize -= bytesRead;  
      } while (bsize != 0);  
   
      return totBytesRead;  
 }  
   
 int writeSocket(int sock, char *buffer, unsigned bufferSize)  
 {  
      return send(sock, buffer, bufferSize, 0);  
 }  
   
 int writeSocket(int sock, char *buffer, unsigned int bufferSize, sockaddr_in &toAddress)  
 {  
      return sendto(sock, buffer, bufferSize, 0, (struct sockaddr *)&toAddress, sizeof(struct sockaddr_in));  
 }  
   
 bool writeSocket(int socket, struct in_addr address, unsigned short port,  
                      unsigned char* buffer, unsigned bufferSize)   
 {  
  do {  
       MAKE_SOCKADDR_IN(dest, address.s_addr, port);  
       int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0, (struct sockaddr*)&dest, sizeof dest);  
       if (bytesSent != (int)bufferSize) {  
            char tmpBuf[100];  
            sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);  
            socketErr(tmpBuf);  
            break;  
       }  
   
       return true;  
  } while (0);  
   
  return false;  
 }  
   
 bool writeSocket(int socket, struct in_addr address, unsigned short port,  
                      unsigned char ttlArg,  
                      unsigned char* buffer, unsigned bufferSize)   
 {  
      // Before sending, set the socket's TTL:  
 #if defined(__WIN32__) || defined(_WIN32)  
 #define TTL_TYPE int  
 #else  
 #define TTL_TYPE u_int8_t  
 #endif  
      TTL_TYPE ttl = (TTL_TYPE)ttlArg;  
      if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl) < 0) {  
           socketErr("setsockopt(IP_MULTICAST_TTL) error: ");  
           return false;  
      }  
   
      return writeSocket(socket, address, port, buffer, bufferSize);  
 }  
   
 int sendRTPOverTCP(int sock, char *buffer, int len, unsigned char streamChannelId)  
 {  
      char const dollar = '$';  
   
      if (send(sock, &dollar, 1, 0) != 1) return -1;  
      if (send(sock, (char*)&streamChannelId, 1, 0) != 1) return -1;  
   
      char sz[2];  
      sz[0] = (char)((len&0xFF00)>>8);  
      sz[1] = (char)(len&0x00FF);  
      if (send(sock, sz, 2, 0) != 2) return -1;  
   
      if (send(sock, buffer, len, 0) != len) return -1;  
   
      return 0;  
 }  
   
 void shutdown(int sock)  
 {  
 #ifdef WIN32  
      if (shutdown(sock, SD_SEND) != 0)  
           socketErr("shutdown error: ");  
 #else  
      if (shutdown(sock, SHUT_RD) != 0)  
           socketErr("shutdown error: ");  
 #endif  
 }  
   
 bool isMulticastAddress(unsigned int address)  
 {  
      // Note: We return False for addresses in the range 224.0.0.0  
      // through 224.0.0.255, because these are non-routable  
      // Note: IPv4-specific #####  
      unsigned int addressInHostOrder = ntohl(address);  
      return addressInHostOrder > 0xE00000FF &&  
           addressInHostOrder <= 0xEFFFFFFF;  
 }  
   
 bool socketJoinGroupSSM(int sock, unsigned int groupAddress, unsigned int sourceFilterAddr)  
 {  
      if (!isMulticastAddress(groupAddress)) return true; // ignore this case  
   
      struct ip_mreq_source imr;  
 #ifdef ANDROID  
   imr.imr_multiaddr = groupAddress;  
   imr.imr_sourceaddr = sourceFilterAddr;  
   imr.imr_interface = INADDR_ANY;  
 #else  
      imr.imr_multiaddr.s_addr = groupAddress;  
      imr.imr_sourceaddr.s_addr = sourceFilterAddr;  
      imr.imr_interface.s_addr = INADDR_ANY;  
 #endif  
      if (setsockopt(sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {  
           socketErr("setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: ", __FUNCTION__);  
           return false;  
      }  
   
      return true;  
 }  
   
 bool socketLeaveGroupSSM(int sock, unsigned int groupAddress, unsigned int sourceFilterAddr)  
 {  
      if (!isMulticastAddress(groupAddress)) return true; // ignore this case  
   
      struct ip_mreq_source imr;  
 #ifdef ANDROID  
   imr.imr_multiaddr = groupAddress;  
   imr.imr_sourceaddr = sourceFilterAddr;  
   imr.imr_interface = INADDR_ANY;  
 #else  
      imr.imr_multiaddr.s_addr = groupAddress;  
      imr.imr_sourceaddr.s_addr = sourceFilterAddr;  
      imr.imr_interface.s_addr = INADDR_ANY;  
 #endif  
      if (setsockopt(sock, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {  
           return false;  
      }  
   
      return true;  
 }  
   
 bool socketJoinGroup(int sock, unsigned int groupAddress)  
 {  
      if (!isMulticastAddress(groupAddress)) return true; // ignore this case  
   
      struct ip_mreq imr;  
      imr.imr_multiaddr.s_addr = groupAddress;  
      imr.imr_interface.s_addr = INADDR_ANY;  
      if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&imr, sizeof (struct ip_mreq)) < 0) {  
 #if defined(__WIN32__) || defined(_WIN32)  
           if (WSAGetLastError() != 0) {  
                // That piece-of-shit toy operating system (Windows) sometimes lies  
                // about setsockopt() failing!  
 #endif  
                socketErr("setsockopt(IP_ADD_MEMBERSHIP) error: ", __FUNCTION__);  
                return false;  
 #if defined(__WIN32__) || defined(_WIN32)  
           }  
 #endif  
      }  
   
      return true;  
 }  
   
 bool socketLeaveGroup(int sock, unsigned int groupAddress)  
 {  
      if (!isMulticastAddress(groupAddress)) return true; // ignore this case  
   
      struct ip_mreq imr;  
      imr.imr_multiaddr.s_addr = groupAddress;  
      imr.imr_interface.s_addr = INADDR_ANY;  
      if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&imr, sizeof (struct ip_mreq)) < 0) {  
           return false;  
      }  
   
      return true;  
 }  
   
 typedef unsigned int     u_int32_t;  
 u_int32_t ReceivingInterfaceAddr = INADDR_ANY;  
 static bool loopbackWorks = 1;  
   
 static bool badAddressForUs(u_int32_t addr)   
 {  
      // Check for some possible erroneous addresses:  
      u_int32_t nAddr = htonl(addr);  
      return (nAddr == 0x7F000001 /* 127.0.0.1 */  
           || nAddr == 0  
           || nAddr == (u_int32_t)(~0));  
 }  
   
 u_int32_t ourIPAddress()   
 {  
      static u_int32_t ourAddress = 0;  
      int sock = -1;  
      struct in_addr testAddr;  
   
      if (ReceivingInterfaceAddr != INADDR_ANY) {  
           // Hack: If we were told to receive on a specific interface address, then   
           // define this to be our ip address:  
           ourAddress = ReceivingInterfaceAddr;  
      }  
   
      if (ourAddress == 0) {  
           // We need to find our source address  
           struct sockaddr_in fromAddr;  
           fromAddr.sin_addr.s_addr = 0;  
   
           // Get our address by sending a (0-TTL) multicast packet,  
           // receiving it, and looking at the source address used.  
           // (This is kinda bogus, but it provides the best guarantee  
           // that other nodes will think our address is the same as we do.)  
           do {  
                loopbackWorks = 0; // until we learn otherwise  
   
                testAddr.s_addr = inet_addr("228.67.43.91"); // arbitrary  
                unsigned short testPort = 15947; // ditto  
   
                sock = setupDatagramSock(testPort, false);  
                if (sock < 0) break;  
   
                if (!socketJoinGroup(sock, testAddr.s_addr)) break;  
   
                unsigned char testString[] = "hostIdTest";  
                unsigned testStringLength = sizeof testString;  
   
                if (!writeSocket(sock, testAddr, testPort, 0, testString, testStringLength))   
                     break;  
   
                // Block until the socket is readable (with a 5-second timeout):  
                fd_set rd_set;  
                FD_ZERO(&rd_set);  
                FD_SET((unsigned)sock, &rd_set);  
                const unsigned numFds = sock+1;  
                struct timeval timeout;  
                timeout.tv_sec = 5;  
                timeout.tv_usec = 0;  
                int result = select(numFds, &rd_set, NULL, NULL, &timeout);  
                if (result <= 0) break;  
   
                unsigned char readBuffer[20];  
                int bytesRead = readSocket1(sock, (char *)readBuffer, sizeof readBuffer, fromAddr);  
                if (bytesRead != (int)testStringLength  
                     || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) {  
                          break;  
                }  
   
                // We use this packet's source address, if it's good:  
                loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr);  
           } while (0);  
   
           if (sock >= 0) {  
                socketLeaveGroup(sock, testAddr.s_addr);  
                closeSocket(sock);  
           }  
   
           // Make sure we have a good address:  
           u_int32_t from = fromAddr.sin_addr.s_addr;  
           if (badAddressForUs(from)) {  
                DPRINTF("This computer has an invalid IP address\n");  
                from = 0;  
           }  
   
           ourAddress = from;  
      }  
   
      return ourAddress;  
 }  
   

2015년 11월 27일 금요일

DirectDraw로 draw시 깜빡임 현상 방지

DirectDraw 로 영상을 출력할때 모니터 주파수에 따라 출력 영상 깜빡임 현상이 발생하는데 이것은 draw 주기가 서로 맞지않기 때문이다.
IDirectDraw7::WaitForVerticalBlank() 메소드를 사용해서 주기를 맞춰주면 깜빡임을 막을 수 있다.

// synchronize with vertical-blank interval
if (m_pDDraw[index].isFirstDisplay) {
pDDraw->pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
m_pDDraw[index].isFirstDisplay = false;
}

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년 11월 20일 금요일

ffmpeg 오디오 리샘플러 사용법 - how to use ffmpeg audio resampler

< Resampler.h >
 #ifndef __RESAMPLER_H__  
 #define __RESAMPLER_H__  
   
 extern "C" {  
 #include "libswresample/swresample.h"  
 #include "libavutil/frame.h"  
 }  
   
 class Resampler  
 {  
 public:  
      Resampler();  
      virtual ~Resampler();  
   
      int open(int in_sample_rate,   
           int in_channel_count,   
           uint64_t in_channel_layout,   
           enum AVSampleFormat in_sample_fmt,  
           int out_sample_rate,   
           int out_channel_count,   
           uint64_t out_channel_layout,   
           enum AVSampleFormat out_sample_fmt);  
      void close();  
      int resample(AVFrame *frame, int inLen);  
      int checkResampler(int sample_rate, int channel_count, int channel_layout, enum AVSampleFormat sample_fmt);  
   
      void moveOutBuffer(int len);  
      unsigned char* outBuf() { return m_pOutBuf; }  
      int outBufIndex() { return m_nOutBufIndex; }  
      void resetOutBufIndex() { m_nOutBufIndex = 0; }  
   
 protected:  
      struct SwrContext*     m_pSwrCtx;  
   
      int                         m_nSampleRateIn;  
      int                         m_nChannelCountIn;  
      uint64_t               m_nChannelLayoutIn;  
      enum AVSampleFormat     m_nSampleFormatIn;  
      int                         m_nSampleRateOut;  
      int                         m_nChannelCountOut;  
      uint64_t               m_nChannelLayoutOut;  
      enum AVSampleFormat     m_nSampleFormatOut;  
   
      unsigned char*     m_pOutBuf;  
      int                    m_nOutBufSize;       
      int                    m_nOutBufIndex;  
   
      FILE*               m_pFile;  
 };  
   
 #endif  
   

< Resampler.cpp >
 #include "Resampler.h"  
 #include "GlobalEnv.h"  
   
 extern "C" {  
 #include "libavutil\opt.h"  
 }  
   
 Resampler::Resampler()  
 {  
      m_pSwrCtx = NULL;  
      m_nSampleRateIn = m_nSampleRateOut = 0;  
      m_nChannelCountIn = m_nChannelCountOut = 0;  
      m_nChannelLayoutIn = m_nChannelLayoutOut = 0;  
      m_nSampleFormatIn = m_nSampleFormatOut = AV_SAMPLE_FMT_NONE;  
      m_pOutBuf = NULL;  
      m_nOutBufSize = m_nOutBufIndex = 0;  
      m_pFile = NULL;  
 }  
   
 Resampler::~Resampler()  
 {  
      if (m_pOutBuf) {  
           delete[] m_pOutBuf;  
           m_pOutBuf = NULL;  
      }  
 }  
   
 int Resampler::open(int in_sample_rate,   
                          int in_channel_count,   
                          uint64_t in_channel_layout,   
                          enum AVSampleFormat in_sample_fmt,  
                          int out_sample_rate,   
                          int out_channel_count,   
                          uint64_t out_channel_layout,   
                          enum AVSampleFormat out_sample_fmt)  
 {  
      int err;  
      char errbuf[128];  
   
      m_pSwrCtx = swr_alloc();  
   
      av_opt_set_int(m_pSwrCtx, "in_sample_rate", in_sample_rate, 0);  
      av_opt_set_int(m_pSwrCtx, "in_channel_count", in_channel_count, 0);  
      av_opt_set_int(m_pSwrCtx, "in_channel_layout", in_channel_layout, 0);  
      av_opt_set_sample_fmt(m_pSwrCtx, "in_sample_fmt", in_sample_fmt, 0);  
   
      av_opt_set_int(m_pSwrCtx, "out_sample_rate", out_sample_rate, 0);  
      av_opt_set_int(m_pSwrCtx, "out_channel_count", out_channel_count, 0);  
      av_opt_set_int(m_pSwrCtx, "out_channel_layout", out_channel_layout, 0);  
      av_opt_set_sample_fmt(m_pSwrCtx, "out_sample_fmt", out_sample_fmt, 0);  
   
      err = swr_init(m_pSwrCtx);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("swr_init failed, err : %d %s\n", err, errbuf);  
           return err;  
      }  
   
      m_nSampleRateIn = in_sample_rate;  
      m_nChannelCountIn = in_channel_count;  
      m_nChannelLayoutIn = in_channel_layout;  
      m_nSampleFormatIn = in_sample_fmt;  
   
      m_nSampleRateOut = out_sample_rate;  
      m_nChannelCountOut = out_channel_count;  
      m_nChannelLayoutOut = out_channel_layout;  
      m_nSampleFormatOut = out_sample_fmt;  
   
      m_nOutBufIndex = 0;  
   
      DXPRINTF("resampler opened\n");  
   
 #if 0  
      m_pFile = fopen("resample.wav", "wb");  
 #endif  
   
      return 0;  
 }  
   
 void Resampler::close()  
 {  
      if (m_pSwrCtx) {  
           swr_free(&m_pSwrCtx);  
           m_pSwrCtx = NULL;  
      }  
   
      if (m_pOutBuf) {  
           delete[] m_pOutBuf;  
           m_pOutBuf = NULL;  
           m_nOutBufSize = m_nOutBufIndex = 0;  
      }  
   
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;  
      }  
 }  
   
 int Resampler::resample(AVFrame *frame, int inLen)  
 {  
      int out_size = av_rescale_rnd(inLen, m_nSampleRateOut, m_nSampleRateIn, AV_ROUND_UP);  
   
      if (out_size*m_nChannelCountOut > m_nOutBufSize) {  
           if (m_pOutBuf) delete[] m_pOutBuf;  
           m_pOutBuf = new unsigned char[out_size*m_nChannelCountOut];  
           m_nOutBufSize = out_size*m_nChannelCountOut;  
      }  
   
      uint8_t *ptr = &m_pOutBuf[m_nOutBufIndex];  

      int ret = swr_convert(m_pSwrCtx, &m_pOutBuf, out_size,   
           (const uint8_t **)frame->extended_data, frame->nb_samples);  
   
      int dst_bufsize = av_samples_get_buffer_size(NULL, m_nChannelCountOut, ret, m_nSampleFormatOut, 1);  
      m_nOutBufIndex += dst_bufsize;  
        
      if (m_pFile) fwrite(ptr, dst_bufsize, 1, m_pFile);  
   
      return ret;  
 }  
   
 int Resampler::checkResampler(int sample_rate, int channel_count, int channel_layout, enum AVSampleFormat sample_fmt)  
 {  
      if (  
      sample_rate != m_nSampleRateIn ||   
      channel_count != m_nChannelCountIn ||  
      channel_layout != m_nChannelLayoutIn ||  
      sample_fmt != m_nSampleFormatIn  
      ) {  
           return -1;  
      }  
   
      return 0;  
 }  
   
 void Resampler::moveOutBuffer(int len)  
 {  
      if (len > 0) {  
           memcpy(m_pOutBuf, &m_pOutBuf[m_nOutBufIndex-len], len);  
           m_nOutBufIndex = len;  
      }  
 }  
   


< Resampler 사용 >
Resampler *m_pResampler;
AVCodec* m_pCodec;
AVCodecContext* m_pCodecCtx;

...
avcodec_open2(m_pCodecCtx, m_pCodec, NULL);
           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);  

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

 retLen = m_pResampler->resample(m_pFrame, out_size);
 out_size = m_pResampler->outBufIndex();
 memcpy(m_pOutBuf, m_pResampler->outBuf(), out_size);    // now m_pOutBuf has resampled audio data
 m_pResampler->resetOutBufIndex();
 
 m_nOutBufSize = out_size;

 if (m_pFile) fwrite(m_pOutBuf, m_nOutBufSize, 1, m_pFile);