|
Введение в
Direct Show.
Приветствую всех, кто обратил внимание на эту статью. С помощью неё Вы
сможете без особого труда проигрывать музыку и видео (в форматах wma,
avi, wav, mp3, mpeg). Результатом данного туториала будет класс,
который существенно упростит Вам жизнь.
Основные
понятия Direct Show и организация класса.
к началу статьи
Работая с Direct Show Вы будете иметь дело с интерфейсами. Каждый
интерфейс выполняет свои функции, один играет звук, второй позволяет
управлять проигрыванием, остановкой, выставлением паузы в проигрываемом
файле, третий позволяет Вам устанавливать реакцию на события окна (в
литературе множества организованных таким образом функций называются
гранями). Это позволяет упростить работу программистам, т.к. они, при
таком раскладе, будут работать с достаточно ограниченным набором
функций, сгруппированных “по темам”.
Для начала определимся с тем какие интерфейсы нам понадобятся:
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>
//сам класс class cMediaPlayer{ 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
|
Подключаем библиотеку,
в которой
находятся необходимые идентификаторы классов и интерфейсов. |
#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 ); //создаем граф фильтров с помощью которого будем проигрывать наш файл 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. Делается это так:
CoInitialize( NULL );
И соответственно при выходе:
CoUninitialize();
ЗЫЫ: Способ связи прежний: dodonov_a_a ___AT___ inbox.ru
Смежные вопросы:
Урок 1.
Инициализация.
Вершинные буфферы и камера.
исходные коды |
|
|