Programando vídeo para Windows por E. J. Bantz, tradução: David M. Machado

Vá direto para a seção

Passo 1 - Criando uma nova janela
Passo 2 - Conectando a janela
Passando Strings para a função SendMessage
Passando Estruturas para a função SendMessage
Processando Imagens de Stream de Vídeo

Step 1 - Criando uma nova janela

O processo de captura de vídeo começa com a função capCreateCaptureWindowA. Quando chamada, esta função retorna um handle(identificador) para a nova janela. O handle(identificador) é um número de 32-bits que é usado para referenciar um objeto (neste caso, uma janela). Este handle(identificador) é o fundamento para o restante do seu programa, e deve ser mantido em um local seguro.

Você pode customizar o visual desta nova janela mudando os parâmetros de estilo. Neste exemplo, a janela será Child(filha, ou seja, ela abre dentro da janela principal e recebe seus atributos) e visível após a criação.

lwndC = capCreateCaptureWindowA("My Capture Window", WS_CHILD Or WS_VISIBLE, 0, 0, 160, 120, Me.hwnd, 0)

Todos os comandos futuros serão emitidos enviando-se uma Mensagem parametrizada para aquela nova janela. A mensagem parametrizada é enviada com a função SendMessage que é pertecente às APIs padrões do Win32. Você passa para esta função:

1 - O handle(identificador) da janela que você gostaria de enviar a mensagem;
2 - O comando;
3 - A mensagem de 32-bits;
4 - O parâmetro de 16-bits;
5 - E finalmente o parâmetro de 32-bits.

As mensagens de 32-bits são trocadas por uma constante de fácil entendimento. Todas as mensagens usadas pela captura de vídeo estão definidas dentro do módulo VBAVICAP.BAS.

PS: A palavra mensagem neste caso, representa uma conversa do programa com a API de modo que eles possam se entender/comunicar.

Passo 2 - Conectando a janela

Uma vez que a janela de captura é criada, você pode conectar a janela ao driver de vídeo. O driver de vídeo é instalado juntamente com sua câmera de vídeo. A conexão é feita com a mensagem WM_CAP_DRIVER_CONNECT. Isto irá "preencher" a janela com o primeiro driver de vídeo encontrado (índice 0). Tenha em mente que poderia enviar esta mensagem para qualquer janela, mas somente esta janela especialmente criada com a capCreateCaptureWindow entenderá o que você está falando.

SendMessage lwnd, WM_CAP_DRIVER_CONNECT, 0, 0

O módulo VBAVICAP.BAS providencia funções para tornar o envio destas mensagens um pouco mais simples. Fazer uma chamada é exatamente como o demonstrado abaixo:

capDriverConnect lwnd, 0

As funções são usadas ao invés da SendMessage para evitar confusão da ordem de qual parâmetroa são passados. Você deve sempre usar as funções disponibilizadas ao invés de SendMessage.

Passando Strings para a função SendMessage

A API SendMessage é sempre chamada passando-se 4 coisas:

1 - Um handle(identificador) de janela (hwnd);
2 - Alguma mensagem para enviar à esta janela (wMsg);
3 - Um curto parâmetro (wParam) - 16-bits.
4 - E um longo parâmetro (lParam) - 32-bits.

O w no wParam representa "WORD(palavra)" e o l no lParam representa "LONG". WORD e LONG são tipos de dados da linguagem C. Uma WORD é um número de 16bits, e um LONG é um número de 32bits. No VB, Eles são chamados repectivamente de INTEGER e LONG. A declaração se parece com isso:

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer, ByVal lParam As Long) As Long

Para passar uma string para esta função, você precisa redefinir lParam para String ao invés de Long. Agora, tenha em mente que enviar informações de tipos incorretos à DLL causaria grandes problemas. Normalmente, enviamos para a função exatamente o que ela espera. Veja você, em Visual Basic, quando você passa uma STRING usando byval para um função externa, enviará um ponteiro indicando a localização dessa STRING.

