scrcpy/app/src/trait/frame_source.c
Romain Vimont 78cba1b7c2 Add session metadata for the video stream
Introduce a new packet type, a "session" packet, containing metadata
about the encoding session. It is used only for the video stream and
currently includes the video resolution.

For illustration, here is a sequence of packets on the video stream:

                                        device rotation
                                        v
    CODEC | SESSION | MEDIA | MEDIA | … | SESSION | MEDIA | MEDIA | …
           1920x1080 <-----------------> 1080x1920 <------------------
                      encoding session 1            encoding session 2

This metadata is not strictly necessary, since the video resolution can
be determined after decoding. However, it allows detection of cases
where the encoder does not respect the requested size (and logs a
warning), even without decoding (e.g., when there is no video playback).

Additional metadata could be added later if necessary, for example the
actual device rotation.

Refs #5918 <https://github.com/Genymobile/scrcpy/pull/5918>
Refs #5894 <https://github.com/Genymobile/scrcpy/pull/5894>
PR #6159 <https://github.com/Genymobile/scrcpy/pull/6159>

Co-authored-by: gz0119 <liyong2@4399.com>
2026-01-10 11:53:22 +01:00

77 lines
2.1 KiB
C

#include "frame_source.h"
#include <assert.h>
void
sc_frame_source_init(struct sc_frame_source *source) {
source->sink_count = 0;
}
void
sc_frame_source_add_sink(struct sc_frame_source *source,
struct sc_frame_sink *sink) {
assert(source->sink_count < SC_FRAME_SOURCE_MAX_SINKS);
assert(sink);
assert(sink->ops);
source->sinks[source->sink_count++] = sink;
}
static void
sc_frame_source_sinks_close_firsts(struct sc_frame_source *source,
unsigned count) {
while (count) {
struct sc_frame_sink *sink = source->sinks[--count];
sink->ops->close(sink);
}
}
bool
sc_frame_source_sinks_open(struct sc_frame_source *source,
const AVCodecContext *ctx,
const struct sc_stream_session *session) {
assert(source->sink_count);
for (unsigned i = 0; i < source->sink_count; ++i) {
struct sc_frame_sink *sink = source->sinks[i];
if (!sink->ops->open(sink, ctx, session)) {
sc_frame_source_sinks_close_firsts(source, i);
return false;
}
}
return true;
}
void
sc_frame_source_sinks_close(struct sc_frame_source *source) {
assert(source->sink_count);
sc_frame_source_sinks_close_firsts(source, source->sink_count);
}
bool
sc_frame_source_sinks_push(struct sc_frame_source *source,
const AVFrame *frame) {
assert(source->sink_count);
for (unsigned i = 0; i < source->sink_count; ++i) {
struct sc_frame_sink *sink = source->sinks[i];
if (!sink->ops->push(sink, frame)) {
return false;
}
}
return true;
}
bool
sc_frame_source_sinks_push_session(struct sc_frame_source *source,
const struct sc_stream_session *session) {
assert(source->sink_count);
for (unsigned i = 0; i < source->sink_count; ++i) {
struct sc_frame_sink *sink = source->sinks[i];
if (sink->ops->push_session &&
!sink->ops->push_session(sink, session)) {
return false;
}
}
return true;
}