|
|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
|
|
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
|
#include "../../lib/mem.h" |
|
|
|
|
#include "../../lib/u_async.h" |
|
|
|
|
#include "stream_media_buffer.h" |
|
|
|
|
|
|
|
|
|
struct media_position { |
|
|
|
|
@ -35,6 +36,7 @@ struct stream_media_cursor {
|
|
|
|
|
uint8_t *scratch[2], *frame; |
|
|
|
|
size_t scratch_capacity, frame_capacity; |
|
|
|
|
uint64_t scratch_seq[2]; |
|
|
|
|
size_t scratch_length[2]; |
|
|
|
|
int scratch_valid[2]; |
|
|
|
|
unsigned scratch_next; |
|
|
|
|
uint8_t opus[STREAM_MEDIA_MAX_OPUS]; |
|
|
|
|
@ -163,6 +165,11 @@ int stream_media_store_feed(struct stream_media_store *s, uint64_t seq, const ui
|
|
|
|
|
for (unsigned i = 0; i < b.video_count; i++) { |
|
|
|
|
const struct stream_media_video *v = &b.video[i]; |
|
|
|
|
if (!v->offset) { |
|
|
|
|
if (s->video_us >= 0 && ((int64_t)v->pts_us <= s->video_us || v->frame_id <= s->last_frame_id)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "media index stale video seq=%llu frame=%llu pts=%llu", |
|
|
|
|
(unsigned long long)seq, (unsigned long long)v->frame_id, (unsigned long long)v->pts_us); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (s->pending_used) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "media index incomplete frame=%llu before frame=%llu; require KEY", |
|
|
|
|
(unsigned long long)s->pending.frame_id, (unsigned long long)v->frame_id); |
|
|
|
|
@ -281,16 +288,9 @@ static int cursor_block(struct stream_media_cursor *c, uint64_t seq, struct stre
|
|
|
|
|
slot = c->scratch_next++ % 2; |
|
|
|
|
int result = stream_buffer_decode(s->buffer, seq, c->scratch[slot], c->scratch_capacity, &length); |
|
|
|
|
if (result != STREAM_OK) return result == STREAM_REJECTED ? STREAM_GAP : result; |
|
|
|
|
c->scratch_seq[slot] = seq; c->scratch_valid[slot] = 1; |
|
|
|
|
c->scratch_seq[slot] = seq; c->scratch_length[slot] = length; c->scratch_valid[slot] = 1; |
|
|
|
|
} else { |
|
|
|
|
/* length сохраняется в первом поле зарезервированного хвоста не нужна: длину даёт metadata RS. */ |
|
|
|
|
const uint8_t *wire; uint64_t actual; size_t wire_length; |
|
|
|
|
int found = STREAM_GAP; |
|
|
|
|
for (unsigned sub = 0; sub < s->descriptor.n; sub++) { |
|
|
|
|
found = stream_buffer_read(s->buffer, seq, (uint8_t)sub, &wire, &wire_length, &actual); |
|
|
|
|
if (found == STREAM_OK) { length = (size_t)stream_read_be(wire + STREAM_BLOCK_LENGTH_OFF, 4); break; } |
|
|
|
|
} |
|
|
|
|
if (found != STREAM_OK) return found; |
|
|
|
|
length = c->scratch_length[slot]; |
|
|
|
|
} |
|
|
|
|
return stream_media_parse(c->scratch[slot], length, block); |
|
|
|
|
} |
|
|
|
|
@ -323,6 +323,8 @@ static int cursor_video(struct stream_media_cursor *c, struct stream_media_packe
|
|
|
|
|
int result = cursor_peek(c, &c->video, 1, &b, &pts); |
|
|
|
|
if (result != STREAM_OK) return result; |
|
|
|
|
const struct stream_media_video *v = &b.video[c->video.entry++]; |
|
|
|
|
if (!c->key_delivered && !c->pending.frame_size |
|
|
|
|
&& (v->offset || !(v->flags & STREAM_MEDIA_KEY) || pts != c->start_us)) continue; |
|
|
|
|
if (!v->offset) { |
|
|
|
|
if (!c->key_delivered && (!(v->flags & STREAM_MEDIA_KEY) || pts != c->start_us)) continue; |
|
|
|
|
if (v->frame_size > c->frame_capacity) { |
|
|
|
|
@ -370,7 +372,7 @@ 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; |
|
|
|
|
} |
|
|
|
|
int64_t limit = position < 0 ? c->start_us : position + lookahead; |
|
|
|
|
int64_t limit = position < 0 ? c->start_us : position > INT64_MAX - lookahead ? INT64_MAX : position + lookahead; |
|
|
|
|
if (ar == STREAM_OK && audio_pts <= limit && (audio_pts < video_pts || vr != STREAM_OK)) { |
|
|
|
|
/* video peek мог переиспользовать scratch аудио: заново взять ту же пачку из двухэлементного cache. */ |
|
|
|
|
result = cursor_block(c, c->audio.seq, &audio); |
|
|
|
|
|