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