2013년 12월 26일 목요일

RTP Header Parsing

< RTPHeader.h >

#ifndef __RTP_HEADER_H__
#define __RTP_HEADER_H__

typedef unsigned short      WORD;
typedef unsigned long       DWORD;

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

typedef unsigned int u_int32_t;
typedef unsigned short u_int16_t;
typedef unsigned char u_int8_t;

typedef struct
{
WORD    cc:     4;      /* csrc count */
WORD    ext:    1;      /* header extension flag */
WORD    pad:    1;      /* padding flag - for encryption */
WORD    ver:    2;      /* protocal version */
WORD    pt:     7;      /* payload type */
WORD    mk:     1;      /* marker bit - for profile */
WORD seq; /* sequence number of this packet */
DWORD ts; /* timestamp of this packet */
DWORD ssrc; /* source of packet */
} RTP_HEADER;

class RtpHeader {
public:
RtpHeader(uint8_t *buf, int len);
virtual ~RtpHeader();

uint8_t* payload();
int length() { return fLength; }
int payloadLen();

uint16_t version() { return fVersion; }
uint16_t padding() { return fPadding; }
uint16_t extension() { return fExtension; }
uint16_t csrcCount() { return fCSRCCount; }
uint16_t markerBit() { return fMarkerBit; }
uint16_t payloadType() { return fPayloadType; }
uint16_t sequenceNum() { return fSequenceNum; }
uint32_t timestamp() { return fTimestamp; }
uint32_t ssrc() { return fSSRC; }

private:
uint8_t* fBuf;
int fLength;
uint16_t fVersion;
uint16_t fPadding;
uint16_t fExtension;
uint16_t fCSRCCount;
uint16_t fMarkerBit;
uint16_t fPayloadType;
uint16_t fSequenceNum;
uint32_t fTimestamp;
uint32_t fSSRC;

uint8_t *fCurPtr;
};

#endif


< RTPHeader.cpp >

#include <Winsock2.h>
#include "RTPHeader.h"

RtpHeader::RtpHeader(uint8_t *buf, int len) : fBuf(NULL), fLength(0), fVersion(0), fPadding(0), fExtension(0), fCSRCCount(0),
fMarkerBit(0), fPayloadType(0), fSequenceNum(0), fTimestamp(0), fSSRC(0)
{
if (len < sizeof(RTP_HEADER))
return;

fBuf = fCurPtr = buf;
fLength = len;

RTP_HEADER *p = (RTP_HEADER *)buf;
fCSRCCount = p->cc;
fExtension = p->ext;
fPadding = p->pad;
fVersion = p->ver;
fPayloadType = p->pt;
fMarkerBit = p->mk;
fSequenceNum = ntohs(p->seq);
fTimestamp = ntohl(p->ts);
fSSRC = ntohl(p->ssrc);

fCurPtr += sizeof(RTP_HEADER);

// check RTP version (it must be 2)
if (fVersion != 2)
DPRINTF("invalid rtp version %u\n", fVersion);

// skip CSRC
if (fCSRCCount > 0) {
if (payloadLen() <= fCSRCCount*4) {
DPRINTF("invalid rtp header, CSRC count error %u\n", fCSRCCount);
} else {
fCurPtr += (fCSRCCount*4);
}
}

// skip Extension field
if (fExtension) {
if (payloadLen() <= 4) {
DPRINTF("invalid rtp header, extension length error\n");
} else {
unsigned extHdr = ntohl(*(unsigned *)fCurPtr); fCurPtr += 4;
unsigned remExtSize = 4*(extHdr&0xFFFF);
if (payloadLen() <= remExtSize) {
DPRINTF("invalid rtp header, extension size error %u\n", remExtSize);
} else {
fCurPtr += remExtSize;
}
}
}

// remove padding
if (fPadding) {
if (payloadLen() <= 0) {
DPRINTF("invalid rtp header, padding error\n");
} else {
unsigned numPaddingBytes = (unsigned)fBuf[fLength-1];
if (payloadLen() <= numPaddingBytes) {
DPRINTF("invalid rtp header, padding number error\n");
} else {
fLength -= numPaddingBytes;
    fPadding = p->pad = 0;
}
}
}
}

