LINUX.ORG.RU

Хитрая очередь 2

 


0

2

Приветствую, отдельно потужно постоянных читателей моих тем ))

Требуется реализовать очередную хитро.опую очередь для многопоточной обработки, поступающих с h264 камеры nalu блоков, которые в контексте libav библиотек от ffmpeg будем считать условными кадрами.

Для мжпег камер на базе std::deque получился рабочий вариант - поток чтения берет например с начала индекс выделенного массива под кадры и прочитав в элемент кладет этот индекс с другой стороны очереди. Потоки записи кадров, онлайн трансляции, определения движения и просто запроса снимков делают тоже самое только наибарот к очереди - что позволяет во всех случая получать условно самый свежий кадр и отбрасывать все старье.

Теперича хочется тоже самое но в контексте 264 кодека - т.е. для случая чтения например я бы мог заполнять очередь до поступления И кадра и сразу ее опустошать при его считывании (тем самым гарантируя что в начале очереди всегда ключевой кадр), но вот никак не соображу - а как же «потребители» будут понимать что очередь сброшена и надо начинать читать ее сначала? uint64_t счётчик сброса у всех потоков вести!?

Может многопоточники на плюсах сорентируют какие стандартные алгоритмы для таких вещей используются!?

★★★

Кольцевой буфер в виде списка. Пусть пишет в него, а карусель крутится, если кто читать будет то всегда с «головы» где самое свежее лежит, и не надо им ничего понимать, всегда получат последний записанный кадр, а жопка (где самые старые кадры) автоматически очищается, каждый раз при записи нового кадра в этот буфер. Указатель на «голову» откуда читать меняй и всё. Если чтение быстрее записи то просто будет читаться одно и тоже.

Мимокрокрокодильный мысливслух. Не в рамках С++, а в целом.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Не, про кольцо думал, но не походит, чтение в общем случае всегда быстрей почти всех потребителей, а цель уменьшить задержку, в конце (возможно даже больше половины) каждого GOP выкидывая все

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 3)
Ответ на: комментарий от imatveev13

Декодер не успевает за камерой, насколько точно пока не знаю.

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

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от imatveev13

не совсем к вопросу темы, но тем не менее собсна через libav я и получаю кучу байт в структуре AVPacket при чтении с камеры, однако не называю это кадрами, поскольку «ключевой кадр» будет содержать последовательность NALU из SPS+PPS+SEI+IDR срезов и иногда некую кучу Не-IDR блоков (как названо это в h264.h H264_NAL_SLICE), поэтому например чтобы посчитать длину будущего массива через размер GOP просто несколько раз при запуске перечитываю кадры с камеры так же как и в парсере проверяя 5ый байт.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)

Если честно, малость не понял логику даже с деком. Я правильно понимаю, что у вас на всех потребителей - одна очередь? И каждый потребитель берёт только то, что лежит сверху?

Если да, то зачем тогда вообще очередь? Двойной-тройной буффер на кадры и всё.

В случае h264. Как минимум можно ничего не класть в очередь если кадр не I (ну или нет PPS пока). И оперировать не одним кадром, а пакетом, равным длине группы (GOP), потому как если ты воткнёшься в середину, то без I и предыдущих/следующих кадров всё равно раскодировать не сможешь нормально. Либо, если артефакты допустимы, то хранить текущий кадр и ближайший I кадр.

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

Ну и вопрос другого плана. Смотри, а что, всем потребителям нужен именно закодированный поток? Если я правильно понял схему, то, к примеру, для вещания и записи достаточно перепаковать, ок, раскодировать не нужно, но тут важна целостность потока. Используй выделенную очередь ложи в начало - бери с конца. Очередь на потребителя.

И другая часть потребителей, это те, которые работают с раскодированными данными, определение движения или запись снимка. Ещё одна очередь на раскодировщик, а у него уже просто двойной или тройной буффер на выходе, откуда можно брать только самый свежий кадр.

Ну а что бы всё успевалось: тюнить. vtune в зубы или hotspot/perf и командочку chrt за пазуху.

UPD прочитал про «слабый арм», про vtune тогда забудь :) про hotspot и perf помни.

hatred ★★★
()
Последнее исправление: hatred (всего исправлений: 1)
Ответ на: комментарий от wolverin

Не, про кольцо думал, но не походит, чтение в общем случае всегда быстрей почти всех потребителей

Я правильно понял, в твой бассейн вода наливается быстрей, чем сливается с него. И это не кратковременные пики, а на постоянке. Тогда «сюрприз» неизбежен, чтобы ты не делал.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

