Проверка VirtualDub

Только что, я сел и проверил проект VirtualDub с помощью PVS-Studio. Выбор был случаен. Я считаю, самое главное регулярно проверять/перепроверять различные проекты, чтобы показать, как развивается анализатор кода PVS-Studio. А какой проект будет проверен, не так важно. Ошибки есть везде. Проект VirtualDub мы уже проверяли в 2011 году, но тогда почти ничего интересного не нашлось. Вот я и решил посмотреть, как обстоят дела, спустя 2 года.

0220_Checking_VirtualDub_ru/image1.png

Я скачал с сайта VirtualDub архив VirtualDub-1.10.3-src.7z. Для анализа я использовал PVS-Studio версии 5.10. На анализ я потратил около часа, так что не судите строго. Наверняка, я что-то пропустил. И наоборот, мог поcчитать корректный код за подозрительный. Прошу, тех, кто поддерживает проект VirtualDub не полагаться на мой отчет, а произвести самостоятельную проверку. Мы всегда идём на встречу open-source сообществу и готовы выделить регистрационный ключ.

Ещё я хочу попросить Эвери Ли отнестись к этой статье с пониманием. В прошлый раз, он очень болезненно воспринял упоминание VirtualDub в одной из моих статей. Я вовсе не хотел, и не хочу сказать, что какая-то программа является глючной. Программные ошибки есть везде. Моя цель показать пользу, которую может давать использование статического анализа кода. Попутно, это сделает open-source проекты немного надёжнее. Это замечательно.

Конечно, разовые проверки малоэффективны. Но с этим, к сожалению, я ничего поделать не могу. Использовать или не использовать регулярно инструменты статического анализа, зависит от разработчиков. Я только могу пытаться объяснить, в чем польза регулярного использования. Вот одна из интересных заметок на эту тему: Лев Толстой и статический анализ кода.

Впрочем, эта статья про ошибки, а не методологию использования статического анализа. Давайте посмотрим, что нового интересного нашел анализатор PVS-Studio в VirtualDub.

Виртуальные деструкторы

В языке программирования C++ деструктор полиморфного базового класса должен объявляться виртуальным. Только так обеспечивается корректное разрушение объекта производного класса через указатель на соответствующий базовый класс.

Я знаю, что все это знают. Однако это не мешает забыть объявить деструктор виртуальным.

В VirtualDub есть класс VDDialogBaseW32:

Как видите, он содержит виртуальные функции. Однако деструктор не объявлен виртуальным. Естественно, он него наследуются классы. Например, VDDialogAudioFilterFormatConvConfig:

Предупреждение выданное PVS-Studio: V599 The destructor was not declared as a virtual one, although the ‘VDDialogBaseW32’ class contains virtual functions. VirtualDub gui. cpp 997

Как видите, для уничтожения объекта используется указатель на базовый класс. Такое удаление объекта приведет к неопределенному поведению программы.

Аналогичная беда существует и с классом VDMPEGAudioPolyphaseFilter.

Ещё о неопределённом поведении

По поводу ошибок, связанных с виртуальным деструктором, вопросов не возникает. Намного более скользкой темой являются операции сдвига. Вот один из таких примеров:

Можно сколько угодно утверждать, что это совершенно надёжный код, который успешно используется уже больше 10 лет. Однако, всё равно, здесь мы имеем дело с неопределенным поведением программы. Вот что стандарт говорит на тему таких конструкций:

The shift operators << and >> group left-to-right.

The operands shall be of integral or unscoped enumeration type and integral promotions are performed.

1. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

2. The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 * 2^E2, reduced modulo one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1*2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

3. The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a non-negative value, the value of the result is the integral part of the quotient of E1/2^E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

То, что код работает, это везение. При осваивании нового компилятора или других ключей для оптимизации, программа может неожиданно изменить своё поведение. Подробнее про сдвиги и о том, стоит или не стоит править код, можно почитать в статье «Не зная брода, не лезь в воду. Часть третья».

