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:
jp9000 2014-04-10 11:59:42 -07:00
parent 92522d1886
commit 519c4f4118
6 changed files with 47 additions and 46 deletions

View File

@ -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++) {

View File

@ -41,6 +41,9 @@ struct encoder_packet {
/* ---------------------------------------------------------------- */
/* Internal video variables (will be parsed automatically) */
/* DTS in microseconds */
int64_t dts_usec;
/**
* Packet priority
*

View File

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

View File

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

View File

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

View File

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