2014년 1월 8일 수요일

copy AVFrame using av_image_copy

AVFrame* clone_av_frame(AVFrame *frame)
{
    AVFrame *new_frame = avcodec_alloc_frame();
    enum AVPixelFormat fmt = (enum AVPixelFormat)frame->format;
    int ret = av_image_alloc(new_frame->data, new_frame->linesize, frame->width, frame-height, fmt, 1);
    if (ret < 0) {
        av_free(new_frame->data[0]);
        av_free(new_frame);
        return 0;
    }
    
    av_image_copy(new_frame->data, new_frame->linesize, (const uint8_t **)frame->data, 
                            frame->linesize, fmt, frame->width, frame->height);

    // copy to new frame
    new_frame->width = frame->width;
    new_frame->height = frame->height;
    new_frame->pict_type = frame->pict_type;
    new_frame->format = (int)fmt;
    ...

    return new_frame;
}

void free_av_frame(AVFrame *frame)
{
    if (frame) {
        av_free(frame->data[0]);
        av_free(frame);
    }
}

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