Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

DirectShow


DirectShow® and Delphi

DirectShow

DirectShow je Microsoft interfejs za programiranje aplikacija (API) koji omogućava Windows aplikacijama da komuniciraju sa Windows "Media" ulaznim uređajima kao što su kamkorderi, web kamere, DVD uređaji, TV kartice, analogne kartice za snimanje i da ih kontrolišu. DirectShow se, takođe, može koristiti za reprodukovanje medijskih datoteka i, u DirectShow modelu, medijska datoteka se prosto tretira kao bilo koji drugi ulazni uređaj.

Svestranost DirectShow-a potiče od njegovog modularnog pristupa. Audio i video tretiraju se kao "strimovi" podataka, a softveri 'modula' su u stanju da kontrolišu te strimove i da komuniciraju s njima dok prolaze iz ulaznog uređaja - web kamere, kartice za snimanje, itd - sve do izlaznog uređaja - zvučne kartice, video monitora ili datoteke.

Strimovi

Video strimovi su ili sekvence nekompresovanih RGB bitmapa ili skupovi numeričkih vrednosti koje predstavljaju originalne slike u komprimovanom obliku. Izborom ispravnog 'modula' softvera, programer može da obezbedi pravilnu obradu strima podataka, kao i njegovo pravilno prikazivanje na izlaznom uređaju.

Audio strimovi sastoje se od sekvenci uzoraka od kojih svaki predstavlja amplitudu zvuka u datom trenutku. Broj uzoraka uzetih u određenom periodu zavisi od željenog kvaliteta zvuka. Na primer, CD kvalitet zvuka koristi 16-bita za predstavljanje svakog uzorka, a uzorci se snimaju i reprodukuju na 44,1 kHz.

Umesto da uzorci predstavljaju stvarnu audio amplitudu, komprimovani audio koristi podatke da predstavi nekoliko uzoraka koji su kombinovani na osnovu određene matematičke formule. Pod uslovom da se "inverzija" iste formule (algoritam) koristiti duž strima da bi se podaci dekompresovali, originalni zvuk će se ispravno reprodukovati.

Filteri

Kompresija je zaista samo nužno zlo koje "smešta" audio ili video podatke određene dužine u prihvatljiv propusni opseg ili prostor za skladištenje, ali mediji mogu biti obrađeni na, gotovo, neograničen broj načina da bi se povećao ili modifikovao originalni ulaz.

Obrada se postiže kroz softverske module koje DirectShow naziva Filteri. Postoje tri osnovne vrste: izvorni filteri za ulaz, filteri za transformisanje zaduženi za preradu i filteri za izlaz. Iako filteri, s vremena na vreme, upadaju na teritoriju jedan drugome, filter za transformaciju poznatiji je kao kodek - kompresija-dekompresija. Opis nije naročito dobar jer gotovo beskonačan broj različitih filtera za transformaciju može da izvrši gotovo beskonačan broj drugih procesa, pored jednostavne kompresije i dekompresije.

Koristeći DirectShow, aplikacija može da poveže filtere i da stvori konfiguraciju kakva god da je potrebna. Mada, kao što sam već pomenuo, uloge filtera mogu da se preklapaju s vremena na vreme, osnovna konfiguracija će uvek biti: izvorni filter koji prihvata ulaz iz spoljnog uređaja, jedan ili više filtera za transformaciju i obradu strima podataka i filter za emitovanje - obično se koristi da predstavi korisniku tok podataka.

Filteri su COM objekti

Ako ste, poput mene, uspeli da izbegnete korišćenje COM u Delphi-ma, ova odrednica može pomalo da vas razočara, ali, srećom, ne treba nam mnogo razumevanja COM-a da bismo koristili DirectShow u Delphi-ma. Već smo upoznati s konceptom Delphi objekata koji imaju osobine i metode. U COM-u, možemo razmišljati o njima kao o interfejsima i treba nam samo jedna ili dve jednostavne tehnike kako bismo "ispitali" ili izložili ove interfejse.

Pratite ovaj link za vrlo kratak uvod u COM. (Pogotovo ako koristite Delphi 5)

Iskreno govoreći, činjenica da nam treba ograničeno razumevanje COM programiranja predstavlja najmanji problem! Sva Microsoft DirectShow dokumentacija (a ima je mnogo!) napisana je na C++ jeziku; i mi treba da steknemo neko znanje programiranja na tom jeziku, kako bismo preveli kod u Delphi. Tu su i neki primeri pisani u Visual Basic-u pa, ako ste upoznati sa VB, ti primeri mogu vam biti od koristi.

DSPack

Uz ostale Windows interfejse za programiranje aplikacija, upotreba DirectShow-a sastoji se od izrade funkcija i poziva procedura. Kao što ćete saznati, Borland je već uradio težak posao u Delphi windows.pas tako što je mnoge Windows API pozive pretvorio u Delphi. Iako Borland nije konvertovao za nas nijedno od DirectShow zaglavlja, veoma smo srećni što je Henri Gourvest to učinio i omogućio nam da besplatno pristupimo plodovima njegovog truda. Pored toga, on i njegov tim proizveli su nekoliko Delphi komponenti spremnih za pokretanje koje nam zaista mogu olakšati rad - posebno kad je reč o kreiranju COM objekta.

Možete preuzeti DSPack na sajtu progdigy.com gde ćete naći i aktivan forum. (Link za preuzimanje).

Budite svesni činjenice da će vam forum delovati pomalo zastrašujuće. Članovi foruma neće vam ponuditi gomile kodova. Očekivaće od vas da dobro proučite DirectShow dokumentaciju sa sajta msdn.microsoft.com. Savladavanje DirectShow-a (na bilo kom programskom jeziku, pretpostavljam) predstavlja niz prepreka i mnogo posrtanja.

Dakle, preuzmite DSPack i pažljivo pratite uputstva za instalaciju. Delphi će sada imati novu karticu na paleti “Components” i potpuno novi skup DirectShow komponenti. Podjednako važno, sada ćete imati sva DirectShow zaglavlja konvertovana na Delphi.

Siguran sam da smo svi, u nekom trenutku, pokušali da instaliramo komponentu treće strane u Delphi samo da bismo saznali da se neće instalirati ili sastaviti. Budite uvereni da je DSPack pokušao i testirao set veoma robusnih komponenti. Ako neće da se instalira, ponovo pročitajte uputstva - posebno u vezi sa Library Paths koje treba da postavite u Delphi IDE i to po redu kojim ste instalirali pakete.

dspack

DirectShow program na Delphi jeziku

Da bismo stekli predstavu o DirectShow-u, počnimo od jednostavnog video projekta koji prikazuje datoteku filma. To je predstavljanje DirectShow-a i prvi primer dat u Microsoft dokumentaciji. Evo Delphi verzije.

