Skip to content

Commit

Permalink
Extract packet merging
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rom1v committed Feb 11, 2023
1 parent f03f322 commit 49eb326
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 49 deletions.
1 change: 1 addition & 0 deletions app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
57 changes: 12 additions & 45 deletions app/src/demuxer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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) {
Expand All @@ -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:
Expand All @@ -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);
Expand Down
4 changes: 0 additions & 4 deletions app/src/demuxer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
48 changes: 48 additions & 0 deletions app/src/packet_merger.c
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions app/src/packet_merger.h
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 49eb326

Please sign in to comment.