❶ 如何用Directshow訪問存儲在內存中的資源
Introction (序)
這一章我們學習怎樣用DX來播放聲音和音樂。我們將會使用DirectX Audio來播放WAV和MIDI文件,使用DirectShow來播放MP3文件。此章我們仍然有一個簡單的程序作例子,它會播放MP3的背景音樂,當用戶用滑鼠點擊某個數字時它會播放一些音效。
DirectX Audio and DirectShow (DirectX Audio和 DirectShow)
我們已經學習過Direct3D和DirectInput了,現在,我要介紹另外兩個DirectX組件:DirectX Audio和DirectShow。我們用DirectX Audio來播放WAV和MIDI文件;用DirectShow來播放媒體流,例如AVI和MP3。在此教程中,我們只學習一下用DirectShow播放MP3的方法。
Wav, Midi and Mp3 - When should I use what?
(WAV,MIDI和MP3——什麼時候該用什麼?)
那末,什麼時候用什麼格式好呢?嗯,這基本上取決於你的個人愛好。下面是這些格式的簡介,過後我會說一下我的選擇:
Wav files (WAV文件)
WAV是一種未經壓縮的數字音頻,CD音質,但是體積非常龐大。
Midi files (MIDI文件)
MIDI文件並沒有保存音頻記錄,它實際上更像是一套演奏指令,所以,它的體積是非常小的。MIDI的音質完全取決於演奏設備(如我們的音效卡),它在高端的設備上能表現出色,而在低端設備上則表現較差。
Mp3 files (MP3文件)
同WAV文件一樣,MP3也是一種數字音頻格式。不同的是,MP3文件是經過壓縮的,而且是有損壓縮,這意味著它的體積將大大地減小,而音質上將會有一些失真(實際上接近CD音質,基本聽不出失真)。而且,MP3是一種媒體流,這意味著在播放它的時候不會將它整個的讀入,取而代之的是分期的做部分讀入。
My preference (我的選擇)
你大概會選擇MP3或MIDI來作背景音樂,特別是當你希望用戶能從網上下載你的程序時。MP3和MIDI格式文件的體積都比較小,它們適合做長時間的背景音樂播放。而當需要播放簡短的聲效時,例如爆炸聲等,我更趨向於使用WAV文件,品質能稍好一些。如果文件的體積不是問題的話,實際上我們可以使用WAV文件來播放所有的聲音和音樂。
Include and Library files (頭文件與庫文件)
我們的項目需要增加以下的頭文件和庫文件:
o dmusici.h
o dsound.h
o dshow.h
o dsound.lib
o strmiids.lib
Setting up DirectX Audio (設置DirectX Audio)
要使用Direct Audio我們需要創建兩個對象:演奏對象(Performance Object,有些資料翻譯為執行對象)和載入器(Loader)。演奏對象是Direct Audio中的最高級對象,用於處理從源到合成器的數據流。載入器用於把文件(WAV或MIDI)載入到聲音段以待播放。這兩個對象在整個應用程序中我們每樣只需要一個,所以,我們已經把它們創建為CGame的成員變數了。下面是它們的定義:
IDirectMusicPerformance8* m_pDirectAudioPerformance;
IDirectMusicLoader8* m_pDirectAudioLoader;
由於Direct Audio是一個純COM,所以我們需要初始化COM。其實很簡單,我們只需要在CGame的構造函數中調用CoInitialize函數即可:
CoInitialize(NULL);
初始化COM後,我們就可以創建上述的那兩個DirectX Audio對象了。為此,我們給CGame做了一個新的函數InitialiseDirectAudio,它將會在Initialise函數中被調用。代碼如下:
bool CGame::InitialiseDirectAudio(HWND hWnd)
{
LogInfo("<br>Initialise DirectAudio:");
//Create the DirectAudio performance object
if(CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,
IID_IDirectMusicPerformance8,
(void**) &m_pDirectAudioPerformance) != S_OK)
{
LogError("<li>Failed to create the DirectAudio perfomance object.");
return false;
}
else
{
LogInfo("<li>DirectAudio perfomance object created OK.");
}
//Create the DirectAudio loader object
if(CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC,
IID_IDirectMusicLoader8,
(void**) &m_pDirectAudioLoader) != S_OK)
{
LogError("<li>Failed to create the DirectAudio loader object.");
return false;
}
else
{
LogInfo("<li>DirectAudio loader object created OK.");
}
//Initialise the performance object
if(FAILED(m_pDirectAudioPerformance->InitAudio(NULL, NULL, hWnd,
DMUS_APATH_SHARED_STEREOPLUSREVERB,
64, DMUS_AUDIOF_ALL, NULL)))
{
LogError("<li>Failed to initialise the DirectAudio perfomance object.");
return false;
}
else
{
LogInfo("<li>Initialised the DirectAudio perfomance object OK.");
}
//Get the our applications "sounds" directory.
CHAR strSoundPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH, strSoundPath);
strcat(strSoundPath, "\\Sounds");
//Convert the path to unicode.
WCHAR wstrSoundPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1, wstrSoundPath, MAX_PATH);
//Set the search directory.
if(FAILED(m_pDirectAudioLoader->SetSearchDirectory(GUID_DirectMusicAllTypes,
wstrSoundPath, FALSE)))
{
LogError("<li>Failed to set the search directory '%s'.", strSoundPath);
return false;
}
else
{
LogInfo("<li>Search directory '%s' set OK.", strSoundPath);
}
return true;
}
我們用CoCreateInstance函數創建了演奏對象和載入器,然後調用了InitAudio模塊來初始化演奏對象。上面代碼中給InitAudio的參數是很典型的,所以你可以沿用它(查看一下SDK中的關於InitAudio函數以及它的參數的內容以獲得更多的了解)。最後,我們設置了搜索目錄。搜索目錄應該被設置為存放聲音文件的目錄,以便載入器可以正確的載入聲音文件。在本例中,搜索目錄被設置為「Sounds」文件夾,它應該在本例的項目文件夾當中。
A new class - CSound (一個新的類——CSound)
我們已經為使用DirectX Audio作了必要的准備工作,現在,我們就可以載入、播放聲音和音樂了。為此,我們做了一個新的類CSound,下面是它的代碼,稍後我會解釋它是怎樣工作的:
CSound::CSound()
{
m_pDirectAudioPerformance = NULL;
m_pDirectAudioLoader = NULL;
m_pSegment = NULL;
m_pGraph = NULL;
m_pMediaControl = NULL;
m_pMediaPosition = NULL;
m_enumFormat = Unknown;
LogInfo("<li>Sound created OK");
}
void CSound::InitialiseForWavMidi(IDirectMusicPerformance8* pDirectAudioPerformance, IDirectMusicLoader8* pDirectAudioLoader)
{
m_pDirectAudioPerformance = pDirectAudioPerformance;
m_pDirectAudioLoader = pDirectAudioLoader;
m_enumFormat = WavMidi;
}
void CSound::InitialiseForMP3()
{
CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC, IID_IGraphBuilder, (void**)&m_pGraph);
m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pMediaControl);
m_pGraph->QueryInterface(IID_IMediaPosition, (void**)&m_pMediaPosition);
m_enumFormat = MP3;
}
CSound::~CSound()
{
Stop();
SafeRelease(m_pSegment);
SafeRelease(m_pGraph);
SafeRelease(m_pMediaControl);
SafeRelease(m_pMediaPosition);
LogInfo("<li>Sound destroyed OK");
}
bool CSound::LoadSound(const char* szSoundFileName)
{
WCHAR wstrSoundPath[MAX_PATH];
CHAR strSoundPath[MAX_PATH];
switch(m_enumFormat)
{
case MP3:
//Get the our applications "sounds" directory.
GetCurrentDirectory(MAX_PATH, strSoundPath);
strcat(strSoundPath, "\\Sounds\\");
strcat(strSoundPath, szSoundFileName);
//Convert the path to unicode.
MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1,
wstrSoundPath, MAX_PATH);
m_pGraph->RenderFile(wstrSoundPath, NULL);
break;
case WavMidi:
//Convert the filename to unicode.
MultiByteToWideChar(CP_ACP, 0, szSoundFileName, -1,
wstrSoundPath, MAX_PATH);
//Load a sound
m_pDirectAudioLoader->LoadObjectFromFile(CLSID_DirectMusicSegment,
IID_IDirectMusicSegment8,
wstrSoundPath,
(void**) &m_pSegment);
m_pSegment->Download(m_pDirectAudioPerformance);
break;
default:
return false;
}
return true;
}
bool CSound::Play(DWORD dwNumOfRepeats)
{
switch(m_enumFormat)
{
case MP3:
//Make sure that we are at the start of the stream
m_pMediaPosition->put_CurrentPosition(0);
//Play mp3
m_pMediaControl->Run();
break;
case WavMidi:
//Set the number of times the sound repeats
m_pSegment->SetRepeats(dwNumOfRepeats); //To loop the sound forever, pass
//in DMUS_SEG_REPEAT_INFINITE
//Play the loaded sound
m_pDirectAudioPerformance->PlaySegmentEx(m_pSegment, NULL, NULL, 0, 0,
NULL, NULL, NULL);
break;
default:
return false;
}
return true;
}
bool CSound::Stop()
{
switch(m_enumFormat)
{
case MP3:
m_pMediaControl->Stop();
break;
case WavMidi:
//Stop the loaded sound
m_pDirectAudioPerformance->StopEx(m_pSegment, 0, 0);
break;
default:
return false;
}
return true;
}
bool CSound::IsPlaying()
{
switch(m_enumFormat)
{
case MP3:
REFTIME refPosition;
REFTIME refDuration;
m_pMediaPosition->get_CurrentPosition(&refPosition);
m_pMediaPosition->get_Duration(&refDuration);
if(refPosition < refDuration)
{
return true;
}
else
{
return false;
}
break;
case WavMidi:
if(m_pDirectAudioPerformance->IsPlaying(m_pSegment, NULL) == S_OK)
{
return true;
}
else
{
return false;
}
break;
default:
return false;
}
}
那末,怎樣用CSound來播放音樂呢?這很簡單。首先,創建一個CSound對象,然後根據你想播放的聲音類型來調用InitialiseForWavMidi或是InitialiseForMP3,二者選其一。接著,調用LoadSound讀取聲音文件。最後,調用Play模塊來播放聲音即可。很簡單,不是嗎?嗯,還有兩個模塊:Stop模塊用來停止播放;IsPlaying模塊用來檢測聲音是否正在播放。下面介紹了各個模塊是怎樣工作的:
InitialiseForWavMidi
InitialiseForWavMidi模塊用於初始化CSound對象以待播放WAV或MIDI文件。它的兩個輸入參數是我們在CGame中創建好的演奏對象和載入器的介面指針,然後這兩個指針會被保存為它的成員變數以備用。我們會把CSound的格式設置為WavMidi。
InitialiseForMP3
InitialiseForMP3模塊用於初始化CSound對象以待播放MP3文件,它沒有輸入參數。InitialiseForMP3調用CoCreateInstance函數創建了一個DirectShow過濾器圖管理器對象。我們能使用CoCreateInstance是因為我們在CGame的構造函數中已經調用過CoInitialize函數了。有了過濾器圖管理器對象之後,我們調用它的模塊QueryInterface來創建另外兩個對象:一個媒體控制對象和一個媒體定位對象。這三個對象的指針將會被保存為CSound對象的成員變數。同理,我們把CSound的格式設置為MP3;
LoadSound
初始化好CSound對象後,我們就可以調用LoadSound來讀取聲音文件了。LoadSound只有一個參數,就是聲音文件的文件名。這里不是一個路徑而是一個文件名是因為所有的聲音文件都應該在我們的項目的「Sounds」目錄中。
如果這個CSound對象的格式為MP3,我們首先會構造出一個完整的文件路徑,然後把它轉換為Unicode字元集的字元串。接著,我們只需要調用過濾器圖管理器對象的RenderFile模塊並構造一個用於播放指定文件的過濾器圖即可。
如果這個CSound對象的格式為WavMidi,我們首先會把文件名轉換成Unicode字元集的字元串。接著我們會調用LoadObjectFromFile,它會載入文件並返回一個段(segment)指針。最後,我們調用段的Download模塊下載波段到演奏對象里。
現在我們已經准備好來播放聲音了。
Play
當文件載入後,我們就能播放它了。Play模塊唯一的可選參數(默認為0)就是重復播放次數,只在播放WAV或MIDI時有效。
如果這個CSound對象的格式為MP3,我們首先會調用媒體定位對象的put_CurrentPosition模塊來確認一下我們是否在媒體流的開始處。然後,我們調用媒體控制對象的Run模塊來播放MP3。
如果這個CSound對象的格式為WavMidi,我們首先會設置重復次數。然後我們調用PlaySegmentEx來播放已經載入的段。如果你想讓聲音永遠重復,輸入DMUS_SEG_REPEAT_INFINITE恆量。
Stop
這能簡單地停止正在播放的聲音。如果是MP3,就調用媒體控制對象的Stop模塊。如果是WAV或MIDI,就調用演奏對象的StopEx模塊並輸入要停止的段。
IsPlaying
最後,IsPlaying模塊用於檢測當前是否正在播放。是就返回true,不是則返回false。如果是MP3,我們會根據當前的媒體流定位與整個媒體流的長度做對比,以得知我們是否正在播放(看是否在流的終點)。如果是WAV,我們就調用演奏對象的IsPlaying模塊並輸入要檢測的段以測試。
Cleaning up (清理)
在Csound的析構函數中我們會停止聲音的播放並釋放對象。在CGame中有一個新的函數CleanUpDirectAudio代碼如下:
void CGame::CleanUpDirectAudio()
{
//Stop all sounds.
m_pDirectAudioPerformance->Stop(NULL, NULL, 0, 0);
//CleanUp
m_pDirectAudioPerformance->CloseDown();
SafeRelease(m_pDirectAudioLoader);
SafeRelease(m_pDirectAudioPerformance);
LogInfo("<li>CleanUpDirectAudio finished.");
}
Here we first stop all sounds from playing (just in case), then we close down the performance object before releasing it and the loader object. Also, in the CGame deconstructor we need to call CoUninitialize to close down the COM library (shown below).
這里我們首先停止所有聲音的播放,然後關閉演奏對象,釋放演奏對象和載入器。而且,在CGame的析構函數中我們會調用CoUninitialize來關掉COM:
CoUninitialize();
那末現在我們能在程序中播放音樂和聲音了。當程序開始時,MP3的背景音樂就會開始播放。移動滑鼠並點擊不同的數字我們對聽到不同的聲音。這里的背景MP3隻是一個小片斷的循環,願意的話你可以替換成你喜歡的音樂。(譯者:我把我的MP3都放了一遍爽)
Summary (摘要)
In this tutorial we learnt how to play sounds and music using two new DirectX components, DirectX Audio and DirectShow. In the next tutorial we will use all of the skills we have seen so far to create our first game: 3D Pong. This game will be simple, but will have all of the key features that you would expect from any game.
❷ 用directshow寫的視頻壓縮程序為什麼在工控機上不能壓縮,工控機是xp系統的,在普通的xp電腦上卻能運行
工控機只是一個加固的PC 機而已,壓縮問題屬於軟體問題,並不是什麼問題都跟硬體掛鉤,這個思想是錯誤的,機子出問題了只要能開機,正常進系統,基本上跟硬體關系不大,先軟後硬逐個來 ,多了解問題,這個過程就是學習!經驗就是這樣來的!100分好多啊,真捨得!
❸ 如何將directshow捕獲到的視頻數據打包成rtp包
捕獲到的數據是裸數據,需要壓縮。你可以找一個壓縮演算法例如x264,壓縮成H264文件。然後編寫RTP 12位元組的頭,將H264數據定界符後面的數據放進去就可以了。如果一幀數據大需就需要再分片。
❹ directshow濾波器
DirectShow使用模組結構,處理的每一個階段都通過一個叫濾波器的COM組件來完成。DirectShow為應用程序提供了一套標準的濾波器,開發人員定製自己的濾波器來擴展DirectShow。這里有幾個步驟來播放AVI視頻文件,每一步都是通過濾波器來完成。
1 從位元組流文件中讀取原始數據(文件源濾波器-File Source Filter)
2 檢測AVI頭,將位元組流解析成單獨的視頻楨和音頻樣本(AVI 劈分濾波器-AVI Splitter Filter)
3 解碼視頻楨(根據壓縮格式的不同,存在不同的解碼濾波器)
4 繪制視頻楨(視頻實現濾波器-Video Render Filter)
5 將音頻樣本送到音效卡(預設的DirectSound設備濾波器)
❺ 關於MPC影音風暴的一些問題
一、Video Renderer和Overlay Mixer
大家知道,Video Renderer (VR)是接收RGB/YUV裸數據,然後在顯示器上顯示的Filter。為提高計算機畫圖性能,根據你計算機顯卡的能力,VR會優先使用DirectDraw以及Overlay表面;如果這些特性得不到顯卡的支持,VR會使用GDI函數進行畫圖。在上級Filter連接到VR時,VR總是先要求當前顯示器設置的色彩位數的RGB格式,如你的機器設置的是24位彩色,則VR首先要求連接的Media type為RGB24。如果你的顯卡支持YUV Overlay表面,那麼在Filter Graph運行起來的時候,VR會動態改變已經連接的Media type,要求上級Filter輸出一種合適的YUV格式。VR Filter上實現了IVideoWindow介面,Filter Graph Manager主要通過這個介面來控制視頻窗口。
那麼,Overlay Mixer又是怎麼回事呢?簡單地說,Overlay Mixer就是能夠將幾路視頻流合成輸出的Filter。這個Filter是特地為DVD回放(DVD有Sub-picture或line-21數據需要疊加顯示)或廣播視頻流(含有line-21數據)而設計的。同時,它還支持硬體解碼器使用Video Port Extensions,就是繞過PCI匯流排,將硬體解碼出來的數據直接送給顯卡顯示。這個Filter同樣優先使用顯卡的DirectDraw能力,而且必須要有Overlay表面。Overlay Mixer有一個輸出Pin,輸出的Media type是:MEDIATYPE_VIDEO,MEDIASUBTYPE_ Overlay;後面一般連上一個Video Renderer。當Filter Graph運行時,實際的圖像顯示工作由Overlay Mixer完成,而Video Renderer只是做一個視頻窗口的管理工作。還有另外一個更常見的Filter:Overlay Mixer 2。這個Filter跟Overlay Mixer功能上是一樣的,只是兩個Filter支持的Format type不同和Merit值不同而已。
Overlay Mixer使用Color keying來實現幾路視頻的合成:它將Color key和sub-picture(或line-21)數據送到主表面,將主視頻數據送到Overlay表面;顯卡然後將兩個表面的數據合成,送到幀緩存(Frame buffer)中進行顯示。典型的情況,Overlay Mixer使用三個Input pin:Pin 0輸入主視頻數據,Pin 1和Pin 2輸入sub-picture數據和line-21數據。Overlay Mixer在內部根據Pin 0輸入的數據來創建Overlay表面。Overlay Mixer向上一般連接的是Video Decoder。如果這是個Software decoder,則Pin 0上的數據傳輸使用標準的IMemInputPin介面;如果使用了硬體加速,則Pin 0上必須使用IAMVideoAccelerator介面。(注意這兩種介面是不能同時使用的!)如果上一級Filter是硬體解碼器的包裝Filter,使用VP pin輸出,則解碼器與Overlay Mixer使用IVPConfig和IVPNotify介面對通訊,以協調工作。Overlay Mixer不支持1394或USB介面的採集設備。Overlay Mixer向下一般連的是Video Renderer。這時Video Renderer只是一個視頻窗口管理器。兩個Filter通過IOverlay和IOverlayNotify介面對進行通訊,以協調工作。(Video Renderer的Input pin有兩種連接方式:VR直接做圖像顯示時,則使用IMemInputPin介面接收視頻流數據;Overlay Mixer做圖像顯示時,則VR使用IOverlay介面與上一級Filter進行通訊,Overlay Mixer與VR之間沒有視頻數據的傳輸。注意這兩種介面是不會同時使用的!)
二、VMR-7
大家看到了,其實Video Renderer與Overlay Mixer有一部分功能是重復的。Video Renderer是最早設計的,設計之初,很多應用情況沒有考慮進去;於是,就用Overlay Mixer來「打補丁」。現在,我們為什麼不把兩部分功能整合一下呢?微軟也正是這么做了!在Windows XP(家庭版和專業版)中,新出現了一個Filter(注冊的名字也叫「Video Renderer」,但兩個Filter的CLSID是不同的,Merit值也不一樣),替代了原來默認的Video Renderer。這個新的Filter,稱之為Video Mixing Renderer Filter 7 (VMR-7),因為它內部使用了DirectDraw 7的技術。可以這么說,VMR是Windows平台上新一代的Video Renderer。值得注意的是,這個Filter僅在Windows XP里集成,在其他任何DirectX發布包里都得不到這個Filter。VMR-7的大致功能如下:支持最多16路輸入流的alpha混合;支持在合成圖像顯示之前得到對其訪問權;支持插入第三方開發的Video Effects和Transitions組件功能等等。還有,VMR連接時不要求RGB的Media type,因為它任何情況下都不會使用GDI函數來畫圖。
三、VMR-9
隨著DirectX 9的發布,又會出現一個新的Video Renderer,稱之為VMR-9。這個Filter使用了Direct3D 9的技術。VMR-9與VMR-7是兩個不同的Filter。VMR-9的性能更加強勁。值得注意的是,為了保持向下兼容,VMR-9的Merit值並不高,它不作為系統默認的Video Renderer;如果你的應用程序只需要很少的視頻顯示控制,建議還是使用各自平台默認的Video Renderer。
四、總結
顯然,結果已經很明顯,如果你想獲得高級的功能和更好的質量,請使用VMR-9,如果你需要更快的速度或者機器配置不夠高,請選擇VMR-7 。
KMP中關於選擇視頻渲染器有這樣一段話:
「請選擇如何在KMP 中輸出圖像。 默認情況下推薦使用覆蓋混合器 。 如果您正在使用 Windows XP或DirectX 9 請選擇 VMR 7
/ 9 未渲染,並使字幕輸出到VMR 表面 。 它將增加字幕的質量 ;如果您無法切換到桌面模式或在 VMR 輸出中使用覆蓋字幕 。 那麼 請選擇使用內建渲染器通過 DirectDraw 渲染直接輸出圖像 。 如果它可以被正確覆蓋那麼使用覆蓋表面將會得到更柔和和更快速 的圖像播放效果。」
1. DirectShow介紹
DirectShow是一個windows平台上的流媒體框架,提供了高質量的多媒體流採集和回放功能。它支持多種多樣的媒體文件格式,包括ASF、MPEG、AVI、MP3和WAV文件,同時支持使用WDM驅動或早期的VFW驅動來進行多媒體流的採集。DirectShow整合了其它的DirectX技術,能自動地偵測並使用可利用的音視頻硬體加速,也能支持沒有硬體加速的系統。
DirectShow大大簡化了媒體回放、格式轉換和採集工作。但與此同時,它也為用戶自定義的解決方案提供了底層流控制框架,從而使用戶可以自行創建支持新的文件格式或其它用途的DirectShow組件。
以下是幾個使用DirectShow編寫的典型應用:DVD播放器、視頻編輯應用、AVI到ASF轉換器、MP3播放器和數字視頻採集應用。
DirectShow是建立在組件對象模型(COM)上的,因此當你編寫DirectShow應用時,你必須具備COM客戶端程序編寫的知識。對於大部分的應用,你不需要實現自己的COM對象,DirectShow提供了大部分你需要的DirectShow組件,但是假如你需要編寫自己的DirectShow組件,你還需要具備編寫COM組件的知識。
1.1. DirectShow支持的格式
DirectShow是一個開放的框架,因此只要有合適的filter來分析和解碼,它可以支持任何格式。DirectShow默認支持以下的文件類型和壓縮格式:
註:打*號的需要Windows Media Format SDK支持
文件類型:
Windows Media? Audio (WMA)*
Windows Media? Video (WMV)*
Advanced Systems Format (ASF)*
Motion Picture Experts Group (MPEG)
Audio-Video Interleaved (AVI)
QuickTime (version 2 and lower)
WAV
AIFF
AU
SND
MIDI
壓縮格式:
Windows Media Video*
ISO MPEG-4 video version 1.0*
Microsoft MPEG-4 version 3*
Sipro Labs ACELP*
Windows Media Audio*
MPEG Audio Layer-3 (MP3) (decompression only)
Digital Video (DV)
MPEG-1 (decompression only)
MJPEG
Cinepak
微軟自己沒有提供MPEG2解碼器,一些可用的DirectShow MPEG2硬體或軟體解碼器是由第三方提供的。
❻ 如何安裝dspack里的directshow元件及使用
首先是安裝dspack
1.Download DSPACK
並將其解壓縮至任意的位置(只要找得到就可以)。並下載及安裝directx 9及
DXSDK(Direct X SDK)。
以下
以\$(DSPACK)\當作dspack所在目錄。
以\$DXSDK\當作DXSDK所在目錄
以\$(BCB)\當作BCB所在目錄
2.將\$(DSPACK)\include里的檔案更新至\$(DXSDK)\include,會覆蓋幾個檔。
再把\$(DXSDK)\include里的檔更新至\$(BCB)\include。
3.以上動作做完,就可安裝dspack directshow元件:
至\$(DSPACK)\packages\目錄里,依序執行下面這三個檔案
DirectX9_BCB6.bpk 執行compiler,儲存
DSPack_BCB6.bpk 執行compiler,儲存
DSPackDesign_BCB6.bpk 執行install
這目錄的檔案,有些是唯讀的,執行前,把唯讀取消
在執行之前,先在Tools->Environment Option->Libary->Libary path
加入\$(DSPACK)\lib、\$(DSPACK)\src\DSPack、\$(DSPACK)\src\DirectX9
在執行之後,工具列就會多了directx元件
注意:請先確定windows系統檔有無d3d9.dll,d3dx9d.dll這兩個檔。在筆者的
系統檔只有d3d9.dll,所以在compiler DirectX9_BCB6.bpk前,筆者先將d3dx9d.lib
及d3dx9.pas從表格里移除,再執行compiler。如此最後在install時,就不有
找不到d3dx9d.dll這個訊息,(至於這樣做會不會有副作用,就不得而知,至少現
在用都還沒出現)。
4.安裝好dspack之後,我們就可以使DSPACK所附的範例試試directshow。
執行Demos\BCB6\Playcap\Playcap.bpr。會出現一個錯誤訊息,這是因為
資料夾被改變,只要做點小改變就可以使用了。這時我們可以從
project->options->directories\conditionals->include path這圖看出
有些include目錄並不正確,只要將其改正即可使用。
..\..\..\Src\DSPack 改成\$(DSPACK)\Src\DSPack
..\..\..\Src\DirectX9改成\$(DSPACK)\Src\DirectX9
D:\DSPACK\Demos\BCB6\Playcap改成\$(DSPACK)\Demos\BCB6\Playcap
相同的,在Libary path也要將
D:\DSPACK\Demos\BCB6\Playcap改成\$(DSPACK)\Demos\BCB6\Playcap
改正之後,就可以執行,執行畫面如下,按下device按鈕就可以看到ccd的名稱
點選之後,如果有接上ccd,就可以看到畫面。轉載,僅供參考。
❼ DirectX 8.0的DirectX 8.0 DirectShow
此版本中的 DirectShow 也進行了大幅度修改。DirectX 8.0 中添加的部分新特性包括:
新的過濾圖形特性Windows Media™ 格式支持視頻編輯支持新的 DVD 支持新的 MPEG-2 傳輸和程序流支持對廣播驅動程序體系結構的支持DirectX 媒體對象過濾圖形管理器支持一些新的特性,包括動態圖形生成和實時來源合成。使用動態圖形生成,您現在可以在圖形運行過程中對過濾圖形進行修改。而以前,要添加或刪除過濾器,應用程序必須停止該圖形,因而打斷數據流。DirectShow 現在還支持實時來源合成——例如,可以將實時音頻流與實時視頻流合成。
兩個新的過濾器使 DirectShow 應用程序可以讀取或寫入 Microsoft Windows Media 格式的文件。ASF Reader 過濾器用於讀取和分析 Windows Media 格式的文件;ASF File Writer 過濾器用於寫入 Windows Media 格式的文件,並能夠執行必要的復合和壓縮操作。DirectShow 和 Windows Media SDK 現在提供了完整的解決方案,用於編寫應用程序來創建和播放 Windows Media 格式流。
新的 DirectShow 編輯服務 (DES) API 支持視頻編輯。DES 建立在核心的 DirectShow 體系結構之上,提供了一組專門為操作視頻編輯工程而設計的介面。DirectShow 的框架更適合於創建視頻編輯應用程序,作為應用程序開發人員,您將從中獲益匪淺。DES 取代了剪切表,同時剪切表將不再受到支持。
兩個新的介面 IDVDControl2 和 IDVDInfo2 極大地擴展了 DVD 導航器的功能。DVD 導航器現在實現了完整的 DVD Annex-J 命令集,既可以播放卡拉 OK,也可以播放影碟。新的 MSWebDVD ActiveX® 控制項使這項功能可在基於腳本的應用程序中使用。
新的過濾器 MPEG-2 Demultiplexer 提供了在推模式下(從實時來源接收數據)對 MPEG-2 傳輸流和程序流的支持。該過濾器與過濾圖形中的新實時來源合成支持配合,可以提供優異的 MPEG-2 增強支持。
Microsoft 廣播驅動程序體系結構 (BDA) 是新一代數/模電視調制設備的規范。在 DirectX 8.0 中,DirectShow 通過一組新的內核模式設備過濾器和「BDA 調制模型」來支持 BDA 兼容設備。「BDA 調制模型」是一組對象的集合,提供了對各種類型的數字和模擬網路進行調制的方法。
DirectX 媒體對象 (DMO) 提供了編寫數據流組件的新方法。與 DirectShow 過濾器相似,DMO 接受輸入數據,並將其用於生成輸出數據。但是,DMO API 比相應的 DirectShow API 簡單得多。因此,DMO 比 DirectShow 過濾器更容易創建、測試和使用。DMO 與 DirectShow 完全兼容。只要您需要使用 DirectShow 提供的服務,例如圖形同步、智能連接、數據流的自動處理和線程管理,您都可以使用 DMO。但是,DMO 不需要使用過濾圖形,因此應用程序無需 DirectShow 就可以使用 DMO。
DirectX 8.0 版本的 DirectShow 包含更多的功能,但限於篇幅,這里就不再多說。請記著試用新增和改進的 DirectShow。
❽ 怎樣用DirectShow來壓縮一個AVI文件
這里我們講解怎樣用DirectShow來壓縮一個AVI文件。我們重點放到視頻壓縮,同樣的方法可以應用到音頻壓縮。
我們分以下幾步來講解:
1、 選擇一個壓縮過濾器
有許多種方法可以壓縮視頻或者音頻,比如:
a、 本地DirectShow過濾器
b、 視頻壓縮管理編碼器(VCM)
c、 音頻壓縮管理編碼器(ACM)
d、 DirectX媒體對象(DMOs)
在DirectShow中,VCM被AVI Compressor過濾器封裝了。同樣的,ACM編碼器也被ACM Wrapper過濾器封裝了。DMOs被DMO Wrapper過濾器封裝。系統設備枚舉器提供了一個統一的方法來枚舉和創建這些壓縮器,我們不用考慮底層的操作。
枚舉設備方法請參照前面的講述。這里我們只給出代碼:
void OnInitDialog(HWND hDlg)
{
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
IEnumMoniker *pEnum = NULL;
IMoniker *pMoniker = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
(void**)&pSysDevEnum);
hr = pSysDevEnum->CreateClassEnumerator(
CLSID_VideoCompressorCategory, &pEnum, 0);
while (S_OK == pEnum->Next(1, &pMoniker, NULL))
{
IPropertyBag *pPropBag = NULL;
pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
(void **)&pPropBag);
VARIANT var;
VariantInit(&var);
hr = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hr))
{
LRESULT iSel = AddString(GetDlgItem(hDlg,
IDC_CODEC_LIST), var.bstrVal);
}
VariantClear(&var);
pPropBag->Release();
pMoniker->Release();
}
SendDlgItemMessage(hDlg, IDC_CODEC_LIST,
LB_SETCURSEL, 0, 0);
pSysDevEnum->Release();
pEnum->Release();
}
創建一個過濾器的實例,調用IMoniker::BindToObject方法。方法會返回一個IBaseFilter介面指針。就象下面那樣:
IBaseFilter *pFilter = NULL;
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
(void**)&pFilter);
if (SUCCEEDED(hr))
{
// 可以使用過濾器了
// 記著釋放IBaseFilter介面指針
}
2、 設置視頻壓縮屬性
視頻壓縮過濾器可以在它的輸出引腳支持IAMVideoCompression介面。使用這個介面可以設置壓縮的屬性,比如楨率,壓縮質量等待。
首先,調用IBaseFilter::EnumPins方法找到過濾器的輸出引腳,然後為介面查詢引腳。一些過濾器不是所有的介面都支持,也有的不支持某個壓縮屬性。為了決定支持的屬性能力,我們調用IAMVideoCompression::GetInfo來確定。這個方法返回一些信息:
a、 一個設置性能的標識
b、 一個描述字元串和版本字元串
c、 默認的楨速率,質量等參數
它按照下面的語法調用:
hr = pCompress->GetInfo(pszVersion, &cbVersion, pszDesc, &cbDesc,
&lKeyFrame, &lPFrame, &dblQuality, &lCap);
pszVersion和pszDesc參數是接收版本和描述字元串的寬字元緩沖區。cbVersion和cbDesc參數接收被請求的緩沖區大小。IkeyFrame,lPFrame和dblQuality參數獲得默認的楨速率,p楨率和質量。質量是用從0.0到1.0的浮點數來表示的。Icap參數獲得一個被或的能力標識,這個被定義為CompressionCaps枚舉類型。
這些參數的任何一個都可以設置為NULL,。例如如果為了獲得版本和描述字元串,第一次調用方法的時候,第一個和第三個參數為NULL。使用返回值cbVersion和cbDesc來分配緩沖區,然後再次調用該方法:
int cbVersion, cbDesc; // Size in bytes, not characters!
hr = pCompress->GetInfo(0, &cbVersion, 0, &cbDesc, 0, 0, 0, 0);
if (SUCCEEDED(hr))
{
WCHAR *pszVersion = new WCHAR[cbVersion/2];
WCHAR *pszDesc = new WCHAR[cbDesc/2];
hr = pCompress->GetInfo(pszVersion, 0, pszDesc, 0, 0, 0, 0, 0);
}
iCap參數提供了對IAMVideoCompression方法的過濾器的支持能力。例如,如果iCap包含了CompressionCaps_CanKeyFrame標志,你就可以調用IAMVideoCompression::get_KeyFrameRate方法來得到關鍵楨的速率,調用IAMVideoCompression::put_KeyFrameRate來設置楨速率。如果iCap的值沒有包含這些參數,那麼就只能使用默認的值了。
if (lCap & CompressionCaps_CanKeyFrame)
{
hr = pCompress->get_KeyFrameRate(&lKeyFrame);
if (FAILED(hr) || lKeyFrame < 0)
{
lKeyFrame = lDefaultKeyFrame; // 來自 GetInfo.
}
}
下面的代碼試著在輸出引腳尋找IAMVideoCompression介面。如果成功,它將會返回默認的和實際的壓縮屬性值。
HRESULT hr = E_FAIL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IAMVideoCompression *pCompress = NULL;
// 尋找支持 IAMVideoCompression的引腳
pFilter->EnumPins(&pEnum);
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = pPin->QueryInterface(IID_IAMVideoCompression, (void**)&pCompress);
pPin->Release();
if (SUCCEEDED(hr)) // Found the interface.
{
break;
}
}
if (SUCCEEDED(hr))
{
long lCap; // 性能標識
long lKeyFrame, lPFrame; // 真實值
double m_Quality;
long lKeyFrameDef, lPFrameDef; //默認值
double QualityDef;
//得到默認值和它的性能
hr = pCompress->GetInfo(0, 0, 0, 0, &KeyFrameDef, &lPFrameDef,
&QualityDef, &lCap);
if (SUCCEEDED(hr))
{
// 得到實際的值.
if (lCap & CompressionCaps_CanKeyFrame)
{
hr = pCompress->get_KeyFrameRate(&lKeyFrame);
if (FAILED(hr) || lKeyFrame < 0)
lKeyFrame = lKeyFrameDef;
}
if (lCap & CompressionCaps_CanBFrame)
{
hr = pCompress->get_PFramesPerKeyFrame(&lPFrame);
if (FAILED(hr) || lPFrame < 0)
lPFrame = lPFrameDef;
}
if (lCap & CompressionCaps_CanQuality)
{
hr = pCompress->get_Quality(&Quality);
if (FAILED(hr) || Quality < 0)
Quality = QualityDef;
}
}
}
注意:如果你使用IcaptureGraphBuilder2介面來創建你的過濾圖形,你可以調用IcaptureGraphBuilder2::FindInterface方法來獲得IAMVideoCompression介面。
3、 建立壓縮圖形
下圖是一個典型的AVI文件的過濾圖形:
AVI_Splitter過濾器從文件的源過濾器(File Source(Async))拉數據,然後分解到視頻和音頻流。視頻解壓縮過濾器解碼被壓縮的視頻,然後重新被視頻壓縮器重新壓縮。
被壓縮的視頻進入到AVI Mux過濾器。音頻流在這個例子中沒有被壓縮,因此它直接從AVI Splitter傳輸到AVI Mux。AVI Mux進行隔行掃描,然後使用File Write過濾器將數據輸出到磁碟上。注意,就算原始文件裡面沒有音頻流,AVI Mux過濾器也是必須的。最簡單的方法創建這種過濾圖形就是使用Capture Graph Builder,這是DirectShow裡面為了建立捕獲圖形或者別的定製的過濾圖形的一個部件。
注意:DirectShow裡麵包含了兩個Capture Graph Builder版本。它們提供了不同的介面和類的標識。早期的版本類標識是CLSID_CaptureGraphBuild,介面是IcaptureGraphBuilder。它兼容存在的應用程序。新版本的類標識是CLSID_CaptureGraphBuilder2新的介面名稱是IcaptureGraphBuilder2。新的介面比老的介面有更多的靈活性。
創建Capture Graph Builder我們還是使用CoCreateInstance:
ICaptureGraphBuilder2 *pBuild = NULL;
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
NULL, CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2, (void **)&pBuild);
然後我們使用Capture Graph Builder來建立過濾圖形:
a、 建立部分渲染的過濾圖形,它包含AVI Mux 過濾器和File Writer過濾器。
b、 添加源過濾器和壓縮過濾器。
c、 連接源過濾器到MUX 過濾器。
下面逐步的解釋每一個細節:
建立渲染段
為了建立過濾圖形的渲染段,調用IcaptureGraphBuilder2::SetOutputFileName方法。它返回一個MUX的過濾器和File Write的指針。MUX是下面建立過濾圖形所需要的,但是這個例子不需要File Write,因此,它的參數為NULL。
IBaseFilter *pMux = NULL;
pBuild->SetOutputFileName(
&MEDIASUBTYPE_Avi, //文件類型
wszOutputFile, // 文件名
&pMux, // 得到一個指向multiplexer的指針
NULL); // 得到一個指向File Write的指針
當該方法返回,MUX過濾器有一個很明顯的參考計數,所以以後一定要確保釋放它。MUX過濾器提供了兩個介面來控制AVI格式:
IconfigInterleaving介面:設置交錯模式
IconfigAviMux介面:設置主流和AVI兼容性的索引
添加源過濾器和壓縮過濾器
下一步我們要在過濾圖形中添加源過濾器和壓縮過濾器。當你調用SetOutputFileName的時候,Capture Graph Builder會自動的創建一個過濾圖形管理器的實例。你可以調用IcaptureGraphBuilder::GetFiltergraph方法來獲得剛才創建的過濾圖形管理器的指針。
IGraphBuilder *pGraph = NULL;
pBuild->GetFiltergraph(&pGraph);
現在我們該調用IgraphBuilder::AddSourceFilter方法來添加非同步文件源過濾器,然後調用IfilterGraph::AddFilter方法來添加視頻壓縮過濾器:
IBaseFilter *pSrc = NULL;
pGraph->AddSourceFilter(wszInputFile, L"Source Filter", &pSrc);
pGraph->AddFilter(pVComp, L"Compressor");
到了這一步我們的狀態就象下圖那樣,源過濾器和壓縮過濾器沒有和別的任何過濾器連接。
連接源到Mux
最後一步就是通過視頻壓縮過濾器連接源過濾器到AVI Mux過濾器。我們使用IcaptureGraphBuilder2::RenderStream方法來連接源過濾器的輸出引腳到指定的過濾器。
前兩個參數指定了用那個源過濾器的引腳來連接,通過指明引腳的分類和媒體類型來實現。非同步文件源過濾器只有一個輸出引腳,所以這些參數要設置成NULL。後三個參數指定了源過濾器,壓縮過濾器,和Mux過濾器。
下面的代碼演示了通過視頻壓縮過濾器來渲染視頻流:
pBuild->RenderStream(
NULL, // 輸出引腳類型
NULL, // 媒體類型
pSrc, // 源過濾器
pVComp, // 壓縮過濾器
pMux);
假定源文件包含了音頻流,AVI Splitter過濾器會在輸出引腳輸出音頻流。為了連接這個管腳我們需要再次調用RenderStream:
pBuild->RenderStream(NULL, NULL, pSrc, NULL, pMux);
這里我們沒有指定壓縮過濾器。而且源過濾器的輸出引腳已經連接了,因此RenderStream方法會搜索一個未連接的輸出引腳到Splitter過濾器。它可以直接連接引腳到MUX過濾器。但是如果源文件沒有音頻流,那麼第二次調用會失敗。
4、 寫文件
如果想寫文件正常的進行,你必須調用ImediaControl::Run方法來運行過濾圖形。等到播放完成後調用ImediaControl::Stop。
如果要顯示文件的寫的進度,你可以使用ImediaSeeking來查詢Mux過濾器。調用ImediaSeeking::GetDuration方法來獲得文件的持續時間。定時的使用ImediaSeeking::GetCurrentPostition方法來獲得當前的位置,但必須要是過濾圖形運行中。
注意,一般情況下我們用ImediaSeeking介面查詢過濾圖形管理器來定位,如果正在寫文件的話,這將是一個特例。這里我們要查詢Mux過濾器就可以了。查詢過濾圖形定位要求回放的時候才可以,而不是寫文件的時候。
看了下面的代碼讀者就會更加清楚了。
IMediaSeeking *pSeek = NULL;
IMediaEventEx *pEvent = NULL;
IMediaControl *pControl = NULL;
REFERENCE_TIME rtTotal;
hr = pMux->QueryInterface(IID_IMediaSeeking, (void**)&pSeek);
hr = pGraph->QueryInterface(IID_IMediaEventEx, (void**)&pEvent);
hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
// 設置DirectShow的時間通報
hr = pEvent->SetNotifyWindow((OAHWND)hwnd, WM_GRAPHNOTIFY, 0);
hr = GetDuration(&rtTotal);
SendDlgItemMessage(hwnd, IDC_PROGRESS1, PBM_SETRANGE, 0,
MAKELPARAM(0, rtTotal / 10000000));
// 開始時間.
UINT_PTR res = SetTimer(hwnd, nIDEvent, 100, NULL);
// 運行過濾圖形.
pControl->Run();
當應用程序收到定時器事件,它就更新當前位置:
void OnTimer(HWND hDlg, IMediaSeeking *pSeek)
{
REFERENCE_TIME rtNow;
HRESULT hr = pSeek->GetCurrentPosition(&rtNow);
if (SUCCEEDED(hr))
{
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETPOS, rtNow/10000000, 0);
}
}
當應用程序收到DirectShow結束事件,它就可以停止圖形,就象下面那樣:
LRESULT CALLBACK WndProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
/* ... */
case WM_GRAPHNOTIFY:
DoHandleEvent();
break;
/* ... */
}
}
void DoHandleEvent()
{
long evCode, param1, param2;
bool bComplete = false;
if (!pEvent) return;
//得到所有的事件
while (SUCCEEDED(pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0))
{
pEvent->FreeEventParams(evCode, param1, param2);
switch(evCode)
{
case EC_USERABORT:
case EC_ERRORABORT:
case EC_COMPLETE:
bComplete = true;
break;
}
}
if (bComplete)
{
pControl->Stop(); // Important! You must stop the graph!
//關掉事件通報.
pEvent->SetNotifyWindow(NULL, 0, 0);
pEvent->Release();
pEvent = NULL;
SendDlgItemMessage(IDC_PROGRESS1, PBM_SETPOS, 0, 0);
KillTimer(hwnd, nIDEvent);
}
}