Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 09-1

index << >>


09.1 Música e vídeo com DirectShow

1.1 Visão geral
Neste capítulo vamos aprender como adicionar música e vídeo em nossas aplicações com o DirectShow. Para isso vamos utilizar a classe GPMediaPlayer que empacota as funcionalidades mínimas para tocar vídeo e música utilizando a mesma estrutura. O foco maior será dado na cobertura das funcionalidades da classe GPMediaPlayer que oferece uma interface popular com os métodos stop(), play(), pause() e outros para controle da mídia carregada. O termo mídia será usado como sinônimo de música ou vídeo. Informamos que não haverá um detalhamento profundo do DirectShow visto que esse não é o foco do nosso curso mas a classe oferecida permitirá a inclusão e a manipulação básica de música e vídeo. A interface mãe do DirectShow: IGraphBuilder
A primeira interface do directshow chama-se IGraphBuilder e esse nome graph(grafo ou gráfico) + builder (construtor) que pode ser traduzido como 'construtor de gráficos' dá uma boa pista da primeira concepção que devemos ter sobre o directshow para entendê-lo. O menor esquema interno para tocar um vídeo com aúdio envolve a combinação de várias peças: o mecanismo inicial do directshow, o mecanismo fatiador que separa o vídeo e o aúdio e que encaminha-os para os canais respectivos de processamento, o decodificador de vídeo, o renderizador de vídeo, o decodificador de áudio e finalmente o renderizador de aúdio. E todos estes módulos colocados em um desenho dá origem a um gráfico complexo fato que justifica o nome da classe mãe do directshow como 'construtor de gráficos', IGraphBuilder. A interface IGraphBuilder monta o esquema de renderização da mídia e produz os outros objetos que permitem o controle da mídia e o processamento de eventos em torno da mídia tocando (playback). Controle do playback da mídia: IMediaControl O objeto da interface IMediaControl permite o controle do playback da mídia, isto é, ele aplica os comandos stop, pause, run (play), pause e outros métodos oferecidos pela interface. Controle dos eventos do playback da mídia: IMediaEventEx O objeto da interface IMediaEventEx permite o processamento de eventos em torno do playback da mídia, por exemplo, é interessante a aplicação ficar ciente quando o playback completa-se ou é abortado pelo usuário para prosseguir depois disso com o fluxo normal da aplicação. A janela de playback do vídeo: IVideoWindow Inicialmente o DirectShow toca o vídeo em sua própria janela e o controle dessa janela é obtido pelo objeto da interface IVideoWindow que também permite que a aplicação ofereça sua própria janela para o playback do vídeo. O DirectShow executa música e vídeo com as mesmas interfaces o que é muito conveniente. Porém esta conveniência estraga-se pelo fato de que inicialmente o DirectShow toca o vídeo em sua própria janela, e quando você usa sua própria janela para tocar o vídeo, depois o DirectShow não permite tocar sua própria música antes da aplicação devolver o controle da janela ao directshow. É inconveniente também rodar o vídeo na janela do DirectShow. Então esses aspectos inconvenientes trazem uma pequena complicação no código da aplicação que precisa verificar quem é o dono da janela de playback antes de tocar música ou vídeo. A classe GPMediaPlayer não gerencia muito bem este aspecto visto que é apenas uma classe de exemplo mas numa aplicação final você deve tratar adequadamente estes comportamentos do DirectShow. 1.2 Estrutura principal da aplicação
Arquivo: entrada.cpp
WinMain() criação da classe da janela registro da classe da janela criação da janela de acordo com a classe definida a aplicação vai operar em modo vídeo ou música? chama initGfx() para inicializar o motor gráfico mostra a janela chama a função tocar_media() para tocar a mídia conforme a decisão tomada pela caixa de diálogo. estabelecimento do laço de mensagens finaliza a aplicação
Arquivo: motor.cpp
Aspectos globais Declaração do ponteiro global para o objeto da classe GPMediaPlayer ( g_Tocador ) Declaração da variável global para evitar a renderização em caso de playback de vídeo ( bVideoAcionado ) initGfx() Inicializa o objeto Direct3d Inicializa o dispositivo renderizador chama inicializar_Buffers() para criar o vertexbuffer chama inicializar_Camera() para a configuração inicial da câmera chama montar_Geometria() para configurar os vértices do vertexbuffer * o arquivo de mídia será tocado depois da inicialização do motor gráfico. processaJanela() tratamento de teclado: permite o controle do playback da mídia através da tecla espaço (abortar), S(parar), T(tocar), P(pausar) chama a função tratarMediaEvento() para processar eventos de mídia como 'playback completo' ou 'playback abortado'. inicializar_Buffers() Criação do buffer de vértices (vertexbuffer) montar_Geometria() Acessa o buffer de vértices calcula a posição circular de cada vértice joga a posição do vértice no vertexbuffer renderizar_Geometria() Declara o formato de vértice utilizado ao directx Indica ao dispositivo o buffer de vértices que será utilizado O temporizador indica a cada dois segundos uma primitiva diferente Renderiza os vértices com g_device->DrawPrimitive() Renderizar() Retorne se o vídeo estiver sendo exibido Limpa a tela chama renderizar_Geometria() para desenhar as primitivas Apresenta a cena Limpar() Libera o buffer de vértices Libere os objetos do DirectShow via g_Tocador.limpar() Libera o dispositivo renderizador Libera o objeto Direct3d
Arquivo: gpmediaplayer.cpp - classe GPMediaPlayer
Como propriedades principais a classe traz os principais objetos do DirectShow, já discutidos na introdução, para manipular música e vídeo. Os nomes das funções são auto-explicativos com exceção de setOwner() que define a janela da aplicação como a janela na qual o vídeo vai ser exibido. inicializar() e limpar() O primeiro método inicializa o motor do DirectShow e o segundo libera os objetos alocados. carregarMusica() e carregarVideo() Estas funções carregam respectivamente música e vídeo configurando uma variável interna sobre o tipo de mídia carregado, música ou vídeo. play(), pause(), stop() São obviamente métodos para tocar, pausar e parar a execução da mídia. GPMediaPlayer() É a função construtora do objeto da classe que apenas zera as variáveis internas inicialmente.
2.1.1 Aspectos globais - Arquivo: motor.h
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - Arquivo: motor.h // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #ifndef motor_h #define motor_h // Inclui as bibliotecas do Direct3D #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") // Indicação se o DirectShow está tocando música ou vídeo #define modo_video 1 #define modo_musica -1 // Definição do formato de vértice utilizado por esta aplicação #define CustomVertex_PositionColored_Format(D3DFVF_XYZ | D3DFVF_DIFFUSE) // Estrutura do vértice customizado struct CustomVertex_PositionColored { float x, y, z; DWORD cor; // Construtor default CustomVertex_PositionColored() {} CustomVertex_PositionColored( float _x, float _y, float _z, DWORD _cor) { x = _x; y = _y; z = _z; cor = _cor; } }; // fim da estrutura CustomVertex_PositionColored // Esta função inicializa o Direct3D HRESULT initGfx (HWND hJanela); // Essa função libera os objetos utilizados void Limpar(); // Essa função desenha a cena void Renderizar(); // Essa função inicializa o buffer de índices e // o buffer de vértices void inicializar_Buffers(void); // Essa função monta formas geométricas void montar_Geometria (void); // Renderiza os vértices em formas geométricas void renderizar_Geometria (void); // Configura a câmera da aplicação void inicializar_Camera(void); // Toca um arquivo de música ou vídeo void tocar_media(void); // Trata evento de mídia void tratarMediaEvento(); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
Indicação se o DirectShow está tocando música ou vídeo #define modo_video 1 #define modo_musica -1 Estas macros são usadas ao longo do código para configurar a aplicação para tocar especialmente música ou vídeo. Naturalmente que tomando os cuidados para gerenciar as inconveniências já citadas na introdução é possível alternar durante a mesma aplicação o playback de vídeo ou música. void tocar_media(void); Esta função toca música ou vídeo conforme a configuração inicial da aplicação. Essa função é chamada por WinMain() logo após a inicialização do motor gráfico. void tratarMediaEvento(); Essa função ilustra como estruturar o código para tratar os eventos de mídia, por exemplo, o evento que informa que o playback completou-se ou foi abortado pelo usuário. Essa função é chamada por processaJanela() no evento maior WM_P_GRAPHNOTIFY disparado pelo DirectShow. Dentro dessa função é verificado mais exatamente qual evento ocorreu em torno do playback da mídia usando um objeto da interface IMediaEventEx. O código que trata o evento WM_P_GRAPHNOTIFY aparece dessa forma em processaJanela(): case WM_P_GRAPHNOTIFY: tratarMediaEvento(); break; LRESULT CALLBACK processaJanela ( // (...) ); Essa função através do tratamento do evento WM_KEYDOWN permite o controle do playback da mídia carregada com as seguintes teclas: if (wParam == 'T') g_Tocador.play(); A tecla 'T' toca o mídia carregada. if (wParam == 'S') g_Tocador.stop(); A tecla 'S' pára a mídia que está tocando. if (wParam == 'P') g_Tocador.pause(); A tecla 'P' páusa a mídia que está tocando. A tecla espaço aborta a execução da mídia deixando por alguns instantes a última imagem no caso do playback de vídeo: if (mediaModo == modo_video) g_Tocador.abortarVideo(2000); if (mediaModo == modo_musica) g_Tocador.stop(); bVideoAcionado = -1; A variável bVideoAcionado configurada com -1 faz a aplicação voltar para o fluxo normal de renderização. Isso simula o ambiente com uma cena de introdução seguida no seu encerramento pelo retorno ao fluxo normal do jogo. 2.1.2 Aspectos globais - Arquivo: motor.cpp
GPMediaPlayer g_Tocador; int bVideoAcionado = 1;
GPMediaPlayer g_Tocador; Este objeto da classe GPMediaPlayer é capaz de tocar música ou vídeo oferecendo os controles populares de mídia como carregar...(), play(), pause() e stop() além de permitir acesso direto aos objetos do DirectShow pelas propriedades públicas listadas abaixo: g_Tocador.gfxConstrutor Acesso ao objeto da interface IGraphBuilder, construtor de gráficos. g_Tocador.mdControle Acesso ao objeto da interface IMediaControl de controle de mídia (Run (play), Stop, Pause, etc...) g_Tocador.mdEventos Acesso ao objeto da interface IMediaEventEx para acesso e tratamento de eventos em torno do playback da mídia. g_Tocador.janelaVideo Acesso ao objeto da interface IVideoWindow de acesso a janela de playback de vídeo. int bVideoAcionado = 1; Esta variável é um flag para evitar o processo de renderização quando o video estiver passando(1). O valor de -1 libera a aplicação para renderizar a geometria. 2.2 Controle do playback da mídia Segue abaixo a listagem de processaJanela(). O código em destaque de controle de evento e de playback de mídia já foi comentado na seção 2.1.1 acima.
// Esta função é chamada por DispatchMessage() LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam) { switch (mensagem) { case WM_DESTROY: // Coloca uma mensagem WM_QUIT na fila de mensagem Limpar(); PostQuitMessage (0); break; case WM_P_GRAPHNOTIFY: tratarMediaEvento(); break; case WM_KEYDOWN: if (wParam == 'T') g_Tocador.play(); if (wParam == 'S') g_Tocador.stop(); if (wParam == 'P') g_Tocador.pause(); // Aborte o vídeo na tecla espaço if (wParam == VK_SPACE ) { if (mediaModo == modo_video) g_Tocador.abortarVideo(2000); if (mediaModo == modo_musica) g_Tocador.stop(); bVideoAcionado = -1; } // endif if (wParam == VK_ESCAPE) { Limpar(); PostQuitMessage( 0); } // endif break; // Essa mensagem vai ocorrer a todo momento case WM_PAINT: // Renderiza a cena Renderizar(); // Invalida a tela para chamar WM_PAINT novamente InvalidateRect( hJanela, NULL, false); break; // Processamento default de mensagens não tratada pela aplicação default: return DefWindowProc (hJanela, mensagem, wParam, lParam); } // endswitch return 0; } // processaJanela().fim
2.3 Renderização no tempo certo O destaque da função Renderizar() é o flag bVideoAcionado que evita o processo de renderização quando o vídeo estiver passando (1).
VOID Renderizar() { if(bVideoAcionado == 1) return; // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor preta g_device->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar a geometria renderizar_Geometria(); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); } // Renderizar().fim
2.4 Tocando o arquivo de mídia
void tocar_media() { // Inicializa o tocador g_Tocador = GPMediaPlayer(); // Inicializa os objetos do DirectShow para tocar mídia g_Tocador.inicializar(); if (mediaModo == modo_video) { g_Tocador.carregarVideo(L"\\gameprog\\gdkmedia\\Video\\RiskyDance.mp4"); // Configura a janela da aplicação como a janela de vídeo g_Tocador.setOwner (hJanela); bVideoAcionado = 1; } // endif if (mediaModo == modo_musica) { g_Tocador.carregarMusica (L"\\gameprog\\gdkmedia\\musica\\megaman3Intro.mp3"); bVideoAcionado = -1; } // Toca o arquivo de mídia g_Tocador.play(); } // tocar_media().fim
g_Tocador = GPMediaPlayer(); Esta linha inicializa o tocador e zera suas variáveis internas. g_Tocador.inicializar(); Com esta linha o tocador inicializa os objetos do DirectShow utilizados para manipulação básica de vídeo e música. Em outras palavras o método inicializar() ilustra como inicializar o motor básico do DirectShow para renderizar música e vídeo. if (mediaModo == modo_video) { g_Tocador.carregarVideo(L"\\gameprog\\gdkmedia\\Video\\RiskyDance.mp4"); g_Tocador.setOwner (hJanela); bVideoAcionado = 1; } // endif Este bloco carrega um vídeo do disco e bloqueia a renderização com 'bVideoAcionado = 1; O método setOwner() informa que a janela da aplicação deve ser usada para receber a renderização do vídeo. if (mediaModo == modo_musica) { g_Tocador.carregarMusica (L"\\gameprog\\gdkmedia\\musica\\megaman3Intro.mp3"); bVideoAcionado = -1; } Este bloco carrega uma música do disco e permite a continuidade da renderização da geometria com 'bVideoAcionado = -1' g_Tocador.play(); Esta linha toca o arquivo de mídia. 2.5 Tratando eventos do playback de mídia
void tratarMediaEvento() { // Coleta dos resultados das operações do directx HRESULT hr; // Variáveis para coleta do evento e informações complementares long cdgEvento; long param1, param2; // Verifica a ocorrência de eventos hr = g_Tocador.mdEventos->GetEvent(&cdgEvento, &param1, &param2, 0); // Trata a ocorrência de eventos while(SUCCEEDED(hr) ) { // Tira o evento da fila de eventos hr = g_Tocador.mdEventos->FreeEventParams(cdgEvento, param1, param2); // Verifica se o evento que o playback da mídia completou-se ou // foi abortada ocorreu if (( cdgEvento == EC_COMPLETE ) || ( cdgEvento == EC_USERABORT )) { // Aborta o playback da mídia g_Tocador.abortarVideo(2000); bVideoAcionado = -1; break; } // endif // Verifica a ocorrência de eventos para alimentar o while{} hr = g_Tocador.mdEventos->GetEvent(&cdgEvento, &param1, &param2, 0); } // endwhile } // tratarMediaEvento()
HRESULT hr; Essa variável é para coletar o resultado da operação de coleta de eventos pelo conjunto g_Tocador.mdEventos->GetEvent() cujo sucesso vai manter ativo o bloco da instrução while. long cdgEvento; Esta variável vai receber qual evento de mídia ocorreu. Os eventos possíveis estão definidos no arquivo evcode.h que fica no seguinte diretório de SDK's da Microsoft conforme nossa instalação: C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include. Dois valores possíveis para eventos que vão ser tratados nessa função são: EC_COMPLETE disparado quando o playback da mídia completou-se e EC_USERABORT que ocorre quando o usuário fecha a janela da aplicação. long param1, param2; Estas variáveis vão receber informações adicionais sobre o evento ocorrido. hr = g_Tocador.mdEventos->GetEvent(&cdgEvento, &param1, &param2, 0); Esta linha verifica a primeira ocorrência de eventos para abrir o bloco while. Se houver eventos as devidas variáveis são preenchidas e hr vai representar sucesso. while(SUCCEEDED(hr) ) { Se houver eventos o bloco while vai sustentar-se. hr = g_Tocador.mdEventos->FreeEventParams(cdgEvento, param1, param2); Esta linha deleta o evento interceptado da fila de eventos de mídia. if (( cdgEvento == EC_COMPLETE ) || ( cdgEvento == EC_USERABORT )) { g_Tocador.abortarVideo(2000); bVideoAcionado = -1; break; } // endif Esse bloco é executado quando a mídia completou sua execução ou quando o playback foi abortado. Em ambos os casos a mídia é parada e a a renderização da geometria ganha continuidade. Repetimos que a classe GPMediaPlayer não é uma produção final que trata adequadamente as considerações de se tocar música ou vídeo. Aqui é um mero exemplo de como tratar um evento. hr = g_Tocador.mdEventos->GetEvent( &cdgEvento, &param1, &param2, 0); } // endwhile Aqui novamente a fila de eventos de mídia é verificada para sustentar a execução do bloco while se houver mais eventos na fila. 3.0 A classe GPMediaPlayer 3.1 Aspectos globais - Arquivo: gpmediaplayer.cpp
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - arquivo: gpmediaplayer.cpp // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <dshow.h> // Biblioteca do DirectShow #pragma comment(lib, "strmiids.lib") // Notificação de eventos do DirectShow #define WM_P_GRAPHNOTIFY WM_APP + 1 // Indicação se o DirectShow está tocando música ou vídeo #define modo_video 1 #define modo_musica -1
#include <dshow.h> Este é o principal arquivo de cabeçalho do DirectShow. #pragma comment(lib, "strmiids.lib") Esta é a principal biblioteca do DirectShow. #define WM_P_GRAPHNOTIFY WM_APP + 1 Esta macro é uma preparação que o DirectShow precisa para enviar eventos de mídia para a aplicação. #define modo_video 1 #define modo_musica -1 Lembramos que estas macros é para assinalar que a aplicação está trabalhando com música ou vídeo. 3.2 Propriedades privadas da classe
class GPMediaPlayer { private: // Membro para receber o handle da janela da aplicação HWND hJanela; // 1 = video // -1 = música int mediaModo;
HWND hJanela; Este membro é para receber o handle da janela da aplicação caso a aplicação indique sua própria janela para receber a renderização do vídeo. A intenção de uso desta variável é para preservar o handle da janela da aplicação quando o DirectShow estiver renderizando música. int mediaModo; Este membro é assinalado com os valores modo_musica ou modo_video para assinalar que a aplicação está processando música ou vídeo para que seja tomado os cuidados especiais que cada mídia pode exigir para a aplicação operar com harmonia. 3.3 Propriedades públicas da classe
public: // Recebe os resultados das operações HRESULT m_hr; // Interface do construtor de gráficos IGraphBuilder *gfxConstrutor; // Interface de controle de mídia (play, stop, pause, etc...) IMediaControl *mdControle; // Interfaces de controle de evento IMediaEventEx *mdEventos; // Interface da janela de vídeo IVideoWindow *janelaVideo;
HRESULT m_hr; Este membro é para guardar temporariamente o resultado das operações. Ele é utilizado geralmente ao longo da classe e efetivamente verificado no método seHouverFalha() que verifica a ocorrência de falhas e mostra mensagens pertinentes ao local do código aonde ocorreu o insucesso. IGraphBuilder *gfxConstrutor; A interface IGraphBuilder monta o esquema de renderização da mídia e produz os outros objetos que permitem o controle da mídia e o processamento de eventos em torno da mídia tocando (playback). IMediaControl *mdControle; O objeto da interface IMediaControl permite o controle do playback da mídia, isto é, ele aplica os comandos stop, pause, run (play), pause e outros métodos oferecidos pela interface. IMediaEventEx *mdEventos; O objeto da interface IMediaEventEx permite o processamento de eventos em torno do playback da mídia, por exemplo, é interessante a aplicação ficar ciente quando o playback completa-se ou é abortado pelo usuário para prosseguir depois disso com o fluxo normal da aplicação. IVideoWindow *janelaVideo; Inicialmente o DirectShow toca o vídeo em sua própria janela e o controle dessa janela é obtido pelo objeto da interface IVideoWindow que também permite que a aplicação ofereça sua própria janela para o playback do vídeo. 3.4 O construtor da classe O construtor da classe simplesmente zera os membros de trabalho.
// Construtor default GPMediaPlayer () { gfxConstrutor = NULL; mdControle = NULL; janelaVideo = NULL; mdEventos = NULL; m_hr = 0; hJanela = NULL; } // Contrutor().fim
3.5 Inialização do DirectShow
void inicializar(void) { // inicializa a interface COM CoInitialize(NULL); // Cria o gerenciador do gráfico de filtros m_hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) &gfxConstrutor); // Cria a interface de controle de mídia m_hr = gfxConstrutor->QueryInterface(IID_IMediaControl, (void **)&mdControle); // Cria a interface de controle de eventos extras m_hr = gfxConstrutor->QueryInterface(IID_IMediaEventEx, (void **)&mdEventos); } // inicializar().fim
CoInitialize(NULL); Esta é a forma de preparar a aplicação para se conectar com objetos da interface da tecnologia tipo COM. m_hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) &gfxConstrutor); Esta é a forma de criar o gráfico de filtros que é a interface mãe do DirectShow. m_hr = gfxConstrutor->QueryInterface(IID_IMediaControl, (void **)&mdControle); Esta é a forma de criar o objeto da interface de controle de mídia. m_hr = gfxConstrutor->QueryInterface(IID_IMediaEventEx, (void **)&mdEventos); Esta é a forma de criar o objeto da interface de controle de eventos. 3.6 Carregando arquivo de vídeo
void carregarVideo( LPCWSTR videoArquivo) { // Carrega o arquivo de mídia apontado m_hr = gfxConstrutor->RenderFile(videoArquivo, NULL); // indica modo de vídeo mediaModo = modo_video; // avisa se houver falha seHouverFalha("Falha: gfxConstrutor->RenderFile()", "GPMediaPlayer:carregarVideo()"); } // carregarVideo().fim
m_hr = gfxConstrutor->RenderFile(videoArquivo, NULL); O método RenderFile() carrega o arquivo de mídia apontado e monta o esquema de renderizá-lo. Depois do trabalho desse método o restante da manipulação da mídia é feita com a interface de controle de mídia. mediaModo = modo_video; Esta linha deixa claro para as outras operações da classe de que o processamento será sobre um vídeo. seHouverFalha("Falha: gfxConstrutor->RenderFile()", "GPMediaPlayer:carregarVideo()"); Em caso de falha esta função pipoca uma mensagem para o usuário com a localidade do insucesso. 3.7 Carregando arquivo de música
void carregarMusica( LPCWSTR musicaArquivo) { // Carrega o arquivo de mídia apontado m_hr = gfxConstrutor->RenderFile(musicaArquivo, NULL); // indica modo de música mediaModo = modo_musica; // avisa se houver falha seHouverFalha("Falha: gfxConstrutor->RenderFile()", "GPMediaPlayer:carregarVideo()"); } // carregarMusica().fim
mediaModo = modo_musica; Esta linha deixa claro para as outras operações da classe de que o processamento será sobre uma música. 3.8 Definindo a janela de playback de vídeo: setOwner() Este método configura a janela da aplicação como sendo a janela que recebe a renderização do vídeo. O funcionamento adequado desse método da forma que está demandou mais de 24 horas de testes e pesquisas na Internet pois pequenos desvios faziam-no deixar de funcionar sem nenhuma pista do que estava errado.
void setOwner( HWND hWnd ) { // Essa função não interessa pra música! if (mediaModo == modo_musica ) return; // Obtém a janela de vídeo if (janelaVideo == NULL) m_hr = gfxConstrutor->QueryInterface(IID_IVideoWindow, (void **) &janelaVideo); // Verifica se houve falha if(seHouverFalha("Falha: gfxConstrutor->QueryInterface()", "Video::setOwner()") ) return; // Muda a janela de vídeo para a janela da aplicação m_hr = janelaVideo->put_Owner( (OAHWND) hWnd ); // Configura janela de notificação mdEventos->SetNotifyWindow((OAHWND)hWnd, WM_P_GRAPHNOTIFY, 0); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->put_Owner()", "Video::setOwner()"); hJanela = hWnd; // Configura o estilo da janela de vídeo m_hr = janelaVideo->put_WindowStyle( WS_CHILD| WS_CLIPSIBLINGS ); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->put_WindowStyle()", "Video::setOwner()"); // Configura o tamanho da janela // Pega o tamanho da janela RECT rect; GetClientRect(hWnd, &rect); // Reconfigura o tamanho da janela m_hr = janelaVideo->SetWindowPosition(0, 0, rect.right, rect.bottom); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->SetWindowPosition()", "Video::setOwner()"); } // setOwner().fim
if (mediaModo == modo_musica ) return; O método setOwner() não interessa para o processamento de música, por isso existe essa linha para evitar mal funcionamento na aplicação quando ela tocar música. m_hr = gfxConstrutor->QueryInterface(IID_IVideoWindow, (void **) &janelaVideo); Esta linha obtém acesso a janela de vídeo do DirectShow através do objeto da interface IVideoWindow inicializado aqui. m_hr = janelaVideo->put_Owner( (OAHWND) hWnd ); Aqui a janela da aplicação torna-se a janela que vai receber a renderização do vídeo. mdEventos->SetNotifyWindow( (OAHWND) hWnd, WM_P_GRAPHNOTIFY, 0); Aqui a janela da aplicação é configurada como a janela que vai receber os eventos de mídia. hJanela = hWnd; Aqui salvamos a janela da aplicação para recuperá-la depois de tocar música. m_hr = janelaVideo->put_WindowStyle( WS_CHILD| WS_CLIPSIBLINGS ); Aqui a janela da aplicação recebe o estilo adequado para ser tratada adequadamente pelo DirectShow. RECT rect; GetClientRect(hWnd, &rect); m_hr = janelaVideo->SetWindowPosition(0, 0, rect.right, rect.bottom); Aqui o DirectShow é informado do tamanho da janela da aplicação. 3.9 Tocando o arquivo de mídia O método play() da nossa classe toca o arquivo de mídia. Quando a mídia é música a janela da aplicação é desconectada da renderização e restaurada quando a mídia é vídeo. Lembramos que esses cuidados tomados não são exaustivos devendo serem melhor implementados numa aplicação final.
void play() { // Desconecte a janela da aplicação se for música if((janelaVideo != NULL) && (mediaModo == modo_musica) ) janelaVideo->put_Owner (NULL); // Reconecte com a janela da aplicação se for vídeo if((janelaVideo != NULL) && (mediaModo == modo_video) ) if (hJanela != NULL) janelaVideo->put_Owner((OAHWND) hJanela); // Toque o arquivo de mídia m_hr = mdControle->Run(); // Avisa se houve falha seHouverFalha("mdControle->Run()", "GPMediaPlayer::play()"); } // play().fim
if((janelaVideo != NULL) && (mediaModo == modo_musica)) janelaVideo->put_Owner (NULL); Desconecte a janela da aplicação se for música if((janelaVideo != NULL) && (mediaModo == modo_video) ) if (hJanela != NULL) janelaVideo->put_Owner((OAHWND) hJanela); Reconecte a janela da aplicação se for vídeo. m_hr = mdControle->Run(); Toque o arquivo de mídia. 3.10 Parando o arquivo de mídia O método stop() pára a execução da mídia.
void stop() { // Pára de tocar o arquivo de mídia m_hr = mdControle->Stop(); // Avisa se houve falha seHouverFalha("mdControle->Stop()", "GPMediaPlayer::stop()"); } // stop().fim
3.11 Pausando o arquivo de mídia O método pause() páusa a execução da mídia até que o método play() seja novamente executado.
void pause() { // Pausa o arquivo de mídia m_hr = mdControle->Pause(); // Avisa se houve falha seHouverFalha("mdControle->Pause()", "GPMediaPlayer::pause()"); } // pause
3.12 Abortando o arquivo de vídeo O método abortarVideo() pára a execução do vídeo, deixa a última imagem por alguns instantes, aborta a execução do vídeo e desconecta a janela da aplicação do DirectShow.
void abortarVideo( DWORD nSleep) { // Pára o arquivo de mídia m_hr = mdControle->Stop(); // Avisa se houve falha seHouverFalha("mdControle->Stop()", "GPMediaPlayer::abortarVideo()"); // Deixe por algum tempo a última imagem if (nSleep > 0) Sleep( nSleep); // Esconda a janela e desconecte da aplicação if (janelaVideo != NULL) { janelaVideo->put_Visible (OAFALSE); janelaVideo->put_Owner (NULL); } // endif } // abortarVideo().fim
3.13 Verificação de ocorrência de erros A função seHouverFalha() verifica o membro m_hr e mostra as mensagens na ocorrência de falhas.
bool seHouverFalha( char *aviso, char *processo) { bool status = false; // Verifica se houve erro if (FAILED(m_hr)) { MessageBox (NULL, aviso, processo, MB_OK); status = true; } // endif return status; } // seHouverFalha().fim
3.14 Liberação dos objetos utilizados
void limpar() { // Libera os objetos utilizados if(gfxConstrutor != NULL) gfxConstrutor->Release(); if(mdControle != NULL) mdControle->Release(); if(janelaVideo != NULL) janelaVideo->Release(); if(mdEventos != NULL) mdEventos->Release(); // Anula os ponteiros gfxConstrutor = NULL; mdControle = NULL; janelaVideo = NULL; mdEventos = NULL; CoUninitialize(); } // limpar().fim
CoUninitialize(); Esta função desconecta a aplicação do processo de comunicação com as interfaces de tecnologia COM. 4.0 O arquivo entrada.cpp 4.1 Escolhendo música ou vídeo
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - arquivo: entrada.cpp // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include "motor.h" // Variável global da classe da janela char sclasseJanela[ ] = "cls_directx"; // Dimensões da janela int g_xtela = 640; int g_ytela = 480; // Indicação se o DirectShow está tocando música ou vídeo int mediaModo; // alça da janela HWND hJanela; int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior, LPSTR sComando,int nExibir) { // Estrutura de recepção das mensagens MSG mensagem; // Estrutura de descrição da janela WNDCLASSEX wcls; // Estrutura que descreve a janela wcls.hInstance = app_instancia; wcls.lpszClassName = sclasseJanela; wcls.lpfnWndProc = processaJanela; wcls.style = CS_HREDRAW | CS_VREDRAW; wcls.cbSize = sizeof (WNDCLASSEX); // O cursor e os ícones da aplicação são default wcls.hIcon = LoadIcon (NULL, IDI_APPLICATION); wcls.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wcls.hCursor = LoadCursor (NULL, IDC_ARROW); // Aplicação sem menu wcls.lpszMenuName = NULL; // Nada de espaço extra atrelado a classe da janela (wcls) wcls.cbClsExtra = 0; // Nada de espaço extra atrelado a janela wcls.cbWndExtra = 0; // Cor default da janela wcls.hbrBackground = ( HBRUSH) COLOR_BACKGROUND; // Registra a janela e retorna se esta operação falhar int status = RegisterClassEx (&wcls); if(status == 0) { MessageBox(NULL, "Registro falhou!", "WinMain()", MB_OK); return 0; } // endif // Com a classe criada pode-se criar a janela DWORD estiloExtra = 0; const char janelaTitulo[] = "prj_DirectShow"; DWORD controleEstilo = WS_OVERLAPPEDWINDOW; int xpos = 160; int ypos = 120; HWND hjanelaPai = HWND_DESKTOP; HMENU sem_menu = NULL; LPVOID dadoExtra = NULL; // Cria a janela hJanela = CreateWindowEx(estiloExtra, sclasseJanela, janelaTitulo, controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu, app_instancia, dadoExtra ); // Verifica se janela foi criada if(hJanela == NULL) { MessageBox(NULL, "Falha na criação da janela!", "WinMain()", MB_OK); return 0; } // endif int res = MessageBox (hJanela, "Acionar modo vídeo ???", "prj_DirectShow", MB_YESNO); if (res == IDYES) mediaModo = modo_video; else mediaModo = modo_musica; // Essa variável recebe informação de erro do Directx HRESULT hr; // Inicia o Direct3D hr = initGfx ( hJanela ); // Encerre a aplicação se houve falha if(FAILED (hr) ) { MessageBox (hJanela, "Direct3D: falha na inicialização", "WinMain()", MB_OK); UnregisterClass( sclasseJanela, wcls.hInstance); return E_FAIL; } // endif // Mostra a janela ShowWindow(hJanela, nExibir); UpdateWindow(hJanela ); tocar_media(); // Rode a bombeamento de mensagens até GetMessage() retornar 0 while (GetMessage(&mensagem, NULL, 0, 0)) { // Traduz mensagem de tecla virtual em mensagem de caracteres TranslateMessage( &mensagem ); // Despacha a mensagem para a função processaJanela */ DispatchMessage( &mensagem ); } // endwhile // O valor de retorno é zero(0) passado por PostQuitMessage() UnregisterClass( sclasseJanela, wcls.hInstance); return mensagem.wParam; } // WinMain().fim
int mediaModo; Já vimos que esta variável define se a aplicação vai tocar uma música ou vídeo. HWND hJanela; Este handle é exportado para o motor da aplicação que o utiliza para configurar a janela como a janela de playback de vídeo. int res = MessageBox (hJanela, "Acionar modo vídeo ???", "prj_DirectShow", MB_YESNO); Ao responder esta caixa de mensagem o usuário define se a aplicação vai rodar uma música ou um vídeo. if (res == IDYES) mediaModo = modo_video; else mediaModo = modo_musica; Depois de responder a caixa de mensagem, o mediaModo é configurado de acordo. tocar_media(); E finalmente a mídia é tocada depois da inicialização do motor gráfico. 5.0 Código fonte do projeto de exemplo: prj_DirectShow
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - Arquivo: motor.h // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #ifndef motor_h #define motor_h // Inclui as bibliotecas do Direct3D #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3dx9.lib") // Indicação se o DirectShow está tocando música ou vídeo #define modo_video 1 #define modo_musica -1 // Definição do formato de vértice utilizado por esta aplicação #define CustomVertex_PositionColored_Format(D3DFVF_XYZ | D3DFVF_DIFFUSE) // Estrutura do vértice customizado struct CustomVertex_PositionColored { float x, y, z; DWORD cor; // Construtor default CustomVertex_PositionColored() {} CustomVertex_PositionColored( float _x, float _y, float _z, DWORD _cor) { x = _x; y = _y; z = _z; cor = _cor; } }; // fim da estrutura CustomVertex_PositionColored // Esta função inicializa o Direct3D HRESULT initGfx (HWND hJanela); // Essa função libera os objetos utilizados void Limpar(); // Essa função desenha a cena void Renderizar(); // Essa função inicializa o buffer de índices e // o buffer de vértices void inicializar_Buffers(void); // Essa função monta formas geométricas void montar_Geometria (void); // Renderiza os vértices em formas geométricas void renderizar_Geometria (void); // Configura a câmera da aplicação void inicializar_Camera(void); // Toca um arquivo de música ou vídeo void tocar_media(void); // Trata evento de mídia void tratarMediaEvento(); // Declaração da função que atende as mensagens da janela LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam); #endif
// ----------------------------------------------------------------------------- // Projeto: prj_DirectShow - arquivo: motor.cpp // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include <d3dx9.h> #include <math.h> #include <time.h> #include <dshow.h> #include "motor.h" #include "gpmediaplayer.cpp" // Variáveis globais // Representa o dispositivo Direct3D LPDIRECT3D9 g_Direct3d = NULL; // Representa o dispositivo Renderizador IDirect3DDevice9* g_device = NULL; // Representa o buffer de vértices IDirect3DVertexBuffer9* g_vbVertices = NULL; GPMediaPlayer g_Tocador; int bVideoAcionado = 1; // Essa variável recebe informação de erro do Directx HRESULT g_hr = 0; // Tamanho da janela extern int g_xtela; extern int g_ytela; extern HWND hJanela; extern int mediaModo; // Constante para cores const DWORD vermelho = 0xFFFF0000; const DWORD branco = 0xFFFFFFFF; const DWORD amarelo = 0xFFFFFF00; UINT g_nVerticesQtd = 60; // Matrizes de configuração da câmera D3DXMATRIX g_mtxMundo; D3DXMATRIX g_mtxVisao; D3DXMATRIX g_mtxProj; // Controla a mudança periodica da primitiva UINT temporizador = 0; // initGfx() - Inicializa o Direct3D HRESULT initGfx( HWND hJanela ) { // Cria o objeto Direct3D que é necessário para criar o dispositivo gráfico g_Direct3d = Direct3DCreate9( D3D_SDK_VERSION); // Verifica se o objeto Direct3D foi criado if(g_Direct3d == NULL) { MessageBox (NULL, "Falha na inialização do Direct3D", "InitGfx()", MB_OK); return E_FAIL; } // endif // Declara a variável para os parâmetros de apresentação D3DPRESENT_PARAMETERS pps; // Limpa a estrutura ZeroMemory( &pps, sizeof(pps) ); // Configura os parâmetros de apresentação // A aplicação vai ter janela pps.Windowed = TRUE; // Esse método transfere rapidamente o backbuffer para a tela imediata pps.SwapEffect = D3DSWAPEFFECT_DISCARD; // Esse formato vai procurar se encaixar no modo de video corrente pps.BackBufferFormat = D3DFMT_UNKNOWN; // Configuração do renderizador a ser criado // Adaptador default (0) int nAdaptador = D3DADAPTER_DEFAULT; // Tipo de dispositivo Hardware ou emulador de referência (software) D3DDEVTYPE dispositivo_tipo = D3DDEVTYPE_HAL; // Flags de configuração do dispositivo DWORD create_flags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // Criamos aqui o dispositivo renderizador g_hr = g_Direct3d->CreateDevice( nAdaptador, dispositivo_tipo, hJanela, create_flags, &pps, &g_device ); // Verifica se houve falha no processo if( FAILED( g_hr ) ) { MessageBox (NULL, "Falha na criação: g_device", "initGfx()", MB_OK); return E_FAIL; } // endif // inicializa o vertexbuffer inicializar_Buffers(); // configura o posicionamento dos vértices montar_Geometria(); // configura câmera da aplicação inicializar_Camera(); // Habilita iluminação default g_device->SetRenderState (D3DRS_LIGHTING, false); return S_OK; } // initGfx().fim void inicializar_Buffers(void) { // Tamanho do vertexbuffer em bytes UINT vbTamanho = sizeof(CustomVertex_PositionColored) * g_nVerticesQtd; // Criação efetiva do buffer de vértices (vertexbuffer) g_hr = g_device->CreateVertexBuffer(vbTamanho, D3DUSAGE_WRITEONLY, CustomVertex_PositionColored_Format, D3DPOOL_MANAGED, &g_vbVertices, NULL); // Verifica falha na criação if(FAILED (g_hr) ) { MessageBox (NULL, "Falha na criação do buffer de vértices", "inicializar_Buffers()", MB_OK); return; } // endif } // inicializar_Buffers().fim void montar_Geometria(void) { // Posicionamento de profundidade float xcol = 0.0f; float ylin = 0.0f; float zpos = 0.0f; // Utilizado para controlar o índice do vértice UINT ndx = 0; // Distribuição circular dos vértices no espaço UINT passo = 360 / g_nVerticesQtd; // Ponteiro de acesso aos dados do buffer de vértices CustomVertex_PositionColored *pVerts; // Aqui a aplicação ganha acesso à memória do buffer de vértices g_vbVertices->Lock( 0, 0, (void**) &pVerts, 0); // Vamos posicionar os vértices em círculo for (float ncx = 0.0f; ncx <= 360.0f; ncx += passo) { // Converte graus para radianos double radianos = ncx * (D3DX_PI / 180.0f); // Tamanho do círculo float nRaio = 2.0f; // (x,y) distribuidos de maneira circular xcol = (float) cos(radianos) * nRaio; ylin = (float) sin(radianos) * nRaio; // Posicionamento do vértice pVerts[ndx] = CustomVertex_PositionColored( xcol, ylin, zpos, vermelho); // Coloca no centro o terceiro vértice de cada triângulo if (ndx % 3 == 0) pVerts[ndx] = CustomVertex_PositionColored( 0, 0, zpos, amarelo); // Atualiza índice de controle do vértice ndx++; } // endfor // Liberação do vertexbuffer g_vbVertices->Unlock(); } // montar_Geometria().fim void renderizar_Geometria() { // Produz a cada dois segundos um valor de 0 a 5. temporizador = ( clock() / 500 ) % 3; // Declara o formato de vértice utilizado pela aplicação g_device->SetFVF( CustomVertex_PositionColored_Format); // Informação do buffer de vértices utilizado g_device->SetStreamSource( 0, g_vbVertices, 0,sizeof(CustomVertex_PositionColored)); if (temporizador == 0) g_device->DrawPrimitive( D3DPT_LINELIST, 0, g_nVerticesQtd / 2); if (temporizador == 1) g_device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, g_nVerticesQtd / 3); } // renderizar_Geometria().fim // Esta função é chamada por DispatchMessage() LRESULT CALLBACK processaJanela (HWND hJanela, UINT mensagem, WPARAM wParam, LPARAM lParam) { switch (mensagem) { case WM_DESTROY: // Coloca uma mensagem WM_QUIT na fila de mensagem Limpar(); PostQuitMessage (0); break; case WM_P_GRAPHNOTIFY: tratarMediaEvento(); break; case WM_KEYDOWN: if (wParam == 'T') g_Tocador.play(); if (wParam == 'S') g_Tocador.stop(); if (wParam == 'P') g_Tocador.pause(); // Aborte o vídeo na tecla espaço if (wParam == VK_SPACE ) { if (mediaModo == modo_video) g_Tocador.abortarVideo(2000); if (mediaModo == modo_musica) g_Tocador.stop(); bVideoAcionado = -1; } // endif if (wParam == VK_ESCAPE) { Limpar(); PostQuitMessage( 0); } // endif break; // Essa mensagem vai ocorrer a todo momento case WM_PAINT: // Renderiza a cena Renderizar(); // Invalida a tela para chamar WM_PAINT novamente InvalidateRect( hJanela, NULL, false); break; // Processamento default de mensagens não tratada pela aplicação default: return DefWindowProc (hJanela, mensagem, wParam, lParam); } // endswitch return 0; } // processaJanela().fim // Limpar() - Libera todos os objetos previamente inicializados // ----------------------------------------------------------------------------- VOID Limpar() { // Libera o buffer de vértices if( g_vbVertices != NULL) g_vbVertices->Release(); // Limpa o DirectShow e seus objetos utilizados g_Tocador.limpar(); // Libera o dispositivo gráfico if( g_device != NULL) g_device->Release(); // Libera o motor do Direct3D if( g_Direct3d != NULL) g_Direct3d->Release(); // Anula os ponteiros g_vbVertices = NULL; g_device = NULL; g_Direct3d = NULL; } // Limpar().fim // ----------------------------------------------------------------------------- // Renderizar() - Desenha a cena // ----------------------------------------------------------------------------- VOID Renderizar() { if(bVideoAcionado == 1) return; // Retorne se o dispositivo estiver nulo if( g_device == NULL) return; // Limpa o backbuffer com uma cor preta g_device->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0); // Começa a cena if( SUCCEEDED( g_device->BeginScene() ) ) { // Vamos renderizar a geometria renderizar_Geometria(); // Finalizando a cena g_device->EndScene(); } // endif // Apresenta o conteúdo do backbuffer na tela g_device->Present( NULL, NULL, NULL, NULL); } // Renderizar().fim void inicializar_Camera(void) { // *************************************************************************** // Inicializa todas as matrizes para elemento neutro D3DXMatrixIdentity( &g_mtxMundo ); D3DXMatrixIdentity( &g_mtxVisao ); D3DXMatrixIdentity( &g_mtxProj ); // *************************************************************************** // Configura a matriz de mundo no dispositivo renderizador g_device->SetTransform( D3DTS_WORLD, &g_mtxMundo ); // *************************************************************************** // Dados para a configuração da matriz de visualização // Aonde está a câmera? - posição da câmera D3DXVECTOR3 cam_pos (0.0f, 0.0f, 5.0f); // Para aonde a câmera está apontada ou olhando? Alvo da câmera D3DXVECTOR3 cam_alvo (0.0f, 0.0f, 0); // A câmera está de cabeça pra baixo? - orientação da câmera D3DXVECTOR3 cam_vetorcima (0.0f, 1.0f, 0.0f); // Configura a matriz de visão D3DXMatrixLookAtLH( &g_mtxVisao, &cam_pos, &cam_alvo, &cam_vetorcima ); // Configura a matriz de visão no dispositivo renderizador g_device->SetTransform( D3DTS_VIEW, &g_mtxVisao ); // *************************************************************************** // Argumentos de configuração da matriz de projeção // aspecto dos gráficos float aspecto = (float) g_xtela / g_ytela; // campo de visão float campo_visao = D3DX_PI / 4; // Trapézio de visualização da câmera ( Frustrum ) float corte_perto = 1.0f; float corte_longe = 1000.0f; // Configura a matriz de projeção D3DXMatrixPerspectiveFovLH( &g_mtxProj, campo_visao, aspecto, corte_perto, corte_longe); // Configura a matriz de projeção no dispositivo renderizador g_device->SetTransform( D3DTS_PROJECTION, &g_mtxProj ); } // inicializar_Camera().fim void tocar_media() { // Inicializa o tocador g_Tocador = GPMediaPlayer(); // Inicializa os objetos do DirectShow para tocar mídia g_Tocador.inicializar(); if (mediaModo == modo_video) { g_Tocador.carregarVideo(L"\\gameprog\\gdkmedia\\Video\\RiskyDance.mp4"); // Configura a janela da aplicação como a janela de vídeo g_Tocador.setOwner (hJanela); bVideoAcionado = 1; } // endif if (mediaModo == modo_musica) { g_Tocador.carregarMusica (L"\\gameprog\\gdkmedia\\musica\\megaman3Intro.mp3"); bVideoAcionado = -1; } // Toca o arquivo de mídia g_Tocador.play(); } // tocar_media().fim void tratarMediaEvento() { // Coleta dos resultados das operações do directx HRESULT hr; // Variáveis para coleta do evento e informações complementares long cdgEvento; long param1, param2; // Verifica a ocorrência de eventos hr = g_Tocador.mdEventos->GetEvent(&cdgEvento, &param1, &param2, 0); // Trata a ocorrência de eventos while(SUCCEEDED(hr) ) { // Tira o evento da fila de eventos hr = g_Tocador.mdEventos->FreeEventParams(cdgEvento, param1, param2); // Verifica se o evento que o playback da mídia completou-se ou // foi abortada ocorreu if (( cdgEvento == EC_COMPLETE ) || ( cdgEvento == EC_USERABORT )) { // Aborta o playback da mídia g_Tocador.abortarVideo(2000); bVideoAcionado = -1; break; } // endif // Verifica a ocorrência de eventos para alimentar o while{} hr = g_Tocador.mdEventos->GetEvent(&cdgEvento, &param1, &param2, 0); } // endwhile } // tratarMediaEvento()
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - arquivo: gpmediaplayer.cpp // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <dshow.h> // Biblioteca do DirectShow #pragma comment(lib, "strmiids.lib") // Notificação de eventos do DirectShow #define WM_P_GRAPHNOTIFY WM_APP + 1 // Indicação se o DirectShow está tocando música ou vídeo #define modo_video 1 #define modo_musica -1 class GPMediaPlayer { private: // Membro para receber o handle da janela da aplicação HWND hJanela; // 1 = video // -1 = música int mediaModo; public: // Recebe os resultados das operações HRESULT m_hr; // Interface do construtor de gráficos IGraphBuilder *gfxConstrutor; // Interface de controle de mídia (play, stop, pause, etc->.) IMediaControl *mdControle; // Interfaces de controle de evento IMediaEventEx *mdEventos; // Interface da janela de vídeo IVideoWindow *janelaVideo; // Construtor default GPMediaPlayer () { gfxConstrutor = NULL; mdControle = NULL; janelaVideo = NULL; mdEventos = NULL; m_hr = 0; hJanela = NULL; } // Contrutor().fim void inicializar(void) { // inicializa a interface COM CoInitialize(NULL); // Cria o gerenciador do gráfico de filtros m_hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) &gfxConstrutor); // Cria a interface de controle de mídia m_hr = gfxConstrutor->QueryInterface(IID_IMediaControl, (void **)&mdControle); // Cria a interface de controle de eventos extras m_hr = gfxConstrutor->QueryInterface(IID_IMediaEventEx, (void **)&mdEventos); } // inicializar().fim void carregarVideo( LPCWSTR videoArquivo) { // Carrega o arquivo de mídia apontado m_hr = gfxConstrutor->RenderFile(videoArquivo, NULL); // indica modo de vídeo mediaModo = modo_video; // avisa se houver falha seHouverFalha("Falha: gfxConstrutor->RenderFile()", "GPMediaPlayer:carregarVideo()"); } // carregarVideo().fim void carregarMusica( LPCWSTR musicaArquivo) { // Carrega o arquivo de mídia apontado m_hr = gfxConstrutor->RenderFile(musicaArquivo, NULL); // indica modo de música mediaModo = modo_musica; // avisa se houver falha seHouverFalha("Falha: gfxConstrutor->RenderFile()", "GPMediaPlayer:carregarVideo()"); } // carregarMusica().fim void setOwner( HWND hWnd ) { // Essa função não interessa pra música! if (mediaModo == modo_musica ) return; // Obtém a janela de vídeo if (janelaVideo == NULL) m_hr = gfxConstrutor->QueryInterface(IID_IVideoWindow, (void **) &janelaVideo); // Verifica se houve falha if(seHouverFalha("Falha: gfxConstrutor->QueryInterface()", "Video::setOwner()") ) return; // Muda a janela de vídeo para a janela da aplicação m_hr = janelaVideo->put_Owner( (OAHWND) hWnd ); // Configura janela de notificação mdEventos->SetNotifyWindow((OAHWND)hWnd, WM_P_GRAPHNOTIFY, 0); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->put_Owner()", "Video::setOwner()"); hJanela = hWnd; // Configura o estilo da janela de vídeo m_hr = janelaVideo->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->put_WindowStyle()", "Video::setOwner()"); // Configura o tamanho da janela // Pega o tamanho da janela RECT rect; GetClientRect(hWnd, &rect); // Reconfigura o tamanho da janela m_hr = janelaVideo->SetWindowPosition(0, 0, rect.right, rect.bottom); // Verifica se houve falha seHouverFalha("Falha: janelaVideo->SetWindowPosition()", "Video::setOwner()"); } // setOwner().fim void play() { // Desconecte a janela da aplicação se for música if((janelaVideo != NULL) && (mediaModo == modo_musica) ) janelaVideo->put_Owner (NULL); // Reconecte com a janela da aplicação se for vídeo if((janelaVideo != NULL) && (mediaModo == modo_video) ) if (hJanela != NULL) janelaVideo->put_Owner((OAHWND) hJanela); // Toque o arquivo de mídia m_hr = mdControle->Run(); // Avisa se houve falha seHouverFalha("mdControle->Run()", "GPMediaPlayer::play()"); } // play().fim void stop() { // Pára de tocar o arquivo de mídia m_hr = mdControle->Stop(); // Avisa se houve falha seHouverFalha("mdControle->Stop()", "GPMediaPlayer::stop()"); } // stop().fim void pause() { // Pausa o arquivo de mídia m_hr = mdControle->Pause(); // Avisa se houve falha seHouverFalha("mdControle->Pause()", "GPMediaPlayer::pause()"); } // pause void abortarVideo( DWORD nSleep) { // Pára o arquivo de mídia m_hr = mdControle->Stop(); // Avisa se houve falha seHouverFalha("mdControle->Stop()", "GPMediaPlayer::abortarVideo()"); // Deixe por algum tempo a última imagem if (nSleep > 0) Sleep( nSleep); // Esconda a janela e desconecte da aplicação if (janelaVideo != NULL) { janelaVideo->put_Visible (OAFALSE); janelaVideo->put_Owner (NULL); } // endif } // abortarVideo().fim bool seHouverFalha( char *aviso, char *processo) { bool status = false; // Verifica se houve erro if (FAILED(m_hr)) { MessageBox (NULL, aviso, processo, MB_OK); status = true; } // endif return status; } // seHouverFalha().fim void limpar() { // Libera os objetos utilizados if(gfxConstrutor != NULL) gfxConstrutor->Release(); if(mdControle != NULL) mdControle->Release(); if(janelaVideo != NULL) janelaVideo->Release(); if(mdEventos != NULL) mdEventos->Release(); // Anula os ponteiros gfxConstrutor = NULL; mdControle = NULL; janelaVideo = NULL; mdEventos = NULL; CoUninitialize(); } // limpar().fim }; // fim da classe
//----------------------------------------------------------------------------- // Projeto: prj_DirectShow - arquivo: entrada.cpp // Esta aplicação mostra como tocar vídeo e música // com o DirectShow. By www.gameprog.com.br // ----------------------------------------------------------------------------- #include <windows.h> #include <d3d9.h> #include "motor.h" // Variável global da classe da janela char sclasseJanela[ ] = "cls_directx"; // Dimensões da janela int g_xtela = 640; int g_ytela = 480; // Indicação se o DirectShow está tocando música ou vídeo int mediaModo; // alça da janela HWND hJanela; int WINAPI WinMain (HINSTANCE app_instancia, HINSTANCE app_anterior, LPSTR sComando,int nExibir) { // Estrutura de recepção das mensagens MSG mensagem; // Estrutura de descrição da janela WNDCLASSEX wcls; // Estrutura que descreve a janela wcls.hInstance = app_instancia; wcls.lpszClassName = sclasseJanela; wcls.lpfnWndProc = processaJanela; wcls.style = CS_HREDRAW | CS_VREDRAW; wcls.cbSize = sizeof (WNDCLASSEX); // O cursor e os ícones da aplicação são default wcls.hIcon = LoadIcon (NULL, IDI_APPLICATION); wcls.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wcls.hCursor = LoadCursor (NULL, IDC_ARROW); // Aplicação sem menu wcls.lpszMenuName = NULL; // Nada de espaço extra atrelado a classe da janela (wcls) wcls.cbClsExtra = 0; // Nada de espaço extra atrelado a janela wcls.cbWndExtra = 0; // Cor default da janela wcls.hbrBackground = ( HBRUSH) COLOR_BACKGROUND; // Registra a janela e retorna se esta operação falhar int status = RegisterClassEx (&wcls); if(status == 0) { MessageBox(NULL, "Registro falhou!", "WinMain()", MB_OK); return 0; } // endif // Com a classe criada pode-se criar a janela DWORD estiloExtra = 0; const char janelaTitulo[] = "prj_DirectShow"; DWORD controleEstilo = WS_OVERLAPPEDWINDOW; int xpos = 160; int ypos = 120; HWND hjanelaPai = HWND_DESKTOP; HMENU sem_menu = NULL; LPVOID dadoExtra = NULL; // Cria a janela hJanela = CreateWindowEx(estiloExtra, sclasseJanela, janelaTitulo, controleEstilo, xpos, xpos, g_xtela, g_ytela, hjanelaPai, sem_menu, app_instancia, dadoExtra ); // Verifica se janela foi criada if(hJanela == NULL) { MessageBox(NULL, "Falha na criação da janela!", "WinMain()", MB_OK); return 0; } // endif int res = MessageBox (hJanela, "Acionar modo vídeo ???", "prj_DirectShow", MB_YESNO); if (res == IDYES) mediaModo = modo_video; else mediaModo = modo_musica; // Essa variável recebe informação de erro do Directx HRESULT hr; // Inicia o Direct3D hr = initGfx ( hJanela ); // Encerre a aplicação se houve falha if(FAILED (hr) ) { MessageBox (hJanela, "Direct3D: falha na inicialização", "WinMain()", MB_OK); UnregisterClass( sclasseJanela, wcls.hInstance); return E_FAIL; } // endif // Mostra a janela ShowWindow(hJanela, nExibir); UpdateWindow(hJanela ); tocar_media(); // Rode a bombeamento de mensagens até GetMessage() retornar 0 while (GetMessage(&mensagem, NULL, 0, 0)) { // Traduz mensagem de tecla virtual em mensagem de caracteres TranslateMessage( &mensagem ); // Despacha a mensagem para a função processaJanela */ DispatchMessage( &mensagem ); } // endwhile // O valor de retorno é zero(0) passado por PostQuitMessage() UnregisterClass( sclasseJanela, wcls.hInstance); return mensagem.wParam; } // WinMain().fim

index << >>

Produzido por Gameprog: Jair Pereira - Agosto/2014 © gameprog.br@gmail.com http://www.gameprog.com.br