#include "../../include/encoder/encoder.h" #include "../../include/utils.h" #include #include #include #include bool gsr_encoder_init(gsr_encoder *self, size_t replay_buffer_num_packets) { memset(self, 0, sizeof(*self)); self->num_recording_destinations = 0; self->recording_destination_id_counter = 0; if(pthread_mutex_init(&self->file_write_mutex, NULL) != 0) { fprintf(stderr, "gsr error: gsr_encoder_init: failed to create mutex\n"); return false; } self->mutex_created = true; if(replay_buffer_num_packets > 0) { if(!gsr_replay_buffer_init(&self->replay_buffer, replay_buffer_num_packets)) { fprintf(stderr, "gsr error: gsr_encoder_init: failed to create replay buffer\n"); gsr_encoder_deinit(self); return false; } self->has_replay_buffer = true; } return true; } void gsr_encoder_deinit(gsr_encoder *self) { if(self->mutex_created) { self->mutex_created = false; pthread_mutex_destroy(&self->file_write_mutex); } gsr_replay_buffer_deinit(&self->replay_buffer); self->has_replay_buffer = false; self->num_recording_destinations = 0; self->recording_destination_id_counter = 0; } void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_context, int64_t pts, int stream_index) { for(;;) { AVPacket *av_packet = av_packet_alloc(); if(!av_packet) break; av_packet->data = NULL; av_packet->size = 0; int res = avcodec_receive_packet(codec_context, av_packet); if(res == 0) { // we have a packet, send the packet to the muxer av_packet->stream_index = stream_index; av_packet->pts = pts; av_packet->dts = pts; if(self->has_replay_buffer) { const double time_now = clock_get_monotonic_seconds(); if(!gsr_replay_buffer_append(&self->replay_buffer, av_packet, time_now)) fprintf(stderr, "gsr error: gsr_encoder_receive_packets: failed to add replay buffer data\n"); } pthread_mutex_lock(&self->file_write_mutex); const bool is_keyframe = av_packet->flags & AV_PKT_FLAG_KEY; for(size_t i = 0; i < self->num_recording_destinations; ++i) { gsr_encoder_recording_destination *recording_destination = &self->recording_destinations[i]; if(recording_destination->codec_context != codec_context) continue; if(is_keyframe) recording_destination->has_received_keyframe = true; else if(!recording_destination->has_received_keyframe) continue; av_packet->pts = pts - recording_destination->start_pts; av_packet->dts = pts - recording_destination->start_pts; av_packet_rescale_ts(av_packet, codec_context->time_base, recording_destination->stream->time_base); // TODO: Is av_interleaved_write_frame needed?. Answer: might be needed for mkv but dont use it! it causes frames to be inconsistent, skipping frames and duplicating frames. // TODO: av_interleaved_write_frame might be needed for cfr, or always for flv const int ret = av_write_frame(recording_destination->format_context, av_packet); if(ret < 0) { char error_buffer[AV_ERROR_MAX_STRING_SIZE]; if(av_strerror(ret, error_buffer, sizeof(error_buffer)) < 0) snprintf(error_buffer, sizeof(error_buffer), "Unknown error"); fprintf(stderr, "gsr error: gsr_encoder_receive_packets: failed to write frame index %d to muxer, reason: %s (%d)\n", av_packet->stream_index, error_buffer, ret); } } pthread_mutex_unlock(&self->file_write_mutex); av_packet_free(&av_packet); } else if (res == AVERROR(EAGAIN)) { // we have no packet // fprintf(stderr, "No packet!\n"); av_packet_free(&av_packet); break; } else if (res == AVERROR_EOF) { // this is the end of the stream av_packet_free(&av_packet); fprintf(stderr, "End of stream!\n"); break; } else { av_packet_free(&av_packet); fprintf(stderr, "Unexpected error: %d\n", res); break; } } } size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts) { if(self->num_recording_destinations >= GSR_MAX_RECORDING_DESTINATIONS) { fprintf(stderr, "gsr error: gsr_encoder_add_recording_destination: failed to add destination, reached the max amount of recording destinations (%d)\n", GSR_MAX_RECORDING_DESTINATIONS); return (size_t)-1; } for(size_t i = 0; i < self->num_recording_destinations; ++i) { if(self->recording_destinations[i].stream == stream) { fprintf(stderr, "gsr error: gsr_encoder_add_recording_destination: failed to add destination, the stream %p already exists as an output\n", (void*)stream); return (size_t)-1; } } pthread_mutex_lock(&self->file_write_mutex); gsr_encoder_recording_destination *recording_destination = &self->recording_destinations[self->num_recording_destinations]; recording_destination->id = self->recording_destination_id_counter; recording_destination->codec_context = codec_context; recording_destination->format_context = format_context; recording_destination->stream = stream; recording_destination->start_pts = start_pts; recording_destination->has_received_keyframe = false; ++self->recording_destination_id_counter; ++self->num_recording_destinations; pthread_mutex_unlock(&self->file_write_mutex); return recording_destination->id; } bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id) { bool found = false; pthread_mutex_lock(&self->file_write_mutex); for(size_t i = 0; i < self->num_recording_destinations; ++i) { if(self->recording_destinations[i].id == id) { self->recording_destinations[i] = self->recording_destinations[self->num_recording_destinations - 1]; --self->num_recording_destinations; found = true; break; } } pthread_mutex_unlock(&self->file_write_mutex); return found; }