static void rtmp_stream_data(void *data, struct encoder_packet *packet) { struct rtmp_stream *stream = data; struct encoder_packet new_packet; bool added_packet = false; if (disconnected(stream)) return; if (packet->type == OBS_ENCODER_VIDEO) obs_parse_avc_packet(&new_packet, packet); else obs_duplicate_encoder_packet(&new_packet, packet); pthread_mutex_lock(&stream->packets_mutex); if (!disconnected(stream)) { added_packet = (packet->type == OBS_ENCODER_VIDEO) ? add_video_packet(stream, &new_packet) : add_packet(stream, &new_packet); } pthread_mutex_unlock(&stream->packets_mutex); if (added_packet) os_sem_post(stream->send_sem); else obs_free_encoder_packet(&new_packet); }
static int send_packet(struct rtmp_stream *stream, struct encoder_packet *packet, bool is_header, size_t idx) { uint8_t *data; size_t size; int recv_size = 0; int ret = 0; #ifdef _WIN32 ret = ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONREAD, (u_long*)&recv_size); #else ret = ioctl(stream->rtmp.m_sb.sb_socket, FIONREAD, &recv_size); #endif if (ret >= 0 && recv_size > 0) { if (!discard_recv_data(stream, (size_t)recv_size)) return -1; } flv_packet_mux(packet, &data, &size, is_header); #ifdef TEST_FRAMEDROPS os_sleep_ms(rand() % 40); #endif ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size, (int)idx); bfree(data); obs_free_encoder_packet(packet); stream->total_bytes_sent += size; return ret; }
static inline void free_packets(struct rtmp_stream *stream) { while (stream->packets.size) { struct encoder_packet packet; circlebuf_pop_front(&stream->packets, &packet, sizeof(packet)); obs_free_encoder_packet(&packet); } }
static void interleave_packets(void *data, struct encoder_packet *packet) { struct obs_output *output = data; struct encoder_packet out; bool was_started; if (!active(output)) return; if (packet->type == OBS_ENCODER_AUDIO) packet->track_idx = get_track_index(output, packet); pthread_mutex_lock(&output->interleaved_mutex); /* if first video frame is not a keyframe, discard until received */ if (!output->received_video && packet->type == OBS_ENCODER_VIDEO && !packet->keyframe) { discard_unused_audio_packets(output, packet->dts_usec); pthread_mutex_unlock(&output->interleaved_mutex); if (output->active_delay_ns) obs_free_encoder_packet(packet); return; } was_started = output->received_audio && output->received_video; if (output->active_delay_ns) out = *packet; else obs_duplicate_encoder_packet(&out, packet); if (was_started) apply_interleaved_packet_offset(output, &out); else check_received(output, packet); insert_interleaved_packet(output, &out); set_higher_ts(output, &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) { if (!was_started) { if (prune_interleaved_packets(output)) { if (initialize_interleaved_packets(output)) { resort_interleaved_packets(output); send_interleaved(output); } } } else { send_interleaved(output); } } pthread_mutex_unlock(&output->interleaved_mutex); }
static void *send_thread(void *data) { struct rtmp_stream *stream = data; os_set_thread_name("rtmp-stream: send_thread"); while (os_sem_wait(stream->send_sem) == 0) { struct encoder_packet packet; if (stopping(stream) && stream->stop_ts == 0) { break; } if (!get_next_packet(stream, &packet)) continue; if (stopping(stream)) { if (can_shutdown_stream(stream, &packet)) { obs_free_encoder_packet(&packet); break; } } if (!stream->sent_headers) { if (!send_headers(stream)) { os_atomic_set_bool(&stream->disconnected, true); break; } } if (send_packet(stream, &packet, false, packet.track_idx) < 0) { os_atomic_set_bool(&stream->disconnected, true); break; } } if (disconnected(stream)) { info("Disconnected from %s", stream->path.array); } else { info("User stopped the stream"); } RTMP_Close(&stream->rtmp); if (!stopping(stream)) { pthread_detach(stream->send_thread); obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); } else { obs_output_end_data_capture(stream->output); } free_packets(stream); os_event_reset(stream->stop_event); os_atomic_set_bool(&stream->active, false); stream->sent_headers = false; return NULL; }
static void discard_to_idx(struct obs_output *output, size_t idx) { for (size_t i = 0; i < idx; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; obs_free_encoder_packet(packet); } da_erase_range(output->interleaved_packets, 0, idx); }
static inline void send_interleaved(struct obs_output *output) { struct encoder_packet out = output->interleaved_packets.array[0]; /* do not send an interleaved packet if there's no packet of the * opposing type of a higher timstamp in the interleave buffer. * this ensures that the timestamps are monotonic */ if (!has_higher_opposing_ts(output, &out)) return; da_erase(output->interleaved_packets, 0); output->info.encoded_packet(output->context.data, &out); obs_free_encoder_packet(&out); }
static void default_encoded_callback(void *param, struct encoder_packet *packet) { struct obs_output *output = param; if (packet->type == OBS_ENCODER_AUDIO) packet->track_idx = get_track_index(output, packet); if (!output->stopped) output->info.encoded_packet(output->context.data, packet); if (output->active_delay_ns) obs_free_encoder_packet(packet); if (packet->type == OBS_ENCODER_VIDEO) output->total_frames++; }
static void drop_frames(struct rtmp_stream *stream, const char *name, int highest_priority, int64_t *p_min_dts_usec) { struct circlebuf new_buf = {0}; uint64_t last_drop_dts_usec = 0; int num_frames_dropped = 0; #ifdef _DEBUG int start_packets = (int)num_buffered_packets(stream); #else UNUSED_PARAMETER(name); #endif circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8); while (stream->packets.size) { struct encoder_packet packet; circlebuf_pop_front(&stream->packets, &packet, sizeof(packet)); last_drop_dts_usec = packet.dts_usec; /* do not drop audio data or video keyframes */ if (packet.type == OBS_ENCODER_AUDIO || packet.drop_priority >= highest_priority) { circlebuf_push_back(&new_buf, &packet, sizeof(packet)); } else { num_frames_dropped++; obs_free_encoder_packet(&packet); } } circlebuf_free(&stream->packets); stream->packets = new_buf; if (stream->min_priority < highest_priority) stream->min_priority = highest_priority; *p_min_dts_usec = last_drop_dts_usec; stream->dropped_frames += num_frames_dropped; #ifdef _DEBUG debug("Dropped %s, prev packet count: %d, new packet count: %d", name, start_packets, (int)num_buffered_packets(stream)); #endif }
static int write_packet(struct flv_output *stream, struct encoder_packet *packet, bool is_header) { uint8_t *data; size_t size; int ret = 0; stream->last_packet_ts = get_ms_time(packet, packet->dts); flv_packet_mux(packet, &data, &size, is_header); fwrite(data, 1, size, stream->file); bfree(data); obs_free_encoder_packet(packet); return ret; }
static void prune_interleaved_packets(struct obs_output *output) { size_t start_idx = 0; while (can_prune_interleaved_packet(output, start_idx)) start_idx++; if (start_idx) { for (size_t i = 0; i < start_idx; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; obs_free_encoder_packet(packet); } da_erase_range(output->interleaved_packets, 0, start_idx); } }
static inline void free_packets(struct rtmp_stream *stream) { size_t num_packets; pthread_mutex_lock(&stream->packets_mutex); num_packets = num_buffered_packets(stream); if (num_packets) info("Freeing %d remaining packets", (int)num_packets); while (stream->packets.size) { struct encoder_packet packet; circlebuf_pop_front(&stream->packets, &packet, sizeof(packet)); obs_free_encoder_packet(&packet); } pthread_mutex_unlock(&stream->packets_mutex); }
static int send_packet(struct rtmp_stream *stream, struct encoder_packet *packet, bool is_header) { uint8_t *data; size_t size; int ret = 0; flv_packet_mux(packet, &data, &size, is_header); #ifdef FILE_TEST fwrite(data, 1, size, stream->test); #else ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size); #endif bfree(data); obs_free_encoder_packet(packet); return ret; }
static void flv_output_data(void *data, struct encoder_packet *packet) { struct flv_output *stream = data; struct encoder_packet parsed_packet; if (!stream->sent_headers) { write_headers(stream); stream->sent_headers = true; } if (packet->type == OBS_ENCODER_VIDEO) { obs_parse_avc_packet(&parsed_packet, packet); write_packet(stream, &parsed_packet, false); obs_free_encoder_packet(&parsed_packet); } else { write_packet(stream, packet, false); } }
static int send_packet(struct rtmp_stream *stream, struct encoder_packet *packet, bool is_header, size_t idx) { uint8_t *data; size_t size; int ret = 0; flv_packet_mux(packet, &data, &size, is_header); #ifdef TEST_FRAMEDROPS os_sleep_ms(rand() % 40); #endif ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size, (int)idx); bfree(data); obs_free_encoder_packet(packet); stream->total_bytes_sent += size; return ret; }
static void drop_frames(struct rtmp_stream *stream) { struct circlebuf new_buf = {0}; int drop_priority = 0; uint64_t last_drop_dts_usec = 0; int num_frames_dropped = 0; debug("Previous packet count: %d", (int)num_buffered_packets(stream)); circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8); while (stream->packets.size) { struct encoder_packet packet; circlebuf_pop_front(&stream->packets, &packet, sizeof(packet)); last_drop_dts_usec = packet.dts_usec; /* do not drop audio data or video keyframes */ if (packet.type == OBS_ENCODER_AUDIO || packet.drop_priority == OBS_NAL_PRIORITY_HIGHEST) { circlebuf_push_back(&new_buf, &packet, sizeof(packet)); } else { if (drop_priority < packet.drop_priority) drop_priority = packet.drop_priority; num_frames_dropped++; obs_free_encoder_packet(&packet); } } circlebuf_free(&stream->packets); stream->packets = new_buf; stream->min_priority = drop_priority; stream->min_drop_dts_usec = last_drop_dts_usec; stream->dropped_frames += num_frames_dropped; debug("New packet count: %d", (int)num_buffered_packets(stream)); }
static void drop_frames(struct rtmp_stream *stream) { struct circlebuf new_buf = {0}; int drop_priority = 0; uint64_t last_drop_dts_usec = 0; blog(LOG_DEBUG, "Previous packet count: %d", (int)num_buffered_packets(stream)); circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8); while (stream->packets.size) { struct encoder_packet packet; circlebuf_pop_front(&stream->packets, &packet, sizeof(packet)); last_drop_dts_usec = packet.dts_usec; if (packet.type == OBS_ENCODER_AUDIO) { circlebuf_push_back(&new_buf, &packet, sizeof(packet)); } else { if (drop_priority < packet.drop_priority) drop_priority = packet.drop_priority; obs_free_encoder_packet(&packet); } } circlebuf_free(&stream->packets); stream->packets = new_buf; stream->min_priority = drop_priority; stream->min_drop_dts_usec = last_drop_dts_usec; blog(LOG_DEBUG, "New packet count: %d", (int)num_buffered_packets(stream)); }
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); da_free(output->interleaved_packets); }