Curso completo de DirectX 9 com C\C++
Gameprog - Escola de programação de jogos digitais
Contato: gameprog.br@gmail.com
Fase 09-1
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, ¶m1, ¶m2, 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, ¶m1, ¶m2, 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, ¶m1, ¶m2, 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,
¶m1, ¶m2, 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, ¶m1, ¶m2, 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, ¶m1, ¶m2, 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