Fix issue when using multiple video encoders
- Fix an issue that could occur when using more than one video encoder. Audio/video would not sync up correctly because they were expected to be paired with a particular encoder. This simply adds a little helper variable to encoder packets that specifies the system time in microseconds. We then use that system time to sync - Fix an issue with x264 with fractional FPS rates (29.97 and 59.94 particularly) where it would create ridiculously large stream outputs. The problem was that you shouldn't set the timebase_* variables in the x264 params manually, let x264 handle the default values for it and leave them at 0. - Make x264 use CFR output, because there's no reason to ever use VFR in this case.
This commit is contained in:
parent
92522d1886
commit
519c4f4118
@ -514,6 +514,10 @@ static inline void do_encode(struct obs_encoder *encoder,
|
||||
}
|
||||
|
||||
if (received) {
|
||||
/* we use system time here to ensure sync with other encoders,
|
||||
* you do not want to use relative timestamps here */
|
||||
pkt.dts_usec = encoder->start_ts / 1000 + packet_dts_usec(&pkt);
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
for (size_t i = 0; i < encoder->callbacks.num; i++) {
|
||||
|
@ -41,6 +41,9 @@ struct encoder_packet {
|
||||
/* ---------------------------------------------------------------- */
|
||||
/* Internal video variables (will be parsed automatically) */
|
||||
|
||||
/* DTS in microseconds */
|
||||
int64_t dts_usec;
|
||||
|
||||
/**
|
||||
* Packet priority
|
||||
*
|
||||
|
@ -34,7 +34,12 @@
|
||||
#include "obs.h"
|
||||
|
||||
#define NUM_TEXTURES 2
|
||||
#define MICROSECOND_DEN 1000000
|
||||
|
||||
static inline int64_t packet_dts_usec(struct encoder_packet *packet)
|
||||
{
|
||||
return packet->dts * MICROSECOND_DEN / packet->timebase_den;
|
||||
}
|
||||
|
||||
struct draw_callback {
|
||||
void (*draw)(void *param, uint32_t cx, uint32_t cy);
|
||||
@ -262,12 +267,6 @@ extern void obs_source_video_tick(obs_source_t source, float seconds);
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* outputs */
|
||||
|
||||
struct il_packet {
|
||||
int64_t input_ts_us;
|
||||
int64_t output_ts_us;
|
||||
struct encoder_packet packet;
|
||||
};
|
||||
|
||||
struct obs_output {
|
||||
char *name;
|
||||
void *data;
|
||||
@ -284,7 +283,7 @@ struct obs_output {
|
||||
int64_t audio_offset;
|
||||
int interleaved_wait;
|
||||
pthread_mutex_t interleaved_mutex;
|
||||
DARRAY(struct il_packet) interleaved_packets;
|
||||
DARRAY(struct encoder_packet) interleaved_packets;
|
||||
|
||||
bool active;
|
||||
video_t video;
|
||||
|
@ -95,15 +95,10 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void free_il_packet(struct il_packet *data)
|
||||
{
|
||||
obs_free_encoder_packet(&data->packet);
|
||||
}
|
||||
|
||||
static inline void free_packets(struct obs_output *output)
|
||||
{
|
||||
for (size_t i = 0; i < output->interleaved_packets.num; i++)
|
||||
free_il_packet(output->interleaved_packets.array+i);
|
||||
obs_free_encoder_packet(output->interleaved_packets.array+i);
|
||||
da_free(output->interleaved_packets);
|
||||
}
|
||||
|
||||
@ -351,28 +346,19 @@ static inline struct audio_convert_info *get_audio_conversion(
|
||||
return output->audio_conversion_set ? &output->audio_conversion : NULL;
|
||||
}
|
||||
|
||||
#define MICROSECOND_DEN 1000000
|
||||
|
||||
static inline int64_t convert_packet_dts(struct encoder_packet *packet)
|
||||
{
|
||||
return packet->dts * MICROSECOND_DEN / packet->timebase_den;
|
||||
}
|
||||
|
||||
static bool prepare_interleaved_packet(struct obs_output *output,
|
||||
struct il_packet *out, struct encoder_packet *packet)
|
||||
struct encoder_packet *out, struct encoder_packet *in)
|
||||
{
|
||||
int64_t offset;
|
||||
|
||||
out->input_ts_us = convert_packet_dts(packet);
|
||||
|
||||
/* audio and video need to start at timestamp 0, and the encoders
|
||||
* may not currently be at 0 when we get data. so, we store the
|
||||
* current dts as offset and subtract that value from the dts/pts
|
||||
* of the output packet. */
|
||||
if (packet->type == OBS_ENCODER_VIDEO) {
|
||||
if (in->type == OBS_ENCODER_VIDEO) {
|
||||
if (!output->received_video) {
|
||||
output->first_video_ts = out->input_ts_us;
|
||||
output->video_offset = packet->dts;
|
||||
output->first_video_ts = in->dts_usec;
|
||||
output->video_offset = in->dts;
|
||||
output->received_video = true;
|
||||
}
|
||||
|
||||
@ -380,47 +366,54 @@ static bool prepare_interleaved_packet(struct obs_output *output,
|
||||
} else{
|
||||
/* don't accept audio that's before the first video timestamp */
|
||||
if (!output->received_video ||
|
||||
out->input_ts_us < output->first_video_ts)
|
||||
in->dts_usec < output->first_video_ts)
|
||||
return false;
|
||||
|
||||
if (!output->received_audio) {
|
||||
output->audio_offset = packet->dts;
|
||||
output->audio_offset = in->dts;
|
||||
output->received_audio = true;
|
||||
}
|
||||
|
||||
offset = output->audio_offset;
|
||||
}
|
||||
|
||||
obs_duplicate_encoder_packet(&out->packet, packet);
|
||||
out->packet.dts -= offset;
|
||||
out->packet.pts -= offset;
|
||||
out->output_ts_us = convert_packet_dts(&out->packet);
|
||||
obs_duplicate_encoder_packet(out, in);
|
||||
out->dts -= offset;
|
||||
out->pts -= offset;
|
||||
|
||||
/* convert the newly adjusted dts to relative dts time to ensure proper
|
||||
* interleaving. if we're using an audio encoder that's already been
|
||||
* started on another output, then the first audio packet may not be
|
||||
* quite perfectly synced up in terms of system time (and there's
|
||||
* nothing we can really do about that), but it will always at least be
|
||||
* within a 23ish millisecond threshold (at least for AAC) */
|
||||
out->dts_usec = packet_dts_usec(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void send_interleaved(struct obs_output *output)
|
||||
{
|
||||
struct il_packet out = output->interleaved_packets.array[0];
|
||||
struct encoder_packet out = output->interleaved_packets.array[0];
|
||||
da_erase(output->interleaved_packets, 0);
|
||||
|
||||
output->info.encoded_packet(output->data, &out.packet);
|
||||
free_il_packet(&out);
|
||||
output->info.encoded_packet(output->data, &out);
|
||||
obs_free_encoder_packet(&out);
|
||||
}
|
||||
|
||||
static void interleave_packets(void *data, struct encoder_packet *packet)
|
||||
{
|
||||
struct obs_output *output = data;
|
||||
struct il_packet out;
|
||||
size_t idx;
|
||||
struct obs_output *output = data;
|
||||
struct encoder_packet out;
|
||||
size_t idx;
|
||||
|
||||
pthread_mutex_lock(&output->interleaved_mutex);
|
||||
|
||||
if (prepare_interleaved_packet(output, &out, packet)) {
|
||||
for (idx = 0; idx < output->interleaved_packets.num; idx++) {
|
||||
struct il_packet *cur_packet;
|
||||
struct encoder_packet *cur_packet;
|
||||
cur_packet = output->interleaved_packets.array + idx;
|
||||
|
||||
if (out.output_ts_us < cur_packet->output_ts_us)
|
||||
if (out.dts_usec < cur_packet->dts_usec)
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,11 @@
|
||||
#include "obs-output-ver.h"
|
||||
#include "rtmp-helpers.h"
|
||||
|
||||
/* FIXME: this is currently hard-coded to h264 and aac! ..not that we'll
|
||||
/* TODO: FIXME: this is currently hard-coded to h264 and aac! ..not that we'll
|
||||
* use anything else for a long time. */
|
||||
|
||||
// #define DEBUG_TIMESTAMPS
|
||||
//#define DEBUG_TIMESTAMPS
|
||||
//#define WRITE_FLV_HEADER
|
||||
|
||||
#define VIDEO_HEADER_SIZE 5
|
||||
#define MILLISECOND_DEN 1000
|
||||
@ -94,11 +95,13 @@ void flv_meta_data(obs_output_t context, uint8_t **output, size_t *size)
|
||||
|
||||
build_flv_meta_data(context, &meta_data, &meta_data_size);
|
||||
|
||||
/* s_write(&s, "FLV", 3);
|
||||
#ifdef WRITE_FLV_HEADER
|
||||
s_write(&s, "FLV", 3);
|
||||
s_w8(&s, 1);
|
||||
s_w8(&s, 5);
|
||||
s_wb32(&s, 9);
|
||||
s_wb32(&s, 0); */
|
||||
s_wb32(&s, 0);
|
||||
#endif
|
||||
|
||||
start_pos = serializer_get_pos(&s);
|
||||
|
||||
|
@ -226,6 +226,7 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
|
||||
obsx264->params.i_keyint_max =
|
||||
keyint_sec * voi->fps_num / voi->fps_den;
|
||||
|
||||
obsx264->params.b_vfr_input = false;
|
||||
obsx264->params.rc.i_vbv_max_bitrate = bitrate;
|
||||
obsx264->params.rc.i_vbv_buffer_size = buffer_size;
|
||||
obsx264->params.rc.i_bitrate = bitrate;
|
||||
@ -233,8 +234,6 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
|
||||
obsx264->params.i_height = voi->height;
|
||||
obsx264->params.i_fps_num = voi->fps_num;
|
||||
obsx264->params.i_fps_den = voi->fps_den;
|
||||
obsx264->params.i_timebase_num = voi->fps_den;
|
||||
obsx264->params.i_timebase_den = voi->fps_num;
|
||||
obsx264->params.pf_log = log_x264;
|
||||
obsx264->params.i_log_level = X264_LOG_WARNING;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user