From 49eb326ce9beb0a993dce0f2a7beb7dcfc3b1c51 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 10 Feb 2023 22:52:40 +0100 Subject: [PATCH] Extract packet merging Config packets must be prepended to the next media packet. Extract the logic to a new sc_packet_merger helper to simplify the demuxer code. --- app/meson.build | 1 + app/src/demuxer.c | 57 +++++++++-------------------------------- app/src/demuxer.h | 4 --- app/src/packet_merger.c | 48 ++++++++++++++++++++++++++++++++++ app/src/packet_merger.h | 43 +++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 49 deletions(-) create mode 100644 app/src/packet_merger.c create mode 100644 app/src/packet_merger.h diff --git a/app/meson.build b/app/meson.build index 3ddd5a5d..5d779756 100644 --- a/app/meson.build +++ b/app/meson.build @@ -21,6 +21,7 @@ src = [ 'src/mouse_inject.c', 'src/opengl.c', 'src/options.c', + 'src/packet_merger.c', 'src/receiver.c', 'src/recorder.c', 'src/scrcpy.c', diff --git a/app/src/demuxer.c b/app/src/demuxer.c index 5cc07f6c..282c07de 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -6,6 +6,7 @@ #include "decoder.h" #include "events.h" +#include "packet_merger.h" #include "recorder.h" #include "util/binary.h" #include "util/log.h" @@ -120,48 +121,7 @@ push_packet_to_sinks(struct sc_demuxer *demuxer, const AVPacket *packet) { static bool sc_demuxer_push_packet(struct sc_demuxer *demuxer, AVPacket *packet) { - bool is_config = packet->pts == AV_NOPTS_VALUE; - - // A config packet must not be decoded immediately (it contains no - // frame); instead, it must be concatenated with the future data packet. - if (demuxer->pending || is_config) { - if (demuxer->pending) { - size_t offset = demuxer->pending->size; - if (av_grow_packet(demuxer->pending, packet->size)) { - LOG_OOM(); - return false; - } - - memcpy(demuxer->pending->data + offset, packet->data, packet->size); - } else { - demuxer->pending = av_packet_alloc(); - if (!demuxer->pending) { - LOG_OOM(); - return false; - } - if (av_packet_ref(demuxer->pending, packet)) { - LOG_OOM(); - av_packet_free(&demuxer->pending); - return false; - } - } - - if (!is_config) { - // prepare the concat packet to send to the decoder - demuxer->pending->pts = packet->pts; - demuxer->pending->dts = packet->dts; - demuxer->pending->flags = packet->flags; - packet = demuxer->pending; - } - } - bool ok = push_packet_to_sinks(demuxer, packet); - - if (!is_config && demuxer->pending) { - // the pending packet must be discarded (consumed or error) - av_packet_free(&demuxer->pending); - } - if (!ok) { LOGE("Could not process packet"); return false; @@ -228,6 +188,9 @@ run_demuxer(void *data) { goto end; } + struct sc_packet_merger merger; + sc_packet_merger_init(&merger); + AVPacket *packet = av_packet_alloc(); if (!packet) { LOG_OOM(); @@ -242,6 +205,13 @@ run_demuxer(void *data) { break; } + // Prepend any config packet to the next media packet + ok = sc_packet_merger_merge(&merger, packet); + if (!ok) { + av_packet_unref(packet); + break; + } + ok = sc_demuxer_push_packet(demuxer, packet); av_packet_unref(packet); if (!ok) { @@ -252,9 +222,7 @@ run_demuxer(void *data) { LOGD("End of frames"); - if (demuxer->pending) { - av_packet_free(&demuxer->pending); - } + sc_packet_merger_destroy(&merger); av_packet_free(&packet); finally_close_sinks: @@ -269,7 +237,6 @@ void sc_demuxer_init(struct sc_demuxer *demuxer, sc_socket socket, const struct sc_demuxer_callbacks *cbs, void *cbs_userdata) { demuxer->socket = socket; - demuxer->pending = NULL; demuxer->sink_count = 0; assert(cbs && cbs->on_ended); diff --git a/app/src/demuxer.h b/app/src/demuxer.h index 86e0536d..e403fe35 100644 --- a/app/src/demuxer.h +++ b/app/src/demuxer.h @@ -21,10 +21,6 @@ struct sc_demuxer { struct sc_packet_sink *sinks[SC_DEMUXER_MAX_SINKS]; unsigned sink_count; - // successive packets may need to be concatenated, until a non-config - // packet is available - AVPacket *pending; - const struct sc_demuxer_callbacks *cbs; void *cbs_userdata; }; diff --git a/app/src/packet_merger.c b/app/src/packet_merger.c new file mode 100644 index 00000000..81b02d2c --- /dev/null +++ b/app/src/packet_merger.c @@ -0,0 +1,48 @@ +#include "packet_merger.h" + +#include "util/log.h" + +void +sc_packet_merger_init(struct sc_packet_merger *merger) { + merger->config = NULL; +} + +void +sc_packet_merger_destroy(struct sc_packet_merger *merger) { + free(merger->config); +} + +bool +sc_packet_merger_merge(struct sc_packet_merger *merger, AVPacket *packet) { + bool is_config = packet->pts == AV_NOPTS_VALUE; + + if (is_config) { + free(merger->config); + + merger->config = malloc(packet->size); + if (!merger->config) { + LOG_OOM(); + return false; + } + + memcpy(merger->config, packet->data, packet->size); + merger->config_size = packet->size; + } else if (merger->config) { + size_t config_size = merger->config_size; + size_t media_size = packet->size; + + if (av_grow_packet(packet, config_size)) { + LOG_OOM(); + return false; + } + + memmove(packet->data + config_size, packet->data, media_size); + memcpy(packet->data, merger->config, config_size); + + free(merger->config); + merger->config = NULL; + // merger->size is meaningless when merger->config is NULL + } + + return true; +} diff --git a/app/src/packet_merger.h b/app/src/packet_merger.h new file mode 100644 index 00000000..e1824c2c --- /dev/null +++ b/app/src/packet_merger.h @@ -0,0 +1,43 @@ +#ifndef SC_PACKET_MERGER_H +#define SC_PACKET_MERGER_H + +#include "common.h" + +#include +#include +#include + +/** + * Config packets (containing the SPS/PPS) are sent in-band. A new config + * packet is sent whenever a new encoding session is started (on start and on + * device orientation change). + * + * Every time a config packet is received, it must be sent alone (for recorder + * extradata), then concatenated to the next media packet (for correct decoding + * and recording). + * + * This helper reads every input packet and modifies each media packet which + * immediately follows a config packet to prepend the config packet payload. + */ + +struct sc_packet_merger { + uint8_t *config; + size_t config_size; +}; + +void +sc_packet_merger_init(struct sc_packet_merger *merger); + +void +sc_packet_merger_destroy(struct sc_packet_merger *merger); + +/** + * If the packet is a config packet, then keep its data for later. + * Otherwise (if the packet is a media packet), then if a config packet is + * pending, prepend the config packet to this packet (so the packet is + * modified!). + */ +bool +sc_packet_merger_merge(struct sc_packet_merger *merger, AVPacket *packet); + +#endif