#include "stdafx.h"
#include "videocapture.h"
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/mathematics.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavdevice/avdevice.h"
#include "inttypes.h"
#include "dshow.h"
#ifdef __cplusplus
};
#endif
CVideoCapture::CVideoCapture()
{
m_pFormatCtx = NULL;
m_pCodecCtx = NULL;
m_cbYuv = NULL;
m_yuvBuffer = NULL;
m_strDeviceName = "";
m_nStreamIndex = -1;
m_isNeedFormatConversion = false;
}
CVideoCapture::~CVideoCapture()
{
}
int CVideoCapture::StartCapture(std::string deviceName, int nFrameRate, ZG_Rect rect, YuvCallBackType cbYuv, void *pContext)
{
m_cbYuv = cbYuv;
m_pContext = pContext;
m_nDstFrameRate = nFrameRate;
m_nDstRect.x = rect.x;
m_nDstRect.y = rect.y;
m_nDstRect.width = rect.width;
m_nDstRect.height = rect.height;
m_yuvBuffer = new char[rect.width*rect.height*3/2];
if(!m_yuvBuffer)
{
goto err;
}
if(initCapture(deviceName, nFrameRate, rect) != 0)
{
goto err;
}
if(startCaptureThread() != 0)
{
goto err;
}
return 0;
err:
StopCapture();
return -1;
}
int CVideoCapture::StopCapture()
{
stopCaptureThread();
unInitCapture();
if(m_yuvBuffer)
{
delete m_yuvBuffer;
m_yuvBuffer = NULL;
}
return 0;
}
int CVideoCapture::initCapture(std::string deviceName, int nFrameRate, ZG_Rect rect)
{
m_strDeviceName = deviceName;
AVInputFormat *pInputFmt = av_find_input_format("dshow");
if(pInputFmt == NULL)
{
return -1;
}
//分配上下文对象
m_pFormatCtx = avformat_alloc_context();
if(m_pFormatCtx == NULL)
{
return -1;
}
AVDictionary *optionDic = NULL;
char szVideoSize[256] = {0};
sprintf(szVideoSize, "%dx%d", rect.width, rect.height);
//设置期望获取到的视频参数
av_dict_set_int(&optionDic, "offset_x", rect.x, 0);
av_dict_set_int(&optionDic, "offset_y", rect.y, 0);
av_dict_set(&optionDic, "video_size", szVideoSize, 0);
av_dict_set_int(&optionDic, "framerate", nFrameRate, 0);
av_dict_set_int(&optionDic, "pix_fmt", AV_PIX_FMT_YUV420P, 0);
//打开流
int nRet = avformat_open_input(&m_pFormatCtx, m_strDeviceName.c_str(), pInputFmt, &optionDic);
if(nRet != 0)
{
goto err;
}
//查找流信息
if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
{
goto err;
}
if(m_pFormatCtx->streams == NULL)
{
goto err;
}
//找视频流信息索引
for (int i = 0; i < m_pFormatCtx->nb_streams; i++)
{
if (m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_nStreamIndex = i;
break;
}
}
if(m_nStreamIndex < 0)
{
goto err;
}
m_pCodecCtx = m_pFormatCtx->streams[m_nStreamIndex]->codec;
if(m_pCodecCtx == NULL)
{
goto err;
}
m_pCodecCtx->time_base.den = nFrameRate;
m_pCodecCtx->time_base.num = 1;
//如果格式不是yuv420 则需要进行图像格式转换 即需要创建SwsContext对象
if(m_pCodecCtx->pix_fmt != AV_PIX_FMT_YUV420P)
{
m_isNeedFormatConversion = true;
}
if(m_isNeedFormatConversion)
{
bool bRet = m_videoConvert.InitConvert(m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt, rect.width, rect.height, AV_PIX_FMT_YUV420P);
if(!bRet)
{
goto err;
}
}
AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
if(pCodec == NULL)
{
goto err;
}
if (avcodec_open2(m_pCodecCtx, pCodec, NULL)<0)
{
goto err;
}
return 0;
err:
unInitCapture();
return -1;
}
int CVideoCapture::unInitCapture()
{
if(m_pFormatCtx)
{
avformat_close_input(&m_pFormatCtx);
avformat_free_context(m_pFormatCtx);
}
m_videoConvert.UnInitConvert();
return 0;
}
void *CVideoCapture::cbCaptureThreadCallBack(void *param)
{
CVideoCapture *pThis = (CVideoCapture *)param;
return pThis->captureThreadCallBack();
}
void *CVideoCapture::captureThreadCallBack()
{
AVFrame *avFrame = av_frame_alloc();
if(avFrame == NULL)
{
return 0;
}
while(m_bRunCapture)
{
AVPacket pkt = {0};
int nRet = av_read_frame(m_pFormatCtx, &pkt);
if(nRet == 0)
{
int gotFrame = 0;
int nRet = avcodec_decode_video2(m_pCodecCtx, avFrame, &gotFrame, &pkt);
if(nRet < 0)
{
char szBuffer[256] = { 0 };
av_make_error_string(szBuffer, 256, nRet);
av_free_packet(&pkt);
break;
}
if(gotFrame)
{
if(m_isNeedFormatConversion)
{
if (m_videoConvert.DoConvert(avFrame, m_yuvBuffer))
{
int width = avFrame->width;
int height = avFrame->height;
if(m_cbYuv)
{
m_cbYuv(m_pContext, m_yuvBuffer, width, height);
}
}
}
else
{
}
}
}
else
{
int i = 0;
int err = ::GetLastError();
int aa = 0;
}
av_free_packet(&pkt);
}
av_frame_free(&avFrame);
return 0;
}
int CVideoCapture::startCaptureThread()
{
m_bRunCapture = true;
int nRet = pthread_create(&m_captureThreadId, NULL, cbCaptureThreadCallBack, this);
if(nRet != 0)
{
return -1;
}
return 0;
}
int CVideoCapture::stopCaptureThread()
{
m_bRunCapture = false;
pthread_join(m_captureThreadId, NULL);
return 0;
}