2016년 1월 28일 목요일

ffmpeg 비디오 필터 사용 소스코드 - ffmpeg video filter sample source code

뮤텍스 소스

< VideoFilter.h >
 #ifndef __VIDEO_FILTER_H__  
 #define __VIDEO_FILTER_H__  
   
 extern "C" {  
 #include "libavfilter/avfiltergraph.h"  
 #include "libavfilter/buffersink.h"  
 #include "libavfilter/buffersrc.h"  
 }  
   
 #include "Mutex.h"  
   
 struct VideoFilterParam {  
      int width;  
      int height;  
      AVPixelFormat pix_fmt;  
      AVRational time_base;  
      AVRational sample_aspect_ratio;  
 };  
   
 class VideoFilter   
 {  
 public:  
      VideoFilter();  
      virtual ~VideoFilter();  
   
      int initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate);  
      void freeFilter();  
   
      int processFilter(AVFrame *newFrame, AVFrame *frame);  
   
      int checkFilter(struct VideoFilterParam &param);  
   
 protected:  
      static void initFilter();  
   
 protected:  
      AVFilterGraph*          m_pFilterGraph;  
      AVFilterContext*     m_pBufferSinkCtx;  
      AVFilterContext*     m_pBufferSrcCtx;       
      AVFrame*               m_pFrame;  
   
      int                    m_nWidth;  
      int                    m_nHeight;  
      AVPixelFormat     m_nPixelFormat;  
      AVRational          m_timeBase;  
      AVRational          m_sampleAspectRatio;  
   
      static MUTEX     m_hMutex;  
      static bool          m_bInit;  
 };  
   
 #endif  
   

