diff --git a/doc/service_lifecycle.md b/doc/service_lifecycle.md index f8279ecd..077b0638 100644 --- a/doc/service_lifecycle.md +++ b/doc/service_lifecycle.md @@ -138,10 +138,14 @@ destroy отсоединённого cursor не обращается к store. Чтение video cursor запрашивает следующий кадр по готовности decoder, независимо от PTS показа; audio cursor сохраняет временной lookahead. Viewers готовят ограниченный батч на запрос: отдельно задаются число видеокадров, audio lookahead и число всех записей. -Desktop принимает до 16 записей и обрабатывает треки по их готовности; Android запрашивает одну запись. +Desktop и Android принимают до 16 записей и обрабатывают треки по их готовности. +VIDEO_END/AUDIO_END следуют за последними пакетами своего трека, не ожидая другого трека; +общий END завершает вход, а окно ждёт опустошения обоих выходов. GAP/CLOSED отменяют старый батч. Получение батча погашает запрос, включая повторные обращения до pull; скрытого чтения следующего батча нет. Callback готовности только будит desktop worker и не вызывает API ядра/bridge. Stop снимает callback под mutex viewer до освобождения его контекста; detach будит ожидающие workers до уничтожения manager. +На Android signal принадлежит worker и живёт до stream_stop и close mixer input; ядро только уведомляет его. +В wait не удерживаются mutex bridge/ядра. Расход PCM будит тот же signal, уведомления сливаются без потери пробуждения. GUI и audio callback будят worker при движении часов/освобождении места; сна между готовыми пакетами нет. Android worker держит один рабочий input и один готовый output MediaCodec; свободный input buffer получает лишь для уже готовых данных и сразу возвращает codec. diff --git a/doc/stream.txt b/doc/stream.txt index 0bcbc99e..e1c560c1 100644 --- a/doc/stream.txt +++ b/doc/stream.txt @@ -829,7 +829,7 @@ Android: При video pressure отбрасываются целые ожидающие кадры, P-кадры запрещены до нового KEY. Кодировщик получает отказ admission и запрашивает KEY с ограничением частоты 500ms. - RX: stream_viewers держит отдельный handle/cursor, demand, status и ограниченный owned батч на session ID. - Android задаёт бюджет в одну запись, соответствующий одному рабочему input StreamDecodePump. + Android принимает до 16 записей: максимум один новый видеокадр и звук в пределах собственного lookahead. Общей RX FIFO нет: остановка/отставание одного reader не задерживает остальных. Получение ответа и сетевые AVAILABLE не запускают автоматическое чтение следующего кадра. Запас остаётся в RS-кольце. want_video запрашивает следующий полный видеокадр независимо от часов показа; @@ -839,7 +839,11 @@ Android: Перед каждым входом проверяется выход; свободный input buffer берётся только для уже готового рабочего кадра. Будущий output ждёт своего PTS и приостанавливает новые video requests; уже запрошенный ответ помещается в рабочий input. Низкая задержка запрашивается через KEY_LOW_LATENCY при заявленной поддержке, но корректность не зависит от этого режима. - За проход обслуживаются до четырёх media replies, до восьми output buffers за один вызов pump. + За проход обрабатываются до 32 готовых записей, до восьми output buffers за один вызов pump. + Готовый батч и расход PCM будят worker через отдельную condition variable, без JVM callbacks из ядра. + Ожидание имеет deadline 5ms при незавершённой работе синхронного MediaCodec, иначе 50ms; + уведомление прерывает ожидание сразу. Между готовыми записями sleep нет. + Worker владеет signal до stream_stop и отсоединения своего mixer input; detach ядра будит signal, не освобождая его. При обычной нехватке данных decoder references сохраняются; GAP/вытеснение требуют нового KEY. Просроченный кадр при нормальной последовательности декодируется без показа. - Фиксированного prebuffer нет. Начало clock/audio output разрешено после первого декодированного кадра. @@ -1041,13 +1045,20 @@ Desktop: Worker держит один рабочий AVPacket и не более двух готовых изображений. Следующий video request не ограничен часами показа. Бюджет нового видео учитывает AVPacket, готовые изображения и оставшиеся кодированные кадры батча. Записи аудио/видео расходуются независимо по готовности потребителя; заполненное видео не блокирует запрос звука. - GAP/FIN/CLOSED обрабатываются после предыдущих записей батча. Между готовыми пакетами нет sleep или нового round trip к ядру. + VIDEO_END/AUDIO_END следуют за последними пакетами своего трека и не ждут расходования другого трека. + END подтверждает выдачу обоих треков. Нулевые бюджеты media также разрешают получение управляющих событий. + GAP/CLOSED отменяют прежний батч и сбрасывают frontend сразу; старые media перед ними не доигрываются. + Между готовыми пакетами нет sleep или нового round trip к ядру. FFmpeg output обслуживается перед input; EAGAIN сохраняет рабочий packet и означает backpressure, не GAP. Дальние кадры и звук не копируются из кольца. SoundTouch и устройство держат только рабочую порцию PCM. Кадры сохраняются до достижения их PTS аудиочасами; задержка показа удерживает clock только этого player. Видео декодируется без задержки frame-threading. При GAP сбрасываются decoder refs и timeline до KEY. - Первый decoded frame разрешает движение clock/audio, защитный guard учитывает наблюдённый decoder lead. - FIN отправляет decoder EOS; выход дренируется теми же ограниченными проходами до EOF, GUI продолжает показ хвоста. + VIDEO_END отправляет decoder EOS, AUDIO_END дренирует SoundTouch независимо от видеодекодера. + Общий END не управляет сбросом отдельных декодеров: иначе заполненный PCM, ожидание первой картинки и decoder EOS образуют цикл. + Выход дренируется теми же ограниченными проходами до EOF, GUI продолжает показ хвоста. + После физического воспроизведения последнего PCM оставшееся видео использует монотонные часы; + после EOF видеодекодера оставшийся звук больше не удерживается ожиданием картинки. Ожидающий кадр не теряется при заполненной очереди изображений. Отсутствие прогресса при наличии работы логируется как ошибка. - При начальном подключении desktop/Android прогревают Opus из кольца до первого KEY (до 500ms истории). Старый звук обрезается по PTS видео, включая начало stereo PCM-пакета; декодер не сбрасывается из-за preroll. @@ -1113,7 +1124,10 @@ StreamDecodePumpTest проверяет конвейеры 4/8 кадров пр test_stream_playback проверяет восемь будущих кадров из кольца без video lookahead; test_android_stream — один ответ при повторных запросах, отсутствие скрытого prefetch и возобновление с прежней позиции. test_stream_view дополнительно задерживает четыре настоящих FFmpeg decoded frames: запуск требует кадра после 80ms, -а короткий поток из трёх кадров получает все изображения после FIN, без потери decoder tail. +а короткий поток из трёх кадров получает все изображения после FIN, в том числе при заполненном аудиобуфере. +Проверяются также 8 кадров, отсутствие звука и аудиотрек короче/длиннее видео; завершение ждёт оба выходных хвоста. +StreamReadQueueTest проверяет независимые EOF, отмену старых записей по GAP/CLOSED и границы записей батча. +test_android_stream проверяет батч из 3 video + 3 audio, уведомления без потерь и пробуждение ожидающего worker при stop. Батчевый сценарий проверяет накопленный поток, раздельные бюджеты треков, независимые readers, уведомления и остановку: 28 video + 50 audio записей проходят через 16 батчей без пропусков/повторов, END следует за последними данными. Диагностика debug: viewer buffer выводит число/максимальный размер батчей и prepare/handoff (среднее/максимум);