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



2015년 10월 7일 수요일

비디오 코덱과 스트리밍 프로토콜

2015년 8월 31일 월요일

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

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

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

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

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

< Thread.cpp >

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

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

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

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

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

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

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

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

2015년 8월 25일 화요일

ffmpeg muxer 를 이용한 스트림 저장 - stream record by ffmpeg muxer

< MediaBuffer.h >
 typedef enum {  
      VideoMedia          = 0x01,  
      AudioMedia          = 0x02,  
      SubtitleMedia       = 0x04,  
 } MediaType;  
   
 class MediaBuffer {  
 public:  
      MediaType          mediaType;  
      unsigned char*     data;  
      int                size;  
      int                keyFrame;  
      int64_t            pts;  
      int64_t            dts;  
      
 public:  
      MediaBuffer() {  
           data = NULL;  
           size = keyFrame = 0;   
           pts = dts = 0;  
      }  
      virtual ~MediaBuffer() {
          if (data) {
              delete[] data;
              data = NULL;
          }
     }
 };     

< FileWriter.h >
 #ifndef __FILE_WRITER_H__  
 #define __FILE_WRITER_H__  
   
 #include "MediaBuffer.h"  
   
 extern "C" {  
 #include "libavformat/avformat.h"  
 };  
   
 class FileWriterOpenParam {  
 public:  
      enum AVCodecID     video_codec_id;  
      int                    width;  
      int                    height;  
      AVRational          frame_rate;  
      unsigned int     gop_size;  
      enum AVCodecID     audio_codec_id;  
      enum AVSampleFormat sample_fmt;  
      int                    channels;  
      int                    sample_rate;  
      unsigned char*     video_extradata;  
      int                    video_extradata_size;  
      unsigned char*     audio_extradata;  
      int                    audio_extradata_size;  
      int                    filepath_format;  
   
      int                    buffering_time;  
   
      FileWriterOpenParam();  
      FileWriterOpenParam(FileWriterOpenParam *param);  
      virtual ~FileWriterOpenParam();  
 };  
   
 enum FILE_SPLIT { SPLIT_DURATION, SPLIT_LOCALTIME };  
   
 class FileWriter  
 {  
 protected:  
      FileWriter();       
 public:  
      virtual ~FileWriter();  
   
 public:  
      virtual int open(char *filepath, FileWriterOpenParam &param);  
      virtual int write(MediaBuffer *pBuffer);  
      virtual void close();  
   
 protected:  
      int64_t     m_nStartPTS;  
      int64_t     m_nStartDTS;  
   
      int          m_nFormat;  
   
      unsigned char*     m_pVideoExtraData;  
      int                m_nVideoExtraDataSize;  

      unsigned char*     m_pAudioExtraData;
      int                m_nAudioExtraDataSize;
 };  
   
   
 #endif  
   

