Browse Source

Read the next live video frame independently of presentation time

master
evgeny 5 days ago
parent
commit
f317d3e989
  1. 18
      src/stream/stream_media_buffer.c
  2. 9
      src/stream/stream_media_buffer.h

18
src/stream/stream_media_buffer.c

@ -351,7 +351,7 @@ static int cursor_peek(struct stream_media_cursor *c, struct cursor_track *track
return STREAM_MORE; return STREAM_MORE;
} }
/* Сборка одного ближайшего H264 кадра; остальные кадры продолжают храниться только в RS. */ /* Сборка одного следующего видеокадра; остальные кадры продолжают храниться только в RS. */
static int cursor_video(struct stream_media_cursor *c, struct stream_media_packet *packet) { static int cursor_video(struct stream_media_cursor *c, struct stream_media_packet *packet) {
for (unsigned work = 0; work < 16; work++) { for (unsigned work = 0; work < 16; work++) {
struct stream_media_block b; int64_t pts; struct stream_media_block b; int64_t pts;
@ -385,12 +385,12 @@ static int cursor_video(struct stream_media_cursor *c, struct stream_media_packe
return STREAM_MORE; return STREAM_MORE;
} }
/* Выдача по времени потребителя оставляет накопленный запас в общем кольце. */ /* Видео выдаётся по готовности decoder, звук — по времени потребителя. */
int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, int64_t video_ahead, int64_t audio_ahead, int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, int want_video, int64_t audio_ahead,
struct stream_media_packet *packet) { struct stream_media_packet *packet) {
if (!c || !packet || video_ahead < -1 || video_ahead > 60000000 || audio_ahead < -1 || audio_ahead > 60000000) { if (!c || !packet || (want_video != 0 && want_video != 1) || audio_ahead < -1 || audio_ahead > 60000000) {
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor read invalid arguments ahead=%lld/%lld", DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor read invalid arguments video=%d audio_ahead=%lld",
(long long)video_ahead, (long long)audio_ahead); return STREAM_ERR_ARGUMENT; want_video, (long long)audio_ahead); return STREAM_ERR_ARGUMENT;
} }
int result = cursor_start(c); int result = cursor_start(c);
if (result != STREAM_OK) { if (result != STREAM_OK) {
@ -402,18 +402,16 @@ int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, in
struct stream_media_block audio, video; int64_t audio_pts = INT64_MAX, video_pts = INT64_MAX; struct stream_media_block audio, video; int64_t audio_pts = INT64_MAX, video_pts = INT64_MAX;
if (!c->store->format.audio_codec) c->audio.ended = 1; if (!c->store->format.audio_codec) c->audio.ended = 1;
int ar = c->audio.ended ? STREAM_GAP : audio_ahead < 0 ? STREAM_WAIT : cursor_peek(c, &c->audio, 0, &audio, &audio_pts); int ar = c->audio.ended ? STREAM_GAP : audio_ahead < 0 ? STREAM_WAIT : cursor_peek(c, &c->audio, 0, &audio, &audio_pts);
int vr = video_ahead < 0 ? STREAM_WAIT : cursor_peek(c, &c->video, 1, &video, &video_pts); int vr = want_video ? cursor_peek(c, &c->video, 1, &video, &video_pts) : STREAM_WAIT;
if ((ar == STREAM_GAP && !c->audio.ended) || (vr == STREAM_GAP && !c->video.ended)) return STREAM_GAP; if ((ar == STREAM_GAP && !c->audio.ended) || (vr == STREAM_GAP && !c->video.ended)) return STREAM_GAP;
if (ar == STREAM_EXPIRED || vr == STREAM_EXPIRED) return STREAM_EXPIRED; if (ar == STREAM_EXPIRED || vr == STREAM_EXPIRED) return STREAM_EXPIRED;
if (ar < 0 || vr < 0) return ar < 0 ? ar : vr; if (ar < 0 || vr < 0) return ar < 0 ? ar : vr;
if (c->audio.ended && c->video.ended) { if (c->audio.ended && c->video.ended) {
*packet = (struct stream_media_packet){.kind = STREAM_MEDIA_READ_END}; return STREAM_OK; *packet = (struct stream_media_packet){.kind = STREAM_MEDIA_READ_END}; return STREAM_OK;
} }
int64_t video_limit = video_ahead < 0 ? -1 : position < 0 ? c->start_us
: position > INT64_MAX - video_ahead ? INT64_MAX : position + video_ahead;
int64_t audio_limit = audio_ahead < 0 ? -1 : position < 0 ? c->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; : position > INT64_MAX - audio_ahead ? INT64_MAX : position + audio_ahead;
int video_allowed = vr == STREAM_OK && video_ahead >= 0 && video_pts <= video_limit; 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)) { if (ar == STREAM_OK && audio_ahead >= 0 && audio_pts <= audio_limit && (audio_pts < video_pts || !video_allowed)) {
/* video peek мог переиспользовать scratch аудио: заново взять ту же пачку из двухэлементного cache. */ /* video peek мог переиспользовать scratch аудио: заново взять ту же пачку из двухэлементного cache. */
result = cursor_block(c, c->audio.seq, &audio); result = cursor_block(c, c->audio.seq, &audio);

9
src/stream/stream_media_buffer.h

@ -1,10 +1,10 @@
/* Индекс медиапотока поверх общего RS-кольца: хранит только позиции/PTS, без копии кодированного потока. /* Индекс медиапотока поверх общего RS-кольца: хранит только позиции/PTS, без копии кодированного потока.
* Один uasync-поток; store принадлежит stream-session, cursor — конкретному локальному reader. * Один uasync-поток; store принадлежит stream-session, cursor — конкретному локальному reader.
* feed получает уже проверенные последовательные пачки; gap обозначает окончательно потерянный диапазон. * feed получает уже проверенные последовательные пачки; gap обозначает окончательно потерянный диапазон.
* Независимые audio/video позиции читают оригинал из кольца. Рабочая память cursor — один H264 кадр, * Независимые audio/video позиции читают оригинал из кольца. Рабочая память cursor — один видеокадр,
* один Opus-пакет и два scratch медиаблока. Курсоры ничего не закрепляют против вытеснения. * один Opus-пакет и два scratch медиаблока. Курсоры ничего не закрепляют против вытеснения.
* read возвращает заимствованный packet до следующего read/destroy; frontend копирует только ближайшие записи. * read возвращает заимствованный packet до следующего read/destroy; frontend копирует только ближайшие записи.
* WAIT — материал ещё не готов/дальше lookahead. GAP/EXPIRED требуют новой точки KEY; END — весь материал выдан. */ * WAIT — материал ещё не готов/дальше audio lookahead. GAP/EXPIRED требуют новой точки KEY; END — весь материал выдан. */
#ifndef STREAM_MEDIA_BUFFER_H #ifndef STREAM_MEDIA_BUFFER_H
#define STREAM_MEDIA_BUFFER_H #define STREAM_MEDIA_BUFFER_H
@ -46,10 +46,11 @@ int stream_media_cursor_create(struct stream_media_cursor **out, struct stream_m
void stream_media_cursor_destroy(struct stream_media_cursor *cursor); void stream_media_cursor_destroy(struct stream_media_cursor *cursor);
void stream_media_cursor_reset(struct stream_media_cursor *cursor); 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); int stream_media_cursor_status(struct stream_media_cursor *cursor, struct stream_media_status *status);
/* position_us<0 — старт: прогреть аудио перед выбранным KEY. Затем отдавать только до position_us+lookahead_us. /* want_video=1 запрашивает следующий кадр независимо от часов показа; 0 оставляет видео в кольце.
* position_us<0 прогревает аудио перед KEY; затем звук выдаётся до position_us+audio_ahead_us, -1 отключает звук.
* При GAP/EXPIRED cursor сбрасывают отдельно: это позволяет frontend сначала обработать разрыв. * При GAP/EXPIRED cursor сбрасывают отдельно: это позволяет frontend сначала обработать разрыв.
* Бюджет одного вызова ограничен; MORE требует отложенного продолжения, WAIT — новых данных/позиции. */ * Бюджет одного вызова ограничен; MORE требует отложенного продолжения, WAIT — новых данных/позиции. */
int stream_media_cursor_read(struct stream_media_cursor *cursor, int64_t position_us, int64_t video_ahead_us, int64_t audio_ahead_us, int stream_media_cursor_read(struct stream_media_cursor *cursor, int64_t position_us, int want_video, int64_t audio_ahead_us,
struct stream_media_packet *packet); struct stream_media_packet *packet);
#ifdef __cplusplus #ifdef __cplusplus

Loading…
Cancel
Save