|
Работа с файлами формата
AVI.
Часть 4. Создание новых AVI-файлов
из существующих потоков.
Код этого примера
показывает как создать новый AVI-файл из
существующих потоков. Он использует класс
cFileDlg для того, чтобы пользователь мог выбрать
существующий файл. Использование этого класса достаточно подробно
было описано в первой части цикла, поэтому здесь останавливаться на
этом не будем. Этот класс был включен только для того, чтобы
максимально упростить код, в то же время сохранив полную свободу в
выборе файла. После того, как имя файла принято от пользователя,
программа получает указатель PAVIFILE, и
затем находит указатель PAVISTREAM для
видео потока в файле.
Затем я вновь использую
класс cFileDlg для получения имени
сохраняемого файла, так как мы будем создавать новый файл.
Большинство необходимых параметров класса уже установлено, нам
требуется добавить 3 строки:
ofd.DlgTitle = "Choose
Location and Name to Save New AVI File"
ofd.DefaultExt = "avi"
szFileOut = "MyFile.avi" 'имя
файла по умолчанию |
Теперь просто
вызываем метод VBGetSaveFileName:
|
res =
ofd.VBGetSaveFileName(szFileOut) |
После этого у нас имеются
имя исходного файла (существующего) и имя выходного файла (не
существующего), и можно вызывать некоторые
AVIFile-функции. Функции в этом примере достаточно сложны для
вызова из Visual Basic. Все три
функции AVISave
требуют указатель на указатель на массив структур
AVI_COMPRESS_OPTIONS. По-моему, единственный способ выполнить
такой вызов - в объявлении функции указать ByRef
AVI_COMPRESS_OPTIONS и затем использовать недокументированную
функцию VarPtr(), чтобы получить указатель
на эту структуру. Затем я передаю этот указатель в
API-функцию и все вроде бы работает.
Вероятно, нужно написать и откомпилировать библиотеку типов (TypeLibrary),
но так как моей целью является написание наиболее простого и
понятного кода, здесь я использую прямые вызовы функций.
Другая проблема с которой
я столкнулся, оказалась более неприятной. Ведь функция
AVISave объявлена в модуле
Vfw.h с использованием преобразования
CDECL. Как Вы должно быть знаете, это одна
из самых худших ситуаций, с которой встречаются программисты
VB API. VB
понимает только преобразование STDCALL
(также оно называется Паскаль- или Фортран- преобразованием). Так
как параметры помещаются в стек в другом порядке, а выборка
происходит также не по порядку, VB не
может вызвать экспортированные таким способом функции. К счастью,
системные программисты из Microsoft
обеспечили совместимость библиотек не только с
C/C++, и включили аналогичную функцию
AVISaveV, которая объявляется через
STDCALL. Это едиснтвенная функция, которую мы можем вызывать
из VB, поэтому я использовал
AVISaveV для вызова
AVISave, однако если вы посмотрите MSDN
по данному вопросу, то заметите, что последний параметр этих двух
функций различен.
Теперь, когда я рассказал
о некоторых встретившихся трудностях, давайте займемся кодом. Нам
нужно показать диалог параметров сжатия, чтобы пользователь мог
выбрать кодек, которым будет сжат полученный AVI-файл.
Прежде всего, я получаю указатель на структуру:
Затем я передаю этот
параметр в функцию по ссылке, что означает получение указателя на
указатель, а это и требуется для вызова API-функции.
3 флага, указанных во втором параметре функции, определяют, как
будет выглядеть диалог параметров сжатия:
res = AVISaveOptions(Me.hWnd,
_
ICMF_CHOOSE_KEYFRAME Or ICMF_CHOOSE_DATARATE Or
ICMF_CHOOSE_PREVIEW, _
1, _
pAVIStream, _
pOpts)
'возвращает истину, если пользователь нажал OK, ложь если
Cancel, или код ошибки |
Если функция возвращает 1
(что означает в C/C++ ИСТИНА),
то можно быть уверенным, что структура
AVI_COMP_OPTIONS верно инициализирована. Теперь у нас имеется
вся информация, чтобы создать новый сжатый поток из видео-потока
файла, открытого в самом начале программы. Если исходный поток уже
был сжат, он будет пересжат с использованием другого кодека (и
возможно с потерей качества). Этот пример будет полезен при
обработке несжатого потока (например, с ТВ-тюнера) или при
подготовке видео ролика без звука для проигрывания в
Win32 Animation Control (замечание:
Animation Control позволяет воспроизводить
несжатый поток или поток с RLE сжатием).
Для создания сжатого видео-потока нам потребуется всего дна строчка
кода:
'пересжимаем
видео-поток с использованием настроек пользователя
res =
AVIMakeCompressedStream(pAVIStreamOut, pAVIStream, opts, 0&) |
Теперь мы имеем новый
интерфейс видео-потока, и все готово для записи его в новый
AVI-файл. Так как это может занять много
времени, функция AVISave позволяет в
реальном времени отображать текущий статус обработки и позволяет
прервать обработку если это необходимо. Шаблон такой функции
обратного вызова я поместил в файл AviDecs.bas
вместе с информацией, необходимой для данного конкретного примера,
поэтому Вы можете посмотреть там пример использования функции. Также
я использовал глобальный флаг "abort",
чтобы дать возможность пользователю прервать процесс обработки
нажатием кнопки. Вот пример вызова функции
AVISave:
pOpts = VarPtr(opts)
'убедимся, что
указатель задан корректно
res = AVISave(szFileOut, 0&, AddressOf AVISaveCallback, 1,
pAVIStreamOut, pOpts) |
Если Вы используете
функцию обратного вызова, включающую в себя
DoEvents, Вы можете перехватить состояние, при котором
пользователь пытается завершить приложение в процессе сохранения
файла. В данном простом приложении я препятствую завершению работы
приложения пользователем, используя одну дополнительную строчку кода
в событии Query_Unload формы. Другой
способ решения проблемы - прекратить сохранение и очистить ресурсы в
тот момент, когда пользователь завершает приложение.
После завершения
сохранения файла Вам нужно освободить ресурсы, занятые
AVISaveOptions, используя функцию
AVISaveOptonsFree:
|
Call AVISaveOptionsFree(1,
pOpts) |
На этом мы завершаем
статью, посвященную созданию новых AVI-файлов
из существующих потоков. Готовый проект можно скачать
здесь.
|