< FileWriter.cpp >
 #include "FileWriter.h"  
 #include "GlobalEnv.h"  
   
 FileWriterOpenParam::FileWriterOpenParam()  
 {  
      video_codec_id = AV_CODEC_ID_NONE;  
      width = height = 0;  
      frame_rate.num = 1;  
      frame_rate.den = 30;  
      gop_size = 30;  
      audio_codec_id = AV_CODEC_ID_NONE;  
      sample_fmt = AV_SAMPLE_FMT_NONE;  
      channels = sample_rate = 0;  
      video_extradata = audio_extradata = NULL;  
      video_extradata_size = audio_extradata_size = 0;  
      filepath_format = 0;  
      buffering_time = 0;  
 }  
   
 FileWriterOpenParam::FileWriterOpenParam(FileWriterOpenParam *param)  
 {  
      video_codec_id = param->video_codec_id;  
      width = param->width;  
      height = param->height;  
      frame_rate.num = param->frame_rate.num;  
      frame_rate.den = param->frame_rate.den;  
      gop_size = param->gop_size;  
      audio_codec_id = param->audio_codec_id;  
      sample_fmt = param->sample_fmt;  
      channels = param->channels;  
      sample_rate = param->sample_rate;  
   
      video_extradata = audio_extradata = NULL;  
      video_extradata_size = audio_extradata_size = 0;  
   
      filepath_format = param->filepath_format;  
   
      if (param->video_extradata_size > 0) {  
           video_extradata_size = param->video_extradata_size;  
           video_extradata = new unsigned char[video_extradata_size];  
           memcpy(video_extradata, param->video_extradata, video_extradata_size);  
      }  
      if (param->audio_extradata_size > 0) {  
           audio_extradata_size = param->audio_extradata_size;  
           audio_extradata = new unsigned char[audio_extradata_size];  
           memcpy(audio_extradata, param->audio_extradata, audio_extradata_size);  
      }  
   
      buffering_time = param->buffering_time;  
 }  
   
 FileWriterOpenParam::~FileWriterOpenParam()  
 {  
      if (video_extradata) { delete[] video_extradata; video_extradata = NULL; }  
      if (audio_extradata) { delete[] audio_extradata; audio_extradata = NULL; }  
 }  
   
 FileWriter::FileWriter() : m_nFormat(0), m_pVideoExtraData(NULL), m_nVideoExtraDataSize(0), m_pAudioExtraData(NULL), m_nAudioExtraDataSize(0) 
 {  
 }  
   
 FileWriter::~FileWriter()  
 {  
 }  
   
 int FileWriter::open(char *filepath, FileWriterOpenParam &param)  
 {  
      m_nStartPTS = m_nStartDTS = 0;  
      m_nFormat = param.filepath_format;  
   
      DXPRINTF("FileWriter %s file opened\n", filepath);  
      return 0;  
 }  
   
 void FileWriter::close()  
 {  
      DX_DELETE_OBJECT(m_pVideoExtraData);
      m_nVideoExtraDataSize = 0;
      DX_DELETE_OBJECT(m_pAudioExtraData);
      m_nAudioExtraDataSize = 0;   
      DXPRINTF("FileWriter closed\n");  
 }  
   
 int FileWriter::write(MediaBuffer *pBuffer)  
 {  
      return 0;  
 }  
   

< FFFileWriter.h >
 #ifndef __FFFILE_WRITER_H__  
 #define __FFFILE_WRITER_H__  
   
 #include "FileWriter.h"  
   
 class FFFileWriter : public FileWriter  
 {  
 public:  
      FFFileWriter();  
      virtual ~FFFileWriter();  
   
      virtual int open(char *filepath, FileWriterOpenParam &param);  
      virtual int write(MediaBuffer *pBuffer);  
      virtual void close();  
   
 protected:  
      AVStream* addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam &param);  
   
 protected:  
      AVFormatContext*     m_pFormatCtx;  
      AVStream*               m_pVideoStream;  
      AVStream*               m_pAudioStream;  
      AVPacket               m_avPacket;  
      FILE*                    m_pFile;  
   
      bool                    m_bWriteHeader;  
 };  
   
 #endif  
   

