|
|
|
|
@ -18,7 +18,8 @@ struct stream_media_receiver {
|
|
|
|
|
/* Проверить весь набор описаний до чтения payload или выделения памяти под кадр. */ |
|
|
|
|
static int media_validate(const struct stream_media_block *b, size_t *video_size, size_t *audio_size, uint32_t *first) { |
|
|
|
|
if (!b || b->video_count > STREAM_MEDIA_MAX_ENTRIES || b->audio_count > STREAM_MEDIA_MAX_ENTRIES |
|
|
|
|
|| !b->format.revision || (!b->video_count && !b->audio_count) || b->format.video_codec != STREAM_MEDIA_H264 |
|
|
|
|
|| !b->format.revision || (!b->video_count && !b->audio_count) |
|
|
|
|
|| b->format.video_codec < STREAM_MEDIA_H264 || b->format.video_codec > STREAM_MEDIA_H266 |
|
|
|
|
|| b->format.audio_codec > STREAM_MEDIA_OPUS_STEREO || !b->format.width || b->format.width > 4096 |
|
|
|
|
|| !b->format.height || b->format.height > 4096 || b->format.rotation > 270 || b->format.rotation % 90) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media format/count invalid present=%d video=%u audio=%u codecs=%u/%u dimensions=%ux%u rotation=%u", |
|
|
|
|
@ -76,7 +77,7 @@ int stream_media_encode(const struct stream_media_block *b, uint8_t *output, siz
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media encode capacity=%zu needed=%zu", capacity, total); return STREAM_ERR_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
memset(output, 0, header); |
|
|
|
|
memcpy(output, "uAV1", 4); |
|
|
|
|
memcpy(output, "uAV2", 4); |
|
|
|
|
output[4] = b->format.video_codec; output[5] = b->format.audio_codec; |
|
|
|
|
output[6] = (uint8_t)b->video_count; output[7] = (uint8_t)b->audio_count; |
|
|
|
|
stream_write_be(output + 8, b->format.width, 2); stream_write_be(output + 10, b->format.height, 2); |
|
|
|
|
@ -103,7 +104,7 @@ int stream_media_encode(const struct stream_media_block *b, uint8_t *output, siz
|
|
|
|
|
|
|
|
|
|
/* Проверить арифметику областей до вычисления указателей, затем валидировать все описания. */ |
|
|
|
|
int stream_media_parse(const uint8_t *data, size_t length, struct stream_media_block *out) { |
|
|
|
|
if (!data || !out || length < STREAM_MEDIA_HEADER_SIZE || length > STREAM_MEDIA_MAX_BLOCK || memcmp(data, "uAV1", 4)) { |
|
|
|
|
if (!data || !out || length < STREAM_MEDIA_HEADER_SIZE || length > STREAM_MEDIA_MAX_BLOCK || memcmp(data, "uAV2", 4)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media parse bad prefix/length=%zu", length); return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
struct stream_media_block b = {.video_count = data[6], .audio_count = data[7]}; |
|
|
|
|
@ -151,6 +152,19 @@ int stream_media_parse(const uint8_t *data, size_t length, struct stream_media_b
|
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int stream_media_key_parse(const uint8_t *data, size_t length, const uint8_t **config, size_t *config_length, |
|
|
|
|
const uint8_t **frame, size_t *frame_length) { |
|
|
|
|
if (!data || length < 5 || length > STREAM_MEDIA_MAX_FRAME || !config || !config_length || !frame || !frame_length) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "KEY envelope invalid length=%zu", length); return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
size_t n = (size_t)stream_read_be(data, 4); |
|
|
|
|
if (n > STREAM_MEDIA_MAX_CONFIG || n >= length - 4) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "KEY configuration invalid config=%zu total=%zu", n, length); return STREAM_ERR_FORMAT; |
|
|
|
|
} |
|
|
|
|
*config = data + 4; *config_length = n; *frame = data + 4 + n; *frame_length = length - 4 - n; |
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Receiver имеет один ограниченный буфер; ожидание KEY не мешает доставке независимого аудио. */ |
|
|
|
|
int stream_media_receiver_create(struct stream_media_receiver **out, stream_media_frame_fn callback, void *arg) { |
|
|
|
|
if (!out || *out || !callback) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media receiver create invalid arguments"); return STREAM_ERR_ARGUMENT; } |
|
|
|
|
@ -180,7 +194,7 @@ int stream_media_receiver_feed(struct stream_media_receiver *r, const uint8_t *d
|
|
|
|
|
return STREAM_OK; |
|
|
|
|
} |
|
|
|
|
if (r->configured && (r->format.audio_codec != b.format.audio_codec |
|
|
|
|
|| (r->format.revision == b.format.revision && (r->format.width != b.format.width |
|
|
|
|
|| (r->format.revision == b.format.revision && (r->format.video_codec != b.format.video_codec || r->format.width != b.format.width |
|
|
|
|
|| r->format.height != b.format.height || r->format.rotation != b.format.rotation)))) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "media conflicting configuration revision=%u/%u dimensions=%ux%u/%ux%u rotation=%u/%u audio=%u/%u", |
|
|
|
|
r->format.revision, b.format.revision, r->format.width, r->format.height, b.format.width, b.format.height, |
|
|
|
|
|