Um ponteiro é um número de 32bits, do tipo LONGO, com o que é esperado. Então deveríamos passar o tipo correto de dados, antes de tudo. Uma coisa mais, não podemos apenas mudar a declaração de SendMessage pois alguns chamadas NECESSITAM que lParam seja LONG. Então, você pode apenas criar uma nova declaração e chamá-la de algo ligeiramente diferente. Aqui está a nova declaração:

Declare Function SendMessageS Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Integer, ByVal lParam As String) As Long

Passando Estruturas para a função SendMessage

Estruturas (Tipos definidos pelo usuário) não são diretamente passadas para as funções. A passagem de estrutura é um pouco mais simples que a passagem de strings. Desta vez, ao invés de deixar o Visual Basic passar o ponteiro para você, faça isto você mesmo. VARPTR() é uma função construtora que retornará a localização na memória (um ponteiro) onde a variável que você passou está armazenada. Em nosso caso, a variável será um User Defined Type(Tipo definido pelo usuário/estrutura). Novamente, um ponteiro é um número de 32bits, ou LONG, o qual nós podemos passar como lParam.

Aqui está um rápido exemplo de como obter os parâmetros de captura.

Dim CAP_PARAMS As CAPTUREPARMS
capCaptureGetSetup lwndC, VarPtr(CAP_PARAMS), Len(CAP_PARAMS)

Processando Imagens de Stream de Vídeo

A questão mais comum que tenho recebido é, "Como eu pego os dados durante a captura de streaming de vídeo?" A resposta está nas funções de chamadas de retorno (call back functions). Use
capSetCallbackonFrame para processar os quadros durante a visualização, e capSetCallbackOnVideoStream para processar quadros durante a captura. Aqui está um exemplo:

capSetCallbackOnFrame lwndC, AddressOf MyFrameCallback

MyFrameCallback é uma função que você criou para sua aplicação. Deve estar localizada em um módulo não em um formulário. Esta será a função que será chamada toda vez que um novo quadro for recebido do driver de vídeo. Esta passa a identificação (handle) do VIDEOHDR o qual deve ser declarado da seguinte forma:

Type VIDEOHDR

lpData As Long '// Endereço do buffer de vídeo
dwBufferLength
As Long '// tamanho, em bytes, do buffer de dados
dwBytesUsed
As Long '// veja abaixo
dwTimeCaptured
As Long '// veja abaixo
dwUser
As Long '// dados específicos do usuário
dwFlags
As Long '// veja abaixo
dwReserved(3)
As Long '// reservado; não use

End Type

lpData é o dado atual do dispositivo. Você terá de manipular a estrutura do VIDEOHDR para obter-lo. Aqui está algum um exemplo de código para copiar a estrutura da memória em uma estrutura(User Defined Type) real do Visual Basic.

Function MyFrameCallback(ByVal lwnd As Long, ByVal lpVHdr As Long) As Long

Debug.Print "FrameCallBack"

Dim VideoHeader As VIDEOHDR
Dim VideoData() As Byte

'//Preenche o cabeçalho de vídeo(VideoHeader) com os dados de lpVHdr
RtlMoveMemory VarPtr(VideoHeader), lpVHdr, Len(VideoHeader)

'// Reserva espaço para os dados
ReDim VideoData(VideoHeader.dwBytesUsed)

'//Copia os dados no array(vetor)
RtlMoveMemory VarPtr(VideoData(0)), VideoHeader.lpData, VideoHeader.dwBytesUsed

Debug.Print VideoHeader.dwBytesUsed
Debug.Print VideoData

End Function

Neste exemplo, RtlMoveMemory deve ser declarado como a seguir:

Declare Sub RtlMoveMemory Lib "kernel32" (ByVal hpvDest As Long, ByVal hpvSource As Long, ByVal cbCopy As Long)

Final (Por enquanto...)

Última Atualização: 21/07/02
E. J. Bantz
http://ej.bantz.com
ej@bantz.com
davidmachado@gmail.com