< VideoFilter.cpp >
 #include "VideoFilter.h"  
 #include "GlobalEnv.h"  
 #include "CommonType.h"  
   
 bool VideoFilter::m_bInit = false;  
 MUTEX VideoFilter::m_hMutex = PTHREAD_MUTEX_INITIALIZER;  
   
 VideoFilter::VideoFilter() : m_pFilterGraph(NULL), m_pBufferSrcCtx(NULL), m_pBufferSinkCtx(NULL), m_pFrame(NULL)  
 {  
      initFilter();  
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 VideoFilter::~VideoFilter()  
 {  
      freeFilter();  
 }  
   
 void VideoFilter::initFilter()  
 {  
      MUTEX_LOCK(&m_hMutex);  
   
      if (!m_bInit) {  
           avfilter_register_all();  
           m_bInit = true;  
      }  
   
      MUTEX_UNLOCK(&m_hMutex);  
 }  
   
 int VideoFilter::initFilter(struct VideoFilterParam &param, int vflip, int hflip, int rotate)  
 {  
      freeFilter();  
   
      char args[128];  
   snprintf(args, sizeof(args),  
       "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",  
       param.width, param.height, param.pix_fmt,   
                param.time_base.num, param.time_base.den,   
       param.sample_aspect_ratio.num, param.sample_aspect_ratio.den);  
   
      m_pFrame = av_frame_alloc();  
      m_pFilterGraph = avfilter_graph_alloc();  
   
      char errbuf[128];  
      int err = avfilter_graph_create_filter(&m_pBufferSrcCtx, avfilter_get_by_name("buffer"), "buffer1", args, NULL, m_pFilterGraph);       
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_create_filter(&m_pBufferSinkCtx, avfilter_get_by_name("buffersink"), "buffersink1", NULL, NULL, m_pFilterGraph);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      AVFilterContext *vflipCtx = NULL;  
      AVFilterContext *hflipCtx = NULL;  
      AVFilterContext *rotateCtx = NULL;  
      AVFilterContext *prevCtx = m_pBufferSrcCtx;  
   
      if (vflip > 0) {  
           err = avfilter_graph_create_filter(&vflipCtx, avfilter_get_by_name("vflip"), "vflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, vflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = vflipCtx;  
      }  
   
      if (hflip > 0) {  
           err = avfilter_graph_create_filter(&hflipCtx, avfilter_get_by_name("hflip"), "hflip1", NULL, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, hflipCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = hflipCtx;  
      }  
   
      if (rotate) {  
           snprintf(args, sizeof(args), "PI*2/360*%d", rotate);  
           err = avfilter_graph_create_filter(&rotateCtx, avfilter_get_by_name("rotate"), "rotate1", args, NULL, m_pFilterGraph);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_graph_create_filter failed, err : %s\n", errbuf);  
                return -1;  
           }  
           err = avfilter_link(prevCtx, 0, rotateCtx, 0);  
           if (err < 0) {  
                av_strerror(err, errbuf, sizeof(errbuf));  
                DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
                return -1;  
           }  
           prevCtx = rotateCtx;  
      }  
   
      err = avfilter_link(prevCtx, 0, m_pBufferSinkCtx, 0);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_link failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      err = avfilter_graph_config(m_pFilterGraph, NULL);  
      if (err < 0) {  
           av_strerror(err, errbuf, sizeof(errbuf));  
           DXPRINTF("avfilter_graph_config failed, err : %s\n", errbuf);  
           return -1;  
      }  
   
      m_nWidth = param.width;  
      m_nHeight = param.height;  
      m_nPixelFormat = param.pix_fmt;  
      m_timeBase.den = param.time_base.den;  
      m_timeBase.num = param.time_base.num;  
      m_sampleAspectRatio.den = param.sample_aspect_ratio.den;  
      m_sampleAspectRatio.num = param.sample_aspect_ratio.num;  
   
      return 0;  
 }  
   
 void VideoFilter::freeFilter()  
 {  
      avfilter_graph_free(&m_pFilterGraph);  
      av_frame_free(&m_pFrame);  
   
      m_nWidth = m_nHeight = 0;  
      m_nPixelFormat = AV_PIX_FMT_NONE;  
      m_timeBase.den = m_timeBase.num = 0;  
      m_sampleAspectRatio.den = m_sampleAspectRatio.num = 0;  
 }  
   
 AVFrame* copyFrame(AVFrame *frame)  
 {  
      AVFrame *new_frame = avcodec_alloc_frame();  
      if (new_frame) {  
           if (av_image_alloc(new_frame->data, new_frame->linesize,  
                frame->width, frame->height, (AVPixelFormat)frame->format, 1) > 0)   
           {  
                av_image_copy(new_frame->data, new_frame->linesize,  
                     (const uint8_t **)frame->data, frame->linesize, (AVPixelFormat)frame->format,  
                     frame->width, frame->height);  
   
                new_frame->width = frame->width;  
                new_frame->height = frame->height;  
                new_frame->pict_type = frame->pict_type;  
                new_frame->format = frame->format;  
   
                return new_frame;  
           }  
           else  
           {  
                av_free(new_frame->data[0]);  
                av_free(new_frame);  
           }  
      }  
   
      return NULL;  
 }  
   
 int VideoFilter::processFilter(AVFrame *newFrame, AVFrame *frame)  
 {  
      if (!m_pFilterGraph) return -1;  
   
      int ret = av_buffersrc_add_frame_flags(m_pBufferSrcCtx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);  
      if (ret >= 0) {  
           while (1) {  
                ret = av_buffersink_get_frame(m_pBufferSinkCtx, m_pFrame);  
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;  
                if (ret < 0) break;  
        
                newFrame = copyFrame(m_pFrame);  
                av_frame_unref(m_pFrame);  
           }  
      }  
      return 0;  
 }  
   
 int VideoFilter::checkFilter(struct VideoFilterParam &param)  
 {  
      if (m_nWidth != param.width || m_nHeight != param.height ||   
           m_nPixelFormat != param.pix_fmt ||  
           m_timeBase.den != param.time_base.den || m_timeBase.num != param.time_base.num ||  
           m_sampleAspectRatio.den != param.sample_aspect_ratio.den ||   
           m_sampleAspectRatio.num != param.sample_aspect_ratio.num)   
      {  
           return -1;  
      }  
   
      return 0;  
 }  
   

< VideoFilter 사용 >

AVCodecContext *pCodecCtx;
...
struct VideoFilterParam param;
param.width = pCodecCtx->width;
param.height = pCodecCtx->height;
param.pix_fmt = pCodecCtx->pix_fmt;
param.time_base.den = pCodecCtx->time_base.den;
param.time_base.num = pCodecCtx->time_base.num;
param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
...
VideoFilter *filter = new VideoFilter();
...
filter->initFilter(param, 1, 1, 30);    // 상하반전, 좌우반전, 30도 회전
...
if (filter->checkFilter(param) < 0) {
    filter->freeFilter();
 
    param.width = pCodecCtx->width;
    param.height = pCodecCtx->height;
    param.pix_fmt = pCodecCtx->pix_fmt;
    param.time_base.den = pCodecCtx->time_base.den;
    param.time_base.num = pCodecCtx->time_base.num;
    param.sample_aspect_ratio.den = pCodecCtx->sample_aspect_ratio.den;
    param.sample_aspect_ratio.num = pCodecCtx->sample_aspect_ratio.num;
}  
filter->processFilter(newFrame, frame);   // newFrame은 처리결과, frame은 디코딩된 프레임
...
filter->freeFilter();

댓글 3개:

  1. 좋은 내용 감사합니다. ffmpeg 관련되서 올려놓으신 코드를 따라가며 공부하고 있는데
    GlobalEnv.h 파일이 없어서 compile이 안되는데 염치없지만 구할수 있을까요??

    답글삭제
    답글
    1. GlobalEnv.h는 없어도 됩니다. include 지우고 DXPRINTF => printf같은 출력문으로 바꾸거나 다 지우면됩니다

      삭제
    2. 빠른 답변 감사드립니다.
      한가지 더 질문이 있습니다. 해당 포스트는 아니고 ffmpeg muxer file writer를 보면
      checkKeyFrame() 함수가 있는데, 해당 함수가 복잡해서 빼 놓으셧다고 댓글이 달려있는데
      해당 if 문을 주석처리해도 저장된 파일이 영상은 정상적으로 나오나요??

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

      삭제