Započnite novi projekat na Delphi jeziku i pritisnite taster OpenDialog na obrascu. Takođe, izaberite FilterGraph komponentu iz palete DSPack i primenite je na obrascu.

project1

Kod Button1Click-a procedura dodaje sledeće:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    if not FilterGraph1.Active then FilterGraph1.Active:= True;
    FilterGraph1.RenderFile(OpenDialog1.Filename);
    FilterGraph1.Play;
  end;
end;

Pokrenite projekat i izaberite datoteku filma (na primer AVI) koju imate na kompjuteru. Otvoriće se novi prozor pod nazivom 'ActiveMovie' i prikazaće film. Vaš prvi DirectShow program!


Odvojeni ActiveMovie prozor pomalo je nepregledan, pa dodajte VideoWindow sa DSPack palete na obrazac projekta:

project2

Sad treba da povežemo VideoWindow i FilterGraph tako što ćemo modifikovati Button1Click procedure na sledeći način:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    if not FilterGraph1.Active then FilterGraph1.Active:= True;
    VideoWindow1.FilterGraph:= FilterGraph1;
    FilterGraph1.RenderFile(OpenDialog1.Filename);
    FilterGraph1.Play;
  end;
end;

Ponovo pokrenite projekat. Ovaj put, video će se prikazati u sopstvenom prozoruPonovo pokrenite projekat. Ovaj put, video će se prikazati u sopstvenom prozoru!

Ok, ostavili smo pomalo neuredan projekat i oslonili smo se na Delphi da ga graciozno zatvori. Treba da dodate drugi Button (Caption:= 'Stop') i sledećoj proceduri Button2Click:

procedure TForm1.Button2Click(Sender: TObject);
begin
    FilterGraph1.Stop;
end;

I, u obliku OnCloseQuery procedure:

    FilterGraph1.ClearGraph;
    FilterGraph1.Active:= False;

Namerno sam jurio malo predaleko da ih vam probudio osećaj za DirectShow u akciji, ali u sledećem delu malo ćemo se vratiti unazad da bismo naučili nešto više o FilterGraph zato što je to kamen temeljac svake DirectShow aplikacije.

Filter Graph

Na prvoj strani pomenuo sam da aplikacije za medije koriste DirectShow za povezivanje raznih COM objekata koji se zovu filteri, projektovanih za obradu ili manipulaciju video ili audio strima na neki način. Takođe, pomenuo sam da se filteri javljaju u tri osnovne vrste: izvorni filteri (kao što su drajveri uređaja), filteri za transformisanje (kao što su kompresor za AVI u MPEG) i filteri za emitovanje (kao što je VideoWindow koji smo videli na prethodnoj strani).

Filtere povezuju "Pinovi". Izvorni filteri imaće barem jedan izlazni pin, filteri za transformisanje imaće barem jedan ulazni i jedan izlazni pin i filteri za emitovanje imaće bar jedan ulazni pin.

Slika koja sledi prikazuje tipičan primer mogućeg povezivanja filtera.

Kompletna kolekcija filtera, kao što je ova, poznata pod imenom Filter Graph i aplikacija izgrađenih prema posebnim zahtevima pomoću DirectShow Object-a zove se Filter Graph Manager - ili, tačnije, aplikacija kreira instancu za Filter Graph Manager, a zatim je koristi da kreira Filter Graph.

Filter Graph Manager može da se koristi za pojedinačno dodavanje filtera na Filter Graph, ali Filter Graph Manager ima logiku nazvanu Intelligent Connect koja se koristi za pretraživanje registratora računara zbog odgovarajućih filtera i njihovog automatskog povezivanja. Ovo je veoma moćna funkcija jer omogućava funkcionalnosti aplikacije da "raste" ako korisnik treba da doda još filtera. Na primer, mnoge aplikacije trećih strana - kao što je WinAmp itd - same će dodati filtere i navesti ih u registru. Pod uslovom da je filter napisan prema DirectShow specifikaciji, može biti upisan u bilo kojoj drugoj aplikaciji za koju DirectShow Intelligent Connect smara da bi bio koristan.

Intelligent Connect Filter Graph Manager-a pokreće se se pozivanjem njegovog rendera ili metoda RenderFile. Pogledajmo jednostavan fajl video plejera prikazan na prethodnoj stranici. Njegov Filter Graph može izgledati ovako:

Međutim, pretpostavimo da je fajl koji smo pustili imao i audio snimak. Filter Graph morao bi da uključi i audio filtere u tom slučaju pa bi mogao da izgleda, otprilike, ovako:

Pretpostavimo da smo izabrali MPEG datoteku umesto AVI; bio bi nam potreban drugačiji set filtera, itd. Pomoću Intelligent Connect-a i RenderFile-a Filter Graph Manager-a dozvoljavamo aplikaciji da automatski kreira Filter Graph posebno za izabrani format fajla - pod uslovom da sistem ima filtere za njega - i poštedećemo sebe napornog rada!

Kao što sam gore pomenuo, Filter Graph Manager omogućava da se filteri dodaju ručno u Filter Graph, a tu su i mnoge druge metode i opcije u vezi s njim. Kasnije ćemo pogledati neke od njih.

Graph Edit

Pošto Render Filter Graph Manager-a i RenderFile methode automatski kreiraju Filter Graph, ne možemo biti sigurni koje je filtere uključio, pa će nam biti od koristi ako bismo mogli da vidimo kreirani Filter Graph. Microsoft je napravio veoma koristan alat "GraphEdit". Iako je zvanično raspoloživ samo kao deo DirectShow SDK (kompleta za razvoj softvera), na internetu se mogu naći njegove razne verzije. Neću vam ovde dati link jer je lako naći ga i preuzeti najnoviju verziju Google pretragom za 'GraphEdit'.

Korisna funkcija GraphEdit-a je da "njuška" na Filter Graph-u koji je stvorila druga aplikacija. Vratimo se našem jednostavnom Delphi plejeru medijskih datoteka koji smo ranije kreirali i videli.

filtergraph

Evo naše jednostavne aplikacije sa Delphi Object Inspector-om koja prikazuje FilterGraph1 komponentu.

Zapazite da sam podesio GraphEdit property na True. Podrazumevana vrednost je False ali, pošto hoćemo da istražimo Filter Graph alatom GraphEdit, moramo da podesimo property na True.

Naravno, mogli smo ga podesiti i u kodu:

   FilterGraph1.GraphEdit:= True;

Pokrenućemo program, otvoriti različite vrste medijskih datoteka i iskoristiti GraphEdit da bismo videli koji je metod Filter Graph kreirao u FilterGraph1.RenderFile().

