|
Работа с файлами формата
AVI.
Часть
3. Работа с
кадрами в существующем AVI-файле.
Следующий шаг при работе
с AVI-файлом - получить кадр (сэмпл). В
прошлых статьях мы уже открыли видео поток в AVI-файле,
поэтому давайте попробуем сохранить каждый кадр потока как отдельный
растровый файл. Это потребует написания большего количества кода,
чем раньше, поэтому вместо копирования кода с этой страницы скачайте
готовую программу
отсюда,
а я просто обращу Ваше внимание на некоторые основополагающие
моменты.
После того, как Вы
скачаете и откроете этот проект в VB5 или
VB6, Вы заметите, что я добавил на форму
текстовое поле (используется для вывода отладочной информации) и
новый класс (cDIB.cls).
Этот класс содержит вызовы некоторых API-функций
при работе с DIB (Device Independent Bitmaps).
Это специальный прием, используемый при работе со структурой
BitmapInfo, имеющей изменяемую длину в
зависимости от глубины цвета растра. В любом случае, опуская
подробности реализации этого класса, Вы заметите, что он добавляет
совсем немного новых строк кода в событие обработки нажатия кнопки.
В вначале процедуры
объявляются пять новых переменных:
Dim dib As cDIB
Dim pGetFrameObj As Long
'указатель на интерфейс
GetFrame
Dim pDIB As Long
'указатель на
упакованный DIB в памяти
Dim bih As BITMAPINFOHEADER
'структура,
передаваемая в функцию GetFrame
Dim i As Long |
Переменная
dib является объявлением экземпляра нашего
нового класса cDIB. Переменная
pGetFrameObj - новый вид указателя,
который требуется для вызова функций
AVIStreamGetFrame. Эти функции автоматически предоставляют
ресурсы для распаковки одного кадра в любую структуру типа
DIB, которую мы укажем, и возвращает
указатель на нее, после чего мы можем обрабатывать эту структуру в
дальнейшем так, как захотим. Для использования этих функций
во-первых я выделяю ресурсы и получаю указатель в переменную
pGetFrameObj:
'инициализация функций
AVISTreamGetFrame* и создание объекта GETFRAME
pGetFrameObj =
AVIStreamGetFrameOpen(pAVIStream, bih)
'получить 24-битные DIB |
Перед этой строчкой
необходимо инициализировать структуру
BITMAPINFOHEADER, так, чтобы получить требуемые результаты (в
данном случае 24-разрядный цвет, несжатый RGB):
With bih
.biBitCount = 24
.biClrImportant = 0
.biClrUsed = 0
.biCompression = BI_RGB
.biHeight = streamInfo.rcFrame.bottom -
streamInfo.rcFrame.top
.biPlanes = 1
.biSize = 40
.biWidth = streamInfo.rcFrame.right -
streamInfo.rcFrame.left
.biXPelsPerMeter = 0
.biYPelsPerMeter = 0
.biSizeImage = (((.biWidth * 3) + 3) And &HFFFC) * .biHeight
End With |
Это позволяет узнать,
какой формат DIB будет использоваться в
дальнейшем, и моя структура DIB может
ссылаться на него (в настоящее время класс DIB
может ссылаться только на неструктурированные DIB
- 16- и 24-разрядного цвета). Если
Вы собираетесь передавать полученные DIB-указатели
в другие функции, такие как DrawDIB,
которые понимают любые форматы, Вам потребуется вызвать функцию
следующим образом:
'функция AVIStream
выбирает наилучший формат отображения автоматически
pGetFrameObj =
AVIStreamGetFrameOpen(pAVIStream, ByVal
AVIGETFRAMEF_BESTDISPLAYFMT) |
Это проще, чем
инициализация структуры BITMAPINFOHEADER,
но это означает, что Вам неизвестно, какой формат будет получен на
выходе - он определяется текущими настройками экрана и "железом".
Теперь, после успешной
инициализации функции
GetFrame нужным нам форматом и получения корректного
указателя в переменной pGetFrameObj, все
что нам нужно сделать - это передать ссылку и номер необходимого
кадра в функцию AVIStreamGetFrame. Затем я
использую класс cDIB растровых ссылок и
пикселов изображения и записи этой информации на диск как растровый
файл. Это потребует еще нескольких строк кода:
'создать класс
для загрузки в него кадров
Set dib = New cDIB
For i = firstFrame To (numFrames - 1) + firstFrame
pDIB = AVIStreamGetFrame(pGetFrameObj, i)
'возвращает упакованные
DIB
If dib.CreateFromPackedDIBPointer(pDIB) Then
Call dib.WriteToFile(App.Path & "\" & i & ".bmp")
txtStatus = "Bitmap " & i + 1 & " of " & numFrames &
" written to app folder"
txtStatus.Refresh
Else
End If
Next
Set dib = Nothing |
Вот и все! Как видите,
переменная i - это счетчик, который я
использую при формировании имени текущего файла. Будьте осторожны
при использовании этого примера с большими AVI-файлами,
так как процесс извлечения кадров невозможно остановить до тех пор,
пока все кадры не будут записаны на диск. Если Вы запустите этот
пример с каким-нибудь AVI-файлом, Вы
получите в папке программы 24-разрядные растровые файлы с именами,
соответствующими номерам кадров. Если Вы хотите узнать, как я
записываю растры на диск, откройте класс cDIB.
Для этого потребуются всего 2 функции - .CreateFromPackedDIBPointer
и .WriteToFile. Другие функции класса
cDIB служат для создания
DIB из растровых файлов, что мы будем
делать в следующей статье. В своей программе Вы можете создать
hDC для каждого кадра и поместить его в
PictureBox вместо записи в файл.
После завершения
обработки потока не забывайте освобождать занятые ресурсы при помощи
функции AVIStreamGetFrameClose:
| Call
AVIStreamGetFrameClose(pGetFrameObj) |
После вызова этой функции
указатель pGetFrameObj перестанет
существовать. Неплохо было бы присвоить ему значение 0, если Вы
собираетесь использовать его в дальнейшем.
|