• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 298  文章 - 377  trackbacks - 0
            <2013年4月>
            31123456
            78910111213
            14151617181920
            21222324252627
            2829301234
            567891011

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            當下比較流行的即時通信工具,比如MSN,QQ等都實現了視音頻的功能,通過視頻,音頻,我們可以更好的和朋友通過網絡進行溝通,本文通過DirectShow技術模擬QQ實現了視頻和音頻的采集,傳輸,基本實現了QQ的視音頻聊天的功能。
              網絡視音頻系統主要功能就在于視音頻的采集,網絡傳輸兩個方面,通過Video Capture系列API函數,你就可以輕松的搞定視頻捕捉,但是對于視頻的網絡傳輸,則要費一番功夫了。 對于視音頻數據的傳輸,只簡單地使用數據報套接字傳輸音視頻數據是不可行的,還必須在UDP層上采用RTP(實時傳輸協議)和RTCP(實時傳輸控制協議)來改善服務質量。實時傳輸協議提供具有實時特征的、端到端的數據傳輸服務。我們在音視頻數據前插入包含有載荷標識、序號、時間戳和同步源標識符的RTP包頭,然后利用數據報套接字在IP網絡上傳輸RTP包,以此改善連續重放效果和音視頻同步。實時傳輸控制協議RTCP用于RTP的控制,它最基本的功能是利用發送者報告和接收者報告來推斷網絡的服務質量,若擁塞狀況嚴重,則改用低速率編碼標準或降低數據傳輸比特率,以減少網絡負荷,提供較好的Q.S保證。

              Directshow對于音視頻的采集提供了很好的接口,利用ICaptureGraphBuilder2接口可以很輕松的建立起視頻捕捉的graph圖,通過枚舉音頻設備Filter,也可以很輕松的實現音頻的捕捉,有點麻煩的是音視頻數據的傳輸,我們可以自己封裝RTP和RTCP的協議,來自己實現一個filter,用來發送和接收音視頻數據,當然了Directshow也提供了一組支持使用RTP協議的網絡傳輸多媒體流的Filters。你也完全可以用Directshow提供的RTP系列的filter實現數據的傳輸。

              下面分析一下這些RTP Filters。

              新定義的Filter包括 RTP Source Filter ,RTP Render Filter,RTP Demux Filter,RTP Receive Playload Handler (RPH) filter,RTP Send Payload (SPH) filter,使用這5個filter構建一個通過RTP協議傳輸音視頻數據的Graph是沒有問題的。

              RTP Source filter被用來從一個單獨的RTP會話中接收RTP和RTCP包。這個filter提供一個指定發送給其它主機RTCP接收器報告和指定網絡地址和端口接口來接收RTP會話的接口。

              RTP Rend filter是用來將數據發到網絡上的一個filter,這個filter也提供了和RTP source Filter 類似的接口。

              RTP Demux filter用來多路分離來自 RTP Source filter的RTP 包,這個filter有一個或者多個輸出的pin。這個Filter提供了如何控制多路分離和如何分配到特定輸出pin的接口。

              RTP RPH Filter 是用來網絡過來的RTP包還原成原來的數據格式,主要支持H.261,H.263,Indeo,G.711,G.723和G.729和常見的多種音視頻負載類型。

              RTP SPH filter則和RPH filter的功能相對,它的任務是將音視頻 壓縮filter輸出的 數據分解為RTP包,它提供的接口有指定最大生成包大小和pt值。

              下面我們看看如何用這些filter來搭建我們采集和傳輸的graph圖。




              圖1和圖2展示了DirectShow RTP中定義的filters如何運用。圖1是一個采集本地多媒體數據并使用RTP協議通過網絡發送的filter graph。它包含一個輸出原始視頻幀的視頻采集filter,緊跟一個壓縮幀的編碼filter。一旦壓縮,這些幀就會被發送到RTP SPH filter,分片打包,生成RTP包,對應的發送到 RTP Render filter,通過網絡傳輸這些包。圖2展現了一個filter graph,用來接收包含視頻流RTP包,播放視頻。這個graph由一個用來接收包的RTP Source filter,一個根據源和負載類型進行分類的RTP Demux filter,一個把RTP包轉為壓縮視頻幀的RTP RPH filter組成。這些filter隨后的是用來解壓幀的解碼filter,一個顯示未壓縮幀的渲染filter。

              有了RTP filter的幫助我們就可以完成類似qq的功能了,可以實現在網絡上進行視頻和音頻的交互了,下面我給出在網絡上兩個客戶端A和B進行音頻和視頻交互的Graph圖。這里我對圖1和圖2中的RTP filter進行了自己封裝,將編解碼filter直接封裝到了RTP Source filter 和RTP Render filter中,這樣Graph圖就顯得很簡潔,RTP Source filter只是用來接收網絡過來的音視頻數據,然后將數據傳遞給客戶程序,RTP Render filter則是將采集到的音視頻數據發送到網絡上的另一個客戶端,編解碼則的工作則封裝到這兩個filter之中。


            圖3 網絡視頻和音頻交互的Graph圖

              如果你也想自己封裝自己的Source 和Render filter,首先你要選擇自己的編解碼,視頻編解碼是選擇H261,H263,還是 MEPG4,音頻是選擇G729還是G711,要首先確定好。選好編解碼,封裝的工作就簡單了。

              不多說了,下面看看我給出的代碼吧。

              首先要定義一下用到的四個RTP filter的CLSID。

            static const GUID CLSID_FG729Render = { 0x3556f7d8, 0x5b5, 0x4015, { 0xb9, 0x40, 0x65, 0xb8, 0x8, 0x94, 0xc8, 0xf9 } }; //音頻發送
            static const GUID CLSID_FG729Source = { 0x290bf11a, 0x93b4, 0x4662, { 0xb1, 0xa3, 0xa, 0x53, 0x51, 0xeb, 0xe5, 0x8e } };//音頻接收
            static const GUID CLSID_FH263Source = { 0xa0431ccf, 0x75db, 0x463e, { 0xb1, 0xcd, 0xe, 0x9d, 0xb6, 0x67, 0xba, 0x72 } };//視頻接收
            static const GUID CLSID_FH263Render = { 0x787969cf, 0xc1b6, 0x41c5, { 0xba, 0xa8, 0x4e, 0xff, 0xa3, 0xdb, 0xe4, 0x1f } };//視頻發送
            //發送和接收音視頻數據的filter
            CComPtr< IBaseFilter > m_pAudioRtpRender ;
            CComPtr< IBaseFilter > m_pAudioRtpSource ;
            CComPtr< IBaseFilter > m_pVideoRtpRender ;
            CComPtr< IBaseFilter > m_pVideoRtpSource ;

            char szClientA[100];
            int iVideoPort = 9937;
            int iAudioPort = 9938;

            //構建視頻的graph圖,并發送數據
            CComPtr< IGraphBuilder > m_pVideoGraphBuilder; //視頻圖形管理器
            CComPtr< ICaptureGraphBuilder2 > m_pVideoCapGraphBuilder;
            CComPtr< IBaseFilter > m_pFilterVideoCap;
            CComPtr< IVideoWindow > m_pVideoWindow;
            CComPtr< IMediaControl > m_pVideoMediaCtrl ;
            CComPtr< IBaseFilter > m_pVideoRenderFilter;

            HRESULT CMyDialog::VideoGraphInitAndSend()
            {
             HRESULT hr;
             hr =m_pVideoGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
             if(FAILED(hr))
              return hr;
             hr =m_pVideoCapGraphBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2);
             if(FAILED (hr))
              return hr;
             m_pVideoCapGraphBuilder->SetFiltergraph(m_pVideoGraphBuilder);
             m_pVideoGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pVideoMediaCtrl);
             m_pVideoGraphBuilder->QueryInterface(IID_IVideoWindow,(void**)&m_pVideoWindow)

             FindDeviceFilter(&m_pFilterVideoCap,CLSID_VideoInputDeviceCategory);
             if(m_pFilterVideoCap)
              m_pVideoGraphBuilder->AddFilter( m_pFilterVideoCap,T2W("VideoCap") ) ;
              //創建預覽的filter
             hr = m_pRenderFilterVideo.CoCreateInstance(CLSID_VideoRenderer);
             if(FAILED(hr))
              return hr;
             m_pVideoGraphBuilder->AddFilter( m_pRenderFilterVideo, L"VideoRenderFilter" );
             Connect(m_pFilterVideoCap ,m_pRenderFilterVideo) ;
             //設置預覽的窗口

             CRect rc ;
             GetClientRect(m_hOwnerWnd, &rc );
             int iWidth = rc.right - rc.left ;
             int iHeight = rc.bottom - rc.top ;
             int iLeft, iTop;
             if((iHeight*1.0)/(iWidth*1.0) >= 0.75)
             {
              //按寬度算
              int tmpiHeight = iWidth*3/4;
              iTop = (iHeight - tmpiHeight)/2;
              iHeight = tmpiHeight;
              iLeft = 0;
             }
             else
             {
              //按高度算
              int tmpiWidth = iHeight*4/3;
              iLeft = (iWidth - tmpiWidth)/2;
              iWidth = tmpiWidth;
              iTop = 0;
             }
             m_pVideoWindow->put_Owner( (OAHWND) m_hPreviewWnd ) ;
             m_pVideoWindow->put_Visible( OATRUE );
             m_pVideoWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ) ;

             //連接到網絡并發送
             CComPtr< IRtpOption > pRenderOption;
             CComPtr< IVideoOption > pVideoOption;

             tagVideoInfo vif(160,120,24);
             int t=((int)(m_iFrameRate/5)*5)+5;
             vif.nBitCount=24;
             vif.nWidth=160;
             vif.nHeight=120;

             hr = ::CoCreateInstance(CLSID_FH263Render, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&m_pVideoRtpRender);
             if(FAILED(hr))
              return hr;
             m_pVideoRtpRender->QueryInterface(IID_IJRTPOption, (void**)&pRenderOption);
             m_pVideoRtpRender->QueryInterface(IID_IVideoOption,(void**)&pVideoOption);
             pVideoOption->SetProperty(&vif);
             pVideoOption->SetSendFrameRate(m_iFrameRate,1);//1 不發送數據,0 實際發送數據
             Connect(m_pFilterVideoCap ,m_pVideoRtpRender) ;
             //連接對方
             hr= pRenderOption->Connect(szClientA,iVideoPort,1024);
             if(FAILED(hr))
              return hr;
             m_pVideoMediaCtrl->Run();
            }
            //視頻的接收
            CComPtr< IGraphBuilder > m_pVideoGraphBuilder; //視頻圖形管理器
            CComPtr< IBaseFilter > m_pFilterVideoCap;
            CComPtr< IVideoWindow > m_pVideoWindow;
            CComPtr< IMediaControl > m_pVideoMediaCtrl ;
            CComPtr< IBaseFilter > m_pVideoRenderFilter;
            HWND m_hRenderWnd ;
            HRESULT VideoRecive()
            {
             HRESULT hr;
             hr=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC,
             IID_IFilterGraph,(void**)&m_pVideoGraphBuilder);

             m_pVideoGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pVideoMediaCtrl);
             m_pVideoGraphBuilder->QueryInterface(IID_IVideoWindow,(void**)&m_pVideoWindow)
             
             hr = ::CoCreateInstance(CLSID_FH263Source, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&m_pVideoRtpSource);
             if(FAILED(hr))
              return hr;
             m_pVideoGraphBuilder->AddFilter(m_pVideoRtpSource, L"My Custom Source");

             CComPtr< IRtpOption > m_pRtpOption;
             CComPtr< IVideoOption > m_pVideoOption;
             m_pVideoRtpSource->QueryInterface(IID_IJRTPOption, (void **)&m_pRtpOption);
             m_pVideoRtpSource->QueryInterface(IID_IVideoOption, (void **)&m_pVideoOption);

             tagVideoInfo vif(160, 120 ,24);
             m_pVideoOption->SetProperty(&vif);
             hr= pRenderOption->Connect(szClientA,iVideoPort +1,1024);
             if(FAILED(hr))
              return hr;

             //創建預覽的filter
             hr = m_pRenderFilterVideo.CoCreateInstance(CLSID_VideoRenderer);
             if(FAILED(hr))
              return hr;
             m_pVideoGraphBuilder->AddFilter( m_pRenderFilterVideo, L"VideoRenderFilter" );
             Connect(m_pVideoRtpSource ,m_pRenderFilterVideo) ;

             CRect rc ;
             GetClientRect(m_hOwnerWnd, &rc );
             int iWidth = rc.right - rc.left ;
             int iHeight = rc.bottom - rc.top ;
             int iLeft, iTop;
             if((iHeight*1.0)/(iWidth*1.0) >= 0.75)
             {
              //按寬度算
              int tmpiHeight = iWidth*3/4;
              iTop = (iHeight - tmpiHeight)/2;
              iHeight = tmpiHeight;
              iLeft = 0;
             }
             else
             {
              //按高度算
              int tmpiWidth = iHeight*4/3;
              iLeft = (iWidth - tmpiWidth)/2;
              iWidth = tmpiWidth;
              iTop = 0;
             }
             m_pVideoWindow->put_Owner( (OAHWND) m_hRenderWnd ) ;
             m_pVideoWindow->put_Visible( OATRUE );
             m_pVideoWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ) ;
             m_pVideoMediaCtrl->Run();

             return S_OK;
            }
            //
            HRESULT FindDeviceFilter(IBaseFilter ** ppSrcFilter,GUID deviceGUID)
            {
             HRESULT hr;
             IBaseFilter * pSrc = NULL;
             CComPtr <IMoniker> pMoniker =NULL;
             ULONG cFetched;

             if (!ppSrcFilter)
              return E_POINTER;

             // Create the system device enumerator
             CComPtr <ICreateDevEnum> pDevEnum =NULL;

             hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
             IID_ICreateDevEnum, (void **) &pDevEnum);
             if (FAILED(hr))
              return hr;

             // Create an enumerator for the video capture devices
             CComPtr <IEnumMoniker> pClassEnum = NULL;

             hr = pDevEnum->CreateClassEnumerator (deviceGUID, &pClassEnum, 0);
             if (FAILED(hr))
              return hr;

             if (pClassEnum == NULL)
              return E_FAIL;

             if (S_OK == (pClassEnum->Next (1, &pMoniker, &cFetched)))
             {
              hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pSrc);
              if (FAILED(hr))
               return hr;
             }
             else
              return E_FAIL;

             *ppSrcFilter = pSrc;

             return S_OK;
            }

            //構建音頻Graph圖,并發送
            CComPtr< IGraphBuilder > m_pAudioGraphBuilder; //音頻圖形管理器
            CComPtr< ICaptureGraphBuilder2 > m_pCapAudioGraphBuilder;
            CComPtr< IBaseFilter > m_pFilterAudioCap;
            CComPtr< IMediaControl > m_pAudioMediaCtrl ;

            HRESULT AudioGraphInit()
            {
             HRESULT hr;
             hr =m_pAudioGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
             if(FAILED(hr))
              return hr;
             hr =m_pCapAudioGraphBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2);
             if(FAILED (hr))
              return hr;
             m_pAudioGraphBuilder->SetFiltergraph(m_pCapAudioGraphBuilder);
             m_pAudioGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pAudioMediaCtrl);

             FindDeviceFilter(&m_pFilterVideoCap,CLSID_AudioInputDeviceCategory);
             if(m_pFilterAudioCap)
              m_pAudioGraphBuilder->AddFilter( m_pFilterAudioCap,T2W("AudioCap") ) ;

             //發送到網絡
             hr =::CoCreateInstance(CLSID_FG729Render,NULL,CLSCTX_INPROC,
             IID_IBaseFilter,(void**)&m_pFilterRtpSendAudio)
             if(FAILED(hr))
              return hr;
             m_pAudioGraphBuilder->AddFilter(m_pAudioRtpRender, L"FilterRtpSendAudio");
             Connect(m_pFilterAudioCap,m_pAudioRtpRender);

             CComPtr< IRtpOption > pOption ;
             m_pAudioRtpRender->QueryInterface(IID_IJRTPOption,(void**)&pOption)
             hr =pOption->Connect(szClientA,iAudioPort,1024);
             if(FAILED(hr))
              return hr;

             m_pAudioMediaCtrl->Run();
             return S_OK;
            }
            //音頻的接收
            CComPtr< IGraphBuilder > m_pAudioGraphBuilder; //音頻圖形管理器
            CComPtr< ICaptureGraphBuilder2 > m_pCapAudioGraphBuilder;
            CComPtr< IBaseFilter > m_pFilterAudioCap;
            CComPtr< IMediaControl > m_pAudioMediaCtrl ;
            CComPtr<IBaseFilter> m_pAudioRender;
            HRESULT AudioRecive()
            {
             HRESULT hr;
             hr =m_pAudioGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
             if(FAILED(hr))
              return hr;
             m_pAudioGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pAudioMediaCtrl);

             hr = m_pAudioRtpSource->CoCreateInstance(CLSID_FG729Source) ;
             if(FAILED(hr))
              return hr;
             m_pAudioGraphBuilder->AddFilter(m_pAudioRtpSource,L"AudioRtp");
             //創建聲卡Renderfilter
             FindDeviceFilter(&m_pAudioRender,CLSID_AudioRendererCategory);
             m_pAudioGraphBuilder->AddFilter(m_pAudioRender,L"AudioRender");
             CComPtr< IRtpOption > pRtpOption ;
             m_pAudioRtpSource->QueryInterface(IID_IJRTPOption,(void**)&pRtpOption)
             hr= pRtpOption->Connect(szClientA,iAudioPort+2,1024);
             if(FAILED (hr))
              return hr;

             Connect(m_pAudioRtpSource,m_pAudioRender);
             
             m_pAudioMediaCtrl->Run();
             return S_OK;
            }
            posted on 2007-09-24 14:45 聶文龍 閱讀(1785) 評論(1)  編輯 收藏 引用 所屬分類: Audio&Video

            FeedBack:
            # re: 用DirectShow實現QQ的音視頻聊天功能 2009-05-12 15:35 蘭蘭
            真是不錯啊,有才的人給我很大啟發了的。。。。。。。。。。。  回復  更多評論
              
            国产亚洲精久久久久久无码| 久久天天躁狠狠躁夜夜2020一| 亚洲∧v久久久无码精品| 国产99久久九九精品无码| 国产精品99久久99久久久| 伊人久久成人成综合网222| 久久嫩草影院免费看夜色| 国产69精品久久久久99| 日韩久久无码免费毛片软件| 狠狠色丁香久久婷婷综合蜜芽五月 | 亚洲午夜无码AV毛片久久| 久久久久国色AV免费看图片| 久久亚洲国产成人精品无码区| 狠狠色婷婷久久综合频道日韩| 久久久久亚洲精品日久生情| 一本大道久久东京热无码AV| 人妻少妇久久中文字幕一区二区| 国产精品女同久久久久电影院| 色综合久久88色综合天天| 久久综合久久鬼色| 少妇精品久久久一区二区三区| 久久久久国产| 国产午夜福利精品久久2021| 久久夜色精品国产亚洲av| 国产精品99久久久久久猫咪| 午夜天堂精品久久久久| 中文字幕久久波多野结衣av| 久久九色综合九色99伊人| 久久天天躁狠狠躁夜夜网站 | 久久精品国产99久久无毒不卡 | 无码精品久久久天天影视 | 久久99精品久久久久久hb无码 | 久久精品国内一区二区三区| 精品久久久久久国产潘金莲 | 久久久久久久久久久久久久 | 国产精品久久国产精品99盘| 久久这里都是精品| 久久午夜无码鲁丝片午夜精品| 国产成人精品久久免费动漫 | 久久国产精品77777| 奇米影视7777久久精品人人爽|