GraphEdit

Kad pokrenete GraphEdit, videćete ekran sličan onom iznad. Kliknite na dugme Toolbar koje sam gore zaokružio i otvoriće vam se prozor za dijalog koji će vam ponuditi da označite Filter Graph neke druge aplikacije. Ako nije pokrenuta druga medijska aplikacija, na listi neće biti nijedan Filter Graph:

 


Ok, pokrenite naš mali Delphi projekat i izaberite medijsku datoteku. Dok se pokreće, otvorite GraphEdit Select a remote filter graph da biste ponovo videli prozor za dijalog i, ovaj put, trebalo bi da imate jedan izbor - ovo je Filter Graf koji je kreirao naš Delphi projekat. Slika ispod predstavlja grafički prikaz Filter Graph-a u GraphEdit-u:

Okvir s leve strane prikazuje naziv fajla koji sam odabrao za prikazivanje (testavi.avi), a s desne strane pokazuje naziv fajla za emitovanje u našem projektu (VideoWindow1), ali ostatak graph-a kreiran je automatski metodom FilterGraph1.RenderFile().

Dok je prozor GraphEdit još uvek aktivan, ukucajte Ctrl H da biste prekinuli vezu GraphEdit-a od Filter Graph-a i označili drugu vrstu fajla za prikazivanje u našem projektu. Da bi se potpuno razlikovao od prethodnog primera, izabrao sam MP3 fajl:

Zapazite da je "Default DirectSound Device" automatski izabran kao filter za emitovanje, a naš VideoWindow1, koji smo naveli u Delphi projektu, ostao je nepovezan i neiskorišćen na levoj strani Graph-a. Intelligent Connect i RenderFile metod koristi ekstenziju i format zaglavlja datoteke da bi utvrdio tip medija i izabrao odgovarajuće filtere za Filter Graph. U ovom slučaju, utvrđeno je da VideoWindow nije potreban. Ipak, još uvek je deo Filter Graph-a, samo što ulazni pin nije povezan ni s čim.

Kad bismo dizajnirali isključivo audio-projekat, mogli bismo izbaciti VideoWindow iz koda za svoj originalni Filter Graph, ali kad bismo pokušali da pustimo video fajl, otvorio bi se nepregledan "plutajući" ActiveMovie prozor - kao što smo videli u svom prvom projektu. Kliknite na Ctrl H da biste isključili GraphEdit iz Filter Graph-a.


GraphEdit može da uradi mnogo više nego da jednostavno prikaže postojeći Filter Graph. Grafikoni mogu biti projektovani i izgrađeni postavljanjem filtera u prozor GraphEdit i povezivanjem ulaznih i izlaznih pinova. Ali, za sada, videli smo kako prikazuje postojeći Filter Graph koji su izgradile druge aplikacija. Ovo može posebno koristiti u komplikovanijim grafikonima gde nešto ne funkcioniše u skladu s našim očekivanjima. Ponekad, Intelligent Connect ne povezuje filtere onako kako želimo, pa moramo ručno da napraviomo grafikon. Onda nastupa GraphEdit za testiranje grafike pre nego što napišemo ijednu liniju koda za njih.

Živi izvori

Prikazivanje datoteka je tako uobičajena pojava da je RenderFile metod stvoren za nas. Međutim, selekcija živog izvora - kao što je web kamera ili mikrofon - malo je komplikovanija, kao što ćemo videti u narednom poglavlju.

Zapamtićete poslednji komentar u ovom odeljku koji kaže da moramo podesiti FilterGraph1.GraphEdit na True da bi nam pokazao naš Filter Graph u GraphEdit-u (pokušajte da ga postavite na False da biste to dokazali!). Neke aplikacije podešavaju ovu opciju na False - namerno ili ne - što, nažalost, znači da ne možemo uvek videti koje filtere koriste druge aplikacije u svom Filter Graph-u.

Živi video-izvori

Ako aplikacija treba da koristi žive video-izvore, u nekom trenutku, u svojoj inicijalizaciji, moraće da odredi koji su joj izvori na raspolaganju. DirectShow obezbeđuje metod za nabrajanje filtera registrovanih na sistemu korisnika po kategorijama. Za obavljanje ovog zadatka služi System Device Enumerator, objekat DirectShow COM-a. Hajde da vidimo kako to funkcioniše u Delphi-ma.

Započnite novi projekat u Delphi-ma. Stavite TListBox na obrazac kao što je ovde prikazano. Koristićemo ListBox da nam navede izvore video ulaza. (Da bi bio stvarno moderan, dodao sam i oznaku):

project3

Pre nego što krenemo dalje, setićete se da DirectShow dokumentacija prikazuje C ++ jezik. Zato moramo da se upoznamo s njim - bar u onoj meri koliko možemo da ga konvertujemo u Delphi. Mislim da će nam koristiti ako ovde pratimo deo koda i uporedimo ga dok idemo napred.

Originalnu dokumentaciju i C++ kod možete naći ovde: Upotreba popisivača sistemskih uređaja

Pošto je System Device Enumerator DirectShow COM objekat, C++ kod počinje kreiranjem sledećeg primera:

// Create the System Device Enumerator.
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
    IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
{
    return hr;
}
// Obtain a class enumerator for the video input device category.
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);

Dakle, kako to radimo u Delphi-ma?

Pa, prvo moramo da dodamo nekoliko DSPack jedinica našoj sekciji uses. System Device Enumerator je u DSUtil jedinici i, pošto ćemo morati a odredimo i kategoriju ulaznog video-uređaja, moramo da dodamo jedinicu gde je to definisano (DirectShow9):

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, DSUtil, DirectShow9;

Najbolje mesto za kreiranje System Device Enumerator-a je, verovatno, u FormCreate obrascu. Uporedite Delphi kod sa C ++ kodom koji je prikazan iznad:

var
  Form1: TForm1;
  SysDev: TSysDevEnum;

implementation

{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
    SysDev:= TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);

Primetićete da je taj, u stvari dvostepeni postupak u C++ jeziku, znatno pojednostavljen u Delphi jeziku zahvaljujući načinu na koji je implementiran u DSPack. Baš imamo sreće! Proglasio sam sopstveni SysDev za TSysDevEnum izvan FormCreate postupka da bih mu proširio opseg tako da kasnije možemo da ga koristimo u nekom drugom postupku.

C++ kod se onda ponavlja kroz uređaje - kod nam ne prikazuje kako su oni prikazani korisniku, osim što komentariše: // Prikazuje ime u vašem korisničkom interfejsu

Možemo i mnogo bolje od toga. Evo kompletnog Delphi koda za unos u ListBox sa našim uređajima za video-ulaz:

