libobs: Fix starting video packet offset in outputs
(This commit also modifies the obs-outputs module) The first video packet video offset (the value used to set the starting point of video data) would be set to the DTS value of the first video packet. However, when b-frames are used, the first DTS value will be negative. This was originally done because FLV muxing requires that the first packet's DTS start from 0. Unfortunately, this would also effectively cause the first packet's PTS/DTS value to be shifted forward by the negative amount, which would cause video sync to be off by a video frame or two. This fixes it to start at the PTS value instead and preserve any negative offsets. Additionally, the FLV muxing code has been fixed to ensure that it adjusts the starting video DTS to 0, and now correctly adjusts the first audio packet's timestamp according to that DTS as well (which it didn't do before).master
parent
2f04010a4a
commit
4a5ac39749
|
@ -1301,7 +1301,7 @@ static bool initialize_interleaved_packets(struct obs_output *output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get new offsets */
|
/* get new offsets */
|
||||||
output->video_offset = video->dts;
|
output->video_offset = video->pts;
|
||||||
for (size_t i = 0; i < audio_mixes; i++)
|
for (size_t i = 0; i < audio_mixes; i++)
|
||||||
output->audio_offsets[i] = audio[i]->dts;
|
output->audio_offsets[i] = audio[i]->dts;
|
||||||
|
|
||||||
|
|
|
@ -177,11 +177,11 @@ bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
||||||
static int32_t last_time = 0;
|
static int32_t last_time = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void flv_video(struct serializer *s, struct encoder_packet *packet,
|
static void flv_video(struct serializer *s, int32_t dts_offset,
|
||||||
bool is_header)
|
struct encoder_packet *packet, bool is_header)
|
||||||
{
|
{
|
||||||
int64_t offset = packet->pts - packet->dts;
|
int64_t offset = packet->pts - packet->dts;
|
||||||
int32_t time_ms = get_ms_time(packet, packet->dts);
|
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
|
||||||
|
|
||||||
if (!packet->data || !packet->size)
|
if (!packet->data || !packet->size)
|
||||||
return;
|
return;
|
||||||
|
@ -212,10 +212,10 @@ static void flv_video(struct serializer *s, struct encoder_packet *packet,
|
||||||
s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1);
|
s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flv_audio(struct serializer *s, struct encoder_packet *packet,
|
static void flv_audio(struct serializer *s, int32_t dts_offset,
|
||||||
bool is_header)
|
struct encoder_packet *packet, bool is_header)
|
||||||
{
|
{
|
||||||
int32_t time_ms = get_ms_time(packet, packet->dts);
|
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
|
||||||
|
|
||||||
if (!packet->data || !packet->size)
|
if (!packet->data || !packet->size)
|
||||||
return;
|
return;
|
||||||
|
@ -245,7 +245,7 @@ static void flv_audio(struct serializer *s, struct encoder_packet *packet,
|
||||||
s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1);
|
s_wb32(s, (uint32_t)serializer_get_pos(s) + 4 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void flv_packet_mux(struct encoder_packet *packet,
|
void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset,
|
||||||
uint8_t **output, size_t *size, bool is_header)
|
uint8_t **output, size_t *size, bool is_header)
|
||||||
{
|
{
|
||||||
struct array_output_data data;
|
struct array_output_data data;
|
||||||
|
@ -254,9 +254,9 @@ void flv_packet_mux(struct encoder_packet *packet,
|
||||||
array_output_serializer_init(&s, &data);
|
array_output_serializer_init(&s, &data);
|
||||||
|
|
||||||
if (packet->type == OBS_ENCODER_VIDEO)
|
if (packet->type == OBS_ENCODER_VIDEO)
|
||||||
flv_video(&s, packet, is_header);
|
flv_video(&s, dts_offset, packet, is_header);
|
||||||
else
|
else
|
||||||
flv_audio(&s, packet, is_header);
|
flv_audio(&s, dts_offset, packet, is_header);
|
||||||
|
|
||||||
*output = data.bytes.array;
|
*output = data.bytes.array;
|
||||||
*size = data.bytes.num;
|
*size = data.bytes.num;
|
||||||
|
|
|
@ -21,14 +21,14 @@
|
||||||
|
|
||||||
#define MILLISECOND_DEN 1000
|
#define MILLISECOND_DEN 1000
|
||||||
|
|
||||||
static uint32_t get_ms_time(struct encoder_packet *packet, int64_t val)
|
static int32_t get_ms_time(struct encoder_packet *packet, int64_t val)
|
||||||
{
|
{
|
||||||
return (uint32_t)(val * MILLISECOND_DEN / packet->timebase_den);
|
return (int32_t)(val * MILLISECOND_DEN / packet->timebase_den);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void write_file_info(FILE *file, int64_t duration_ms, int64_t size);
|
extern void write_file_info(FILE *file, int64_t duration_ms, int64_t size);
|
||||||
|
|
||||||
extern bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
extern bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
||||||
bool write_header, size_t audio_idx);
|
bool write_header, size_t audio_idx);
|
||||||
extern void flv_packet_mux(struct encoder_packet *packet,
|
extern void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset,
|
||||||
uint8_t **output, size_t *size, bool is_header);
|
uint8_t **output, size_t *size, bool is_header);
|
||||||
|
|
|
@ -32,12 +32,15 @@
|
||||||
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
|
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
struct flv_output {
|
struct flv_output {
|
||||||
obs_output_t *output;
|
obs_output_t *output;
|
||||||
struct dstr path;
|
struct dstr path;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
bool active;
|
volatile bool active;
|
||||||
bool sent_headers;
|
bool sent_headers;
|
||||||
int64_t last_packet_ts;
|
int64_t last_packet_ts;
|
||||||
|
|
||||||
|
bool got_first_video;
|
||||||
|
int32_t start_dts_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *flv_output_getname(void *unused)
|
static const char *flv_output_getname(void *unused)
|
||||||
|
@ -98,7 +101,8 @@ static int write_packet(struct flv_output *stream,
|
||||||
|
|
||||||
stream->last_packet_ts = get_ms_time(packet, packet->dts);
|
stream->last_packet_ts = get_ms_time(packet, packet->dts);
|
||||||
|
|
||||||
flv_packet_mux(packet, &data, &size, is_header);
|
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset,
|
||||||
|
&data, &size, is_header);
|
||||||
fwrite(data, 1, size, stream->file);
|
fwrite(data, 1, size, stream->file);
|
||||||
bfree(data);
|
bfree(data);
|
||||||
obs_encoder_packet_release(packet);
|
obs_encoder_packet_release(packet);
|
||||||
|
@ -168,6 +172,8 @@ static bool flv_output_start(void *data)
|
||||||
if (!obs_output_initialize_encoders(stream->output, 0))
|
if (!obs_output_initialize_encoders(stream->output, 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
stream->got_first_video = false;
|
||||||
|
|
||||||
/* get path */
|
/* get path */
|
||||||
settings = obs_output_get_settings(stream->output);
|
settings = obs_output_get_settings(stream->output);
|
||||||
path = obs_data_get_string(settings, "path");
|
path = obs_data_get_string(settings, "path");
|
||||||
|
@ -199,6 +205,12 @@ static void flv_output_data(void *data, struct encoder_packet *packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet->type == OBS_ENCODER_VIDEO) {
|
if (packet->type == OBS_ENCODER_VIDEO) {
|
||||||
|
if (!stream->got_first_video) {
|
||||||
|
stream->start_dts_offset =
|
||||||
|
get_ms_time(packet, packet->dts);
|
||||||
|
stream->got_first_video = true;
|
||||||
|
}
|
||||||
|
|
||||||
obs_parse_avc_packet(&parsed_packet, packet);
|
obs_parse_avc_packet(&parsed_packet, packet);
|
||||||
write_packet(stream, &parsed_packet, false);
|
write_packet(stream, &parsed_packet, false);
|
||||||
obs_encoder_packet_release(&parsed_packet);
|
obs_encoder_packet_release(&parsed_packet);
|
||||||
|
|
|
@ -347,7 +347,8 @@ static int send_packet(struct rtmp_stream *stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flv_packet_mux(packet, &data, &size, is_header);
|
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset,
|
||||||
|
&data, &size, is_header);
|
||||||
|
|
||||||
#ifdef TEST_FRAMEDROPS
|
#ifdef TEST_FRAMEDROPS
|
||||||
droptest_cap_data_rate(stream, size);
|
droptest_cap_data_rate(stream, size);
|
||||||
|
@ -876,6 +877,7 @@ static bool init_connect(struct rtmp_stream *stream)
|
||||||
stream->total_bytes_sent = 0;
|
stream->total_bytes_sent = 0;
|
||||||
stream->dropped_frames = 0;
|
stream->dropped_frames = 0;
|
||||||
stream->min_priority = 0;
|
stream->min_priority = 0;
|
||||||
|
stream->got_first_video = false;
|
||||||
|
|
||||||
settings = obs_output_get_settings(stream->output);
|
settings = obs_output_get_settings(stream->output);
|
||||||
dstr_copy(&stream->path, obs_service_get_url(service));
|
dstr_copy(&stream->path, obs_service_get_url(service));
|
||||||
|
@ -1089,10 +1091,17 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet)
|
||||||
if (disconnected(stream) || !active(stream))
|
if (disconnected(stream) || !active(stream))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (packet->type == OBS_ENCODER_VIDEO)
|
if (packet->type == OBS_ENCODER_VIDEO) {
|
||||||
|
if (!stream->got_first_video) {
|
||||||
|
stream->start_dts_offset =
|
||||||
|
get_ms_time(packet, packet->dts);
|
||||||
|
stream->got_first_video = true;
|
||||||
|
}
|
||||||
|
|
||||||
obs_parse_avc_packet(&new_packet, packet);
|
obs_parse_avc_packet(&new_packet, packet);
|
||||||
else
|
} else {
|
||||||
obs_encoder_packet_ref(&new_packet, packet);
|
obs_encoder_packet_ref(&new_packet, packet);
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&stream->packets_mutex);
|
pthread_mutex_lock(&stream->packets_mutex);
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ struct rtmp_stream {
|
||||||
struct circlebuf packets;
|
struct circlebuf packets;
|
||||||
bool sent_headers;
|
bool sent_headers;
|
||||||
|
|
||||||
|
bool got_first_video;
|
||||||
|
int64_t start_dts_offset;
|
||||||
|
|
||||||
volatile bool connecting;
|
volatile bool connecting;
|
||||||
pthread_t connect_thread;
|
pthread_t connect_thread;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue