Programowanie w języku C/C++ » Artykuły

Aplikacja multimedialna Windows - DirectShow

Dobrze wyposażyć swój program w możliwość odtwarzania popularnych plików multimedialnych. Nie raz zachodzi potrzeba odtworzyć w aplikacji dłuższy plik dźwiękowy, o ile odtworzyć muzykę w nieskompresowanym pliku WAVE, nie jest za specjalnie trudno, o tyle z popularnymi formatami skompresowanymi, takimi jak MP3, OGG, itd. już nie jest tak łatwo. Waga plików WAVE robi swoje, więc skompresowany dźwięk jest dużo lepszym rozwiązaniem.
Ale czy odtwarzanie plików mp3 musi być skomplikowane? Okazuje się, że wcale nie. W Windowsie z pomocą przychodzi nam pakiet o nazwie DirectShow. Jego ogólnym przeznaczeniem jest właśnie odtwarzanie przeróżnych plików multimedialnych, od plików WAVE, MIDI przez pliki audio skompresowane takie jak mp3, po pliki video np. w formacie AVI.
DirectShow z punktu widzenia binarnego wchodzi(tak mi się wydaje) w skład pakietu DirectX, co skutkuje, tym, że aby go używać, użytkownik musi mieć w systemie zainstalowaną odpowiednią wersję DirectX. Natomiast z punktu widzenia programistów DirectShow wchodzi w skład Windows SDK, czyli zwykłych plików WindowsAPI, w związku z tym nie trzeba instalować oddzielnie DirectX SDK, aby móc kompilować aplikacje z użyciem DirectShow.
Plik nagłówkowy DirectShow to "DShow.h", natomiast biblioteka statyczna, którą trzeba dolinkować to "Strmiids.lib"("libstrmiids.a" dla kompilatora gcc).

Budowa DirectShow


Programowo DirectShow(jak i pozostałe pakiety DirectX) składa się z obiektów COM. Architektura DShow składa się z tzw. "filtrów"(chunks), które są połączone obiektami o nazwie "pins"(zapomniałem nazwy polskiego odpowiednika xd). Każdy filtr jest za coś odpowiedzialny, odtwarzanie pliku multimedialnego polega na przejściu danych multimedialnych i ich przetworzeniu przez filtry. Filtry są tworzone w zależności od typu odtwarzanego zasobu multimedialnego.

Na obrazku(żywcem z msdn) mamy przykładowe filtry, domyślnie tworzone przy odtwarzaniu pliku AVI. Każdy „prostokąt” symbolizuje filtr, pierwszy jest odpowiedzialny za pobieranie danych z dysku(DShow umożliwia także pobieranie danych, z innych źródeł: internetu, pamięci). Następnie dane z filtru pobierającego wędrują do drugiego filtru SplitterAVI, rozdziela on strumień AVI na audio i video, które wędrują już do osobnych filtrów. Audio trafia do filtru wyjściowego, domyślnie DirectShow korzysta z filtru wyjścia DirectSound, można także stworzyć filtr z wyjściem starego API dźwiękowego(funkcje z rodziny waveOut). Strumień video wędruje do filtra dekompresującego obraz video, który trafia do filtra wyjścia video(filtra renderującego). DirectShow posiada filtry do rysowania obrazu technikami DirectDraw, Direct3D, oraz alternatywnie, gdy nie ma dostępu do poprzednich filtrów rysujących, przez GDI(ciekawe jakie byłoby odświeżanie :D). Oczywiście przy odtwarzaniu powiedzmy pliku MP3 schemat używanych filtrów będzie zupełnie inny. Filtry możemy tworzyć ręcznie, ale robotę może także za nas wykonać DirectShow, tworząc domyślny schemat filtrów dla konkretnego formatu.

GraphEdit