procedure TForm1.FormCreate(Sender: TObject);
var
    i: Integer;

begin
    SysDev:= TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
    if SysDev.CountFilters > 0 then
    begin
      for i:= 0 to SysDev.CountFilters - 1 do
         ListBox1.Items.Add(SysDev.Filters[i].FriendlyName);
    end;
end;

Pokrenite projekat i "FriendlyNames" uređaja za video ulaz izlistaće se u ListBox-u:

project3run

Dakle, pošto smo nabrojali uređaje za video-ulaz na svom sistemu, šta je sledeće? Moramo izabrati jedan od uređaja i prikazati njegov video u prozoru VideoWindow.

Živi video-izvor

U poslednjem poglavlju, videli smo kako se koristi DirectShow System Device Enumerator za popunjavanje spiska uređaja za video-ulaz na našem sistemu. Koristeći različite klase ID-a kad smo stvorili sopstveni System Device Enumerator (SysDevEnum), mogli smo da nabrojimo i drugu kategoriju.

Na primer, sledeće je nabrajanje uređaja za audio-ulaz:

    SysDev:= TSysDevEnum.Create(CLSID_AudioInputDeviceCategory);

Naći ćete kompletnu listu kategorija filtera, zajedno sa svojim klasnim identifikatorima (CLSID) na msdn sajtu.

Međutim, za sada, držaćemo se naših uređaja za video-ulaz, jer moramo da izaberemo uređaj sa ListBox-a i prikažemo video u prozoru VideoWindow.

Promenite veličinu našeg obrsca projekta i dodajte VideoWindow sa DSPack palete, a zatim i FilterGraph kao u našim prethodnim projektima. Ovaj put, međutim, moramo da dodamo filter komponentu koja će “prikazati” koji god ulazni uređaj da izaberemo (naš filter source).

project4

Kao i u našim prethodnim projektima, moramo da povežemo VideoWindow sa FilterGraph-om. U ovom projektu, takođe, treba da povežemo novi Filter1 sa FilterGraph-om. Kao i ranije, možemo ih povezati u kodu ili u Delphi Object Inspector-u:

   VideoWindow1.FilterGraph:= FilterGraph1;
   Filter1.FilterGraph:= FilterGraph1;

project4a

Dakle, hoćemo mogućnost da kliknemo na video-uređaj prikazan na ListBox-u i da prikažemo njegov video u prozoru VideoWindow tako da nam treba neki kod u ListBox-ovom OnClick Event-u.

Krenimo korak po korak. Prvo treba da proverimo da FilterGraph nije već aktivan i da ne prikazuje nijedan video:

procedure TForm1.ListBox1Click(Sender: TObject);
begin
   if FilterGraph1.Active then FilterGraph1.Active:= False;
   FilterGraph1.ClearGraph;

end;

Ako smo kliknuli na odgovarajući uređaj na Listbox-u, sada treba da podesimo Filter1.BaseFilter na naš odabrani video-uređaj i da aktiviramo FilterGraph

    if ListBox1.ItemIndex > -1 then
    begin
      Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);
      FilterGraph1.Active:= True;

Kad se FilterGraph aktivira, možemo pozvati drugi DirectShow COM Object pod nazivom zove ICaptureGraphBuilder2. To je jezgro, "radnik" koji gradi Filter Graph. Pošto smo koristili I CaptureGraphBuilder2, iz DirectShow-a, moramo da podesimo svoj FilterGraph1.Mode na 'gmCapture'. Ako zaboravimo ovaj korak, dobićemo obaveštenje da je došlo do greške. Ovo je Delphi kod:

      FilterGraph1.Mode:= gmCapture;
      with FilterGraph1 as ICaptureGraphBuilder2 do
      begin
        RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter,
                     nil, VideoWindow1 as IBaseFilter);
      end;

I, na kraju, moramo podesiti FilterGraph da radi sa:

      FilterGraph1.Play;

Hajde da povežemo ceo kod i da pokrenemo program:

procedure TForm1.ListBox1Click(Sender: TObject);
begin
   if FilterGraph1.Active then FilterGraph1.Active:= False;
   FilterGraph1.ClearGraph;
   if ListBox1.ItemIndex > -1 then
   begin
      Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);
      FilterGraph1.Active:= True;

      FilterGraph1.Mode:= gmCapture;
      with FilterGraph1 as ICaptureGraphBuilder2 do
      begin
        RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter,
                     nil, VideoWindow1 as IBaseFilter);
      end;
 
      FilterGraph1.Play;
   end;
end;

 

Važna napomena za one koji koriste Delphi 5


Dobro, ovde smo preskočili nekoliko važnih linija koda. Pošto su važni za razumevanje DirectShow-a i njegove primene u DSPack, u sledećem poglavlju ćemo malo pažljivije pogledati te linije.

Filteri i TBaseFilter

U poslednjem odeljku preskočili smo nekoliko linija, pa ćemo pokušati da ih podrobnije shvatimo pre nego što krenemo dalje. Prva linija koju smo uzeli “zdravo za gotovo” glasi:
   Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);

Svi filteri imaju TBaseFilter interfejs koji može izložiti osnovni uređaj povezan s njim. Kada smo kreirali naš System Device Enumerator u Form-ovom FormCreate Event-u, napravili smo strukturu koja sadrži, između ostalog, pokazivače za drajvere uređaja, pod nazivom monikeri. Ovim pokazivačima može se naknadno pristupiti jednostavno pozivajući se na njihove indekse u strukturi - prvi indeks je 0. (Još jedan član strukture je CountFilters koji smo koristili za i:= 0 za SysDev.CountFilters - 1 do da bismo popunili ListBox).

Pošto indeks svake stavke u LlistBox-u 'odražava' istu strukturu, možemo preuzeti bilo koji od ovih Moniker pokazivača pomoću GetMoniker(indeks: ceo broj) metoda System Device Enumerator-a i postaviti svojstvo BaseFilter.Moniker-a na isti pokazivač.

Iako bi to bilo prilično "nefleksibilno", mogli smo ručno da postavimo BaseFilter Filtera u vreme dizajniranja i potpuno izostaviti linije gore prikazanog koda. Onda bismo se, naravno, "zaglavili" sa ulaznim uređajem koji bismo teško kodirali. Zaista ga koristim samo kao demonstraciju suštine BaseFilter.Moniker-a i odnosa sa indeksom uređaja u ListBox-u1.Kliknite na tri tačke (...) uporedo sa tasterom TBaseFilter property da biste otvorili Base Filter Editor.

basefilter

 


ICaptureGraphBuilder2.RenderStream

Hajde da pogledamo ostale linije koda koje smo preskočili:

      with FilterGraph1 as ICaptureGraphBuilder2 do
      begin
        RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter,
                     nil, VideoWindow1 as IBaseFilter);
      end;

