четверг, 25 мая 2023 г.

[prog] Ошибочность ощущения что стал меньше ошибаться :)))

Когда я начал программировать, а было это уже давным-давно, то главным впечатлением было "как же много мы, люди, ошибаемся". Про древнее высказывание "Errare humanum est" уже тогда был наслышан, но не подозревал, насколько это верно :)

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

Однако, время идет, опыт (смею надеятся) накапливается. И время от времени начинает складываться ощущение, что этот самый опыт позволяет тебе ошибаться меньше. В основном за счет заученных способов избежать ходьбы по граблям и привитым привычкам тестировать как можно раньше (а также попыток писать код в стиле, при котором компилятор чаще бьет тебя по рукам).

Вот складывается такое ощущение на некотором временном отрезке. А потом как будто кто-то с толстенным гроссбухом выходит из сумрака и говорит: "У вас тут долгов поднакопилось, надо бы отдать"... И ошибки начинают валиться как из рога изобилия. Иногда догоняя тебя спустя восемь или девять лет.

Сразу же приходит понимание, что это самое ощущение есть не что иное, как очередная ошибка.

Errare humanum est, короче говоря.

Так и живем ;)

понедельник, 22 мая 2023 г.

[prog.c++] Так вот на счет AVChannelLayout...

...в качестве дополнения к этому посту: Хочется странного: особое отношение C++ного компилятора к структурам, объявленным как extern "C". Попробую вспомнить особенность перехода с FFMPEG 4.4 на FFMPEG 5.1, связанную с AVChannelLayout.

В текущем проекте нужно снимать видео/аудио потоки с камер посредством FFMPEG. В случае с аудио потоками бывает нужно один из них воспроизводить. Так же может потребоваться какие-то из них записывать (если записывать, то вместе с видео).

Приходить аудио может в разных форматах (благо FFMPEG отличается всеядностью), а вот для воспроизведения должен использоваться зафиксированный в коде формат. Для записи также должен использоваться зафиксированный в коде формат, но (ЕМНИП) не такой, как для воспроизведения.

Кроме того, снятые AVFrame протаскиваются через цепочку SObjectizer-овских агентов и i-й агент в цепочке, в принципе, может не иметь всей информации об исходном потоке и его состоянии. Т.е. агент, который напрямую дергает FFMPEG, обнаруживает разрыв и переподключение к камере (соответственно, у него пересоздаются AVCodecContext-ы), а вот последующие агенты могут информацию о переподключениях не получить и не узнать, что новые AVFrame содержат аудио-данные уже в другом формате.

В общем, говоря про код, есть две задачки: a) передать конфигурацию агентам, которые занимаются перекодированием аудио-данных (для воспроизведения или записи) и b) проверять формат аудио-данных в очередном AVFrame чтобы поймать момент внезапной смены формата (если таковая смена вообще происходит).

Для этого была сделана простая структурка, содержащая минимум полей: channel_layout в виде единственного std::int64_t, sample_fmt в виде AVSampleFormat и sample_rate в виде обычного int-а. В общем, тривиальный POD тип с constexpr-конструктором. Экземпляры этого типа используются и для того, чтобы зафиксировать формат аудио для воспроизведения/записи, и для того, чтобы сохранить информацию о параметрах текущего аудио-потока.

Благодаря тому, что описание формата можно было объявлять как constexpr константу, то в нескольких местах кода были сделаны static_assert-ы, для того, чтобы в compile-time проверять, что написанный под определенный аудио-формат код остается актуальным (т.е. если где-то описание формата вдруг поменяли, а код не поправили, то это будет обнаружено компилятором).

Все это хорошо работало потому, что в FFMPEG 4.4 количество и расположение аудио-каналов описывалось, по сути, единственным целым числом -- channel_layout. Иногда этот channel_layout мог быть нулевым (т.е. не заданным явно), тогда значение channel_layout можно было вывести на основании значения nb_channels.

Но вот в FFMPEG 5.1 целочисленное поле channel_layout задеприкейтили (но хотя бы поддерживают), и ввели новый тип данных AVChannelLayout, который является совокупностью из channel_layout+nb_channel из FFMPEG 4.4, но не только. Экземпляр AVChannelLayout теперь может содержать и дополнительную информацию. В том числе и информацию, которая расположена в динамической памяти, т.е. в AVChannelLayout может лежать и вполне себе владеющий указатель.

Поскольку сейчас AVChannelLayout -- это отнюдь не простая структура, то пришлось менять существующий в проекте тип, описывающий формат аудио. Т.е. поле channel_layout типа std::int64_t нужно было заменить на ch_layout типа AVChannelLayout.

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