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:
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user