Možda se sećate, iz našeg prethodnog projekta, koristili smo FilterGraph1.RenderFile(FileName: WideString) pa zašto sada upošljavamo FilterGraph1 kao ICaptureGraphBuilder2 ?

Podrazumeva se da FilterGraph samo izlaže interfejse (metode) IGraphBuilder objekta koji, nažalost, nije u stanju da izgradi grafikon za živi striming video. ICaptureGraphBuilder2 je moćniji interfejs, sa dodatnim metodama, pa nam upotreba FilterGraph1 kao ICaptureGraphBuilder2, omogućava da iskoristimo dodatne metode - posebno njegovu sposobnost da koristi Intelligent Connect i RenderStream sa živim video strimom.

(Kao što je navedeno na prethodnoj strani, Delphi 5 ne se može koristiti kao operater na ovaj način, tako da se pokazivač na FilterGraph1 ICapturGraphBuilder2 mora dobiti korišćenjem metode FilterGraph1.QueryInterface. Ako ste preskočili osnovni uvod u COM, možda ćete poželeti da ga sad pogledate.)

OK, hajde da pogledamo RenderStream. Možete naći C++ dokumentaciju na msdn sajtu a ispod i Delphi ekvivalent. Otkucao sam jedan dodatni RenderStream na slici ispod da biste stekli uvid u Delphi kod, tako da možemo pogledati parametre:

renderstream

RenderStream uzima u obzir 5 argumenata:

ArgumentOpcijeKomentari
pKategorija: PGUID PIN_CATEGORY_CAPTURE
PIN_CATEGORY_PREVIEW
PIN_CATEGORY_CC
pCategory je pokazivač za globalni, univerzalni,
Identifikator PGUID). Stavljamo prefiks @ da naglasimo da je to Delphi pokazivač (Definisan u DirectShow9.pas)
pTip: PGUIDMEDIA TYPE - vidite msdnOvaj argument je često najbolje ostaviti na nuli i pustiti RenderStream da radi za sebe. (Definisan u DirectShow9.pas)
pIzvor: InterfejsFilter izvora. Zapamtite da je typecast: Filter1 kao IBaseFilter
pfKompresor: IBaseFilterOpcioni središnji filter. Ovde podešen na null (RenderStream će popuniti praznine)
pfPrikazivač: IBaseFilterVideoWindow. Ponovo se setite napomene o IBaseFilter (Evo i zašto)

Ima još mnogo podataka na msdn web sajtu pa vam preporučujem da ih pogledate i da u glavi uradite prevod na Delphi tamo gde je to potrebno.

Ukratko, možemo ga opisati kao:

RenderStream(which-output-pin, media-type, source-filter, intermediate-filter, renderer-filter)

Ovde, u našem projektu, određujemo Source Filter i Renderer Filter puštajući RenderStream-ovom Intelligent Connect-u da ubaci bilo koje filtere u sredinu. Ako ponovo pogledamo Filter Graph uz pomoć GraphEdit-a, videćemo da je RenderStream zaista ubacio dodatni filter - u stvari, dva filtera: The Smart Tee and the Color Space Converter.

Primetićete da je izlaz iz Smart Tee Filter-a preuzet iz 'Preview' pin-a. To je zato što smo odredili PIN_CATEGORY_PREVIEW kao prvi argument u RenderStream-u. Da smo odredili PIN_CATEGORY_CAPTURE, izlaz iz Smart Tee-ja bio bi uzet iz njegovog 'CAPTURE' pin-a.

U većini aplikacija verovatno biste želeli da koriste oba izlazna pina iz Smart Tee-a, dakle, u čemu je razlika? U suštini, razlika je vrlo mala. Video strim podeljen je na dva dela i predstavljen na oba izlaza. Jedina prava razlika je u tome što će PREVIEW pin da odbaci video-okvire ako računaru ponestane CPU resursa, dok će CAPTURE pin učiniti sve da do toga ne dođ.

PREVIEW pin obično se koristi onako kako smo ga mi ovde koristili - da obezbedi prikaz za monitor. Izlaz iz CAPTURE pin-a koristi se u kritičnijim situacijama gde je, na primer, izvor zarobljen u datoteci. Bilo bi teško uočiti da nema okvira na monitoru, ali bilo bi idealno kad bismo ih izbegavali na papiru.

Kad sam već pomenuo pisanje video-fajla, u narednom delu pozabavićemo se time.

Pisanje AVI fajla

Ako pogledamo interfejs za ICaptureGraphBuilder2 na msdn sajtu, pored RenderStream metoda, videćemo još nešto što će nam se učiniti korisnim -- SetOutputFileName.

Hajde da pogledamo Delphi i da ga uporedimo sa beleškama na msdn sajtu:

Potrebna su četiri parametra.

Prvi određuje Media SUBTYPE i može biti jedan od navedenih:

MEDIASUBTYPE_Avi Audio-Video Interleaved (AVI)
MEDIASUBTYPE_Asf Advanced Systems Format (ASF)

Drugi parametar je PWideChar koji ukazuje na ime fajla.

Treći i četvrti parametri su interesantni jer predstavljaju vrednosti vraćene iz funkcije.

Kad pozivamo SetOutputFileName, stvara se multiplekser filter na osnovu vrednosti prvog parametra. Za AVI, stvara AV I Multiplexer Filter. Za ASF, stvara WM ASF Writer. Dodaje multiplekser filter graph-u i vraća pokazivač s vom IBaseFilter interfejsu u trećem parametru.

Konačno, parametar se vraća kao pokazivač interfejs datoteci pisca IFileSinkFilter.

Dakle, od tog parametra liste saznali smo da moramo navesti naziv datoteke koju smo počeli da pišemo. Iz trećeg parametra znamo da moramo proglasiti promenljivu (tipa IBaseFilter) čiji će SetOutputFileName koristiti da nam vrati pokazivač na Multiplekser koji kreira. A mi znamo, iz četvrtog parametra, da moramo proglasiti drugu promenljivu (tipa IFileSinkFilter) za pokazivač na samom fajlu pisca.

Hajde da dodamo OpenDialog komponentu na Form, nekoliko deklaracija varijabli našoj proceduri ListBox1Click projekta i liniju ili dve koda da bismo dobili naziv fajla.

procedure TForm1.ListBox1Click(Sender: TObject);
var
   CaptureFile: WideString;
   Multiplexer: IBaseFilter;
   Writer: IFileSinkFilter;
begin
   if OpenDialog1.Execute then
     CaptureFile:= OpenDialog1.FileName;

