obs-outputs: Allow p-frames to be dropped

Previously, for an unknown reason p-frames were marked as highest
priority along with i-frames (keyframes), which means they could not be
dropped.  This would cause a problem where if for whatever reason
there's too much congestion, data would continually buffer.  This fixes
the issue by dropping p-frames at a separate (higher) threshold than
b-frames.
master
jp9000 2016-08-13 01:16:32 -07:00
parent 7d5df34a6b
commit 34590b4b6a
1 changed files with 53 additions and 22 deletions

View File

@ -42,6 +42,7 @@
#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
#define OPT_DROP_THRESHOLD "drop_threshold_ms" #define OPT_DROP_THRESHOLD "drop_threshold_ms"
#define OPT_PFRAME_DROP_THRESHOLD "pframe_drop_threshold_ms"
#define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec" #define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec"
#define OPT_BIND_IP "bind_ip" #define OPT_BIND_IP "bind_ip"
@ -86,6 +87,8 @@ struct rtmp_stream {
/* frame drop variables */ /* frame drop variables */
int64_t drop_threshold_usec; int64_t drop_threshold_usec;
int64_t min_drop_dts_usec; int64_t min_drop_dts_usec;
int64_t pframe_drop_threshold_usec;
int64_t pframe_min_drop_dts_usec;
int min_priority; int min_priority;
int64_t last_dts_usec; int64_t last_dts_usec;
@ -696,6 +699,8 @@ static bool init_connect(struct rtmp_stream *stream)
obs_service_t *service; obs_service_t *service;
obs_data_t *settings; obs_data_t *settings;
const char *bind_ip; const char *bind_ip;
int64_t drop_p;
int64_t drop_b;
if (stopping(stream)) if (stopping(stream))
pthread_join(stream->send_thread, NULL); pthread_join(stream->send_thread, NULL);
@ -719,11 +724,17 @@ static bool init_connect(struct rtmp_stream *stream)
dstr_copy(&stream->password, obs_service_get_password(service)); dstr_copy(&stream->password, obs_service_get_password(service));
dstr_depad(&stream->path); dstr_depad(&stream->path);
dstr_depad(&stream->key); dstr_depad(&stream->key);
stream->drop_threshold_usec = drop_b = (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD);
(int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000; drop_p = (int64_t)obs_data_get_int(settings, OPT_PFRAME_DROP_THRESHOLD);
stream->max_shutdown_time_sec = stream->max_shutdown_time_sec =
(int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC); (int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC);
if (drop_p < (drop_b + 200))
drop_p = drop_b + 200;
stream->drop_threshold_usec = 1000 * drop_b;
stream->pframe_drop_threshold_usec = 1000 * drop_p;
bind_ip = obs_data_get_string(settings, OPT_BIND_IP); bind_ip = obs_data_get_string(settings, OPT_BIND_IP);
dstr_copy(&stream->bind_ip, bind_ip); dstr_copy(&stream->bind_ip, bind_ip);
@ -785,14 +796,18 @@ static inline size_t num_buffered_packets(struct rtmp_stream *stream)
return stream->packets.size / sizeof(struct encoder_packet); return stream->packets.size / sizeof(struct encoder_packet);
} }
static void drop_frames(struct rtmp_stream *stream) static void drop_frames(struct rtmp_stream *stream, const char *name,
int highest_priority, int64_t *p_min_dts_usec)
{ {
struct circlebuf new_buf = {0}; struct circlebuf new_buf = {0};
int drop_priority = 0;
uint64_t last_drop_dts_usec = 0; uint64_t last_drop_dts_usec = 0;
int num_frames_dropped = 0; int num_frames_dropped = 0;
debug("Previous packet count: %d", (int)num_buffered_packets(stream)); #ifdef _DEBUG
int start_packets = (int)num_buffered_packets(stream);
#else
UNUSED_PARAMETER(name);
#endif
circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8); circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8);
@ -804,56 +819,71 @@ static void drop_frames(struct rtmp_stream *stream)
/* do not drop audio data or video keyframes */ /* do not drop audio data or video keyframes */
if (packet.type == OBS_ENCODER_AUDIO || if (packet.type == OBS_ENCODER_AUDIO ||
packet.drop_priority == OBS_NAL_PRIORITY_HIGHEST) { packet.drop_priority >= highest_priority) {
circlebuf_push_back(&new_buf, &packet, sizeof(packet)); circlebuf_push_back(&new_buf, &packet, sizeof(packet));
} else { } else {
if (drop_priority < packet.drop_priority)
drop_priority = packet.drop_priority;
num_frames_dropped++; num_frames_dropped++;
obs_free_encoder_packet(&packet); obs_free_encoder_packet(&packet);
} }
} }
circlebuf_free(&stream->packets); circlebuf_free(&stream->packets);
stream->packets = new_buf; stream->packets = new_buf;
stream->min_priority = drop_priority;
stream->min_drop_dts_usec = last_drop_dts_usec; if (stream->min_priority < highest_priority)
stream->min_priority = highest_priority;
*p_min_dts_usec = last_drop_dts_usec;
stream->dropped_frames += num_frames_dropped; stream->dropped_frames += num_frames_dropped;
debug("New packet count: %d", (int)num_buffered_packets(stream)); #ifdef _DEBUG
debug("Dropped %s, prev packet count: %d, new packet count: %d",
name,
start_packets,
(int)num_buffered_packets(stream));
#endif
} }
static void check_to_drop_frames(struct rtmp_stream *stream) static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
{ {
struct encoder_packet first; struct encoder_packet first;
int64_t buffer_duration_usec; int64_t buffer_duration_usec;
size_t num_packets = num_buffered_packets(stream);
const char *name = pframes ? "p-frames" : "b-frames";
int priority = pframes ?
OBS_NAL_PRIORITY_HIGHEST : OBS_NAL_PRIORITY_HIGH;
int64_t *p_min_dts_usec = pframes ?
&stream->pframe_min_drop_dts_usec :
&stream->min_drop_dts_usec;
int64_t drop_threshold = pframes ?
stream->pframe_drop_threshold_usec :
stream->drop_threshold_usec;
if (num_buffered_packets(stream) < 5) if (num_packets < 5)
return; return;
circlebuf_peek_front(&stream->packets, &first, sizeof(first)); circlebuf_peek_front(&stream->packets, &first, sizeof(first));
/* do not drop frames if frames were just dropped within this time */ /* do not drop frames if frames were just dropped within this time */
if (first.dts_usec < stream->min_drop_dts_usec) if (first.dts_usec < *p_min_dts_usec)
return; return;
/* if the amount of time stored in the buffered packets waiting to be /* if the amount of time stored in the buffered packets waiting to be
* sent is higher than threshold, drop frames */ * sent is higher than threshold, drop frames */
buffer_duration_usec = stream->last_dts_usec - first.dts_usec; buffer_duration_usec = stream->last_dts_usec - first.dts_usec;
if (buffer_duration_usec > stream->drop_threshold_usec) { if (buffer_duration_usec > drop_threshold) {
drop_frames(stream); debug("buffer_duration_usec: %lld", buffer_duration_usec);
debug("dropping %" PRId64 " worth of frames", drop_frames(stream, name, priority, p_min_dts_usec);
buffer_duration_usec);
} }
} }
static bool add_video_packet(struct rtmp_stream *stream, static bool add_video_packet(struct rtmp_stream *stream,
struct encoder_packet *packet) struct encoder_packet *packet)
{ {
check_to_drop_frames(stream); check_to_drop_frames(stream, false);
check_to_drop_frames(stream, true);
/* if currently dropping frames, drop packets until it reaches the /* if currently dropping frames, drop packets until it reaches the
* desired priority */ * desired priority */
@ -899,7 +929,8 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet)
static void rtmp_stream_defaults(obs_data_t *defaults) static void rtmp_stream_defaults(obs_data_t *defaults)
{ {
obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 600); obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 500);
obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 800);
obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 5); obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 5);
obs_data_set_default_string(defaults, OPT_BIND_IP, "default"); obs_data_set_default_string(defaults, OPT_BIND_IP, "default");
} }