2016년 7월 13일 수요일

소켓 프로그래밍에 대한 조언

언어와 플랫폼을 불문하고 소켓 프로그래밍은 형태가 조금씩 다를뿐이지 내부구현은 완전히 동일하다.
기본적으로 POSIX 소켓 API 로 제공되는 low level API 들이 있고 각 플랫폼마다 이것들을 래핑한 상위 레이어들이 존재하는 형태이다.

응용 프로그램에서는 이러한 low level API를 가지고 개발할 것인지 좀더 편리한 상위 라이브러리를
이용할 것인지 결정해서 개발하면된다.

소켓 프로그래밍을 처음 입문하는 개발자들은 개발에 용이한 상위 라이브러리가 아무래도 많이 끌릴것이다. 심지어 경력 개발자들도 이러한 라이브러리를 이용해서 개발하는것을 선호하기도한다.

개인적으로 소켓 프로그래밍은 반드시 low level API로 개발해야한다고 생각한다. 사실 말이 low level이지 그냥 소켓 API 들이다. C#/Java 등의 언어에서 제공되는 상위 클래스들은 이러한 소켓 API 들을 래핑해서 사용하기 편리하게 만들어주는 역할밖에 하지않는다.

문제는 이러한 라이브러리를 사용해서 개발하다보면 내부구현을 볼 수가 없어서 다양한 상황들이 발생하는 네트워크 프로그래밍에서 올바른 대처를 하기가 어렵다는 점이다. 물론 잘 사용하면 대부분 문제는 없지만 이러한 방법에 길들여지면 소켓 프로그래밍이나 TCP/IP의 기본원리를 이해하기 힘들다는 점도있다.

사실 소켓 프로그래밍을 시작하기 전에 기본적인 TCP/IP 에 대한 이해가 필요하다. 개발을 해가면서 이해하든 미리 공부를 하든 기본적인 TCP/UDP의 동작정도는 알고 시작하는것이 좋다.

여기서 중요한 점은 응용 프로그램과 TCP/IP간의 관계에 대해서 잘 알아야한다는것이다. 
사실 실제적인 데이터 전송과 수신은 TCP/IP가 하는것이지 응용 프로그램이 하는것이 아니다.
응용 프로그램과 TCP/IP는 서로 다른 계층에 존재하고 소켓 API를 통해 서로 데이터를 주고받는데 응용 프로그램의 역할과 TCP/IP의 역할을 잘 알아야 특정한 상황이 발생했을때 이것을 분석해서 원인을 유추하는것이 가능하다.
또한 응용 프로그램간의 프로토콜을 정의하고 구현하는데도 이러한 지식이 필요하다.

마지막으로 한가지 더하자면 통신 프로그램은 아주 정교하게 작성해야한다. TCP 서버/클라이언트 개발자는 잘 알겠지만 통신 상황에서는 너무나도 다양한 케이스들이 발생한다. 대상이 일반 유저라면 생각할 수 있는 거의 모든 경우의 수가 다 발생한다고 보면된다. 
적당히 데이터 주고받는 테스트 정도만 수행하고 릴리즈를 했다간 큰 낭패를 볼 수 있다. 네트워크 상황은 시시각각 변하기때문에 특정 상황에 대한 스냅샷을 잡기가 굉장히 힘들고 이것을 사후에 분석하는것도 어렵기때문에 애초에 robust한 프로그래밍과 테스트가 필요하다.

2016년 7월 4일 월요일

C# 소켓 Select 함수 사용방법 - C# Socket Select

C# 소켓 프로그래밍에서도 기존 POSIX select 함수를 그대로 사용할 수 있다. 방식은 제공되는 Socket 클래스의 Select 정적 멤버함수를 사용하면된다.