Sada možemo izgraditi Filter Graph koristeći FilterGraph1 kao ICaptureGraphBuilder2 na isti način kao i pre, ali možemo uključiti poziv za SetOutputFileName da kreira multiplekser i podesi pisca datoteke. Onda možemo povezati CAPTURE pin filtera video-izvora za multipleksera koji je stvorio SetoutputFilename poziv.

   with FilterGraph1 as ICaptureGraphBuilder2 do
   begin
     SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CaptureFile), Multiplexer, Writer);
     RenderStream(@PIN_CATEGORY_CAPTURE, nil, Filter1 as IBaseFilter,
                   nil, Multiplexer as IBaseFilter);

U redu, evo kompletne procedure za ListBox1Click. Dodatni kod potreban je za pisanje fajla koji je prikazan tamnoplavom bojom:

procedure TForm1.ListBox1Click(Sender: TObject);
var
   CaptureFile: WideString;
   Multiplexer: IBaseFilter;
   Writer: IFileSinkFilter;
begin
   if OpenDialog1.Execute then
     CaptureFile:= OpenDialog1.FileName;

   if FilterGraph1.Active then FilterGraph1.Active:= False;
   FilterGraph1.ClearGraph;

   if ListBox1.ItemIndex > -1 then
   begin
      Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);
      FilterGraph1.Active:= True;

      FilterGraph1.Mode:= gmCapture;
      with FilterGraph1 as ICaptureGraphBuilder2 do
      begin
        SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CaptureFile), Multiplexer, Writer);
        RenderStream(@PIN_CATEGORY_CAPTURE, nil, Filter1 as IBaseFilter,
                     nil, Multiplexer as IBaseFilter);

        RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter,
                     nil, VideoWindow1 as IBaseFilter);
      end;

      FilterGraph1.Play;
   end;
end;

Pokrenite projekat i unesite ime datoteke sa ekstenzijom .avi. Mi smo teško kodirali medijsku podvrstu kao MEDIASUBTYPE_Avi; "Moćniji" program bi, naravno, proverio ekstenziju fajla. Pokrenite GraphEdit i povežite ga na naš Filter Graph. Trebalo bi da vidite nešto slično ovome;

Zašto uopšte koristiti multiplekser? AVI znači Audio-Video-Interleaved pa je razumna pretpostavka za SetOutputFilename da će se filteru dodati zvuk.

Dodavanje zvuka AVI datoteci

U prethodnim odeljcima videli smo kako se pušta fajl i kako se uživo piše video AVI fajla. U ovom odeljku videćemo kako da dodate zvuk.

Dodavanje zvuka aplikaciji može biti iznenađujuće složeno, jer audio-ulazni uređaji obično imaju mnogo ulaznih linija - mikrofon, liniju, CD audio, talas itd - i aplikacija mora omogućiti korisniku da izabere između njih.

Da bismo počeli i dali sebi malo inspiracije, pomalo ću varati na ovom prvom projektu 'čvrsto kodiranje' ulaza.

Ovde je naš projekat u toku, a dodao sam mu drugi filter - nazvan Filter2

project5

Možda se sećate da smo gledali filter videa - BaseFilter i njegove osobine, pa smo rekli kako koristi direktan “pristup” video-izvoru. Hajde da izaberetmo novi (audio) filter - Filter2 i proverimo osobine BaseFilter. Kliknite na tri tačke (...) zajedno sa (TBaseFilter) da biste otvorili Base Filter Editor.

audio

Pogledajte listu filtera i otvorite stavku za audio-zapis izvora. Ako imate samo jednu zvučnu karticu u sistemu, biće naveden samo jedan izvor. Kliknite na njega da ga markirate. U donjem desnom uglu Base Filter Editor-a, videćete listu ulaza, pod nazivom "Pins". Nažalost, ne možemo da koristimo ovu listu da bismo izabrali svoj ulaz; za ovaj prvi audio-projekat, moramo biti sigurni da smo izaberali izvor koji želimo da koristimo u standardnom Windows panelu audio-miksera. Ali, izaberite audio Basefilter (ja sam izabrao "SoundMaxDigitalAudio” na gornjoj slici) i kliknite na 'OK' da biste zatvorili editor.

Sada samo treba da postavite FilterGraph audio-filtera na naš FilterGraph. Kao i obično, možemo to učiniti u Delphi Object Inspector-u ili u programskom kodu. Ja ću to uraditi u kodu. Dakle, ovde je relevantan deo našeg projekta sa dodatnim kodom za audio označen žutom bojom.

procedure TForm1.ListBox1Click(Sender: TObject);
var
   CaptureFile: WideString;
   Multiplexer: IBaseFilter;
   Writer: IFileSinkFilter;
begin
   if OpenDialog1.Execute then
     CaptureFile:= OpenDialog1.FileName;

   if FilterGraph1.Active then FilterGraph1.Active:= False;
   FilterGraph1.ClearGraph;

   if ListBox1.ItemIndex > -1 then
   begin
      Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);
      FilterGraph1.Active:= True;

      FilterGraph1.Mode:= gmCapture;
      Filter2.FilterGraph:= FilterGraph1;

     with FilterGraph1 as ICaptureGraphBuilder2 do
     begin
       SetOutputFileName(MEDIASUBTYPE_Avi, PWideChar(CaptureFile), Multiplexer, Writer);
       RenderStream(@PIN_CATEGORY_CAPTURE, nil, Filter1 as IBaseFilter,
                     nil, Multiplexer as IBaseFilter);

       RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter,
                     nil, VideoWindow1 as IBaseFilter);

       RenderStream(nil, nil, Filter2 as IBaseFilter,  
                   nil, Multiplexer as IBaseFilter);  
     end;
   end;

   FilterGraph1.Play;
end;

Obratite pažnju na to da kako novi RenderStream ne navodi Pin kategoriju. Vrednost nil ovde znači "koristite bilo koji pin koji vam je na raspolaganju". Primetite, takođe, da je ulazni filter - Filter2 (naš audio-filter), kao i obično, ne postoji srednji filter, a odredišni filter je Multiplexer.

Pokrenite projekat i dajte ekstenziju AVI imenu fajla AVI. Otvorite GraphEdit i povežite ga na naš filter graph. Trebalo bi da vidite nešto slično ovome. Iako je Intelligent Connect sam dodao filter ili dva, opazićete da osnovna struktura - posebno u pogledu filtera CAPTURE, PREVIEW i Renderer - prati naš Delphi kod:

audio

U sledećem odeljku videćemo kako da nabrojite audio-uređaje kako biste omogućili korisniku da izabere jedan umesto teško-kodiranih, kao što smo ovde radili.

Uređaji za audio-unos

