|
|
|
|
@ -351,7 +351,7 @@ static int cursor_peek(struct stream_media_cursor *c, struct cursor_track *track
|
|
|
|
|
return STREAM_MORE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сборка одного ближайшего H264 кадра; остальные кадры продолжают храниться только в RS. */ |
|
|
|
|
/* Сборка одного следующего видеокадра; остальные кадры продолжают храниться только в RS. */ |
|
|
|
|
static int cursor_video(struct stream_media_cursor *c, struct stream_media_packet *packet) { |
|
|
|
|
for (unsigned work = 0; work < 16; work++) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Выдача по времени потребителя оставляет накопленный запас в общем кольце. */ |
|
|
|
|
int stream_media_cursor_read(struct stream_media_cursor *c, int64_t position, int64_t video_ahead, int64_t audio_ahead, |
|
|
|
|
/* Видео выдаётся по готовности decoder, звук — по времени потребителя. */ |
|
|
|
|
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) { |
|
|
|
|
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; |
|
|
|
|
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 video=%d audio_ahead=%lld", |
|
|
|
|
want_video, (long long)audio_ahead); return STREAM_ERR_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
int result = cursor_start(c); |
|
|
|
|
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; |
|
|
|
|
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 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_EXPIRED || vr == STREAM_EXPIRED) return STREAM_EXPIRED; |
|
|
|
|
if (ar < 0 || vr < 0) return ar < 0 ? ar : vr; |
|
|
|
|
if (c->audio.ended && c->video.ended) { |
|
|
|
|
*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 |
|
|
|
|
: 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)) { |
|
|
|
|
/* video peek мог переиспользовать scratch аудио: заново взять ту же пачку из двухэлементного cache. */ |
|
|
|
|
result = cursor_block(c, c->audio.seq, &audio); |
|
|
|
|
|