音视频聊天开发: 1 视频采集

来源:互联网 发布:linux ftp权限设置 编辑:程序博客网 时间:2024/05/12 00:59
一整套音视频聊天项目,一般包括:

视频采集-->编码-->传输-->解码-->播放
声音采集-->编码-->传输-->解码-->播放

传输部分,还分P2P和中转。
因为还要加一些指令,实时消息,相当于一个完整的IM再带上语音视频功能。

最近整理一些以前做的资料,发现整理成一个完整的项目更有效,否则一堆一堆零散代码沉积下去就成泥沙了。

这里以最简单的实现方式,完成一个音视频聊天功能,不求最优,只求最简,网络和音视频的初学者一看就懂。

功能包括:
客户端:音视频采集,编解码,播放,传输。
服务端:简单用户管理,类Stun,穿透Nat,中转

充分利用开源项目,比如编解码用ffmpeg,视频采集用CCameraDS,声音采集用PortAudio等。
不过,音视频通信,最重要的实际是网络部分。主要是UDP通信。这部分完全用socket自己写,不使用开源项目,这样可以快速
理解并熟练掌握网络socket通信。

计划一个文章讲一个内容,音视频因为是比较成熟的技术,相对篇幅少一些。socket通信虽然代码量少,但比较重要,到那里再多讲一些。
这样算起来,10几到20来篇就差不多了。不过经历时间可能会不少,初步计划到2014年底。

按照上面说的顺序,先从视频的采集开始吧。
视频采集通常用DirectShow, 或者结合OpenCV,这里有一个不错的资料可供参考:
http://wiki.opencv.org.cn/index.php/%E4%BD%BF%E7%94%A8DirectShow%E9%87%87%E9%9B%86%E5%9B%BE%E5%83%8F
这个是不带dshow库的,需要自己下载安装。
http://download.csdn.net/download/wqvbjhc/2809976
这个下载里面带有dshow .h和.lib,应该不用再安装dshow开发包了。不过我机器已经安装了,没有试。

不过,这里面的采集:
while(1)
{
//获取一帧
pFrame = m_CamDS.QueryFrame();
//显示
cvShowImage(g_szTitle, pFrame);
if (cvWaitKey(20) == 'q')
{
break;
}
}
很明显,是阻塞的,需要启一个线程。
从源码也可以看出:m_pSampleGrabber->SetOneShot(TRUE); 

个人比较喜欢使用ISampleGrabber的回调方式给出视频流,所以项目里对CCameraDS做了一点改造,这里简单写一下改造的部分,完整代码在附件里。
在CCameraDS类里面,加一个子类(也可以独立一个class,根据自己喜欢):
class CSampleGrabberCB : public ISampleGrabberCB 
{
public: 
CSampleGrabberCB();


STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();

STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample );
STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize );

public:
BOOL m_bCaptureVideo;
int  m_nID;
camera_data_cb m_cb;
void* m_pParam;
}; 
再声明一个变量:
CSampleGrabberCB m_cGrabberCB;

在OpenCamera里面,m_pSampleGrabber->SetOneShot(TRUE);的地方修改一下:


#ifndef ISampleGrabberCB_CALLBACK
m_pSampleGrabber->SetOneShot(TRUE);
#else
m_pSampleGrabber->SetOneShot(FALSE);
m_pSampleGrabber->SetCallback(&m_cGrabberCB, 1);
#endif


然后,不再调用QueryFrame,另外实现三个控制运行停止的函数:
HRESULT CCameraDS::Run()
{
HRESULT hr = S_OK;
OAFilterState state;

    if( !m_pMediaControl )
    {
        return E_UNEXPECTED;
    }


    hr = m_pMediaControl->Run();
state = State_Stopped;

while (State_Running != state && SUCCEEDED(hr))
{
hr = m_pMediaControl->GetState(100, &state);
Sleep(100);
}

    return hr;
}


HRESULT CCameraDS::Pause()
{
HRESULT hr = S_OK;

    if( !m_pMediaControl )
    {
        return E_UNEXPECTED;
    }
    hr = m_pMediaControl->Pause();

    return hr; 
}


HRESULT CCameraDS::Stop()
{
    HRESULT hr = S_OK;
    OAFilterState state;

    if( !m_pMediaControl )
    {
        return E_UNEXPECTED;
    }

    hr = m_pMediaControl->Stop();
    state = State_Running;

    while( State_Stopped != state && SUCCEEDED(hr))
    {
        hr = m_pMediaControl->GetState(100, &state);
        Sleep(100);
    }

    return hr;
}

这样看起来清爽多了。
使用的时候,m_CamDS.OpenCamera之后,再调用m_CamDS.Run();。
然后,STDMETHODIMP CCameraDS::CSampleGrabberCB::BufferCB( double dblSampleTime, BYTE* pBuffer, long lBufferSize )
就得到视频了。我们可以在外面定义一个回调函数,有视频就回调给外面进行处理。
工程是用vc2008创建的。vc6也没问题,不过以后要调用ffmpeg sdk, vc6会比较麻烦,所以这里使用了vc2008。
代码临时放在附件里,找个时间再传到git或sf上。
代码是想到哪里就写到哪里,可能有点乱。这个项目的目的是最简原则,先用最简单的方法实现,然后再慢慢优化。最好的方法应该是用Qt来开发。暂时先用vc2008对付着。

完整代码在https://github.com/sxcong/rttim
附件代码只包含本章内容,适合循序渐进看。
Client.rar
0 0
原创粉丝点击