|
Работа с файлами формата
AVI.
Часть 5. Создание новых AVI-файлов
из отдельных кадров.
К этому моменту у Вас
должна появиться отличная идея использования функций
AVIFile. Последний пример показывает, как
создать AVI-файл из последовательности
отдельных растровых файлов (BMP). Если у
Вас нет карты захвата изображения или коммерческого графического
программного обеспечения, Вы можете подготовить серию картинок в
стандартном Paint Brush.
Просто сохраните несколько разных изображений, имеющих одинаковую
ширину, высоту и глубину цвета, и вы получите то, с чем можно
экспериментировать в
этой
программе. Также Вы можете воспользоваться утилитой, созданной нами
в третьей статье цикла для создания последовательности изображений
из имеющегося AVI-файла. Затем вы можете
как угодно перемешать полученные кадры и использовать программу,
описанную в этой статье.
Единственное новшество в
коде этого примера - это то, что я создаю видео-поток из "исходников"
(в отличие от копирования из существующего потока) и использую
другой способ сохранения потока в AVI-файл
(AVISave в этом примере не используется).
Код этого примера должен быть понятен Вам, если вы прочли все
предыдущие статьи цикла.
Поток может создаваться
путем инициализации структуры AVI_STREAM_INFO
и передачи ее в функцию AVIFileCreateStream.
Когда эта функция выполнится, второй параметр будет содержать верный
указатель на вновь созданный интерфейс потока:
' Заполняем заголовок
для видео-потока
With strhdr
.fccType = mmioStringToFOURCC("vids", 0&)
'тип видео
потока
.fccHandler = 0&
'ссылка по
умолчанию
.dwScale = 1
.dwRate = Val(txtFPS)
'кадров в
секунду
.dwSuggestedBufferSize = bmp.SizeImage
'размер одного
кадра в пикселах
Call SetRect(.rcFrame, 0, 0, bmp.Width, bmp.Height)'прямоугольник
для потока
End With
'проверяем
ввод пользователя
If strhdr.dwRate < 1 Then strhdr.dwRate = 1
If strhdr.dwRate > 30 Then strhdr.dwRate = 30
'и создаем поток
res = AVIFileCreateStream(pfile, ps, strhdr)
If (res <> AVIERR_OK) Then GoTo error |
Теперь, когда переменная
ps указывает на поток, мы можем
использовать ее для создания сжатого потока, содержащего всю
необходимую информацию для записи его в файл.
Прежде всего,
пользователь должен выбрать кодек для сжатия потока:
'получаем
параметры сжатия от пользователя
'Внимание! Эти вызовы API требуют
указателя на указатель на структуру
pOpts = VarPtr(opts)
res = AVISaveOptions(Me.hWnd, _
ICMF_CHOOSE_KEYFRAME Or
ICMF_CHOOSE_DATARATE, _
1, _
ps, _
pOpts)
If res <> 1 Then 'In C TRUE = 1
Call AVISaveOptionsFree(1, pOpts)
GoTo error
End If |
Затем мы передаем эту
информацию вместе с самим потоком в функцию
AVIMakeCompressedStream:
'создаем сжатый
поток
res = AVIMakeCompressedStream(psCompressed,
ps, opts, 0&)
If res <> AVIERR_OK Then GoTo error |
Когда эта функция
завершается успешно, переменная psCompressed
указывает на интерфейс сжатого потока, который может записывать
сжатый видео-поток в формате, заданном структурами
AVI_STREAM_INFO и
AVI_COMPRESS_OPTIONS. Все, что мы должны сделать далее -
установить параметры DIB? которые должны
быть переданы в поток. Это требует использования структуры
BITMAPINFO, что означает переменную
изменяющегося размера, которую достаточно трудно использовать в
VB. К счастью, здесь нам поможет класс
cDIB. В этом классе имеется функция,
принимающая файл с диска. Ранее я вызывал эту функцию для считывания
первого кадра потока из файла для того, чтобы получить размер буфера
для структуры AVI_STREAM_INFO:
'получаем первый
кадр потока из файла
Set bmp = New cDIB
lstDIBList.ListIndex = 0
If bmp.CreateFromFile(lstDIBList.Text) <> True Then
MsgBox "Could not load first bitmap file in list!",
vbExclamation, App.title
GoTo error
End If |
Сейчас мы можем
использовать ту же структуру для установки формата видео-потока.
Прежде всего нам необходимо записать всю необходимую информацию в
структуру BITMAPINFO:
With BI
.biBitCount = bmp.BitCount
.biClrImportant = bmp.ClrImportant
.biClrUsed = bmp.ClrUsed
.biCompression = bmp.Compression
.biHeight = bmp.Height
.biWidth = bmp.Width
.biPlanes = bmp.Planes
.biSize = bmp.SizeInfoHeader
.biSizeImage = bmp.SizeImage
.biXPelsPerMeter = bmp.XPPM
.biYPelsPerMeter = bmp.YPPM
End With |
И затем передаем эту
структуру вместе с потоком в функцию
AVIStreamSetFormat:
'устанавливаем
формат сжатого потока
res = AVIStreamSetFormat(psCompressed,
0, ByVal bmp.PointerToBitmapInfo, bmp.SizeBitmapInfo)
If (res <> AVIERR_OK) Then GoTo error |
Если эта функция
завершается успешно, то сжатый поток полностью инициализирован и
готов принять несколько DIB для записи. Вы
сделаете это путем передачи этого потока и позиции, с которой нужно
начать запись, в функцию AVIStreamWrite.
Обратите внимание, что здесь вновь используется класс
cDIB, так как он содержит метод, который
возвращает прямой указатель на биты, которые нужно передать в
функцию:
'Записываем
каждый кадр видео
For i = 0 To lstDIBList.ListCount - 1
lstDIBList.ListIndex = i
bmp.CreateFromFile (lstDIBList.Text)
'считываем растр
res = AVIStreamWrite(psCompressed, _
i, _
1, _
bmp.PointerToBits, _
bmp.SizeImage, _
AVIIF_KEYFRAME, _
ByVal 0&, _
ByVal 0&)
If res <> AVIERR_OK Then GoTo error
'Show user feedback
imgPreview.Picture = LoadPicture(lstDIBList.Text)
imgPreview.Refresh
lblStatus = "Frame number " & i & " saved"
lblStatus.Refresh
Next
lblStatus = "Finished!" |
Как только этот цикл
завершит запись изображений, Вам нужно освободить все ресурсы и
файловые указатели. Файл, созданный Вами в начале этой процедуры,
теперь содержит обычный видео-поток, и его можно воспроизвести в
Windows Media Player или в другом
проигрывателе. Чтобы лучше разобраться в работе примера, изучите код
программы. Когда Вы запустите этот пример, вы должны будете выбрать
несколько файлов изображений, один за другим, и указать частоту
кадров от 1 до 30 кадров в секунду. Полный текст примера можно
скачать
здесь.
На этом цикл статей,
посвященных обработке AVI-файлов,
завершен. Как обычно, со всеми комментариями и вопросами вы можете
обращаться по адресу:
bousoft@mail.ru.
|