Категория: DirectX 8
Просмотров: 6119

    Приветствую всех, кто обратил внимание на эту статью. С помощью неё Вы сможете без особого труда проигрывать музыку и видео (в форматах wma, avi, wav, mp3, mpeg). Результатом данного туториала будет класс (надстройка над DirectShow), который существенно упростит Вам жизнь.

    Работая с DirectShow Вы будете иметь дело с интерфейсами. Каждый интерфейс выполняет свои функции, один играет звук, второй позволяет управлять проигрыванием, остановкой, выставлением паузы в проигрываемом файле, третий позволяет Вам устанавливать реакцию на события окна (в литературе множества организованных таким образом функций называются гранями). Это позволяет упростить работу программистам, т.к. они, при таком раскладе, будут работать с достаточно ограниченным набором функций, сгруппированных “по темам”.

    Для начала определимся с тем какие интерфейсы DirectShow нам понадобятся:

    IgraphBuilder – при загрузке файла данный интерфейс выстраивает граф фильтров, с помощью которого данный из файла будут преобразованы к удобному для вывода формату.

    MediaControl – Позволяет управлять проигрыванием файла (останавливать, запускать, приостанавливать вывод файла)

    MediaEventEx – С помощью данного интерфейса устанавливается реакция на события проигрываемого файла (точнее потока в котором проигрывается файл – далее будем просто говорить “поток”).

    IVideoWindow – управляет параметрами вывода (размеры и положение окна вывода).

    IMediaSeeking – осуществляет поиск по медиа файлу.

    IBasicAudio – управляет выводом звука.

    Теперь определим файл:

//необходимые хидеры
#include <dshow.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>
#include <string.h>

//сам класс-надстройка над DirectShow
class		cMediaPlayer{
	//интерфейсы DirectShow
	IGraphBuilder		*GraphBuilder;
    	MediaControl		*MediaControl;
    	MediaEventEx		*MediaEvent;
	IVideoWindow		*VideoWindow;
	IMediaSeeking		*MediaSeeking;
	IBasicAudio		*BasicAudio;

	//зацикленный ли файл?
	bool				Looped;
public:
	//конструктор
	cMediaPlayer( void );
	//деструктор
	~cMediaPlayer();
	//загрузить файл
	void				LoadFile( HWND , char* );
	//начать проигрывание файла
	void				PlayFile( void );
	//остановить файл
	void				StopFile( void );
	//поменять громкость
	void				SetVolume( long );
	//направит вывод в какое-либо окно
	void				AttachToWindow( HWND );
	//перейти в полноэкранный режим
	void				ToggleFullscreen( HWND );
	//определяем окно, которое будет получать сообщения от потока
	void				SetNotifyWindow( HWND , long , long );
	//функция обработки событий
	void				HandleEvent( void );
};

    Это будет сообщение, сообщающее нашему приложению, что в потоке имело место какое-то событие

#define		WM_DSNOTIFICATION		WM_USER + 1

    Подключаем библиотеку, в которой находятся необходимые идентификаторы классов и интерфейсов DirectShow.

#pragma comment  ( lib , "strmiids.lib" )

    Конструктор. Просто занулливаем все что можно.

cMediaPlayer::cMediaPlayer( void ){
	Looped = true;
	GraphBuilder = NULL;
    	MediaControl = NULL;
    	MediaEvent = NULL;
	VideoWindow = NULL;
	MediaSeeking = NULL;
	BasicAudio = NULL;
}

    Теперь собственно загрузка:

void				cMediaPlayer::LoadFile( HWND hWnd , char *path ){
//подключаем функции для работы с WCHAR
	USES_CONVERSION;
//конвертим строку в нужный формат
	WCHAR wpath[ 1000 ];

	wcscpy( wpath , T2W( path ) );

	//создаем интерфейсы
	CoCreateInstance( CLSID_FilterGraph , NULL , CLSCTX_INPROC_SERVER , 
                        IID_IGraphBuilder , ( void ** )&GraphBuilder );
   	GraphBuilder->QueryInterface( IID_IMediaControl , ( void ** )&MediaControl );
    	GraphBuilder->QueryInterface( IID_IMediaEventEx , ( void ** )&MediaEvent );
	GraphBuilder->QueryInterface( IID_IVideoWindow , ( void ** )&VideoWindow );
	GraphBuilder->QueryInterface( IID_IMediaSeeking , ( void ** )&MediaSeeking );
	GraphBuilder->QueryInterface( IID_IBasicAudio , ( void ** )&BasicAudio );
	//создаем граф фильтров DirectShow с помощью которого будем проигрывать наш файл
    	GraphBuilder-> RenderFile( wpath , NULL );
	//указываем окно, в которое будет направляться вывод
	VideoWindow->put_Owner( ( OAHWND ) hWnd );
}