U prethodnom odeljku, dodali smo zvuk našem AVI piscu fajla, ali “varali smo” navodeći da je audio uređaj u vreme dizajniranja u osnovnom filteru filtera2. Ovde ćemo nabrojati ulazne uređaje u kodu i prikazati ih u Listbox-u, tako da korisnici mogu izabrati uređaj.

Evo obrasca koji smo koristili za naš projekat. Promenio sam veličinu ListBox1 da nam omogući malo više prostora i dodao drugu listu koju ćemo popuniti sistemom ulaznih audio-uređaja.

audio1

Nabrojaćemo ulazne audio-uređaje u formularu FormCreate event na isti način kao što smo uradili sa video-uređajima:

var
  Form1: TForm1;
  SysDev: TSysDevEnum;
  AudioDevEnum: TSysDevEnum; 

implementation

{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
    i: Integer;
begin
   
// Video SysDev enumeration here
// Create an Audio Input Device Enumerator
    AudioDevEnum:= TSysDevEnum.Create(CLSID_AudioInputDeviceCategory);
    if AudioDevEnum.CountFilters > 0 then
    begin
      for i := 0 to AudioDevEnum.CountFilters - 1 do
      begin
        ListBox2.Items.Add(AudioDevEnum.Filters[i].FriendlyName);
      end;
    end;
end;

Dakle, sada imamo ListBox2 popunjen poznatim imenima ulaznih audio-uređaja. Moj sistem ima samo jedan - audio na matičnoj ploči - zove se 'SoundMaxDigitalAudio'.

Hajde da pogledamo neke od osobina i funkcija AudioDevEnum strukture pomoću Delphi “uvida u kod”.

Već smo koristili opciju CountFilters da bismo odredili broj ulaznih uređaja za naš for...do loop i koristili smo opcije filtera da bismo ponovo prikazali FriendlyName za svaki filter u ListBox-u.

Podsetimo se da je Filter2 audio filter u našem projektu i da sad treba dodeliti naš Filter2.BaseFilter.Moniker tako što ćemo odrediti Moniker za naš izabrani ulazni uređaj.

Klikom na FriendyName u Listbox-u odredili smo indeks uređaja za unos u AudioDevEnum strukturi. Možemo koristiti GetMoniker(index: Integer) funkcije kako bismo preuzeli Moniker, gde je index isti kao izabrani indeks uređaja FriendlyName u ListBox2-u.

Očigledno mesto da se to uradi je ListBox2Click event.

procedure TForm1.ListBox2Click(Sender: TObject);
begin
    if ListBox2.ItemIndex = -1 then Exit;
    Filter2.BaseFilter.Moniker:= AudioDevEnum.GetMoniker(ListBox2.ItemIndex);

end;

U redu, nabrojali smo ulazne audio-uređaje, izlistali ih u LlistBox-u i postavili svoj Filter2.BaseFilter.Moniker na izabrani uređaj. Do sada, ovo je veoma slično načinu postavljanja video filtera (Filter1). Ali, odavde, zvuk postaje malo komplikovaniji.

Pogledajte još jednom na ulaz audio-filtera Filter Grapha u našem prethodnom projektu:

Naša aplikacija treba da navede različite opcije za ulaz (CD plejer, mikrofon, Aux, itd) i da omogući korisniku izbor jedne od njih. U sledećem delu pozabavićemo se time.

TPinList

Do sada smo, u svom projektu, koristili System Device Enumerator da bismo nabrojali filtere za video-ulaz i za audio-ulaz. Kao što smo videli na kraju poslednjeg dela, sada je potrebno nabrojati sve ulazne pinove audio-filtera. Za to koristimo PinList koji DSPack dokumentacija opisuje kao “pomoćnika koji nabraja pinove na filteru", Da bih video kako funkcioniše, evo ponovo naše Forme. Dodao sam ComboBox kome možemo dodeliti imena ulaznih pinova audio-filtera.

PinList

 

Dakle, moramo da dodamo nekoliko promenljivih u ListBox2Click event i da kreiramo svoju PinList. Stavio sam PinList van procedure da bih joj proširio opseg jer ćemo je koristiti u mnogim procedurama.

var
  Form1: TForm1;
  SysDev: TSysDevEnum;
  AudioDevEnum: TSysDevEnum;
  PinList: TPinList 

Ne zaboravite da oslobodite PinList-u u obrascu OnCloseQuery event.

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  FilterGraph1.Active:= False;
  FilterGraph1.ClearGraph;
  PinList.Free; 
end;

Kreiramo PinList-u u ListBox2Click event-u ali, pre nego što je napravimo, moramo aktivirati FilterGraph:

procedure TForm1.ListBox2Click(Sender: TObject);
var
   i: Integer;
   aBool: LongBool;

begin
   if ListBox2.ItemIndex = -1 then Exit;
   Filter2.BaseFilter.Moniker:= AudioDevEnum.GetMoniker(ListBox2.ItemIndex);
   Filter2.FilterGraph:= FilterGraph1;

   FilterGraph1.Active:= True;
   PinList:= TPinList.Create(Filter2 as IBaseFilter);

Pored ulaznih pinova, filter takođe ima i izlazni pin koji će, naravno, biti uključeni u PinList. Pošto ne želimo da pin bude uključen u spisak koji ćemo prikazati korisnicima našeg projekta, treba da ga lociramo na listi, a zatim da ga obrišemo.

(Zapravo, ima mnogo izlaznih pinova koji odgovaraju audio-formatima za koje je Filter sposoban - o tome ćemo detaljnije pričati na sledećoj stranici, ali, za sada, samo treba da ih izbrišete.):

   i:= 0;
   while i < PinList.Count do
   begin
     if PinList.PinInfo[i].dir = PINDIR_OUTPUT then
       PinList.Delete(i)
     else
       inc(i);
   end;

U gornjem kodu, koristili smo PinList.PinInfo[index].dir da bismo potražili izlazni pin (PINDIR_OUTPUT). Ako koristimo Delphi code insight, možemo proveriti i druge mogućnosti. (Ovde možete naći referencu za MSDN biblioteku). U ovom trenutku zainteresovani smo i za achName (audio channel Name) koji je, jednostavno, ime pina (CD plejer, mikrofon, itd).

U redu, sada smo sredili PinList-u (tj. obrisali smo izlazni pin sa liste) i možemo kopirati stavke u ComboBox1 na svom obrascu:

   for i:= 0 to PinList.Count - 1 do
   begin
     ComboBox1.Items.Add(PinList.PinInfo[i].achName);
   end;

Da biste dodali završni detalj dok petljamo kroz PinList da bismo popunili ComboBox, možemo da proverimo Windows audio mixer panel da vidimo koji je ulaz trenutno odabran i da bismo na njega podesili ItemIndex. Naravno, korisnik sad može odabrati drugi achName pina iz ComboBox-a i promeniti ItemIndex ali ComboBox će, bar u početku, pokazati trenutni ulaz.

   for i:= 0 to PinList.Count - 1 do
   begin
     ComboBox1.Items.Add(PinList.PinInfo[i].achName);
     with (PinList.Items[i] as IAMAudioInputMixer) do get_Enable(aBool);
     if aBool then ComboBox1.ItemIndex:= i;
   end;

Imajte na umu da smo PinList postavili kao IAMAudioInputMixer. Ovo je "pokazivač" Windows audio miksera i mi smo u mogućnosti da utvrdimo da li je ta stavka sa PinList-e trenutno izabrana u mikseru sa funkcijom get_Enable(aBool: LongBool) koji vraća True (u aBool) ako je stavka odabrana. Onda možemo postaviti Combobox1.ItemIndex na tu stavku.

Dole je navedena celokupna ListBox2Click procedura

procedure TForm1.ListBox2Click(Sender: TObject);
var
   PinList: TPinList;
   i: Integer;
   aBool: LongBool;

begin
   if ListBox2.ItemIndex = -1 then Exit;
   Filter2.BaseFilter.Moniker:= AudioDevEnum.GetMoniker(ListBox2.ItemIndex);
   Filter2.FilterGraph:= FilterGraph1;

   FilterGraph1.Active:= True;
   PinList:= TPinList.Create(Filter2 as IBaseFilter);

   i:= 0;
   while i < PinList.Count do
   begin
     if PinList.PinInfo[i].dir = PINDIR_OUTPUT then
       PinList.Delete(i)
     else
       inc(i);
   end;

   for i:= 0 to PinList.Count - 1 do
   begin
     ComboBox1.Items.Add(PinList.PinInfo[i].achName);
     with (PinList.Items[i] as IAMAudioInputMixer) do get_Enable(aBool);
     if aBool then ComboBox1.ItemIndex:= i;
   end;
end;

Skoro smo uspeli! Samo treba da dodelimo novo ime PinList achName (pod pretpostavkom da je korisnik izabrao drugačiji ulaz) Windows audio-mikseru za unos. Najbolje mesto da se to uradi je u ListBox1Click event-u neposredno pre nego što izgradimo Filter Graph.

procedure TForm1.ListBox1Click(Sender: TObject);

   :

   if ListBox1.ItemIndex > -1 then
   begin
     Filter1.BaseFilter.Moniker:= SysDev.GetMoniker(ListBox1.ItemIndex);
     FilterGraph1.Active:= True;

      if ComboBox1.ItemIndex = -1 then
      begin 
        ShowMessage('No audio device selected');
        FilterGraph1.Active:= False;  
        Exit;  
      end  
      else  
        with (PinList.Items[ComboBox1.ItemIndex] as IAMAudioInputMixer) do
         put_Enable(True);  

     FilterGraph1.Mode:= gmCapture;
     Filter2.FilterGraph:= FilterGraph1;

     with FilterGraph1 as ICaptureGraphBuilder2 do
     begin
   
   :

Dakle, imamo ga. Jedan aspekt audio ulaza koji nismo dotakli je uzimanje uzoraka i pitanje da li koristimo jedan ili dva kanala. Pogledaćemo to u sledećem delu, a ako želite da imate bolji uvid u IAMAudioInputMixer, naći ćete ga ovde na MSDN-u.

TAudioMediaType

Većina medijskih aplikacija negde ima mogućnost konfigurisanja za korisnika da izabere ulazne audio-uzorke itd. DirectShow pruža IEnumMediaTypes interfejs kome se može pristupiti kroz Delphi uz TEnumMediaType.

Naš "testni" projekat postaje pomalo nezgrapan, ali sada ćemo videti da li možemo da dodamo i ovaj objekat na njega. Proširio sam ga i dodao treći ListBox - ListBox3

Počinjemo od proglašenja našeg TEnumMediaType za globalnu promenljivu:

var
  Form1: TForm1;
  SysDev: TSysDevEnum;
  AudioDevEnum: TSysDevEnum;
  PinList: TPinList
  AudioMediaTypes: TEnumMediaType;  

Možemo stvoriti AudioMediaTypes u ListBox2Click event-u i dodeliti mu stavke iz tih predmeta u PinList koji su navedeni kao PINDIR_OUTPUT. Onda jednostavno popunimo Listbox AudioMediaTypes stavkama i podesimo podrazumevani ListBox3.ItemIndex

   :
   :
   AudioMediaTypes:= TEnumMediaType.Create;
   i:= 0;
   while i < PinList.Count do
   begin
     if PinList.PinInfo[i].dir = PINDIR_OUTPUT then
     begin
       AudioMediaTypes.Assign(PinList.Items[i]);
       PinList.Delete(i);
     end
     else
       inc(i);
   end;

   for i:= 0 to AudioMediaTypes.Count - 1 do
     ListBox3.Items.Add(AudioMediaTypes.MediaDescription[i]);

   ListBox3.ItemIndex:= 0;

   :

Kad pokrenemo svoj projekat, ListBox3 treba da izlista sve audio-formate.

Završni deo slagalice je konfigurisanje audio-strima sa izabranim formatom. Možemo da uradimo to u ListBox1Click event-u pre nego što počnemo da gradimo Filter Graph, ali moramo da konfigurišemo sve izlaze u PinList-i sa medijskim formatom koji smo odabrali sa ListBox3. Setićete se da smo izbrisali izlaze sa Pin Liste.

Dakle, moramo da oslobodimo PinListu i da napravimo novu. Tada možemo podesiti format svakog izlaza prema formatu koji je izabran u ListBox3

   :
   :
   PinList.Free;
   PinList:= TPinList.Create(Filter2 as IBaseFilter);

   for i:= 0 to PinList.Count - 1 do
   begin
     if PinList.PinInfo[i].dir = PINDIR_OUTPUT then
         with (PinList.Items[i] as IAMStreamConfig) do
            SetFormat(AudioMediaTypes.Items[ListBox3.ItemIndex].AMMediaType^);
   end;

   FilterGraph1.Mode:= gmCapture;
   Filter2.FilterGraph:= FilterGraph1;

   with FilterGraph1 as ICaptureGraphBuilder2 do
   begin
   :
   :

Najzad, ne zaboravite da kliknete na Free PinList and AudioMediaTypes u opciji Form OnCloseQuery event.

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  FilterGraph1.Active:= False;
  FilterGraph1.ClearGraph;
  PinList.Free;
  AudioMediaTypes.Free;
end;

Published (Last edited): 20-12-2012 , source: http://vwlowen.co.uk/directshow/page01.htm