RtpHeader::~RtpHeader()
{
}

uint8_t* RtpHeader::payload()
{
return fCurPtr;
}

int RtpHeader::payloadLen()
{
uint8_t *ptrLast = &fBuf[fLength-1];
return ptrLast-fCurPtr+1;
}


< 사용 >
...
RtpHeader *rtp = new RtpHeader((uint8_t*)buf, len);

unsigned short pt = rtp->payloadType();
unsigned short mk = rtp->markerBit();
unsigned short seqnum = rtp->sequenceNum();
unsigned int ts = rtp->timestamp();
unsigned int rtpSSRC = rtp->ssrc();

 delete rtp;

2013년 12월 18일 수요일

vc++에서 ffmpeg dll 로드실패 처리 - avcodec_register_all 실행시 crash

vc++ 에서 ffmpeg dll (--enable-shared 옵션) 링크에 성공한 후 실행하면 첫번째

avcodec_register_all 함수에서 죽는 현상 발생한다.(다른 함수들도 마찬가지)

vc++ property -> Linker -> Optimization -> References 를 Keep Unreferenced Data (/OPT:NOREF)

로 변경해서 적용할것.

2013년 8월 30일 금요일

convert AV_SAMPLE_FMT_FLTP to AV_SAMPLE_FMT_S16 with ffmpeg software resampler

extern "C" {
#include "libavcodec\avcodec.h"
#include "libswresample\swresample.h"
#include "libavutil\opt.h"
};

struct SwrContext *m_pSwrCtx;
AVCodec *m_pCodec;
AVCodecContext *m_pCodecCtx;
AVPacket m_avPacket;
AVFrame *m_pFrame;
uint8_t m_pOutBuf = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*5];
...

< open resampler >

m_pSwrCtx = swr_alloc();

uint64_t channel_layout = m_pCodecCtx->channel_layout;
if (channel_layout == 0)
channel_layout = av_get_default_channel_layout(m_pCodecCtx->channels);

av_opt_set_int(m_pSwrCtx, "in_channel_layout", channel_layout, 0);
av_opt_set_int(m_pSwrCtx, "in_sample_rate", m_pCodecCtx->sample_rate, 0);
av_opt_set_sample_fmt(m_pSwrCtx, "in_sample_fmt", m_pCodecCtx->sample_fmt, 0);

av_opt_set_int(m_pSwrCtx, "out_channel_layout", channel_layout, 0);
av_opt_set_int(m_pSwrCtx, "out_sample_rate", m_pCodecCtx->sample_rate, 0);
av_opt_set_sample_fmt(m_pSwrCtx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);


err = swr_init(m_pSwrCtx);

< do resampling >
...
int got_frame;
int retLen = avcodec_decode_audio4(m_pCodecCtx, m_pFrame, &got_frame, &m_avPacket);

if (retLen <= 0) {
DPRINTF("audio decode error : %d\n", retLen);
return retLen;
}

// audio resampling
int out_size = av_samples_get_buffer_size(NULL, m_pCodecCtx->channels, m_pFrame->nb_samples, m_pCodecCtx->sample_fmt, 1);

if (out_size > m_nOutBufSize) {
delete[] m_pOutBuf;
m_pOutBuf = new unsigned char[out_size];
m_nOutBufSize = out_size;
}

retLen = swr_convert(m_pSwrCtx, &m_pOutBuf, out_size,
(const uint8_t **)m_pFrame->extended_data, m_pFrame->nb_samples);

out_size = retLen*m_pCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
m_nOutBufSize = out_size;

return retLen;

< close resampler >

swr_free(&m_pSwrContext);


2013년 7월 4일 목요일

mingw ffmpeg x264 연동 빌드 - mingw ffmpeg with x264 build configuration

* x264 를 먼저 빌드후 ffmpeg 빌드할때 아래와 같이 x264 경로를 명시해준다.

< x264 빌드 >
./configure --enable-win32thread --extra-cflags="-fno-stack-check -fno-stack-protector -mno-stack-arg-probe"

