bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags) { bool encoded, has_video, has_audio, has_service; size_t num_mixes = num_audio_mixes(output); if (!output) return false; if (output->active) return false; convert_flags(output, flags, &encoded, &has_video, &has_audio, &has_service); if (!encoded) return false; if (has_service && !obs_service_initialize(output->service, output)) return false; if (has_video && !obs_encoder_initialize(output->video_encoder)) return false; if (has_audio && !initialize_audio_encoders(output, num_mixes)) return false; if (has_video && has_audio) { if (!pair_encoders(output, num_mixes)) { return false; } } return true; }
static bool initialize_interleaved_packets(struct obs_output *output) { struct encoder_packet *video; struct encoder_packet *audio[MAX_AUDIO_MIXES]; struct encoder_packet *last_audio[MAX_AUDIO_MIXES]; size_t audio_mixes = num_audio_mixes(output); size_t start_idx; if (!get_audio_and_video_packets(output, &video, audio, audio_mixes)) return false; for (size_t i = 0; i < audio_mixes; i++) last_audio[i] = find_last_packet_type(output, OBS_ENCODER_AUDIO, i); /* ensure that there is audio past the first video packet */ for (size_t i = 0; i < audio_mixes; i++) { if (last_audio[i]->dts_usec < video->dts_usec) { output->received_audio = false; return false; } } /* clear out excess starting audio if it hasn't been already */ start_idx = get_interleaved_start_idx(output); if (start_idx) { discard_to_idx(output, start_idx); if (!get_audio_and_video_packets(output, &video, audio, audio_mixes)) return false; } /* get new offsets */ output->video_offset = video->dts; for (size_t i = 0; i < audio_mixes; i++) output->audio_offsets[i] = audio[i]->dts; #if DEBUG_STARTING_PACKETS == 1 int64_t v = video->dts_usec; int64_t a = audio[0]->dts_usec; int64_t diff = v - a; blog(LOG_DEBUG, "output '%s' offset for video: %lld, audio: %lld, " "diff: %lldms", output->context.name, v, a, diff / 1000LL); #endif /* subtract offsets from highest TS offset variables */ output->highest_audio_ts -= audio[0]->dts_usec; output->highest_video_ts -= video->dts_usec; /* apply new offsets to all existing packet DTS/PTS values */ for (size_t i = 0; i < output->interleaved_packets.num; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; apply_interleaved_packet_offset(output, packet); } return true; }
static inline void start_audio_encoders(struct obs_output *output, encoded_callback_t encoded_callback) { size_t num_mixes = num_audio_mixes(output); for (size_t i = 0; i < num_mixes; i++) { obs_encoder_start(output->audio_encoders[i], encoded_callback, output); } }
static inline bool audio_valid(const struct obs_output *output, bool encoded) { if (encoded) { size_t mix_count = num_audio_mixes(output); if (!mix_count) return false; for (size_t i = 0; i < mix_count; i++) { if (!output->audio_encoders[i]) { return false; } } } else { if (!output->audio) return false; } return true; }
static int prune_premature_packets(struct obs_output *output) { size_t audio_mixes = num_audio_mixes(output); struct encoder_packet *video; int video_idx; int max_idx; int64_t duration_usec; int64_t max_diff = 0; int64_t diff = 0; video_idx = find_first_packet_type_idx(output, OBS_ENCODER_VIDEO, 0); if (video_idx == -1) { output->received_video = false; return -1; } max_idx = video_idx; video = &output->interleaved_packets.array[video_idx]; duration_usec = video->timebase_num * 1000000LL / video->timebase_den; for (size_t i = 0; i < audio_mixes; i++) { struct encoder_packet *audio; int audio_idx; audio_idx = find_first_packet_type_idx(output, OBS_ENCODER_AUDIO, i); if (audio_idx == -1) { output->received_audio = false; return -1; } audio = &output->interleaved_packets.array[audio_idx]; if (audio_idx > max_idx) max_idx = audio_idx; diff = audio->dts_usec - video->dts_usec; if (diff > max_diff) max_diff = diff; } return diff > duration_usec ? max_idx + 1 : 0; }
static bool initialize_interleaved_packets(struct obs_output *output) { struct encoder_packet *video; struct encoder_packet *audio[MAX_AUDIO_MIXES]; size_t audio_mixes = num_audio_mixes(output); video = find_first_packet_type(output, OBS_ENCODER_VIDEO, 0); if (!video) output->received_video = false; for (size_t i = 0; i < audio_mixes; i++) { audio[i] = find_first_packet_type(output, OBS_ENCODER_AUDIO, i); if (!audio[i]) { output->received_audio = false; return false; } } if (!video) { return false; } /* get new offsets */ output->video_offset = video->dts; for (size_t i = 0; i < audio_mixes; i++) output->audio_offsets[i] = audio[i]->dts; /* subtract offsets from highest TS offset variables */ output->highest_audio_ts -= audio[0]->dts_usec; output->highest_video_ts -= video->dts_usec; /* apply new offsets to all existing packet DTS/PTS values */ for (size_t i = 0; i < output->interleaved_packets.num; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; apply_interleaved_packet_offset(output, packet); } return true; }