Вот полный список мест, где анализатор PVS-Studio обнаружил Undefined behavior или Unspecified behavior в проекте VirtualDub.

Опечатки

Диагностическое сообщение выданное PVS-Studio: V529 Odd semicolon ‘;’ after ‘while’ operator. VirtualDub crash. cpp 462

Обратите внимание на точку с запятой, стоящую после ‘while’. Здесь или ошибка или код неверно отформатирован. Я думаю, это именно ошибка. Цикл «while(*pszHeap++);» пройдет до конца строки и в результате, переменная ‘pszHeap’ будет указывать на память после терминального нуля. Проверка «if (pszHeap[-1]==’.’)» не имеет смысла. По адресу «pszHeap[-1]» всегда находится терминальный ноль.

Рассмотрим другую опечатку, при работе со строками.

Диагностическое сообщение выданное PVS-Studio: V637 Two opposite conditions were encountered. The second condition is always false. Check lines: 183, 184. VirtualDub backface. cpp 183

Этот код должен пропустить всё, что содержится в кавычках. По крайней мере, мне так кажется. Однако, условие (*s && *s != ‘»‘) сразу является ложным. Возможно, код должен был выглядеть так:

Оператор new генерирует исключения при ошибке выделения памяти

В старом коде, часто можно встретить проверку того, что вернул оператор new. Выглядит это так:

Современные компиляторы, поддерживающие стандарт языка Си++, должны сгенерировать исключение, если не удалось выделить память. Можно сделать, чтобы оператор ‘new’ не генерировал исключения, но к рассматриваемым случаям, сейчас это отношения не имеет.

Таким образом, проверка if (!p) является лишней. В целом такой код безобиден. Лишняя проверка. Ничего страшного.

Однако старый код может привести к неприятным последствиям. Рассмотрим фрагмент из проекта VirtualDub.

Диагностическое сообщение выданное PV-Studio: V668 There is no sense in testing the ‘next’ pointer against null, as the memory was allocated using the ‘new’ operator. The exception will be generated in the case of memory allocation error. VirtualDub hexviewer. cpp 2012

Если в строке «char *revstring = new char[nFindLength];» возникнет исключение, то произойдет утечка памяти. Операторы delete[] не будут вызваны. Не критичная ошибка, но всё равно стоит про неё упомянуть.

Здесь, перечислены все места, где в VirtualDub проверяется указатель после вызова оператора ‘new’.

Ссылка на уничтоженный объект

Диагностическое сообщение выданное PVS-Studio: V558 Function returns the reference to temporary local object: tmp. VirtualDub vdstl. h 460

Функция реализована неправильно. Она возвращает ссылку на локальный объект ‘tmp’. После выхода из функции, он будет уже разрушен. Работа с этой ссылкой, приведёт к неопределенному поведению.

Кстати, оператор ++, находящийся рядом, реализован правильно.

В начале используем, потом проверяем

В разных программах, часто можно встретить ошибку, когда указатель сначала разыменовывается, а уже только потом, сравнивается с NULL. Такие ошибки не проявляют себя очень долгое время, так как равенство указателя NULL, это нештатная редкая ситуация. Эти недочёты, существуют и в коде VirtualDub. Пример:

Диагностическое сообщение выданное PVS-Studio: V595 The ‘mpData’ pointer was utilized before it was verified against nullptr. Check lines: 1422, 1429. Tessa context_d3d9.cpp 1422

В начале указатель «mpData» разыменовывается. Затем проверяется: «if (mpData)». Такие ошибка обычно возникаю в процессе рефакторинга. Новый код вставляется до нужных проверок.

Другие места, где анализатор выдает предупреждение V595, перечислены здесь.

Работа с типом HRESULT

Диагностическое сообщение выданное PVS-Studio: V545 Such conditional expression of ‘if’ operator is incorrect for the HRESULT type value ‘AVIStreamInfoA(pas, & asi, sizeof asi)’. The SUCCEEDED or FAILED macro should be used instead. VirtualDub avireadhandlertunnelw32.cpp 230

