Prime late stream audio without waiting for its playback clock

This commit is contained in:
evgeny committed 2026-10-16 17:24:25 +02:00
1 parent 0c26daf3f5
commit c03fe3da71
2 files changed
+16 -10

No files matched your search

+14 -10
View File
@@ -35,7 +35,7 @@ struct stream_media_cursor {
unsigned tracks;
int64_t start_us;
int audio_started;
int64_t audio_blocked_pts;
int64_t audio_start_us;
uint8_t *scratch[2], *frame;
size_t scratch_capacity, frame_capacity;
uint64_t scratch_seq[2];
@@ -260,7 +260,7 @@ void stream_media_cursor_destroy(struct stream_media_cursor *c) {
void stream_media_cursor_reset(struct stream_media_cursor *c) {
if (!c) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor reset: null cursor"); return; }
c->initialized = 0; c->video_revision = 0; c->pending_used = 0;
c->audio_started = 0; c->audio_blocked_pts = -1;
c->audio_started = 0; c->audio_start_us = -1;
c->pending = (struct stream_media_video){0}; c->pending_format = (struct stream_media_format){0};
c->scratch_valid[0] = c->scratch_valid[1] = 0;
c->video = (struct cursor_track){0}; c->audio = (struct cursor_track){0};
@@ -274,7 +274,7 @@ static int cursor_start(struct stream_media_cursor *c) {
if (s->failed) return STREAM_ERR_FORMAT;
if (c->initialized) return STREAM_OK;
if (!s->count || s->key_seq == UINT64_MAX || !media_find(s, s->key_seq)) return s->finished ? STREAM_GAP : STREAM_WAIT;
c->generation = s->generation; c->start_us = s->key_us;
c->generation = s->generation; c->start_us = s->key_us; c->audio_start_us = -1;
c->video.seq = s->key_seq; c->audio.seq = s->key_seq;
for (unsigned i = 0; i < s->count; i++) {
struct media_position *p = media_at(s, i);
@@ -426,8 +426,18 @@ int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, in
if (c->audio.ended && c->video.ended) {
*packet = (struct stream_media_packet){.kind = STREAM_MEDIA_READ_END}; return STREAM_OK;
}
/* Поздний первый звук должен прогреть decoder до запуска audio clock. Основание фиксировано,
* поэтому неподвижные часы разрешают только один lookahead, а не всё оставшееся аудио. */
if (ar == STREAM_OK && c->audio_start_us < 0) {
c->audio_start_us = audio_pts > c->start_us ? audio_pts : c->start_us;
if (audio_pts > c->start_us) DEBUG_INFO(DEBUG_CATEGORY_STREAM,
"media cursor audio startup source=%llx video_pts=%lld audio_pts=%lld gap_us=%lld",
(unsigned long long)c->store->descriptor.source_node_id, (long long)c->start_us,
(long long)audio_pts, (long long)(audio_pts - c->start_us));
}
int64_t audio_position = position > c->audio_start_us ? position : c->audio_start_us;
int64_t audio_limit = audio_ahead < 0 ? -1 : position < 0 ? c->start_us
: position > INT64_MAX - audio_ahead ? INT64_MAX : position + audio_ahead;
: audio_position > INT64_MAX - audio_ahead ? INT64_MAX : audio_position + audio_ahead;
int video_allowed = want_video && vr == STREAM_OK;
if (ar == STREAM_OK && audio_ahead >= 0 && audio_pts <= audio_limit && (audio_pts < video_pts || !video_allowed)) {
/* video peek мог переиспользовать scratch аудио: заново взять ту же пачку из двухэлементного cache. */
@@ -445,11 +455,5 @@ int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, in
return STREAM_OK;
}
if (video_allowed) return cursor_video(c, packet);
if (!c->audio_started && ar == STREAM_OK && audio_ahead >= 0 && audio_pts > audio_limit && c->audio_blocked_pts != audio_pts) {
c->audio_blocked_pts = audio_pts;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "media cursor first audio waits source=%llx seq=%llu pts=%lld position=%lld limit=%lld video_requested=%d",
(unsigned long long)c->store->descriptor.source_node_id, (unsigned long long)c->audio.seq,
(long long)audio_pts, (long long)position, (long long)audio_limit, want_video);
}
return ar == STREAM_MORE || vr == STREAM_MORE ? STREAM_MORE : STREAM_WAIT;
}
+2
View File
@@ -50,6 +50,8 @@ void stream_media_cursor_reset(struct stream_media_cursor *cursor);
int stream_media_cursor_status(struct stream_media_cursor *cursor, struct stream_media_status *status);
/* want_video=1 запрашивает следующий кадр независимо от часов показа; 0 оставляет видео в кольце.
* position_us<0 прогревает аудио перед KEY; затем звук выдаётся до position_us+audio_ahead_us, -1 отключает звук.
* Если звук начинается позже KEY, lookahead отсчитывается минимум от первого аудиопакета:
* decoder получает стартовый PCM при неподвижных часах; размер разрешённого окна остаётся ограниченным.
* При GAP/EXPIRED cursor сбрасывают отдельно: это позволяет frontend сначала обработать разрыв.
* Бюджет одного вызова ограничен; MORE требует отложенного продолжения, WAIT — новых данных/позиции. */
int stream_media_cursor_read(struct stream_media_cursor *cursor, int64_t position_us, int want_video, int64_t audio_ahead_us,