|
|
|
|
@ -32,7 +32,7 @@ struct stream_publisher {
|
|
|
|
|
struct stream_publisher_status status; |
|
|
|
|
struct publisher_queue video, audio; |
|
|
|
|
uint64_t group, next_frame, last_pts[2], drain_until, report_us; |
|
|
|
|
int seen[2], stopping; |
|
|
|
|
int seen[2], stopping, audio_enabled, audio_dirty; |
|
|
|
|
}; |
|
|
|
|
static void publisher_process(void *arg); |
|
|
|
|
|
|
|
|
|
@ -180,6 +180,10 @@ static void publisher_process(void *arg) {
|
|
|
|
|
} |
|
|
|
|
uint64_t now = get_time_us(); |
|
|
|
|
if (p->handle && !stream_handle_active(p->handle)) publisher_close(p, stream_handle_reason(p->handle)); |
|
|
|
|
if (p->handle && p->audio_dirty && p->status.state == STREAM_PUBLISHER_LIVE) { |
|
|
|
|
if (stream_publish_set_audio_enabled(p->handle, p->audio_enabled) != STREAM_OK) publisher_close(p, STREAM_CLOSE_FAILED); |
|
|
|
|
else { p->status.audio_enabled = p->audio_enabled; p->audio_dirty = 0; } |
|
|
|
|
} |
|
|
|
|
if (p->video.head && now - p->video.head->created > MEDIA_AGE_US) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "publisher video expired bytes=%zu records=%u; require KEY", p->video.bytes, p->video.count); |
|
|
|
|
records_clear(&p->video); p->status.need_key = 1; |
|
|
|
|
@ -246,11 +250,27 @@ int stream_publisher_open(struct stream_publisher *p, uint64_t id, uint64_t grou
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "publisher open busy id=%llu owner=%llu", (unsigned long long)id, (unsigned long long)p->status.id); |
|
|
|
|
} else if (publisher_schedule(p) == 0) { |
|
|
|
|
p->status = (struct stream_publisher_status){.id = id, .state = STREAM_PUBLISHER_OPENING, .need_key = 1}; |
|
|
|
|
p->audio_enabled = f->audio_codec != 0; p->audio_dirty = 1; |
|
|
|
|
p->group = group; p->format = *f; p->next_frame = 0; p->stopping = 0; memset(p->seen, 0, sizeof(p->seen)); result = 0; |
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&p->lock); return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int stream_publisher_set_audio_enabled(struct stream_publisher *p, uint64_t id, int enabled) { |
|
|
|
|
if (!p || !id || (enabled != 0 && enabled != 1)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "publisher audio change invalid arguments"); return -1; |
|
|
|
|
} |
|
|
|
|
pthread_mutex_lock(&p->lock); int result = -1; |
|
|
|
|
if (p->status.id != id || p->stopping || (enabled && !p->format.audio_codec) |
|
|
|
|
|| (p->status.state != STREAM_PUBLISHER_OPENING && p->status.state != STREAM_PUBLISHER_LIVE)) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "publisher audio change refused id=%llu owner=%llu state=%d enabled=%d codec=%u", |
|
|
|
|
(unsigned long long)id, (unsigned long long)p->status.id, p->status.state, enabled, p->format.audio_codec); |
|
|
|
|
} else if (publisher_schedule(p) == 0) { |
|
|
|
|
p->audio_enabled = enabled; p->audio_dirty = 1; result = 0; |
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&p->lock); return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Admission сохраняет PTS только принятого кадра; новая revision коммитится атомарно с KEY. */ |
|
|
|
|
int stream_publisher_feed(struct stream_publisher *p, uint64_t id, int video, uint64_t pts, int key, |
|
|
|
|
const uint8_t *data, size_t length, const struct stream_media_format *f) { |
|
|
|
|
|