Функция AVIStreamInfo() возвращает значение типа HRESULT. Этот тип нельзя интерпретировать как ‘bool’. Информация, хранящаяся в переменной типа HRESULT, имеет достаточно сложную структуру. Для проверки значения HRESULT необходимо использовать макрос SUCCEEDED или FAILED, объявленные в «WinError. h». Вот как устроены эти макросы:

Правильный вариант кода:

Аналогичные предупреждения PVS-Studio выдаёт на следующие строки:

  • avireadhandlertunnelw32.cpp 238
  • avireadhandlertunnelw32.cpp 335
  • inputfileavi. cpp 440
  • context_d3d11.cpp 959

Магические числа

Задание длины строки с помощью числа не очень хорошая идея. Очень легко ошибиться, подсчитывая символы. Пример:

Диагностическое сообщение выданное PVS-Studio: V512 A call of the ‘memcmp’ function will lead to underflow of the buffer ‘»GL_EXT_blend_subtract»‘. Riza opengl. cpp 393

Длина строки «GL_EXT_blend_subtract» не 20, а 21 символ. Ошибка некритична. На практике коллизий не возникнет. Тем не менее, следует избегать таких магических чисел. Лучше использовать специальный макрос для подсчета длины строки. Пример:

В Си++ можно сделать более безопасную шаблонную функцию:

Преимущество второго варианта — нельзя случайно передать в качестве аргумента простой указатель. Подробнее этот приём описан в статье «PVS-Studio vs Chromium».

Абсолютные пути

Диагностическое сообщение выданное PVS-Studio: V631 Consider inspecting the ‘LoadLibraryA’ function call. Defining an absolute path to the file or directory is considered a poor style. VirtualDub leaks. cpp 67, 69

Думаю понятно, чем этот код плох. Конечно, код связан с отладкой, и вряд ли как-то негативно скажется на конечных пользователях. Но всё равно, было бы лучше получить правильный путь до Program Files.

Неправильный аргумент

Диагностическое сообщение выданное PVS-Studio: V576 Incorrect format. Consider checking the fourth actual argument of the ‘printf’ function. The argument is expected to be not greater than 32-bit. Asuka lookup. cpp 56

Переменная ‘rva’ является 64-битным типом. Это значит, что в стек будет помещено 8 байт. Функция printf() является функцией с переменным количеством аргументов. Тип данных, которые она должна обработать, задаётся с помощью строки форматирования. В данном случае, переменная ‘rva’ будет обработана как 32-битная переменная («%x»).

Приведёт данная ошибка к сбою или нет, зависит от того, как организует передачу аргументов компилятор и от разрядности платформы. Например, в Win64 все целочисленные типы вначале приводятся к 64-битному типу, и уже затем помещаются в стек. Проблемы, что переменная займет в стеке больше места, чем надо, не будет.

Впрочем, если переменная ‘rva’ хранит значения более INT_MAX, её значение в любом случае будет распечатано некорректно.

Аналогичные предупреждения анализатор выдаёт здесь:

Неправильные сравнения

Диагностическое сообщение выданное PVS-Studio: V547 Expression ‘res < 0’ is always false. Unsigned type value is never < 0. Riza w32videocodecpack. cpp 828

Переменная ‘res’ имеем беззнаковый тип DWORD. Это значит, что выражение «res < 0» всегда равно значению ‘false’.

Аналогичная проверка содержится здесь: w32videocodec. cpp 284.

Рассмотрим ещё одну схожую ошибку.

Диагностическое сообщение выданное PVS-Studio: V605 Consider verifying the expression: icErr <= — 400L. An unsigned value is compared to the number -400. system error_win32.cpp 54

Переменная ‘ icErr’ имеет тип ‘unsigned’. Следовательно, перед сравнением число ‘-400’ будет неявно преобразовано в ‘unsigned’. Значение ‘-400’ превратится в 4294966896. Таким образом, сравнение (icErr <= -400) эквивалентно (icErr <= 4294966896). По всей видимости, это не то, что хотел программист.