< ffmpeg 빌드 >
./configure --enable-memalign-hack --extra-cflags="-fno-stack-check -fno-stack-protector -mno-stack-arg-probe" --enable-libx264 --enable-gpl --extra-cflags=-I../x264-snapshot-20120208-2245 --extra-ldflags=-L../x264-snapshot-20120208-2245

2013년 6월 21일 금요일

ffmpeg 을 이용한 비디오 스트림 파일저장 - ffmpeg muxer file writer

http://greenday96.blogspot.kr/2015/08/ffmpeg-muxer-stream-record-by-ffmpeg.html <= 최신 포스트 참조(비디오+오디오 저장)

* ffmpeg muxer 사용 - avformat_alloc_output_context2 함수의 filename 확장자에 따라 avi/mp4/ts 등 포멧으로 저장가능


< FileWriter.h >

class FileWriter
{
public:
    FileWriter();
    virtual ~FileWriter();

    int openFile(char *filename, int codec, int width, int height);
    int writeFile(enum AVMediaType type, char *buf, int len);
    void closeFile();

protected:
    AVStream* addVideoStream(enum CodecID codec_id, int width, int height);
    int writeVideo(char *buf, int len);
    int writeAudio(char *buf, int len);

protected:
    AVFormatContext *m_pFormatContext;
    AVStream *m_pVideoStream;
    AVPacket m_avPacket;

    enum CodecID m_nCodecID;

    __int64 m_nFrameCount;
};


< FileWriter.cpp >

#include "FileWriter.h"

FileWriter::FileWriter()
{
    static int init = 0;
    if (init == 0)
    {
       av_register_all();
       init = 1;
    }

    m_pFormatContext = NULL;
    m_pVideoStream = NULL;
}

FileWriter::~FileWriter()
{
    closeFile();
}

int FileWriter::openFile(char *filename, int codec, int width, int height)
{
    int err;

    m_nFrameCount = 0;
    m_nCodecID = CODEC_ID_NONE;

    err = avformat_alloc_output_context2(&m_pFormatContext, NULL, NULL, filename);
    if (!m_pFormatContext || err < 0) {
       printf("[%s] avformat_alloc_output_context2: error %d\n", __FUNCTION__, err);
       return -1;
    }

    av_init_packet(&m_avPacket);

    AVOutputFormat *fmt = m_pFormatContext->oformat;

    if (codec == 0) {
       fmt->video_codec = CODEC_ID_H264;
       m_nCodecID = CODEC_ID_H264;
    } else if (codec == 1) {
       fmt->video_codec = CODEC_ID_MPEG4;
       m_nCodecID = CODEC_ID_MPEG4;
    }

    m_pVideoStream = addVideoStream(fmt->video_codec, width, height);
    if (!m_pVideoStream) {
       printf("[%s] addVideoStream failed\n", __FUNCTION__);
       return -1;
    }

    err = avio_open(&m_pFormatContext->pb, filename, AVIO_FLAG_WRITE);
    if (err < 0) {
       printf("[%s] avio_open failed: error %d\n", __FUNCTION__, err);
       closeFile();
       return -1;
    }

#ifdef FFMPEG_1_2_1
    err = avformat_write_header(m_pFormatContext, NULL);
#else
    err = av_write_header(m_pFormatContext);
#endif

    return 0;
}

void FileWriter::closeFile()
{
        if (m_pFormatContext) {
           av_write_trailer(m_pFormatContext);
           if (m_pVideoStream) {
               if (m_pVideoStream->codec) {
     avcodec_close(m_pVideoStream->codec);
}
            }

       av_free_packet(&m_avPacket);
       avio_close(m_pFormatContext->pb);
       avformat_free_context(m_pFormatContext);
       m_pFormatContext = NULL;
    }
}

int FileWriter::writeFile(enum AVMediaType type, char *buf, int len)
{
    if (type == AVMEDIA_TYPE_VIDEO)
        return writeVideo(buf, len);
    else if (type == AVMEDIA_TYPE_AUDIO)
       return writeAudio(buf, len);

    return -1;
}

