Мультимедийный плеер на Ассемблере
Мультимедийный плеер на Ассемблере
Сегодня будем программировать нечто действительно интересное, а именно универсальный мультимедийный плеер. Универсальность реализуется мощью функционала DirectShow, а также установленными в системе аудио- и видеокодеками. И пусть это не полноценное приложение, большинство основных функций плеера в нем будут реализованы.
Сперва, как обычно, нам понадобится здоровенная пачка различных GUID'ов, интерфейсов и констант. Значение CLSID_FilterGraph почему-то даже не описано в SDK, пришлось покопаться в интернетах.
Code (Assembler) : Убрать нумерацию
- ; GUID {E436EBB3-524F-11CE-9F53-0020AF0BA770}
- CLSID_FilterGraph \
- dd 0E436EBB3h
- dw 0524Fh
- dw 011CEh
- db 09Fh, 053h, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; GUID {56A868A9-0AD4-11CE-B03A-0020AF0BA770}
- IID_IGraphBuilder \
- dd 056A868A9h
- dw 00AD4h
- dw 011CEh
- db 0B0h, 03Ah, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; IID_IGraphBuilder Interface
- struct IGraphBuilder
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IGraphBuilder
- AddFilter dd ? ; 00Ch
- RemoveFilter dd ? ; 010h
- EnumFilters dd ? ; 014h
- FindFilterByName dd ? ; 018h
- ConnectDirect dd ? ; 01Ch
- Reconnect dd ? ; 020h
- Disconnect dd ? ; 024h
- SetDefaultSyncSource dd ? ; 028h
- Connect dd ? ; 02Ch
- Render dd ? ; 030h
- RenderFile dd ? ; 034h
- AddSourceFilter dd ? ; 038h
- SetLogFile dd ? ; 03Ch
- Abort dd ? ; 040h
- ShouldOperationContinue dd ? ; 044h
- ends
- ; GUID {56A868B1-0AD4-11CE-B03A-0020AF0BA770}
- IID_IMediaControl \
- dd 056A868B1h
- dw 00AD4h
- dw 011CEh
- db 0B0h, 03Ah, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; IID_IMediaControl Interface
- struct IMediaControl
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IMediaControl
- GetTypeInfoCount dd ? ; 00Ch
- GetTypeInfo dd ? ; 010h
- GetIDsOfNames dd ? ; 014h
- _Invoke dd ? ; 018h
- Run dd ? ; 01Ch
- Pause dd ? ; 020h
- Stop dd ? ; 024h
- GetState dd ? ; 028h
- RenderFile dd ? ; 02Ch
- AddSourceFilter dd ? ; 030h
- get_FilterCollection dd ? ; 034h
- get_RegFilterCollection dd ? ; 038h
- StopWhenReady dd ? ; 03Ch
- ends
- ; GUID {56A868B4-0AD4-11CE-B03A-0020AF0BA770}
- IID_IVideoWindow \
- dd 056A868B4h
- dw 00AD4h
- dw 011CEh
- db 0B0h, 03Ah, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; IID_IVideoWindow Interface
- struct IVideoWindow
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IVideoWindow
- GetTypeInfoCount dd ? ; 00Ch
- GetTypeInfo dd ? ; 010h
- GetIDsOfNames dd ? ; 014h
- _Invoke dd ? ; 018h
- put_Caption dd ? ; 01Ch
- get_Caption dd ? ; 020h
- put_WindowStyle dd ? ; 024h
- get_WindowStyle dd ? ; 028h
- put_WindowStyleEx dd ? ; 02Ch
- get_WindowStyleEx dd ? ; 030h
- put_AutoShow dd ? ; 034h
- get_AutoShow dd ? ; 038h
- put_WindowState dd ? ; 03Ch
- get_WindowState dd ? ; 040h
- put_BackgroundPalette dd ? ; 044h
- get_BackgroundPalette dd ? ; 048h
- put_Visible dd ? ; 04Ch
- get_Visible dd ? ; 050h
- put_Left dd ? ; 054h
- get_Left dd ? ; 058h
- put_Width dd ? ; 05Ch
- get_Width dd ? ; 060h
- put_Top dd ? ; 064h
- get_Top dd ? ; 068h
- put_Height dd ? ; 06Ch
- get_Height dd ? ; 070h
- put_Owner dd ? ; 074h
- get_Owner dd ? ; 078h
- put_MessageDrain dd ? ; 07Ch
- get_MessageDrain dd ? ; 080h
- get_BorderColor dd ? ; 084h
- put_BorderColor dd ? ; 088h
- get_FullScreenMode dd ? ; 08Ch
- put_FullScreenMode dd ? ; 090h
- SetWindowForeground dd ? ; 094h
- NotifyOwnerMessage dd ? ; 098h
- SetWindowPosition dd ? ; 09Ch
- GetWindowPosition dd ? ; 0A0h
- GetMinIdealImageSize dd ? ; 0A4h
- GetMaxIdealImageSize dd ? ; 0A8h
- GetRestorePosition dd ? ; 0ACh
- HideCursor dd ? ; 0B0h
- IsCursorHidden dd ? ; 0B4h
- ends
- ; GUID {36B73880-C2C8-11CF-8B46-00805F6CEF60}
- IID_IMediaSeeking \
- dd 036B73880h
- dw 0C2C8h
- dw 011CFh
- db 08Bh, 046h, 000h, 080h, 05Fh, 06Ch, 0EFh, 060h
- ; IID_IMediaSeeking Interface
- struct IMediaSeeking
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IMediaSeeking
- GetCapabilities dd ? ; 00Ch
- CheckCapabilities dd ? ; 010h
- IsFormatSupported dd ? ; 014h
- QueryPreferredFormat dd ? ; 018h
- GetTimeFormat dd ? ; 01Ch
- IsUsingTimeFormat dd ? ; 020h
- SetTimeFormat dd ? ; 024h
- GetDuration dd ? ; 028h
- GetStopPosition dd ? ; 02Ch
- GetCurrentPosition dd ? ; 030h
- ConvertTimeFormat dd ? ; 034h
- SetPositions dd ? ; 038h
- GetPositions dd ? ; 03Ch
- GetAvailable dd ? ; 040h
- SetRate dd ? ; 044h
- GetRate dd ? ; 048h
- GetPreroll dd ? ; 04Ch
- ends
- ; GUID {56A868C0-0AD4-11CE-B03A-0020AF0BA770}
- IID_IMediaEventEx \
- dd 056A868C0h
- dw 00AD4h
- dw 011CEh
- db 0B0h, 03Ah, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; IID_IMediaEventEx Interface
- struct IMediaEventEx
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IMediaEventEx
- GetTypeInfoCount dd ? ; 00Ch
- GetTypeInfo dd ? ; 010h
- GetIDsOfNames dd ? ; 014h
- _Invoke dd ? ; 018h
- GetEventHandle dd ? ; 01Ch
- GetEvent dd ? ; 020h
- WaitForCompletion dd ? ; 024h
- CancelDefaultHandling dd ? ; 028h
- RestoreDefaultHandling dd ? ; 02Ch
- FreeEventParams dd ? ; 030h
- SetNotifyWindow dd ? ; 034h
- SetNotifyFlags dd ? ; 038h
- GetNotifyFlags dd ? ; 03Ch
- ends
- ; GUID {56A868B5-0AD4-11CE-B03A-0020AF0BA770}
- IID_IBasicVideo \
- dd 056A868B5h
- dw 00AD4h
- dw 011CEh
- db 0B0h, 03Ah, 000h, 020h, 0AFh, 00Bh, 0A7h, 070h
- ; IID_IBasicVideo Interface
- struct IBasicVideo
- ; IUnknown
- QueryInterface dd ? ; 000h
- AddRef dd ? ; 004h
- Release dd ? ; 008h
- ; IBasicVideo
- GetTypeInfoCount dd ? ; 00Ch
- GetTypeInfo dd ? ; 010h
- GetIDsOfNames dd ? ; 014h
- _Invoke dd ? ; 018h
- get_AvgTimePerFrame dd ? ; 01Ch
- get_BitRate dd ? ; 020h
- get_BitErrorRate dd ? ; 024h
- get_VideoWidth dd ? ; 028h
- get_VideoHeight dd ? ; 02Ch
- put_SourceLeft dd ? ; 030h
- get_SourceLeft dd ? ; 034h
- put_SourceWidth dd ? ; 038h
- get_SourceWidth dd ? ; 03Ch
- put_SourceTop dd ? ; 040h
- get_SourceTop dd ? ; 044h
- put_SourceHeight dd ? ; 048h
- get_SourceHeight dd ? ; 04Ch
- put_DestinationLeft dd ? ; 050h
- get_DestinationLeft dd ? ; 054h
- put_DestinationWidth dd ? ; 058h
- get_DestinationWidth dd ? ; 05Ch
- put_DestinationTop dd ? ; 060h
- get_DestinationTop dd ? ; 064h
- put_DestinationHeight dd ? ; 068h
- get_DestinationHeight dd ? ; 06Ch
- SetSourcePosition dd ? ; 070h
- GetSourcePosition dd ? ; 074h
- SetDefaultSourcePosition dd ? ; 078h
- SetDestinationPosition dd ? ; 07Ch
- GetDestinationPosition dd ? ; 080h
- SetDefaultDestinationPosition dd ? ; 084h
- GetVideoSize dd ? ; 088h
- GetVideoPaletteEntries dd ? ; 08Ch
- GetCurrentImage dd ? ; 090h
- IsUsingDefaultSource dd ? ; 094h
- IsUsingDefaultDestination dd ? ; 098h
- ends
- CLSCTX_INPROC_SERVER = 0x1
- WM_APP = 0x8000
- WM_GRAPHNOTIFY = WM_APP + 1
- EC_COMPLETE = 0x01
- AM_SEEKING_AbsolutePositioning = 0x1
- State_Stopped = 0
- State_Paused = 1
- State_Running = 2
Code (Assembler) : Убрать нумерацию
- ; Инициализация всех интерфейсов
- invoke CoCreateInstance,CLSID_FilterGraph,NULL,\
- CLSCTX_INPROC_SERVER,IID_IGraphBuilder,\
- pGraphBuilder
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.QueryInterface],[pGraphBuilder],\
- IID_IMediaControl,pMediaControl
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.QueryInterface],[pGraphBuilder],\
- IID_IVideoWindow,pVideoWindow
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.QueryInterface],[pGraphBuilder],\
- IID_IMediaSeeking,pMediaSeeking
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.QueryInterface],[pGraphBuilder],\
- IID_IMediaEventEx,pEvent
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.QueryInterface],[pGraphBuilder],\
- IID_IBasicVideo,pBasicVideo
Code (Assembler) : Убрать нумерацию
- ; Загрузить файл для воспроизведения
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.RenderFile],[pGraphBuilder],\
- path,NULL
Code (Assembler) : Убрать нумерацию
- ; Назначить окно для воспроизведения видео
- invoke GetDlgItem,[hwnddlg],ID_VIDEO
- mov ebx,eax
- mov eax, [pVideoWindow]
- mov eax, [eax]
- stdcall dword [eax+IVideoWindow.put_Owner],[pVideoWindow],\
- ebx
- mov eax, [pBasicVideo]
- mov eax, [eax]
- stdcall dword [eax+IBasicVideo.get_VideoWidth],[pBasicVideo],\
- pWidth
- mov eax, [pBasicVideo]
- mov eax, [eax]
- stdcall dword [eax+IBasicVideo.get_VideoHeight],[pBasicVideo],\
- pHeight
- cmp [pWidth],IMG_X
- jae loc_need_resize
- cmp [pHeight],IMG_Y
- jbe loc_create_video
- loc_need_resize:
- ; Пресчет размеров по горизонтали
- mov eax,[pWidth]
- cmp eax,[pHeight]
- jae @f
- mov eax,[pWidth]
- xor edx,edx
- mov ecx,IMG_Y
- imul ecx
- xor edx,edx
- mov ecx,[pHeight]
- idiv ecx
- mov [pWidth],eax
- mov [pHeight],IMG_Y
- jmp loc_create_video
- @@:
- mov eax,[pHeight]
- xor edx,edx
- mov ecx,IMG_X
- imul ecx
- xor edx,edx
- mov ecx,[pWidth]
- idiv ecx
- mov [pHeight],eax
- mov [pWidth],IMG_X
- loc_create_video:
- mov [deltaX],0
- mov [deltaY],0
- ; Смещение по X
- cmp [pWidth],IMG_X
- jae @f
- mov eax,IMG_X
- sub eax,[pWidth]
- shr eax,1
- mov [deltaX],eax
- @@:
- ; Смещение по Y
- cmp [pHeight],IMG_Y
- jae @f
- mov eax,IMG_Y
- sub eax,[pHeight]
- shr eax,1
- mov [deltaY],eax
- @@:
- ; Установить стиль окна
- mov eax, [pVideoWindow]
- mov eax, [eax]
- stdcall dword [eax+IVideoWindow.put_WindowStyle],[pVideoWindow],\
- WS_CHILD+WS_CLIPSIBLINGS
- ; Установить размер области воспроизведения
- mov eax, [pVideoWindow]
- mov eax, [eax]
- stdcall dword [eax+IVideoWindow.SetWindowPosition],[pVideoWindow],\
- [deltaX],[deltaY],[pWidth],[pHeight]
- ; Назначить окно для событий плеера
- mov eax, [pEvent]
- mov eax, [eax]
- stdcall dword [eax+IMediaEventEx.SetNotifyWindow],[pEvent],\
- [hwnddlg],WM_GRAPHNOTIFY,NULL ; [hwnddlg]
- ; Заблокировать окно воспроизведения
- invoke EnableWindow,ebx,FALSE
Осталось только запустить файл на воспроизведение. Делается это при помощи метода Run управляющего интерфейса IMediaControl.
Code (Assembler) : Убрать нумерацию
- ; Запустить видео
- mov eax, [pMediaControl]
- mov eax, [eax]
- stdcall dword [eax+IMediaControl.Run],[pMediaControl]
Code (Assembler) : Убрать нумерацию
- ; Поставить видео на паузу
- mov eax, [pMediaControl]
- mov eax, [eax]
- stdcall dword [eax+IMediaControl.Pause],[pMediaControl]
Code (Assembler) : Убрать нумерацию
- ; Остановить видео
- mov eax, [pMediaControl]
- mov eax, [eax]
- stdcall dword [eax+IMediaControl.Stop],[pMediaControl]
Code (Assembler) : Убрать нумерацию
- ; Получить состояние воспроизведения
- mov eax, [pMediaControl]
- mov eax, [eax]
- stdcall dword [eax+IMediaControl.GetState],[pMediaControl],\
- 0,pState
Code (Assembler) : Убрать нумерацию
- ; Получить длительность медиафайла
- mov eax, [pMediaSeeking]
- mov eax, [eax]
- stdcall dword [eax+IMediaSeeking.GetDuration],[pMediaSeeking],\
- lDuration
Code (Assembler) : Убрать нумерацию
- ; Получить текущую позицию воспроизведения
- mov eax, [pMediaSeeking]
- mov eax, [eax]
- stdcall dword [eax+IMediaSeeking.GetPositions],[pMediaSeeking],\
- lDuration,pStop
Code (Assembler) : Убрать нумерацию
- ; Перемотать поток на начало
- mov dword[lDuration],0
- mov dword[lDuration+4],0
- mov eax, [pMediaSeeking]
- mov eax, [eax]
- stdcall dword [eax+IMediaSeeking.SetPositions],[pMediaSeeking],\
- lDuration,AM_SEEKING_AbsolutePositioning,\
- lDuration,0
В зависимости от тех или иных условий, графический фильтр может посылать сообщение WM_GRAPHNOTIFY назначенному ранее окну. Обработчик этого сообщения выглядит примерно следующим образом:
Code (Assembler) : Убрать нумерацию
- cmp [msg],WM_GRAPHNOTIFY
- je wmgraph
- ...
- ...
- wmgraph:
- ; Получить событие из очереди
- mov eax, [pEvent]
- mov eax, [eax]
- stdcall dword [eax+IMediaEventEx.GetEvent],[pEvent],\
- evCode, param1, param2, NULL
- or eax,eax
- ; Событий в очереди больше нет
- jnz processed
- ; Освободить событие
- mov eax, [pEvent]
- mov eax, [eax]
- stdcall dword [eax+IMediaEventEx.FreeEventParams],[pEvent],\
- [evCode],[param1],[param2]
- ; Воспроизведение завершено?
- cmp [evCode],EC_COMPLETE
- jne @f
- ...
- ; Действия по завершению воспроизведения
- ...
- @@:
- ; Обработать следующее сообщение
- jmp wmgraph
Code (Assembler) : Убрать нумерацию
- ; Прибраться за собой
- mov eax, [pEvent]
- mov eax, [eax]
- stdcall dword [eax+IMediaEventEx.SetNotifyWindow],[pEvent],\
- NULL,NULL,NULL
- mov eax, [pEvent]
- mov eax, [eax]
- stdcall dword [eax+IMediaEventEx.Release],[pEvent]
- mov eax, [pBasicVideo]
- mov eax, [eax]
- stdcall dword [eax+IBasicVideo.Release],[pBasicVideo]
- mov eax, [pVideoWindow]
- mov eax, [eax]
- stdcall dword [eax+IVideoWindow.Release],[pVideoWindow]
- mov eax, [pMediaControl]
- mov eax, [eax]
- stdcall dword [eax+IMediaControl.Release],[pMediaControl]
- mov eax, [pMediaSeeking]
- mov eax, [eax]
- stdcall dword [eax+IMediaSeeking.Release],[pMediaSeeking]
- mov eax, [pGraphBuilder]
- mov eax, [eax]
- stdcall dword [eax+IGraphBuilder.Release],[pGraphBuilder]
В приложении пример программы с исходным текстом, которая воспроизводит видеофайл из архива.
Просмотров: 623 | Комментариев: 8
Комментарии
Отзывы посетителей сайта о статье
Mikl___
(04.06.2024 в 02:49):
Здравствуйте ManHunter!
В случае если размер видео фрагмента оказывается больше чем поле Edit Вы подгоняете размер видео фрагмента под размер Edit'a. Как бы Вы поступили, если бы пришлось динамически изменять размер диалога под размер видео? Если бы плеер строился на базе окна -- тогда понятно, нужно использовать MoveWindow(). А что делать если плеер построен на базе диалога?
В случае если размер видео фрагмента оказывается больше чем поле Edit Вы подгоняете размер видео фрагмента под размер Edit'a. Как бы Вы поступили, если бы пришлось динамически изменять размер диалога под размер видео? Если бы плеер строился на базе окна -- тогда понятно, нужно использовать MoveWindow(). А что делать если плеер построен на базе диалога?
ManHunter
(23.05.2024 в 08:02):
Конечно. Я именно так и сделал, только с аудиоплеером.
Mikl___
(23.05.2024 в 03:57):
Здравствуйте ManHunter!
Можно ли использовать код из Вашей статьи "Управление громкостью звука в системе" для управления громкостью звука в "Мультимедийном плеере на Ассемблере" или потребуется полная переделка управлением громкостью?
Можно ли использовать код из Вашей статьи "Управление громкостью звука в системе" для управления громкостью звука в "Мультимедийном плеере на Ассемблере" или потребуется полная переделка управлением громкостью?
ManHunter
(02.05.2024 в 13:57):
Mikl___, поддерживает не плеер, а система. Нет кодеков - нет поддержки, есть кодеки - поддерживает. У меня с установленным k-lite megapack крутит даже flv.
Тем более, что это ни в коем случае не полноценное приложение, а только каркас. Никаких проверок не делается, никакой диагностики не выводится.
Тем более, что это ни в коем случае не полноценное приложение, а только каркас. Никаких проверок не делается, никакой диагностики не выводится.
Mikl___
(02.05.2024 в 13:52):
ManHunter, а mp4 ваш плейер поддерживает? Вклад в общую копилку -- посмотрите у меня в "Сказках дядюшки Римуса о x64" 52-ую главу "Братец Кролик и просмотр видео-файлов". Там россыпь разных видео-плейеров с исходным текстом и экзе
ManHunter
(26.04.2024 в 11:51):
Проверено на Win7x86, Win7x64, Win10x86, Win10x64, все прекрасно работает.
User
(26.04.2024 в 10:14):
как заставить воспроизводить файл из архива, или любой другой? При запуске рядом с файлом не воспроизводит. drug-n-drop не поддерживается. Через открыть как тоже
Добавить комментарий
Заполните форму для добавления комментария
После загрузки файла методами get_VideoWidth и get_VideoHeight получаем размер видео. При этом мы знаем размер своего окна, или хотя бы панели управления плеером, и размер экрана, где располагается окно плеера. Меняем размеры диалога и EDIT'а в нем таким образом, чтобы по ширине получился размер видео, а по высоте размер видео плюс высота панели управления. Но при этом диалог делается не менее ширины панели управления и не более ширины монитора, по высоте не более высоты монитора. Кнопочки перемещаются в соответствии с шириной диалога. Для работы потребуется SetWindowPos.
В обработчике окна отслеживается несколько событий. WM_GETMINMAXINFO - для ограничения изменения размеров диалога в разрешенных пределах. WM_SIZE - для пересчета размеров элементов и перемещения кнопочек.
Ссылки в помощь:
https://www.manhunter.ru/assem...ov_okna.html
https://www.manhunter.ru/assem...ti_okna.html
https://www.manhunter.ru/assem...ckbar32.html
https://www.manhunter.ru/assem...emblere.html