да, поэтому я планирую, что когда бассейн полный (поступил IDR срез) сливать все и начинать набирать сначала, тут как то проблемы я не вижу, пока не очень понятно как тем кто на разных уровнях под водой объяснить что бассейн пустой…

или вообще логика не такая должна быть в условиях когда надо отбросить зря прочитанные данные )

wolverin ★★★
() автор топика
Ответ на: комментарий от ya-betmen

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

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 2)
Ответ на: комментарий от wolverin

ю, что когда бассейн полный (поступил IDR срез) сливать все и начинать набирать сначала

А чем тогда кольцевой буфер плох? Да и зачем сливать всё, перед закидыванием нового пакета, выкидываем один старый.

pathfinder ★★★★
()
Последнее исправление: pathfinder (всего исправлений: 1)
Ответ на: комментарий от wolverin

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

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

А у тебя похоже есть много потребителей, которые читают из общего списка. При этом «кадры» оттуда не удаляются.

Или я неправильно понял?

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

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

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

Т.е. у тебя есть массив, в котором лежат «кадры». Каждый клиент имеет свой текущий индекс кадра, который читает.

Ты хочешь в определённый момент сказать всем, я всё почистил - начинай с нуля.

Я правильно понял?

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

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

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 2)

Сделай atomic int, в который выставляешь значение равное количеству потребителей. И каждый потребитель в начале цикла смотрит на значение и если оно больше нуля, то отнимает единицу и начинает чтение с начала очереди.

WatchCat ★★★★★
()

Можете глянуть как сделана синхронизация между affplay: https://github.com/kcat/openal-soft/blob/master/examples/alffplay.cpp

Тут есть синхронизация между аудио и видео «потребителем», а также синхронизация между чтением и декодированием. Можно доделать, чтобы пакеты дропались из send очереди, если их долго не декодировали (send очередь намного больше recieve).

а как же «потребители» будут понимать что очередь сброшена и надо начинать читать ее сначала

Я так понял send очередь может ставить atomic флаг, что она была сброшена. Вопрос, что будет если поток два раза надо будет дропать.

P.S: много не понял, поэтому мог вообще не на то ответить.

maxis11
()

У меня вопрос, если всё идет в рамках одного процесса и нескольких потоков, то почему нельзя использовать std::shared_ptr и индивидуальные очереди для каждого потока-получателя? Поток, генерирующий кадры, засунет shared_ptr в очередь каждого получателя и забудет о судьбе этого кадра. Объект с кадром освободится, когда не будет ни одного потока с удерживающим его std::shared_ptr.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

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

но тут мне мысль накинули - для чтение кадров с камеры классическое кольцо, размер которого гарантирует на его круге наличие последнего I кадра (набор срезов, из которого его можно восстановить), но хрен его знает где - любой поток подключаясь к этой очереди выполняет обратный поиск от головы кольца опорного пакета со срезами и начинает оттуда пользовать очередь, теперь надо придумать что делать если поток чтения с камеры догоняет например медленный поток декодера или пытаться обеспечивать диким размером буфер, чтобы самый медленный поток никогда не успевал «догонаться» за время цикла обработки пакета…

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

неее, тут зрада будет, получатся рывки сильнее будут (чем изначальный вариант с очередью, когда рывок обеспечивать только внутри ГОПа), когда оглянувшись назад и увидев что поток чтения с камеры догоняет и надо рвануть до следующего опорника )

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

потому что кадры за время GOP имеют обратную связь друг с другом

Ох, ну имеют. Как это противоречит тому, что я сказал? Мне это не очевидно. Ладно, ты справишься со своей проблемой сам, я уверен.

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

противоречие в том, что невозможно технически обработать все кадры поступившие с камеры, а ваш вариант подразумевает что ничего не пропускается, да еще наверное копируется память каждому потоку

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

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

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 2)
Ответ на: комментарий от wolverin

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

wolverin ★★★
() автор топика
Ответ на: комментарий от wolverin

вопщим схема оказалась рабочей - стандартное кольцо, учитывающее положение головы, ключевого среза и позиции декодера и если требуется ожидании на условной переменной ключевого кадра с учетом близости головы к ключевому срезу с учетом времени всех операций подсчитанных методом тыка для конкретной железки, оценка 3х переменных внутри мутекса, размер буфера гарантированно вмещает 1 idr срез… остальное вродь детали

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от wolverin

видос конеш интересный получился )) 50% кадров размазываются по секунде в виде замедления движа и в конце рывок, но зато артефакт только во время рывка

статичная картинка уапще идеальна ))

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 2)