int FileWriter::writeVideo(char *buf, int len)
{
    if (!m_pFormatContext)
       return -1;

    int ret = 0;

    av_init_packet(&m_avPacket);

    m_avPacket.stream_index = m_pVideoStream->index;
    m_avPacket.data = (uint8_t *)buf;
    m_avPacket.size = len;

    if (checkKeyFrame(buf, len) == 1)
       m_avPacket.flags |= AV_PKT_FLAG_KEY;

    ret = av_interleaved_write_frame(m_pFormatContext, &m_avPacket);
    //ret = av_write_frame(m_pFormatContext, &m_avPacket);

    m_nFrameCount++;
    return ret;
}

AVStream* FileWriter::addVideoStream(enum CodecID codec_id, int width, int height)
{
    AVStream *st;
    AVCodecContext *c;

    st = av_new_stream(m_pFormatContext, 0);
    if (!st) {
       printf("[%s] Could not alloc stream\n", __FUNCTION__);
       return NULL;
    }

    c = st->codec;
    c->codec_id = codec_id;
    c->codec_type = AVMEDIA_TYPE_VIDEO;

    c->width = width;
    c->height = height;

    c->time_base.den = 30;
    c->time_base.num = 1;
    c->gop_size = 30;

    c->pix_fmt = PIX_FMT_YUV420P;

    // some formats want stream headers to be separate
    if(m_pFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
       c->flags |= CODEC_FLAG_GLOBAL_HEADER;

    return st;
}

int FileWriter::writeAudio(char *buf, int len)
{
    return -1;
}

2013년 4월 17일 수요일

ffmpeg file play source code

< FileReader.h >


extern "C" {
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
#include "libavutil\avutil.h"
}

class FileReader
{
public:    
    FileReader();
    virtual ~FileReader();

    int openFile(char *filename);
    void closeFile();
    int running();
    ...
    int seek(int pos);

protected:
    AVFormatContext *m_pFormatContext;
    AVPacket m_avPacket;
    AVIndexEntry *m_pIndexEntries;
    int m_nIndexEntries;

    enum CodecID m_nVideoCodecId;
    int m_nWidth, m_nHeight;
    int m_nVideoStreamId, m_nAudioStreamId;
    ...
    BOOL m_bReadThreadRun;
};


< FileReader.cpp >

#include "FileReader.h"

FileReader::FileReader()
{
    static int init = 0;
    if (init == 0)
    {
        WaitForSingleObject(hMutexCodec, INFINITE);
        av_register_all();
        ReleaseMutex(hMutexCodec);
        init = 1;
    }

    m_pFormatContext = NULL;

    m_nVideoStreamId = m_nAudioStreamId = AVMEDIA_TYPE_UNKNOWN;

    m_pIndexEntries = NULL;
    m_nIndexEntries = 0;

    m_bReadThreadRun = false;
}

FileReader::~FileReader()
{
    closeFile();
}

int FileReader::openFile(char *filepath)
{
    int err;

#ifdef FFMPEG_0_8
    err = av_open_input_file(&m_pFormatContext, filepath, NULL, 0, NULL);
#else
    err = avformat_open_input(&m_pFormatContext, filepath, NULL, NULL);
#endif

    if (err < 0) {
        printf("[%d] av_open_input_file: error %d\n", __FUNCTION__, err);
        return -2;
    }

    err = av_find_stream_info(m_pFormatContext);
    if (err < 0) {
        printf("[%d] av_find_stream_info: error %d\n", __FUNCTION__, err);
        closeFile();
        return -2;
    }

    av_init_packet(&m_avPacket);

    AVCodecContext *videoCodec = NULL;

    for (int i=0; i<m_pFormatContext->nb_streams; i++)
    {
        AVStream *stream = m_pFormatContext->streams[i];

        if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoCodec = stream->codec;
            m_nWidth = videoCodec->width;
            m_nHeight = videoCodec->height;

            m_pIndexEntries = stream->index_entries;
            m_nIndexEntries = stream->nb_index_entries;

            m_nVideoCodecId = videoCodec->codec_id;
            m_nVideoStreamId = stream->id;
        }
        else if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            m_nAudioStreamId = stream->id;
            ...
        }
    }

    return 0;
}