< 일반적인 select 사용 >
         try  
         {  
           ArrayList selectList = new ArrayList();  
             
           selectList.Add(mySock);             
   
           if (selectList.Count == 0)  
           {  
             Thread.Sleep(10);  
             return;  
           }  
   
           Socket.Select(selectList, null, null, 1000000);  
   
           foreach (Socket sock in selectList)  
           {                    
             if (sock == mySock)
             {
                 // do something with mySock...
             }
           }            
         }  
         catch (Exception ex)  
         {  
           Trace.WriteLine(ex.ToString());  
         }  

< TCP Connection 타임아웃 >
      
Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSock.Blocking = false;
...
public bool Connect(IPAddress serverIP, int serverPort, int timeout) 
{
       try  
       {  
         clientSock.Connect(serverIP, serverPort);        
         return true;  
       }  
       catch (SocketException ex)  
       {          
         if (ex.SocketErrorCode == SocketError.WouldBlock)  
         {  
           ArrayList selectArray = new ArrayList();  
           selectArray.Add(clientSock);  
   
           Socket.Select(null, selectArray, null, timeout * 1000000);  
   
           if (selectArray.Count == 0)  
           {
             Trace.WriteLine(ex.ToString());  
             return false;  
           }
   
           return true;  
         }    
       }  
       catch (Exception ex)  
       {          
         Trace.WriteLine(ex.ToString());  
         return false;  
       }  
}

2016년 6월 20일 월요일