Jest to program do wizualnej prezentacji filtrów używanych przez DShow. Jest dodatkiem do Visual Studio, ale można go znaleźć i pobrać oddzielnie. Otwórzmy program. Po uruchomieniu go należy kliknąć: File -> Render Media File… wybieramy jakiś plik multimedialny, po czym pokaże nam się domyślny schemat filtrów używanych przy odtwarzaniu tego pliku. Można zmieniać i przekształcać dokładając/usuwając filtry. Przykładowy schemat obsługi plików w formach rmvb i poniżej mp3.


Kodzenie


To niewielkiej(zważywszy na rozmiar zagadnienia) porcji informacji, możemy zająć się pisaniem aplikacji odtwarzającej pliki multimedialne. Na początek stwórzmy interfejs IGraphBuilder. Dziedziczy on z interfejsu IFilterGraph. Interfejs IFilterGraph reprezentuje schemat filtrów, za jego pośrednictwem możemy dodawać i edytować ręcznie filtry. IGraphBuilder jako następca IFilterGraph jest wyżej poziomowy i służy do „automatycznych budowań” domyślnych filtrów, temu interfejsowi wystarczy jedynie ścieżka do pliku, całą niskopoziomową brudną robotę z filtrami odwala za nas ;) Kolejnym potrzebnym interfejsem będzie IMediaControl. Ten interfejs służy do kontrolowania stanu filtrów, metoda Run odtwarza(renderuje) filtry, metoda Stop() i Pause() są wiadomo od czego ;)

#define _CRT_SECURE_NO_WARNINGS//dla Visual C++
#include <iostream>
#include <windows.h>
#include <dshow.h>
 
using namespace std;
 
//Interfejsy
IGraphBuilder *pGraph=NULL;//buduje filtry
IMediaControl *pControl=NULL;//kontroluje odtwarzanie
 
void Close(bool error,bool ComInited)
{
  if(pControl) pControl->Release();
  if(pGraph) pGraph->Release();
  if(ComInited==true) CoUninitialize();//deinicjacja COM
  if(error==true)
  {
    cout<<"Wystapil blad...\n";
    system("PAUSE");
  }
}
 
int main(int,char**)
{
  char Path[MAX_PATH];//ścieżka do pliku
  cout<<"Podaj sciezke do pliku multimedialnego: ";
  cin>>Path;
  HRESULT hr;//zwracane wartości
  //inicjalizacja COM w apliakcji
  hr=CoInitialize(NULL);
  if(FAILED(hr))
  {
    Close(true,false);
    return 0;
  }
  //tworzymy obiekt IGraphBuilder(tworznie obiektu COM o identyfikatorze IID_IGraphBuilder)
  hr=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void**)&pGraph);
  if(FAILED(hr))
  {
    Close(true,true);
    return 0;
  }
  //tworzymy interfejs IMediaControl
  hr=pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
  if(FAILED(hr))
  {
    Close(true,true);
    return 0;
  }
  //tworzymy filtry dla podanego pliku metodą RenderFile
  wchar_t WPath[MAX_PATH];//unikodowa wersja ścieżki(metoda przyjmuje tylko tekst w unicode)
  //w ścieżce nie może być spacji, bo cin chyba domyślnie wstawia znaki NULL w miejsce spacji
  MultiByteToWideChar(CP_OEMCP,0,Path,MAX_PATH,WPath,MAX_PATH);//konwersja ANSI => Unicode
  hr=pGraph->RenderFile(WPath,NULL);//stworzenie filtrów dla określonego pliku
  if(FAILED(hr))
  {
    cout<<"Nie udalo sie otworzyc pliku, w nazwie nie moze byc spacji.\n";
    Close(true,true);
    return 0;
  }
  pControl->Run();//rozpoczynamy odtwarzanie multimediów
  if(FAILED(hr))
  {
    Close(true,true);
    return 0;
  }
  system("PAUSE");
  pControl->Stop();
  Close(false,true);//sprzątamy
  return 0;
}

No i tym sposobem mamy konsolowy odtwarzacz multimediów, od muzyki po filmy ;)
Są to dopiero podstawy podstaw, po pełna wiedzę odsyłam do msdn: http://msdn.microsoft.com/en-us/library/ms783323%28VS.85%29.aspx