void FileReader::closeFile()
{
    if (m_pFormatContext) {
        av_free_packet(&m_avPacket);
        av_close_input_file(m_pFormatContext);
        m_pFormatContext = NULL;
        m_pIndexEntries = NULL;
        m_nIndexEntries = 0;
    }
}

int FileReader::running()
{
    int err;

    while (m_bReadThreadRun)
    {
        err = av_read_frame(m_pFormatContext, &m_avPacket);
        if (err < 0) 
        {
            Sleep(10);
            continue;
        }

        if (m_avPacket.stream_index == m_nVideoStreamId)
        {
            process video stream here...
        }
        else if (m_avPacket.stream_index == m_nAudioStreamId)
        {
            process audio stream here...
        }

        av_free_packet(&m_avPacket);
        Sleep(10);
    }

    return 0;
}

int FileReader::seek(int pos)
{
    if (m_pFormatContext)
        //return av_seek_frame(m_pFormatContext, m_nVideoStreamId, pos, AVSEEK_FLAG_BYTE);   // file position 으로 seek
        return av_seek_frame(m_pFormatContext, m_nVideoStreamId, pos, AVSEEK_FLAG_FRAME);    // frame count 로 seek

    return -1;
}

2013년 3월 28일 목요일

C++ double linked-list Template Queue

< BufferQueue.h - cpp 만들지 말것, 빌드시 링크에러 발생 >
 #ifndef __BUFFER_QUEUE_H__  
 #define __BUFFER_QUEUE_H__  
   
 #include "Mutex.h"  
 #include "CommonType.h"  
   
 template <typename T>  
 class BufferQueue  
 {  
 public:  
      BufferQueue(int maxQueCount);  
      virtual ~BufferQueue();  
   
      int push_back(T *data);  
      T* pop_front();  
   
      int push_front(T *data);  
      T* pop_back();  
   
      void clear();  
      int count() { return m_nCount; }  
      int maxCount() { return m_nMaxQueCount; }  
      bool isFull();  
   
 protected:  
      int                         m_nMaxQueCount;  
      int                         m_nCount;  
      MUTEX                    m_hMutex;  
   
      template <typename DT>  
      class Element  
      {  
           friend class BufferQueue<DT>;  
   
      private:  
           Element(DT *inData) : data(inData), next(NULL), prev(NULL)  
           {}  
   
           DT *data;  
           Element *next, *prev;  
      };  
   
      Element<T> *m_pHead, *m_pTail;  
 };  
   
 // 템플릿은 미리 컴파일이 되어있어야 함  
 template <typename T>  
 BufferQueue<T>::BufferQueue(int maxQueCount)  
 {  
      m_nMaxQueCount = maxQueCount;  
      m_nCount = 0;  
      m_pHead = m_pTail = NULL;  
      MUTEX_INIT(&m_hMutex);  
 }  
   
 template <typename T>  
 BufferQueue<T>::~BufferQueue()  
 {  
      clear();  
      MUTEX_DESTROY(&m_hMutex);  
 }  
   
 template <typename T>  
 void BufferQueue<T>::clear()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *tmp;  
      T *data;  
   
      while (m_pHead != NULL)  
      {  
           tmp = m_pHead;  
           m_pHead = tmp->next;  
           data = tmp->data;  
           delete data;  
           delete tmp;  
      }  
   
      m_pHead = m_pTail = NULL;  
      m_nCount = 0;  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 template <typename T>  
 int BufferQueue<T>::push_back(T *data)  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (m_nMaxQueCount > 0) {  
           if (m_nCount >= m_nMaxQueCount) {  
                MUTEX_UNLOCK(&m_hMutex);  
                return -1;  
           }  
      }       
   
      Element<T> *elm = new Element<T>(data);  
   
      if (m_pHead == NULL)  
      {  
           m_pHead = elm;  
           m_pTail = elm;  
      }  
      else  
      {  
           elm->prev = m_pTail;  
           m_pTail->next = elm;  
           m_pTail = elm;  
      }  
   
      m_nCount++;  
   
      MUTEX_UNLOCK(&m_hMutex);  
   
      return m_nCount;  
 }  
   
 template <typename T>  
 T* BufferQueue<T>::pop_front()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *elm;  
      T *data = NULL;  
   
      if (m_nCount == 0)  
           goto exit;  
   
      if (m_pHead == NULL)  
           goto exit;  
   
      elm = m_pHead;  
      data = m_pHead->data;  
      m_pHead = m_pHead->next;  
      if (m_pHead) m_pHead->prev = NULL;  
      delete elm;  
   
      if (!m_pHead)  
           m_pTail = NULL;  
   
      m_nCount--;  
   
 exit:  
      MUTEX_UNLOCK(&m_hMutex);  
   
      return data;  
 }  
   
 template <typename T>  
 int BufferQueue<T>::push_front(T *data)  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (m_nMaxQueCount > 0) {  
           if (m_nCount >= m_nMaxQueCount) {  
                MUTEX_UNLOCK(&m_hMutex);  
                return -1;  
           }  
      }       
   
      Element<T> *elm = new Element<T>(data);  
   
      if (m_pHead == NULL)  
      {  
           m_pHead = elm;  
           m_pTail = elm;  
      }  
      else  
      {  
           elm->next = m_pHead;  
           m_pHead->prev = elm;  
           m_pHead = elm;  
      }  
   
      m_nCount++;  
   
      MUTEX_UNLOCK(&m_hMutex);  
   
      return m_nCount;  
 }  
   
 template <typename T>  
 T* BufferQueue<T>::pop_back()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      Element<T> *elm;  
      T *data = NULL;  
   
      if (m_nCount == 0)  
           goto exit;  
   
      if (m_pTail == NULL)  
           goto exit;  
   
      elm = m_pTail;  
      data = m_pTail->data;  
      m_pTail = m_pTail->prev;  
      if (m_pTail) m_pTail->next = NULL;  
      delete elm;  
   
      if (!m_pTail)  
           m_pHead = NULL;  
   
      m_nCount--;  
   
 exit:  
      MUTEX_UNLOCK(&m_hMutex);  
   
      return data;  
 }  
   
 template <typename T>  
 bool BufferQueue<T>::isFull()  
 {  
      if (m_nMaxQueCount > 0) {  
           return m_nCount>=m_nMaxQueCount;  
      }  
   
      return false;  
 }  
   
 #endif     