C# 간단한 시리얼 통신 라이브러리 소스코드 - simple c# source code for serial communication

 using System;  
 using System.Collections.Generic;  
 using System.Diagnostics;  
 using System.IO.Ports;  
 using System.Linq;  
 using System.Text;  
 using System.Threading;  
 using System.Threading.Tasks;  
   
 namespace SerialCommLib  
 {        
   public class SerialComm
   {    
     public delegate void DataReceivedHandlerFunc(byte[] receiveData);  
     public DataReceivedHandlerFunc DataReceivedHandler;  
   
     public delegate void DisconnectedHandlerFunc();  
     public DisconnectedHandlerFunc DisconnectedHandler;
   
     private SerialPort serialPort;  
      
     public bool IsOpen   
     {   
       get   
       {   
         if (serialPort != null) return serialPort.IsOpen;  
         return false;  
       }   
     }  
      
     // serial port check
     private Thread threadCheckSerialOpen;  
     private bool isThreadCheckSerialOpen = false;  
   
     public SerialComm()  
     {  
     }  
   
     public bool OpenComm(string portName, int baudrate, int databits, StopBits stopbits, Parity parity, Handshake handshake)  
     {  
       try  
       {  
         serialPort = new SerialPort();  
   
         serialPort.PortName = portName;  
         serialPort.BaudRate = baudrate;  
         serialPort.DataBits = databits;  
         serialPort.StopBits = stopbits;  
         serialPort.Parity = parity;  
         serialPort.Handshake = handshake;  
   
         serialPort.Encoding = new System.Text.ASCIIEncoding();  
         serialPort.NewLine = "\r\n";  
         serialPort.ErrorReceived += serialPort_ErrorReceived;     
         serialPort.DataReceived += serialPort_DataReceived; 
   
         serialPort.Open();  
   
         StartCheckSerialOpenThread();  
         return true;  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
         return false;  
       }  
     }  
   
     public void CloseComm()  
     {  
       try  
       {  
         if (serialPort != null)  
         {  
           StopCheckSerialOpenThread();  
           serialPort.Close();  
           serialPort = null;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     public bool Send(string sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData);  
           return true;  
         }          
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, 0, sendData.Length);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     public bool Send(byte[] sendData, int offset, int count)  
     {  
       try  
       {  
         if (serialPort != null && serialPort.IsOpen)  
         {  
           serialPort.Write(sendData, offset, count);  
           return true;  
         }  
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
       return false;  
     }  
   
     private byte[] ReadSerialByteData()  
     {  
       serialPort.ReadTimeout = 100;  
       byte[] bytesBuffer = new byte[serialPort.BytesToRead];  
       int bufferOffset = 0;  
       int bytesToRead = serialPort.BytesToRead;  
   
       while (bytesToRead > 0)  
       {  
         try  
         {  
           int readBytes = serialPort.Read(bytesBuffer, bufferOffset, bytesToRead - bufferOffset);  
           bytesToRead -= readBytes;  
           bufferOffset += readBytes;  
         }  
         catch (TimeoutException ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
   
       return bytesBuffer;  
     }  
   
     private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)  
     {  
       try  
       {  
         byte[] bytesBuffer = ReadSerialByteData();  
         string strBuffer = Encoding.ASCII.GetString(bytesBuffer);  

         if (DataReceivedHandler != null)  
           DataReceivedHandler(bytesBuffer);  
   
         Debug.WriteLine("received(" + strBuffer.Length + ") : " + strBuffer);     
       }  
       catch (Exception ex)  
       {  
         Debug.WriteLine(ex.ToString());  
       }  
     }  
   
     private void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)  
     {  
       Debug.WriteLine(e.ToString());  
     }  
      
     private void StartCheckSerialOpenThread()  
     {   
       isThreadCheckSerialOpen = true;  
       threadCheckSerialOpen = new Thread(new ThreadStart(ThreadCheckSerialOpen));  
       threadCheckSerialOpen.Start();  
     }  
   
     private void StopCheckSerialOpenThread()  
     {  
       if (isThreadCheckSerialOpen)  
       {  
         isThreadCheckSerialOpen = false;  
         if (Thread.CurrentThread != threadCheckSerialOpen)  
           threadCheckSerialOpen.Join();
       }  
     }  
   
     private void ThreadCheckSerialOpen()  
     {  
       while (isThreadCheckSerialOpen)  
       {  
         Thread.Sleep(100);  
   
         try  
         {  
           if (serialPort == null || !serialPort.IsOpen)  
           {  
             Debug.WriteLine("seriaport disconnected");  
             if (DisconnectedHandler != null)  
               DisconnectedHandler();  
             break;  
           }  
         }  
         catch (Exception ex)  
         {  
           Debug.WriteLine(ex.ToString());  
         }  
       }  
     }  
   }  
 }  
   


< SerialComm 사용 >

using System.IO.Ports;

using SerialCommLib;
...
SerialComm serial = new SerialComm();
serial.DataReceivedHandler = DataReceivedHandler;
serial.DisconnectedHandler = DisconnectedHandler;
...
serial.OpenComm("COM3", 9600, 8, StopBits.One, Parity.None, Handshake.None);
...
serial.CloseComm();

private void DataReceivedHandler(byte[] receiveData)
{
    // do something with receiveData here
}

private void DisconnectedHandler()
{
    Console.WriteLine("serial disconnected");
}

* 라이브러리 사용은 시리얼 데이터 수신과 연결종료 이벤트 핸들러를 등록해서 콜백으로 받아서 처리하는 방식으로 간단하게 사용할 수 있다.

C# SerialPort 클래스는 이벤트 수신 방식과 쓰레드 방식 두 가지로 사용할 수 있는데 쓰레드 방식보다 이벤트 수신 방식이 더 간단하고 직관적이다.
SerialPort 클래스의 Read 계열 메소드는 블러킹 방식으로 동작하기때문에 데이터 수신 이벤트 발생시 반드시 BytesToRead 에 명시된 모든 바이트를 읽어서 수신버퍼를 비워줘야한다.


2016년 2월 4일 목요일

OpenCV sample build

cmake -G "Visual Studio 11 2012" -DBUILD_EXAMPLES=1

* samples 아래에 있는 소스들 솔루션/프로젝트 파일 만들 수 있다.

2016년 1월 28일 목요일

ffmpeg 비디오 필터 사용 소스코드 - ffmpeg video filter sample source code

뮤텍스 소스

< VideoFilter.h >
 #ifndef __VIDEO_FILTER_H__  
 #define __VIDEO_FILTER_H__  
   
 extern "C" {  
 #include "libavfilter/avfiltergraph.h"  
 #include "libavfilter/buffersink.h"  
 #include "libavfilter/buffersrc.h"  
 }  
   
 #include "Mutex.h"  
   
 struct VideoFilterParam {  
      int width;  
      int height;  
      AVPixelFormat pix_fmt;  
      AVRational time_base;  
      AVRational sample_aspect_ratio;  
 };  
   
 class VideoFilter   
 {  
 public:  
      VideoFilter();  
      virtual ~VideoFilter();  
   
      int initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate);  
      void freeFilter();  
   
      int processFilter(AVFrame *newFrame, AVFrame *frame);  
   
      int checkFilter(struct VideoFilterParam &param);  
   
 protected:  
      static void initFilter();  
   
 protected:  
      AVFilterGraph*          m_pFilterGraph;  
      AVFilterContext*     m_pBufferSinkCtx;  
      AVFilterContext*     m_pBufferSrcCtx;       
      AVFrame*               m_pFrame;  
   
      int                    m_nWidth;  
      int                    m_nHeight;  
      AVPixelFormat     m_nPixelFormat;  
      AVRational          m_timeBase;  
      AVRational          m_sampleAspectRatio;  
   
      static MUTEX     m_hMutex;  
      static bool          m_bInit;  
 };  
   
 #endif  
   

< VideoFilter.cpp >
 #include "VideoFilter.h"  
 #include "GlobalEnv.h"  
 #include "CommonType.h"  
   
 bool VideoFilter::m_bInit = false;  
 MUTEX VideoFilter::m_hMutex = PTHREAD_MUTEX_INITIALIZER;  
   
 VideoFilter::VideoFilter() : m_pFilterGraph(NULL), m_pBufferSrcCtx(NULL), m_pBufferSinkCtx(NULL), m_pFrame(NULL)  
 {  
      initFilter();  
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 VideoFilter::~VideoFilter()  
 {  
      freeFilter();  
 }  
   
 void VideoFilter::initFilter()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (!m_bInit) {  
           avfilter_register_all();  
           m_bInit = true;  
      }  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 int VideoFilter::initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate)  
 {  
      freeFilter();  
   
      char args[128];  
   snprintf(args, sizeof(args),  
       "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",  
       param.width, param.height, param.pix_fmt,   
                param.time_base.num, param.time_base.den,   
       param.sample_aspect_ratio.num, param.sample_aspect_ratio.den);  
   
      m_pFrame = av_frame_alloc();  
      m_pFilterGraph = avfilter_graph_alloc();  
   
      char errbuf[128];  
      int err = avfilter_graph_create_filter(&m_pBufferSrcCtx, avfilter_get_by_name("buffer"), "buffer1", args, NULL, m_pFilterGraph);       
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_create_filter(&m_pBufferSinkCtx, avfilter_get_by_name("buffersink"), "buffersink1", NULL, NULL, m_pFilterGraph);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      AVFilterContext *vflipCtx = NULL;  
      AVFilterContext *hflipCtx = NULL;  
      AVFilterContext *rotateCtx = NULL;  
      AVFilterContext *prevCtx = m_pBufferSrcCtx;  
   
      if (vflip > 0) {  
           err = avfilter_graph_create_filter(&vflipCtx, avfilter_get_by_name("vflip"), "vflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, vflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = vflipCtx;  
      }  
   
      if (hflip > 0) {  
           err = avfilter_graph_create_filter(&hflipCtx, avfilter_get_by_name("hflip"), "hflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, hflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = hflipCtx;  
      }  
   
      if (rotate) {  
           snprintf(args, sizeof(args), "PI*2/360*%d", rotate);  
           err = avfilter_graph_create_filter(&rotateCtx, avfilter_get_by_name("rotate"), "rotate1", args, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, rotateCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = rotateCtx;  
      }  
   
      err = avfilter_link(prevCtx, 0, m_pBufferSinkCtx, 0);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_config(m_pFilterGraph, NULL);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_config failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      m_nWidth = param.width;  
      m_nHeight = param.height;  
      m_nPixelFormat = param.pix_fmt;  
      m_timeBase.den = param.time_base.den;  
      m_timeBase.num = param.time_base.num;  
      m_sampleAspectRatio.den = param.sample_aspect_ratio.den;  
      m_sampleAspectRatio.num = param.sample_aspect_ratio.num;  
   
      return 0;  
 }  
   
 void VideoFilter::freeFilter()  
 {  
      avfilter_graph_free(&m_pFilterGraph);  
      av_frame_free(&m_pFrame);  
   
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 AVFrame* copyFrame(AVFrame *frame)  
 {  
      AVFrame *new_frame = avcodec_alloc_frame();  
      if (new_frame) {  
           if (av_image_alloc(new_frame->data, new_frame->linesize,  
                frame->width, frame->height, (AVPixelFormat)frame->format, 1) > 0)   
           {  
                av_image_copy(new_frame->data, new_frame->linesize,  
                     (const uint8_t **)frame->data, frame->linesize, (AVPixelFormat)frame->format,  
                     frame->width, frame->height);  
   
                new_frame->width = frame->width;  
                new_frame->height = frame->height;  
                new_frame->pict_type = frame->pict_type;  
                new_frame->format = frame->format;  
   
                return new_frame;  
           }  
           else  
           {  
                av_free(new_frame->data[0]);  
                av_free(new_frame);  
           }  
      }  
   
      return NULL;  
 }  
   
 int VideoFilter::processFilter(AVFrame *newFrame, AVFrame *frame)  
 {  
      if (!m_pFilterGraph) return -1;  
   
      int ret = av_buffersrc_add_frame_flags(m_pBufferSrcCtx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);  
      if (ret >= 0) {  
           while (1) {  
                ret = av_buffersink_get_frame(m_pBufferSinkCtx, m_pFrame);  
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;  
                if (ret < 0) break;  
        
                newFrame = copyFrame(m_pFrame);  
                av_frame_unref(m_pFrame);  
           }  
      }  
      return 0;  
 }  
   
 int VideoFilter::checkFilter(struct VideoFilterParam &param)  
 {  
      if (m_nWidth != param.width || m_nHeight != param.height ||   
           m_nPixelFormat != param.pix_fmt ||  
           m_timeBase.den != param.time_base.den || m_timeBase.num != param.time_base.num ||  
           m_sampleAspectRatio.den != param.sample_aspect_ratio.den ||   
           m_sampleAspectRatio.num != param.sample_aspect_ratio.num)   
      {  
           return -1;  
      }  
   
      return 0;  
 }  
   

< VideoFilter 사용 >

AVCodecContext *pCodecCtx;
...
struct VideoFilterParam param;
param.width = pCodecCtx->width;
param.height = pCodecCtx->height;
param.pix_fmt = pCodecCtx->pix_fmt;
param.time_base.den = pCodecCtx->time_base.den;
param.time_base.num = pCodecCtx->time_base.num;
param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
...
VideoFilter *filter = new VideoFilter();
...
filter->initFilter(param, 1, 1, 30);    // 상하반전, 좌우반전, 30도 회전
...
if (filter->checkFilter(param) < 0) {
    filter->freeFilter();
 
    param.width = pCodecCtx->width;
    param.height = pCodecCtx->height;
    param.pix_fmt = pCodecCtx->pix_fmt;
    param.time_base.den = pCodecCtx->time_base.den;
    param.time_base.num = pCodecCtx->time_base.num;
    param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
    param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
}  
filter->processFilter(newFrame, frame);   // newFrame은 처리결과, frame은 디코딩된 프레임
...
filter->freeFilter();

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();