//определяем параметры вывода
void				cMediaPlayer::AttachToWindow( HWND hWnd ){
	RECT			rect;
	//размеры вьюпорта
	GetClientRect( hWnd , &rect );
	//устанавливаем прямоугольник для вывода
	VideoWindow->SetWindowPosition( rect.top , rect.left , rect.right , rect.bottom );
	//этот метод помещает видео окно повер z-буффера
	VideoWindow->SetWindowForeground( OATRUE );
	//активизируем и показываем окно
	ShowWindow( hWnd , SW_NORMAL );
	//обновляем окно
	UpdateWindow( hWnd );
	//по-новой указываем окно, в которое будет направляться вывод
	VideoWindow->put_Owner( ( OAHWND ) hWnd );
	//устанавливаем фокус
	SetFocus( hWnd );
}

//установка громкости
void				cMediaPlayer::SetVolume( long volume ){
	BasicAudio->put_Volume( volume );
}

//остановить файл и перемотать на начало
void				cMediaPlayer::StopFile( void ){
	LONGLONG pos = 0;
	//останавливает все фильтры в графе
	MediaControl->Stop();
	//поиск начала
	MediaSeeking->SetPositions( &pos , AM_SEEKING_AbsolutePositioning , 
		NULL , AM_SEEKING_NoPositioning );
	//ставим на паузу
	MediaControl->Pause();
}

    Освобождаем ресурсы

cMediaPlayer::~cMediaPlayer(){
	VideoWindow->Release();
	MediaControl->Release();
	MediaEvent->Release();
	MediaSeeking->Release();
	GraphBuilder->Release();
}

    Надеюсь следующая функция в комментариях не нуждается?

void				cMediaPlayer::PlayFile( void ){MediaControl->Run();}

    Все функции для манипуляции выводом у нас есть, теперь надо собственно знать, когда их использовать. Когда, собственно, наш поток дает о себе знать?

    На самом деле все просто: нашему приложению посылается сообщение о том что поток нам хочет что-то сообщить. Как только мы получили такое сообщение, просто вызываем обработчик сообщений.

    С помощью следующей функции мы сообщаем потоку, на какое сообщение наша программа будет реагировать:

void			cMediaPlayer::SetNotifyWindow( HWND hWnd , long message , long lParam ){
	if( VideoWindow ){
		MediaEvent->SetNotifyWindow( ( OAHWND )hWnd , message , 0 );
	}
}

    А с помощью данной функции сообщаем как мы будем реагировать:

void			cMediaPlayer::HandleEvent( void ){
	if( MediaEvent ){
		LONG evCode, evParam1, evParam2;
		while( SUCCEEDED( MediaEvent->GetEvent( &evCode, 
				( LONG_PTR * ) &evParam1 , ( LONG_PTR *) &evParam2 , 0 ) ) ){
			MediaEvent->FreeEventParams( evCode , evParam1 , evParam2 );

			if( EC_COMPLETE == evCode ){
	            		LONGLONG pos=0;

				MediaSeeking->SetPositions( &pos , AM_SEEKING_AbsolutePositioning ,
                                   		NULL , AM_SEEKING_NoPositioning );
				//зацикленный ли файл?
				if( Looped )PlayFile();
			}
		}
	}
}

    Вот на первое время и все. Для того что бы сделать простенький проигрыватель и интерактивную менюшку (в которой кнопочки будут приятно щелкать при нажатии) этого должно хватить. Но возможности Direct Show этим далеко не ограничены. Возможно я когда-либо расскажу Вам как делать менее тривиальный вещи, а пока – пока!

    ЗЫ: совсем забыл – чтобы весь наш агрегат заработал нам надо инициализировать библиотеку для работы с COM (DirectShow использует COM как и другие компоненты DirectX'а). Делается это так:

CoInitialize( NULL );

    И соответственно при выходе:

CoUninitialize();

    Вот на сегодня и все, некоторые скажут: "тема DirectShow не раскрыта" )), и будут отчести правы, ибо осталось ещё много интересных вещей, таких как фильтры, проигрывание dvd, анимированные текстуры (да, да, их можно сделать и с помощью DirectShow тоже). Однако не планировалось что данная статья охватит всю библиотеку, а станет первым этапом в Вашем знакомстве с DirectShow. Посему разрешите откланяться.

    ЗЫЫ: Способ связи прежний: Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.

    исходные коды