Разное странное

Диагностическое сообщение выданное PVS-Studio: V501 There are identical sub-expressions ‘stream. mChunkCount’ to the left and to the right of the ‘&&’ operator. VirtualDub avioutputfile. cpp 761

Два раза проверяется переменная ‘stream. mChunkCount’. Одна проверка лишняя или забыли проверить что-то ещё.

Диагностическое сообщение выданное PVS-Studio: V570 The ‘this->hic’ variable is assigned to itself. Riza w32videocodecpack. cpp 253

Диагностическое сообщение выданное PVS-Studio: V646 Consider inspecting the application’s logic. It’s possible that ‘else’ keyword is missing. VirtualDub optdlg. cpp 120

Возможно, код неправильно отформатирован. А возможно, забыто ключевое слово ‘else’.

Диагностическое сообщение выданное PVS-Studio: V519 The ‘mbAudioHardwarePresent’ variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 274, 275. VDCapture cap_screen. cpp 275

Заключение

Как видите, даже при разовом запуске, статический анализ может быть полезен. Но намного полезнее запускать его регулярно. Ведь предупреждения (warnings) в компиляторе, программисты включают не один раз перед релизом, а пользуются ими постоянно. Та же самая ситуация и с инструментами статического анализа. Постоянное их использование позволяет оперативно устранять ошибки. Можно рассматривать PVS-Studio как некую надстройку над компилятором, которая выдаёт больше интересных предупреждений. Наилучший вариант, это использование инкрементального анализа кода. Вы обнаружите новые ошибки сразу после компиляции измененных файлов.

VirtualDub — бесплатный видеоредактор поддерживающий работу с avi, mp4, wmv, flv, mpg, mkv, mp3, wav, mov, gif и еще более чем 70 форматами. VirtualDub может обрабатывать видео без рекомпрессии, что позволяет обрезать и склеивать видео без потери качества меньше чем за минуту.

Для корректной работы VirtualDub обязательно необходимо установить кодеки. Также рекомендуем установить пакет из 200+ фильтров и плагины.

Основные возможности VirtualDub:

  • удаление фрагмента видео
  • склеивание нескольких видео в одно
  • удаление и добавление звуковых дорожек
  • вырезание звука и видео в отдельные файлы
  • синхронизация аудио и видео
  • поворот видео
  • удаление шума
  • изменение размера кадра
  • настройка количества кадров в секунду
  • редактирование видео без перепаковки звука и наоборот
  • поддержка нескольких звуковых дорожек
  • запись видео с экрана
  • создание GIF-анимации
  • перекодировка видео и аудио другими кодеками

Как обрезать видео?

  1. Включаем: Видео → ‘Прямопотоковое копирование’
  2. Включаем: Аудио → ‘Прямопотоковое копирование’
  3. Выделяем начало и конец удаляемого фрагмента кнопками
  4. Удаляем клавишей Del
  5. Файл → ‘Сохранить как AVI’ → Указываем имя и расширение(не обязательно AVI)

Как склеить несколько видео в одно?

  1. Открываем первый фрагмент: Файл → ‘Открыть видео файл’
  2. Добавляем другие фрагменты: Файл → ‘добавить AVI сегмент’
    Если кнопка ‘добавить AVI сегмент’ неактивна, то необходимо предварительно пересохранить все части видео в формат AVI программой VirtualDub (Даже если они уже AVI).
  3. Чтобы не было потери качества включаем ‘Прямопотоковое копирование’ во вкладках Аудио и Видео.
  4. Сохраняем: Файл → ‘Сохранить как AVI’ → Указываем имя и расширение(не обязательно AVI)

