Give audio device lead and video decode lead independent read limits
This commit is contained in:
1 parent
75bd6f1ce1
commit
866ca0923d
2 files changed
+28
-15
No files matched your search
@@ -32,6 +32,7 @@ struct stream_media_cursor {
|
|||||||
struct cursor_track video, audio;
|
struct cursor_track video, audio;
|
||||||
uint32_t generation;
|
uint32_t generation;
|
||||||
int initialized, key_delivered;
|
int initialized, key_delivered;
|
||||||
|
unsigned tracks;
|
||||||
int64_t start_us;
|
int64_t start_us;
|
||||||
uint8_t *scratch[2], *frame;
|
uint8_t *scratch[2], *frame;
|
||||||
size_t scratch_capacity, frame_capacity;
|
size_t scratch_capacity, frame_capacity;
|
||||||
@@ -201,11 +202,14 @@ void stream_media_store_finish(struct stream_media_store *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Курсор хранит только собственные рабочие буферы, не копию накопленного медиапотока. */
|
/* Курсор хранит только собственные рабочие буферы, не копию накопленного медиапотока. */
|
||||||
int stream_media_cursor_create(struct stream_media_cursor **out, struct stream_media_store *s) {
|
int stream_media_cursor_create(struct stream_media_cursor **out, struct stream_media_store *s, unsigned tracks) {
|
||||||
if (!out || *out || !s) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor create invalid arguments"); return STREAM_ERR_ARGUMENT; }
|
if (!out || *out || !s || !(tracks & STREAM_MEDIA_TRACK_VIDEO) || (tracks & ~3u)) {
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor create invalid arguments tracks=%u", tracks); return STREAM_ERR_ARGUMENT;
|
||||||
|
}
|
||||||
struct stream_media_cursor *c = u_calloc(1, sizeof(*c));
|
struct stream_media_cursor *c = u_calloc(1, sizeof(*c));
|
||||||
if (!c) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor allocation failed"); return STREAM_ERR_MEMORY; }
|
if (!c) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor allocation failed"); return STREAM_ERR_MEMORY; }
|
||||||
c->store = s; c->scratch_capacity = stream_input_size(&s->descriptor);
|
c->store = s; c->tracks = tracks; c->scratch_capacity = stream_input_size(&s->descriptor);
|
||||||
|
c->audio.ended = !(tracks & STREAM_MEDIA_TRACK_AUDIO);
|
||||||
if (c->scratch_capacity > STREAM_MEDIA_MAX_BLOCK) c->scratch_capacity = STREAM_MEDIA_MAX_BLOCK;
|
if (c->scratch_capacity > STREAM_MEDIA_MAX_BLOCK) c->scratch_capacity = STREAM_MEDIA_MAX_BLOCK;
|
||||||
for (unsigned i = 0; i < 2; i++) c->scratch[i] = u_malloc((uint32_t)c->scratch_capacity);
|
for (unsigned i = 0; i < 2; i++) c->scratch[i] = u_malloc((uint32_t)c->scratch_capacity);
|
||||||
if (!c->scratch[0] || !c->scratch[1]) {
|
if (!c->scratch[0] || !c->scratch[1]) {
|
||||||
@@ -228,6 +232,7 @@ void stream_media_cursor_reset(struct stream_media_cursor *c) {
|
|||||||
c->initialized = c->key_delivered = 0; c->pending_used = 0;
|
c->initialized = c->key_delivered = 0; c->pending_used = 0;
|
||||||
c->scratch_valid[0] = c->scratch_valid[1] = 0;
|
c->scratch_valid[0] = c->scratch_valid[1] = 0;
|
||||||
c->video = (struct cursor_track){0}; c->audio = (struct cursor_track){0};
|
c->video = (struct cursor_track){0}; c->audio = (struct cursor_track){0};
|
||||||
|
c->audio.ended = !(c->tracks & STREAM_MEDIA_TRACK_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Выбирается последний полный KEY; тёплая история аудио перед ним остаётся в том же RS-кольце. */
|
/* Выбирается последний полный KEY; тёплая история аудио перед ним остаётся в том же RS-кольце. */
|
||||||
@@ -258,7 +263,7 @@ int stream_media_cursor_status(struct stream_media_cursor *c, struct stream_medi
|
|||||||
struct stream_media_store *s = c->store;
|
struct stream_media_store *s = c->store;
|
||||||
int result = cursor_start(c);
|
int result = cursor_start(c);
|
||||||
struct stream_media_status status = {.ready_us = -1, .video_us = -1, .audio_us = -1, .start_us = -1,
|
struct stream_media_status status = {.ready_us = -1, .video_us = -1, .audio_us = -1, .start_us = -1,
|
||||||
.audio_enabled = s->configured && s->format.audio_codec, .finished = s->finished};
|
.audio_enabled = s->configured && s->format.audio_codec && (c->tracks & STREAM_MEDIA_TRACK_AUDIO), .finished = s->finished};
|
||||||
if (result < 0) return result;
|
if (result < 0) return result;
|
||||||
if (c->initialized) {
|
if (c->initialized) {
|
||||||
for (unsigned i = s->count; i-- > 0;) {
|
for (unsigned i = s->count; i-- > 0;) {
|
||||||
@@ -268,6 +273,8 @@ int stream_media_cursor_status(struct stream_media_cursor *c, struct stream_medi
|
|||||||
}
|
}
|
||||||
status.ready_us = status.video_us;
|
status.ready_us = status.video_us;
|
||||||
if (status.audio_enabled && status.audio_us < status.ready_us) status.ready_us = status.audio_us;
|
if (status.audio_enabled && status.audio_us < status.ready_us) status.ready_us = status.audio_us;
|
||||||
|
if (s->finished && c->video.ended && status.audio_enabled) status.ready_us = status.audio_us;
|
||||||
|
status.video_ended = c->video.ended; status.audio_ended = c->audio.ended;
|
||||||
status.start_us = c->start_us; status.generation = c->generation; status.initialized = 1;
|
status.start_us = c->start_us; status.generation = c->generation; status.initialized = 1;
|
||||||
}
|
}
|
||||||
*out = status;
|
*out = status;
|
||||||
@@ -351,9 +358,11 @@ static int cursor_video(struct stream_media_cursor *c, struct stream_media_packe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Выдача по времени потребителя оставляет накопленный запас в общем кольце. */
|
/* Выдача по времени потребителя оставляет накопленный запас в общем кольце. */
|
||||||
int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, int64_t lookahead, struct stream_media_packet *packet) {
|
int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, int64_t video_ahead, int64_t audio_ahead,
|
||||||
if (!c || !packet || lookahead < 0 || lookahead > 1000000) {
|
struct stream_media_packet *packet) {
|
||||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor read invalid arguments ahead=%lld", (long long)lookahead); return STREAM_ERR_ARGUMENT;
|
if (!c || !packet || video_ahead < -1 || video_ahead > 60000000 || audio_ahead < -1 || audio_ahead > 60000000) {
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media cursor read invalid arguments ahead=%lld/%lld",
|
||||||
|
(long long)video_ahead, (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) {
|
||||||
@@ -363,17 +372,19 @@ int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, in
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
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;
|
||||||
int ar = c->store->format.audio_codec ? cursor_peek(c, &c->audio, 0, &audio, &audio_pts) : STREAM_GAP;
|
|
||||||
if (!c->store->format.audio_codec) c->audio.ended = 1;
|
if (!c->store->format.audio_codec) c->audio.ended = 1;
|
||||||
int vr = cursor_peek(c, &c->video, 1, &video, &video_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);
|
||||||
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 limit = position < 0 ? c->start_us : position > INT64_MAX - lookahead ? INT64_MAX : position + lookahead;
|
int64_t video_limit = position < 0 ? c->start_us : position > INT64_MAX - video_ahead ? INT64_MAX : position + video_ahead;
|
||||||
if (ar == STREAM_OK && audio_pts <= limit && (audio_pts < video_pts || vr != STREAM_OK)) {
|
int64_t audio_limit = position < 0 ? c->start_us : position > INT64_MAX - audio_ahead ? INT64_MAX : position + audio_ahead;
|
||||||
|
int video_allowed = vr == STREAM_OK && video_ahead >= 0 && video_pts <= video_limit;
|
||||||
|
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);
|
||||||
if (result != STREAM_OK) return result;
|
if (result != STREAM_OK) return result;
|
||||||
@@ -382,6 +393,6 @@ int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, in
|
|||||||
*packet = (struct stream_media_packet){.kind = STREAM_MEDIA_READ_AUDIO, .format = audio.format, .audio = a};
|
*packet = (struct stream_media_packet){.kind = STREAM_MEDIA_READ_AUDIO, .format = audio.format, .audio = a};
|
||||||
return STREAM_OK;
|
return STREAM_OK;
|
||||||
}
|
}
|
||||||
if (vr == STREAM_OK && video_pts <= limit) return cursor_video(c, packet);
|
if (video_allowed) return cursor_video(c, packet);
|
||||||
return STREAM_WAIT;
|
return STREAM_WAIT;
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,8 @@ extern "C" {
|
|||||||
#define STREAM_MEDIA_INDEX_BYTES (4u * 1024u * 1024u)
|
#define STREAM_MEDIA_INDEX_BYTES (4u * 1024u * 1024u)
|
||||||
#define STREAM_MEDIA_READ_AHEAD_US 80000
|
#define STREAM_MEDIA_READ_AHEAD_US 80000
|
||||||
#define STREAM_MEDIA_PREROLL_US 500000
|
#define STREAM_MEDIA_PREROLL_US 500000
|
||||||
|
#define STREAM_MEDIA_TRACK_VIDEO 1
|
||||||
|
#define STREAM_MEDIA_TRACK_AUDIO 2
|
||||||
|
|
||||||
struct stream_media_store;
|
struct stream_media_store;
|
||||||
struct stream_media_cursor;
|
struct stream_media_cursor;
|
||||||
@@ -31,7 +33,7 @@ struct stream_media_packet {
|
|||||||
struct stream_media_status {
|
struct stream_media_status {
|
||||||
int64_t ready_us, video_us, audio_us, start_us;
|
int64_t ready_us, video_us, audio_us, start_us;
|
||||||
uint64_t generation;
|
uint64_t generation;
|
||||||
int initialized, audio_enabled, finished;
|
int initialized, audio_enabled, finished, video_ended, audio_ended;
|
||||||
};
|
};
|
||||||
|
|
||||||
int stream_media_store_create(struct stream_media_store **out, struct stream_buffer *buffer, const struct stream_descriptor *descriptor);
|
int stream_media_store_create(struct stream_media_store **out, struct stream_buffer *buffer, const struct stream_descriptor *descriptor);
|
||||||
@@ -40,14 +42,14 @@ int stream_media_store_feed(struct stream_media_store *store, uint64_t seq, cons
|
|||||||
void stream_media_store_gap(struct stream_media_store *store, uint64_t first, uint64_t last);
|
void stream_media_store_gap(struct stream_media_store *store, uint64_t first, uint64_t last);
|
||||||
void stream_media_store_finish(struct stream_media_store *store);
|
void stream_media_store_finish(struct stream_media_store *store);
|
||||||
void stream_media_store_trim(struct stream_media_store *store);
|
void stream_media_store_trim(struct stream_media_store *store);
|
||||||
int stream_media_cursor_create(struct stream_media_cursor **out, struct stream_media_store *store);
|
int stream_media_cursor_create(struct stream_media_cursor **out, struct stream_media_store *store, unsigned tracks);
|
||||||
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.
|
/* position_us<0 — старт: прогреть аудио перед выбранным KEY. Затем отдавать только до position_us+lookahead_us.
|
||||||
* При GAP/EXPIRED cursor сбрасывают отдельно: это позволяет frontend сначала обработать разрыв.
|
* При GAP/EXPIRED cursor сбрасывают отдельно: это позволяет frontend сначала обработать разрыв.
|
||||||
* Бюджет одного вызова ограничен; WAIT при сборке длинного кадра допускает повтор сразу/по следующему событию. */
|
* Бюджет одного вызова ограничен; WAIT при сборке длинного кадра допускает повтор сразу/по следующему событию. */
|
||||||
int stream_media_cursor_read(struct stream_media_cursor *cursor, int64_t position_us, int64_t lookahead_us,
|
int stream_media_cursor_read(struct stream_media_cursor *cursor, int64_t position_us, int64_t video_ahead_us, int64_t audio_ahead_us,
|
||||||
struct stream_media_packet *packet);
|
struct stream_media_packet *packet);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
Reference in new issue
Block a user