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.
This commit is contained in:
Romain Vimont 2023-02-10 22:52:40 +01:00
parent f03f32267e
commit 49eb326ce9
5 changed files with 104 additions and 49 deletions

View file

@ -21,6 +21,7 @@ src = [
'src/mouse_inject.c', 'src/mouse_inject.c',
'src/opengl.c', 'src/opengl.c',
'src/options.c', 'src/options.c',
'src/packet_merger.c',
'src/receiver.c', 'src/receiver.c',
'src/recorder.c', 'src/recorder.c',
'src/scrcpy.c', 'src/scrcpy.c',

View file

@ -6,6 +6,7 @@
#include "decoder.h" #include "decoder.h"
#include "events.h" #include "events.h"
#include "packet_merger.h"
#include "recorder.h" #include "recorder.h"
#include "util/binary.h" #include "util/binary.h"
#include "util/log.h" #include "util/log.h"
@ -120,48 +121,7 @@ push_packet_to_sinks(struct sc_demuxer *demuxer, const AVPacket *packet) {
static bool static bool
sc_demuxer_push_packet(struct sc_demuxer *demuxer, AVPacket *packet) { 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); 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) { if (!ok) {
LOGE("Could not process packet"); LOGE("Could not process packet");
return false; return false;
@ -228,6 +188,9 @@ run_demuxer(void *data) {
goto end; goto end;
} }
struct sc_packet_merger merger;
sc_packet_merger_init(&merger);
AVPacket *packet = av_packet_alloc(); AVPacket *packet = av_packet_alloc();
if (!packet) { if (!packet) {
LOG_OOM(); LOG_OOM();
@ -242,6 +205,13 @@ run_demuxer(void *data) {
break; 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); ok = sc_demuxer_push_packet(demuxer, packet);
av_packet_unref(packet); av_packet_unref(packet);
if (!ok) { if (!ok) {
@ -252,9 +222,7 @@ run_demuxer(void *data) {
LOGD("End of frames"); LOGD("End of frames");
if (demuxer->pending) { sc_packet_merger_destroy(&merger);
av_packet_free(&demuxer->pending);
}
av_packet_free(&packet); av_packet_free(&packet);
finally_close_sinks: finally_close_sinks:
@ -269,7 +237,6 @@ void
sc_demuxer_init(struct sc_demuxer *demuxer, sc_socket socket, sc_demuxer_init(struct sc_demuxer *demuxer, sc_socket socket,
const struct sc_demuxer_callbacks *cbs, void *cbs_userdata) { const struct sc_demuxer_callbacks *cbs, void *cbs_userdata) {
demuxer->socket = socket; demuxer->socket = socket;
demuxer->pending = NULL;
demuxer->sink_count = 0; demuxer->sink_count = 0;
assert(cbs && cbs->on_ended); assert(cbs && cbs->on_ended);

View file

@ -21,10 +21,6 @@ struct sc_demuxer {
struct sc_packet_sink *sinks[SC_DEMUXER_MAX_SINKS]; struct sc_packet_sink *sinks[SC_DEMUXER_MAX_SINKS];
unsigned sink_count; 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; const struct sc_demuxer_callbacks *cbs;
void *cbs_userdata; void *cbs_userdata;
}; };

48
app/src/packet_merger.c Normal file
View file

@ -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;
}

43
app/src/packet_merger.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef SC_PACKET_MERGER_H
#define SC_PACKET_MERGER_H
#include "common.h"
#include <stdbool.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
/**
* 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