Add packet interleaving and improve encoder API

- Add interleaving of video/audio packets for outputs that are encoded
   and expect both video and audio data, sorting the packets and sending
   them to the output when both video and audio is received.

 - Combine create and initialize callbacks for the encoder API callback
   interface.
This commit is contained in:
jp9000
2014-04-04 23:21:19 -07:00
parent 42be968759
commit 8c74db9ffc
8 changed files with 175 additions and 89 deletions

View File

@@ -51,14 +51,6 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
if (encoder->info.defaults)
encoder->info.defaults(encoder->settings);
encoder->data = encoder->info.create(encoder->settings, encoder);
if (!encoder->data) {
pthread_mutex_destroy(&encoder->callbacks_mutex);
obs_data_release(encoder->settings);
return false;
}
pthread_mutex_lock(&obs->data.encoders_mutex);
da_push_back(obs->data.encoders, &encoder);
pthread_mutex_unlock(&obs->data.encoders_mutex);
@@ -188,7 +180,8 @@ static void obs_encoder_actually_destroy(obs_encoder_t encoder)
da_free(encoder->outputs);
pthread_mutex_unlock(&encoder->outputs_mutex);
encoder->info.destroy(encoder->data);
if (encoder->data)
encoder->info.destroy(encoder->data);
obs_data_release(encoder->settings);
pthread_mutex_destroy(&encoder->callbacks_mutex);
pthread_mutex_destroy(&encoder->outputs_mutex);
@@ -265,15 +258,16 @@ void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
if (!encoder) return;
obs_data_apply(encoder->settings, settings);
if (encoder->info.update)
if (encoder->info.update && encoder->data)
encoder->info.update(encoder->data, encoder->settings);
}
bool obs_encoder_get_extra_data(obs_encoder_t encoder, uint8_t **extra_data,
size_t *size)
{
if (encoder && encoder->info.extra_data)
return encoder->info.extra_data(encoder, extra_data, size);
if (encoder && encoder->info.extra_data && encoder->data)
return encoder->info.extra_data(encoder->data, extra_data,
size);
return false;
}
@@ -293,9 +287,11 @@ bool obs_encoder_initialize(obs_encoder_t encoder)
if (encoder->active)
return true;
encoder->initialized = encoder->info.initialize(encoder,
encoder->settings);
return encoder->initialized;
if (encoder->data)
encoder->info.destroy(encoder->data);
encoder->data = encoder->info.create(encoder->settings, encoder);
return encoder->data != NULL;
}
static inline size_t get_callback_idx(
@@ -321,7 +317,7 @@ void obs_encoder_start(obs_encoder_t encoder,
bool success = true;
bool first = false;
if (!encoder || !new_packet || !encoder->initialized) return;
if (!encoder || !new_packet || !encoder->data) return;
pthread_mutex_lock(&encoder->callbacks_mutex);

View File

@@ -109,7 +109,8 @@ struct obs_encoder_info {
*
* @param settings Settings for the encoder
* @param encoder OBS encoder context
* @return Data associated with this encoder context
* @return Data associated with this encoder context, or
* NULL if initialization failed.
*/
void *(*create)(obs_data_t settings, obs_encoder_t encoder);
@@ -120,16 +121,6 @@ struct obs_encoder_info {
*/
void (*destroy)(void *data);
/**
* Initializes the encoder with the specified settings
*
* @param data Data associated with this encoder context
* @param settings Settings for the encoder
* @return true if the encoder settings are valid and the
* encoder is ready to be used, false otherwise
*/
bool (*initialize)(void *data, obs_data_t settings);
/**
* Encodes frame(s), and outputs encoded packets as they become
* available.

View File

@@ -259,6 +259,12 @@ 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;
@@ -268,8 +274,13 @@ struct obs_output {
signal_handler_t signals;
proc_handler_t procs;
bool received_video;
bool received_audio;
int64_t first_video_ts;
int64_t video_offset;
int64_t audio_offset;
pthread_mutex_t interleaved_mutex;
DARRAY(struct encoder_packet) interleaved_packets;
DARRAY(struct il_packet) interleaved_packets;
bool active;
video_t video;
@@ -304,7 +315,6 @@ struct obs_encoder {
struct obs_encoder_info info;
obs_data_t settings;
bool initialized;
bool active;
uint32_t timebase_num;

View File

@@ -1,5 +1,5 @@
/******************************************************************************
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -222,7 +222,6 @@ void obs_register_encoder(const struct obs_encoder_info *info)
CHECK_REQUIRED_VAL(info, getname, obs_register_encoder);
CHECK_REQUIRED_VAL(info, create, obs_register_encoder);
CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder);
CHECK_REQUIRED_VAL(info, initialize, obs_register_encoder);
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
REGISTER_OBS_DEF(cur_encoder_info_size, obs_encoder_info,

View File

@@ -93,10 +93,15 @@ 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++)
obs_free_encoder_packet(output->interleaved_packets.array+i);
free_il_packet(output->interleaved_packets.array+i);
da_free(output->interleaved_packets);
}
@@ -342,9 +347,89 @@ 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)
{
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 (!output->received_video) {
output->first_video_ts = out->input_ts_us;
output->video_offset = packet->dts;
output->received_video = true;
}
offset = output->video_offset;
} else{
/* don't accept audio that's before the first video timestamp */
if (!output->received_video ||
out->input_ts_us < output->first_video_ts)
return false;
if (!output->received_audio) {
output->audio_offset = packet->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);
return true;
}
static inline void send_interleaved(struct obs_output *output)
{
struct il_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);
}
static void interleave_packets(void *data, struct encoder_packet *packet)
{
struct obs_output *output = data;
struct il_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;
cur_packet = output->interleaved_packets.array + idx;
if (out.output_ts_us < cur_packet->output_ts_us)
break;
}
da_insert(output->interleaved_packets, idx, &out);
/* when both video and audio have been received, we're ready
* to start sending out packets (one at a time) */
if (output->received_audio && output->received_video)
send_interleaved(output);
}
pthread_mutex_unlock(&output->interleaved_mutex);
}
static void hook_data_capture(struct obs_output *output, bool encoded,
@@ -354,6 +439,9 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
void *param;
if (encoded) {
output->received_video = false;
output->received_video = false;
encoded_callback = (has_video && has_audio) ?
interleave_packets : output->info.encoded_packet;
param = (has_video && has_audio) ? output : output->data;