< BufferQueue 사용 >

class MediaBuffer
{
public:
enum MediaType mediaType;
char *data;
int size;
int streamIndex;
int keyFrame;
__int64 timestamp;
__int64 pos;
}
...
BufferQueue<MediaBuffer> *m_pVideoQue = new BufferQueue<MediaBuffer>(30);
...
MediaBuffer *pBuffer = new MediaBuffer();
if (m_pVideoQue->push_back(pBuffer) < 0)
    delete pBuffer;
...
MediaBuffer *pBuffer = m_pVideoQue->pop_front();
...

* linked-list 로 할 수도 있지만 향후 확장성을 고려하여 double linked-list 로 구현

ffmpeg 로그 출력 설정



static void activex_av_log(const char *fmt, va_list vargs)
{
char szMsg[512] = {0};
vsprintf(szMsg, fmt, vargs);
OutputDebugString("[activex_av_log]");
OutputDebugString(szMsg);
}


static void av_log_output(void *ptr, int level, const char *fmt, va_list vargs)
{
KLog log;
static int print_prefix = 1;
AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
if (level > av_log_get_level())
return;
if (print_prefix && avc)
log.WriteFileLog("[%s @ %p]", avc->item_name(ptr), ptr);
print_prefix = strstr(fmt, "\n") != NULL;
activex_av_log(fmt, vargs);
//http_vlog(fmt, vargs);
}

...
int level = av_log_get_level();    // 로그레벨 가져오기
av_log_set_level(99);    // 로그레벨 설정
av_log_set_callback(av_log_output);   // 로그출력 콜백함수 설정


* 위와같이 설정하면 윈도우에서 debugview 를 통해 ffmpeg 로그를 볼수 있다

MinGW ffmpeg build configuration

./configure --enable-memalign-hack --extra-cflags="-fno-stack-check -fno-stack-protector -mno-stack-arg-probe"

2013년 2월 28일 목요일

WPF Custom Toggle Image Button

http://www.syntaxstudio.co.uk/2012/12/creating-a-toggle-image-button-control-in-wpf/

사이트의 소스를 수정하여 ImageButton 을 구현하였다. 기존 소스를 확장하여
Check/Uncheck 토글뿐 아니라 일반 버튼으로도 사용할 수 있다.

< ImageButton.xaml >
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
           xmlns:local="clr-namespace:SyntaxStudio.ToggleImageButton">  
   
   <Style x:Key="ImageButton" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="FontWeight" Value="Bold" />  
     <Setter Property="FontSize" Value="14" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     Margin="0,0,0,12"  
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Bottom">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
   
   <Style x:Key="ImageButton1" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Center">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
 </ResourceDictionary>  

< ImageButton.cs >
 // --------------------------------  
 // <copyright file="ToggleImageButton.cs" company="SyntaxStudio">  
 //   Copyright 2012, www.syntaxstudio.co.uk  
 // </copyright>  
 // <author>Ryan Haworth</author>  
 // ---------------------------------  
   
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Windows;  
 using System.Windows.Controls;  
 using System.Windows.Data;  
 using System.Windows.Documents;  
 using System.Windows.Input;  
 using System.Windows.Media;  
 using System.Windows.Media.Imaging;  
 using System.Windows.Navigation;  
 using System.Windows.Shapes;  
 using System.Windows.Controls.Primitives;  
   
 namespace SyntaxStudio.ToggleImageButton  
 {  
   /// <summary>  
   /// The is is a custom control class for the toggle button. This toggle button will  
   /// accept two images. As the button is toggled between an inactive state to an  
   /// active state the button will change its image.  
   /// </summary>  
   public class ImageButton : ButtonBase  
   {  
     private static readonly DependencyProperty CurrentImageProperty = DependencyProperty.Register("CurrentImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedImageProperty = DependencyProperty.Register("UnCheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedHoverImageProperty = DependencyProperty.Register("UnCheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedImageProperty = DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedHoverImageProperty = DependencyProperty.Register("CheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register("PressedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty DisabledImageProperty = DependencyProperty.Register("DisabledImage", typeof(ImageSource), typeof(ImageButton));  
   
     private bool isHovering = false;  
   
     private bool isChecked = false;  
     public bool IsChecked  
     {  
       get { return isChecked; }  
       set  
       {  
         if (value == false)  
         {  
           if (isHovering == false)  
             CurrentImage = UnCheckedImage;  
           else  
             CurrentImage = UnCheckedHoverImage;  
         }  
         else  
         {  
           if (isHovering == false)  
             CurrentImage = CheckedImage;  
           else  
             CurrentImage = CheckedHoverImage;  
         }  
         isChecked = value;  
       }  
     }  
   
     private bool isDisabled = false;  
     public bool IsDisabled  
     {  
       get { return isDisabled; }  
       set  
       {  
         if (value == false)  
         {  
           CurrentImage = UnCheckedImage;  
         }  
         else  
         {  
           CurrentImage = DisabledImage;  
         }  
       }  
     }  
   
     public ImageSource CurrentImage  
     {  
       get { return (ImageSource)GetValue(CurrentImageProperty); }  
       set { SetValue(CurrentImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedImageProperty); }  
       set { SetValue(UnCheckedImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedHoverImageProperty); }  
       set { SetValue(UnCheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource CheckedImage  
     {  
       get { return (ImageSource)GetValue(CheckedImageProperty); }  
       set { SetValue(CheckedImageProperty, value); }  
     }  
   
     public ImageSource CheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(CheckedHoverImageProperty); }  
       set { SetValue(CheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource PressedImage  
     {  
       get { return (ImageSource)GetValue(PressedImageProperty); }  
       set { SetValue(PressedImageProperty, value); }  
     }  
   
     public ImageSource DisabledImage  
     {  
       get { return (ImageSource)GetValue(DisabledImageProperty); }  
       set { SetValue(DisabledImageProperty, value); }  
     }  
   
     public ImageButton()  
     {  
       CurrentImage = UnCheckedImage;  
     }  
   
     static ImageButton()  
     {  
       DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));  
     }  
   
     protected override void OnMouseEnter(MouseEventArgs e)  
     {  
       base.OnMouseEnter(e);  
   
       isHovering = true;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedHoverImage != null)  
           CurrentImage = UnCheckedHoverImage;  
       }  
       else  
       {  
         if (CheckedHoverImage != null)  
           CurrentImage = CheckedHoverImage;  
       }  
     }  
   
     protected override void OnMouseLeave(MouseEventArgs e)  
     {  
       base.OnMouseLeave(e);  
   
       isHovering = false;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedImage != null)  
           CurrentImage = UnCheckedImage;  
       }  
       else  
       {  
         if (CheckedImage != null)  
           CurrentImage = CheckedImage;  
       }  
     }  
   
     protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonDown(e);  
       if (PressedImage != null)  
         CurrentImage = PressedImage;  
     }  
   
     protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonUp(e);  
       if (PressedImage != null)  
       {  
         if (IsChecked == true)  
           CurrentImage = CheckedImage;  
         else  
           CurrentImage = UnCheckedImage;  
       }  
     }  
   }  
 }  
   