Кодируем видео другим кодеком

  1. Включаем: Видео → ‘Режим полной обработки’
  2. Видео → ‘Компрессия’ → Выбираем кодек(Xvid, MPEG-2, H.264. )
  3. Включаем: Аудио → ‘Режим полной обработки’
  4. Аудио → ‘Компрессия’ → Выбираем кодек(MPEG Layer 3, IMA. )
  5. Сохраняем: Файл → ‘Сохранить как AVI’ → Указываем имя и расширение(не обязательно AVI)

Избавляемся от ошибок VirtualDub — ставим кодеки

Без установки внешних кодеков VirtualDub может выдавать ошибки при попытке открыть файл.
Исправляется это установкой K-lite Codek Pack.
При установке этого пакета кодеков необходимо отметить флажками кодек ffdshow.

Пакет плагинов VirtualDub Plugin Pack

  • AC3 — поддержка АС3 аудио
  • MPEG-2 — форматы mpg, vob, m2v, vro
  • WMV — asf, wmv, wma
  • QuickTime — mov, mp4
  • FLIC — fli, flc
  • FLV
  • DirectShow

Набор Фильтров VirtualDub Filter Pack

Содержит более 200 фильтров. Рассмотрим несколько:

  • 2:1 reduction — уменьшает видео в 2 раза
  • Invert — инвертирует цвета
  • Levels — регулирование уровня освещения
  • Rotate — поворот на 90, 180, 270 градусов
  • Subtitler — добавление субтитров
  • Motion blur — размывает движения
  • Logo — добавление логотипа
  • Flip horizontally, Flip vertically — поворот по горизонтали, вертикали
  • Levels — уровень освещенности
  • Colorize — приведение к одноцветному
  • Grayscale — приведение к черно-белому
  • MSU Subtitle and Logo Remover, Logoway — удаление субтитров и логотипов
  • MSU Smart Brightness and Contrast Filter — яркость и контрастность
  • MSU Denoising Filter, Temporal cleaner — удаление шума
  • MSU Smart Sharpen Filter, Unsharp Mask — увеличение резкости, четкости
  • Video DeNoise — удаление шума
  • Zoom — изменение перспективы

У меня есть много видео кодеки, установленные на моей машине, но это только позволяет мне выбрать из очень простой выбор.

должен ли я вручную указать VirtualDub на кодеки?

3 ответов

VirtualDub требует видео для Windows (Vfw) совместимый кодек для распаковки видео.
Кодеки DirectShow, например используемые проигрывателем Windows Media, не подходят.

Это означает, что не все кодеки, установленные на вашем компьютере могут быть использованы в VirtualDub.

очень хороший пакет кодеков с VFW ffdshow.
Просто убедитесь, что вы включите все ваши формат декодеры под VFW Configuration — > Decoder — > Codecs.

как вручную добавить/удалить / переключить кодеки?

в Windows NT / 2000 эти параметры хранятся в реестре вместе с VIDC. xxxx часть в качестве ключа и имя драйвера в качестве значения. Большинство кодеков хранятся в:

вы также можете найти их в подразделе:

обычно, после изменения вам нужно только перезапустить приложения, которые используют кодеки, а не перезапустить весь система.

Source и более подробную информацию о VirtualDub и кодеков.

нет, но вы должны использовать 32-битную версию Virtualdub, так как большинство кодеков не перекомпилированы для 64-битной версии.

Решил оцифровать кассету. Подсоединил видеомагнитофон к компу через устройство видеозахвата. Цифрую с помощью VirtualDub. Но вот в чём проблема, VirtualDub захватывает видео в формате без сжатия, соответственно на выходе мы получаем гигААААнтские файлы. Не могу понять (я олень), каким образом "присоединить" к VirtualDub кодеки (h.264 или другие), чтоб он сразу сжимал. На компе все кодеки установлены, но VirtualDub их не видит. Проц позволяет сжимать видео в реальном времени. Гугление вопроса мне не помогло.

Источники:

https://pvs-studio. com/ru/b/0220/

https://teh-fed. ru/virtualdub-ne-vidit-kodeki/

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: