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;