< 사용예 >
...
xmlns:ToggleImageButton="clr-namespace:SyntaxStudio.ToggleImageButton" 
...
                <ToggleImageButton:ImageButton x:Name="btnToggleBorder" Width="65" Height="35" 
                                               CurrentImage="/images/uncheck.png"
                                               UnCheckedImage="/images/uncheck.png" UnCheckedHoverImage="/images/uncheck_hover.png" 
                                               CheckedImage="/images/check.png" CheckedHoverImage="check_hover.png" 
                                               Click="btnToggleBorder_Click"/>
...
btnToggleBorder.IsChecked = true/false 를 줘서 토글상태 변경




WPF Grid Sliding Animation

http://www.codeproject.com/Articles/18379/WPF-Tutorial-Part-2-Writing-a-custom-animation-cla
에 있는 클래스를 조금 수정하여 Grid 패널을 숨기고 보여주는 기능을 애니메이션으로 구현.
기존 클래스를 사용하면 Grid Column/Row 의 GridLength 속성이 Pixel 인 경우 제대로 동작하지 않는 문제와 애니메이션후 동작하지않는 문제가 있었고 위 주소를 참조하여 이를 해결.

< GridLengthAnimation.cs >

namespace GridAnimationDemo
{
    internal class GridLengthAnimation : AnimationTimeline
    {
        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength),
                typeof(GridLengthAnimation));
        }

        public override Type TargetPropertyType
        {
            get
            {
                return typeof(GridLength);
            }
        }

        protected override System.Windows.Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public static readonly DependencyProperty FromProperty;
        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.FromProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.FromProperty, value);
            }
        }

        public static readonly DependencyProperty ToProperty;
        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.ToProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.ToProperty, value);
            }
        }

        public override object GetCurrentValue(object defaultOriginValue,
            object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;

            if (fromVal > toVal)
            {
                return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal,
                ((GridLength)GetValue(GridLengthAnimation.FromProperty)).GridUnitType);
            }

            return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal,
            ((GridLength)GetValue(GridLengthAnimation.ToProperty)).GridUnitType);
        }
    }
}

< Show/Hide 애니메이션 >

GridLength orgGridWidth = gridMain.ColumnDefinitions[0].Width;
int minPanelWidth = 30;
        private void btnSlidePanel_Click(object sender, RoutedEventArgs e)
        {
            if (gridMain.ColumnDefinitions[0].Width.Value > minPanelWidth)  // hide grid
            {
                orgGridWidth = gridMain.ColumnDefinitions[0].Width;                
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = new GridLength(minPanelWidth, GridUnitType.Pixel);
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = new GridLength(minPanelWidth, GridUnitType.Pixel);
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
            else // show grid
            {
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = orgGridWidth;
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = orgGridWidth;
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
        }