< FFFileWriter.cpp >
 #include "FFFileWriter.h"  
 #include "GlobalEnv.h"  
   
 FFFileWriter::FFFileWriter() : m_pFormatCtx(NULL), m_pVideoStream(NULL), m_pAudioStream(NULL), m_pFile(NULL)  
 {  
      av_register_all();  
      m_bWriteHeader = false;  
 }  
   
 FFFileWriter::~FFFileWriter()  
 {  
 }  
   
 int FFFileWriter::open(char *filepath, struct FileWriterOpenParam &param)  
 {  
      int err;  
      char errbuf[128];  
   
      err = avformat_alloc_output_context2(&m_pFormatCtx, NULL, NULL, filepath);  
      if (!m_pFormatCtx) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avformat_alloc_output_context2 failed, err: %d, %s\n", err, errbuf);  
           if (err == -22) {  
                m_pFile = fopen(filepath, "wb");  
                if (m_pFile) {  
                     FileWriter::open(filepath, param);  
                     return 0;  
                }  
           }  
           return -1;  
      }  
   
      av_init_packet(&m_avPacket);  
   
      AVOutputFormat *fmt = m_pFormatCtx->oformat;  
      fmt->video_codec = param.video_codec_id;  
      fmt->audio_codec = param.audio_codec_id;  
   
      if (fmt->video_codec != AV_CODEC_ID_NONE)  
           m_pVideoStream = addStream(AVMEDIA_TYPE_VIDEO, param.video_codec_id, param);  
      if (fmt->audio_codec != AV_CODEC_ID_NONE)  
           m_pAudioStream = addStream(AVMEDIA_TYPE_AUDIO, param.audio_codec_id, param);  
   
      err = avio_open(&m_pFormatCtx->pb, filepath, AVIO_FLAG_WRITE);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avio_open failed, err: %d, %s\n", err, errbuf);  
           return -1;  
      }  
   
      err = avformat_write_header(m_pFormatCtx, NULL);  
      if (err < 0) {            
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avformat_write_header failed, err: %d, %s\n", err, errbuf);  
           return -1;  
      }  
      m_bWriteHeader = true;  
   
      // write extra data  
      if (m_pVideoStream && param.video_extradata_size > 0) {  
           av_init_packet(&m_avPacket);  
             
           m_avPacket.stream_index = m_pVideoStream->index;  
           m_avPacket.data = param.video_extradata;  
           m_avPacket.size = param.video_extradata_size;  
   
           m_avPacket.pts = m_avPacket.dts = 0;  
           m_pVideoStream->cur_dts = 0;  
   
           av_write_frame(m_pFormatCtx, &m_avPacket);  
      }  
   
      if (m_pAudioStream && param.audio_extradata_size > 0) {  
           av_init_packet(&m_avPacket);  
             
           m_avPacket.stream_index = m_pAudioStream->index;  
           m_avPacket.data = param.audio_extradata;  
           m_avPacket.size = param.audio_extradata_size;  
   
           m_avPacket.pts = m_avPacket.dts = 0;  
           m_pVideoStream->cur_dts = 0;  
   
           av_write_frame(m_pFormatCtx, &m_avPacket);  
      }  
   
      FileWriter::open(filepath, param);       
   
      return 0;  
 }  
   
 AVStream* FFFileWriter::addStream(enum AVMediaType mediaType, enum AVCodecID codec_id, FileWriterOpenParam &param)  
 {  
      AVCodecContext *codecCtx;  
      AVStream *stream = NULL;  
   
 #if 0  
      AVCodec *codec = avcodec_find_encoder(codec_id);  
      if (!codec) {  
           DXPRINTF("Could not find encoder for '%s'\n", avcodec_get_name(codec_id));  
      }  
 #endif  
   
      if (mediaType == AVMEDIA_TYPE_VIDEO) {  
           stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->video_codec);  
      } else if (mediaType == AVMEDIA_TYPE_AUDIO) {  
           if (param.sample_fmt == AV_SAMPLE_FMT_NONE) return NULL;     // cannot write  
           stream = avformat_new_stream(m_pFormatCtx, m_pFormatCtx->audio_codec);  
      }  
   
      if (!stream) {  
           DXPRINTF("avformat_new_stream failed\n");  
           return NULL;  
      }  
   
      stream->id = m_pFormatCtx->nb_streams-1;  
   
      codecCtx = stream->codec;  
      codecCtx->codec_id = codec_id;  
      codecCtx->codec_type = mediaType;  
   
      if (mediaType == AVMEDIA_TYPE_AUDIO) {  
           int sample_rate = param.sample_rate == 0 ? 44100 : param.sample_rate;     // to prevent divide by zero exception  
           //codecCtx->sample_fmt = param.sample_fmt;  
           codecCtx->channels = param.channels;  
           codecCtx->sample_rate = sample_rate;  
           codecCtx->time_base.num = 1;  
           codecCtx->time_base.den = sample_rate;  

           if (param.audio_extradata_size > 0) {
               codecCtx->extradata = (uint8_t *)av_malloc(param.audio_extradata_size);
               memcpy(codecCtx->extradata, param.audio_extradata, param.audio_extradata_size);
               codecCtx->extradata_size = param.audio_extradata_size;
           }
      } else if (mediaType == AVMEDIA_TYPE_VIDEO) {  
           // to prevent divide by zero exception  
           int width = param.width == 0 ? 1280 : param.width;  
           int height = param.height == 0 ? 720 : param.height;            
   
           if (param.frame_rate.den == 0) {  
                param.frame_rate.den = 30;  
                param.frame_rate.num = 1;  
                param.gop_size = 30;  
           }  
   
           codecCtx->codec_id = codec_id;  
           codecCtx->width = width;  
           codecCtx->height = height;  
           codecCtx->time_base.den = param.frame_rate.den;  
           codecCtx->time_base.num = param.frame_rate.num;  
           codecCtx->gop_size = param.gop_size;  
           codecCtx->pix_fmt = PIX_FMT_YUV420P;  

           if (param.video_extradata_size > 0) {
                codecCtx->extradata = (uint8_t *)av_malloc(param.video_extradata_size);
                memcpy(codecCtx->extradata, param.video_extradata, param.video_extradata_size);
                codecCtx->extradata_size = param.video_extradata_size;
           }
      }  
   
      if (m_pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
           codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
           
      int err = avcodec_parameters_from_context(stream->codecpar, codecCtx);
   
      return stream;  
 }  
   
 void FFFileWriter::close()  
 {  
      if (m_pFormatCtx) {  
           if (m_bWriteHeader) {  
                av_write_trailer(m_pFormatCtx);  
                m_bWriteHeader = false;  
           }  

           if (m_pVideoStream) {
                if (m_pVideoStream->codec) {
                    if (m_pVideoStream->codec->extradata) {
                        av_freep(&m_pVideoStream->codec->extradata);   
                        m_pVideoStream->codec->extradata_size = 0;
                    }
                    avcodec_close(m_pVideoStream->codec);
                }
           }
           if (m_pAudioStream) {
                if (m_pAudioStream->codec) {
                    if (m_pAudioStream->codec->extradata) {
                        av_freep(&m_pAudioStream->codec->extradata);
                        m_pAudioStream->codec->extradata_size = 0;
                    }
                    avcodec_close(m_pAudioStream->codec);
                }
           }

           av_free_packet(&m_avPacket);  
           avio_close(m_pFormatCtx->pb);  
           avformat_free_context(m_pFormatCtx);  
           m_pFormatCtx = NULL;  
      }  
   
      if (m_pFile) {  
           fclose(m_pFile);  
           m_pFile = NULL;            
      }  
   
      FileWriter::close();  
 }  
   
 int FFFileWriter::write(MediaBuffer *pBuffer)  
 {  
      if (m_pFile) {     // raw stream writing  
           if (pBuffer->mediaType == VideoMedia)  
                return fwrite(pBuffer->data, pBuffer->size, 1, m_pFile);  
           return -1;  
      }  
   
      AVStream *stream = NULL;  
   
      av_init_packet(&m_avPacket);  
   
      if (pBuffer->mediaType == VideoMedia) {  
           stream = m_pVideoStream;  
           if (pBuffer->keyFrame == 1)  
                m_avPacket.flags |= AV_PKT_FLAG_KEY;  
      } else if (pBuffer->mediaType == AudioMedia) {  
           stream = m_pAudioStream;  
           if (!m_pVideoStream)  
                m_avPacket.flags |= AV_PKT_FLAG_KEY;  
      }  
   
      if (!stream) return -1;  
   
      m_avPacket.stream_index = stream->index;  
      m_avPacket.data = pBuffer->data;  
      m_avPacket.size = pBuffer->size;  
   
      // timestamp  
      if (pBuffer->pts == -1) m_nStartPTS = 0;  
      if (pBuffer->dts == -1) m_nStartDTS = 0;  
   
      if (m_nStartPTS == 0) m_nStartPTS = pBuffer->pts;  
      if (m_nStartDTS == 0) m_nStartDTS = pBuffer->dts;  
   
      int64_t pts = pBuffer->pts - m_nStartPTS;  
      int64_t dts = pBuffer->dts - m_nStartDTS;  
   
      m_avPacket.pts = pts;
      m_avPacket.dts = dts;
      AVRational time_base = { 1, 1000 };
      av_packet_rescale_ts(&m_avPacket, time_base, stream->time_base);
      stream->cur_dts = m_avPacket.dts - 1;
        
      return av_write_frame(m_pFormatCtx, &m_avPacket);  
 }  
   

< FFFileWriter 사용법 >

FFFileWriter *pWriter = new FFFileWriter();

// 해당 스트림에 맞는 비디오/오디오 속성을 입력
FileWriterOpenParam *param = new FileWriterOpenParam();
param->video_codec_id = AV_CODEC_ID_H264;
param->width = 1280;
param->height = 720;
param->frame_rate.num = 1;
param->frame_rate.den = 30;
param->gop_size = 30;
...

// 파일 열기
pWriter->open("record_file.ts", *param);
delete param;
...

// 파일 쓰기
unsigned char *buf => encoded buffer
int buf_size => encoded buffer size
MediaBuffer *pBuffer = new MediaBuffer();
pBuffer->mediaType = VideoMedia;
memcpy(pBuffer->data, buf, buf_size);
pBuffer->size = buf_size;
pBuffer->pts = pts 값 (millisecond 단위)
pBuffer->dts = dts 값 (millisecond 단위)

pWriter->write(pBuffer);

delete pBuffer;

// 파일 닫기 - 모든 스트림 저장후 마무리
pWriter->close();
delete pWriter;